解析Jdk与Cglib代理
一、JDK动态代理
1.1 JDK动态代理简介
JDK动态代理是Java语言提供的一种实现动态代理的方式,其基本原理是利用反射机制在运行时动态生成代理类和代理对象。
1.1.1 基本原理
- 接口定义:定义一个接口(或者是一组接口),用于描述需要被代理的行为。
- InvocationHandler接口:编写一个实现了
InvocationHandler接口的类,该类负责实际的代理逻辑。InvocationHandler接口只有一个方法invoke(Object proxy, Method method, Object[] args),当代理对象的方法被调用时,invoke方法会被调用,并在其中执行代理逻辑。 - Proxy类:使用
Proxy类的newProxyInstance方法动态地创建代理对象。newProxyInstance方法接受三个参数:ClassLoader、一个接口数组和一个InvocationHandler对象。在运行时,Proxy类会动态生成一个实现了指定接口的代理类,并通过传入的InvocationHandler对象来调用实际的代理逻辑。 - 代理对象调用:当调用代理对象的方法时,实际上是调用了InvocationHandler接口的invoke方法,该方法会根据被调用的方法和传入的参数执行相应的代理逻辑。
1.1.2 使用场景
日志记录:通过代理可以在方法执行前后记录日志,实现日志记录的功能。
性能监控:可以在方法执行前后记录方法的执行时间,从而进行性能监控。
事务管理:在方法执行前后开启和提交事务,实现事务管理的功能。
权限控制:在方法执行前进行权限验证,实现权限控制的功能。
远程调用:可以通过代理在调用远程对象的方法时添加网络通信的逻辑,实现远程调用的功能。
1.2 Spring 如何利用动态代理实现 AOP
Spring AOP的实现基于代理模式和装饰器模式,在目标方法执行前后或异常抛出时,通过代理对象来执行额外的逻辑,如日志记录、事务管理、权限控制等。通过配置切面和通知,可以将这些额外逻辑统一地应用到多个目标类的方法中,从而实现横切关注点的分离和复用。
在Spring AOP中,主要利用了JDK动态代理和CGLIB动态代理两种方式。
- JDK动态代理:
- 当被代理的目标对象实现了接口时,Spring会使用JDK动态代理。
- Spring AOP利用java.lang.reflect.Proxy类来创建代理对象,该类要求被代理的类必须实现至少一个接口。
- Spring在运行时动态生成了一个实现了相同接口的代理对象,代理对象中的方法会委托给InvocationHandler接口的实现类来执行增强逻辑。
- JDK动态代理的优势在于它不需要引入额外的库,但缺点是被代理的类必须实现接口。
- CGLIB动态代理:
- 当被代理的目标对象没有实现接口时,Spring会使用CGLIB动态代理。
- CGLIB是一个强大的,高性能的代码生成库,它通过在运行时生成字节码的方式来动态创建代理类。
- Spring AOP利用CGLIB来生成被代理对象的子类,并在子类中重写需要增强的方法,将增强逻辑织入到重写的方法中。
- CGLIB动态代理的优势在于它可以代理没有实现接口的类,但缺点是需要引入CGLIB库,并且生成的代理类会比较庞大。
1.3 JdkDynamicAopProxy 类
1.3.1 JdkDynamicAopProxy 类结构
JdkDynamicAopProxy类的实现做一些准备工作,包括声明变量、初始化变量、定义静态成员等。
//final 类不能被继承
final class JdkDynamicAopProxy implements org.springframework.aop.framework.AopProxy, InvocationHandler, Serializable {
/** use serialVersionUID from Spring 1.2 for interoperability. */
private static final long serialVersionUID = 5531744639992436476L;
private static final String COROUTINES_FLOW_CLASS_NAME = "kotlinx.coroutines.flow.Flow";
private static final boolean coroutinesReactorPresent = ClassUtils.isPresent(
"kotlinx.coroutines.reactor.MonoKt", JdkDynamicAopProxy.class.getClassLoader());
/** We use a static Log to avoid serialization issues. */
private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);
/** Config used to configure this proxy. 用于保存代理配置信息 */
private final org.springframework.aop.framework.AdvisedSupport advised;
/** Cached in {@link org.springframework.aop.framework.AdvisedSupport#proxyMetadataCache}.
* 保存被代理的接口
* */
private transient ProxiedInterfacesCache cache;
}
/**
* Holder for the complete proxied interfaces and derived metadata,
* to be cached in {@link org.springframework.aop.framework.AdvisedSupport#proxyMetadataCache}.
* @since 6.1.3
*/
private static final class ProxiedInterfacesCache {
//保存被代理的接口
final Class<?>[] proxiedInterfaces;
//表示被代理的接口是否定义类equals方法
final boolean equalsDefined;
final boolean hashCodeDefined;
}
1.3.2 getProxy 方法的实现
//重写父类的接口或者方法
@Override
public Object getProxy() {//使用默认类加载器
return getProxy(ClassUtils.getDefaultClassLoader());
}
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {//允许传入自定义类加载器
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
return Proxy.newProxyInstance(determineClassLoader(classLoader), this.cache.proxiedInterfaces, this);
}
1.3.3 determineClassLoader 方法的实现
/**
* Determine whether the JDK bootstrap or platform loader has been suggested ->
* use higher-level loader which can see Spring infrastructure classes instead.
* 确定最终使用的类加载器
*/
private ClassLoader determineClassLoader(@Nullable ClassLoader classLoader) {
if (classLoader == null) {
// JDK bootstrap loader -> use spring-aop ClassLoader instead.
//返回当前类的类加载器,通常是应用程序类加载器
return getClass().getClassLoader();
}
if (classLoader.getParent() == null) {
// Potentially the JDK platform loader on JDK 9+
//获取当前类的类加载器
ClassLoader aopClassLoader = getClass().getClassLoader();
//获取父类加载器
ClassLoader aopParent = aopClassLoader.getParent();
while (aopParent != null) {//找到最顶层父类加载器
if (classLoader == aopParent) {
// Suggested ClassLoader is ancestor of spring-aop ClassLoader
// -> use spring-aop ClassLoader itself instead.
//如果当前类加载器等于当前父类加载器,直接返回
return aopClassLoader;
}
aopParent = aopParent.getParent();
}
}
// Regular case: use suggested ClassLoader as-is.
return classLoader;
}
1.3.4 newProxyInstance 方法的实现
主要用于创建代理实例,其中包含了一些安全性检查和异常处理。
//传入类加载器、代理接口数组、调用处理程序
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//检查调用处理程序是否为null
Objects.requireNonNull(h);
//复制接口数组,确保不会修改原始数组
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
//安全检查
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
* 生存代理对象
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//获取代理类构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//返回代理对象
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
1.3.5 InvocationHandler 接口
InvocationHandler接口定义了一个用于处理代理对象方法调用的统一入口,当代理对象的方法被调用时,会触发invoke方法的执行,通过实现invoke方法来定义代理对象方法调用时的行为,例如添加日志、实现权限控制等。
public interface InvocationHandler {
//传入代理对象,被调用的方法、方法 参数
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
在 invoke 方法中,会根据方法名和参数,调用对应的拦截器。
```java
/**
* Implementation of {@code InvocationHandler.invoke}.
* <p>Callers will see exactly the exception thrown by the target,
* unless a hook method throws an exception.
*/
@Override
public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
if (!this.cache.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
else if (!this.cache.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return org.springframework.aop.framework.AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(org.springframework.aop.framework.Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = org.springframework.aop.framework.AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fall back on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
@Nullable Object[] argsToUse = org.springframework.aop.framework.AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
MethodInvocation invocation =
new org.springframework.aop.framework.ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != void.class && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
if (coroutinesReactorPresent && KotlinDetector.isSuspendingFunction(method)) {
return COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName()) ?
org.springframework.aop.framework.CoroutinesUtils.asFlow(retVal) : org.springframework.aop.framework.CoroutinesUtils.awaitSingleOrNull(retVal, args[args.length - 1]);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
org.springframework.aop.framework.AopContext.setCurrentProxy(oldProxy);
}
}
}
1.3 实践与应用
1.3.1 编写自定义的 AOP 拦截器步骤
- 编写自定义拦截器:
- 创建一个类,实现 Spring 的 MethodInterceptor 接口,该接口定义了拦截器的核心方法 invoke。
- 在 invoke 方法中编写自定义的拦截逻辑,比如在目标方法执行前后执行一些操作,或者替换目标方法的执行等。
public class CustomInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 在目标方法执行前输出日志
System.out.println("Before invoking method: " + invocation.getMethod().getName());
// 执行目标方法
Object result = invocation.proceed();
// 在目标方法执行后输出日志
System.out.println("After invoking method: " + invocation.getMethod().getName());
return result;
}
}
创建一个注解 CustomAnnotation,用来标记需要被拦截的方法。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
}
修改 UserService 和 UserServiceImpl,在需要拦截的方法上添加 CustomAnnotation 注解。
public interface UserService {
@CustomAnnotation
void addUser(String username);
}
public class UserServiceImpl implements UserService {
@Override
@CustomAnnotation
public void addUser(String username) {
System.out.println("User added: " + username);
}
}
- 配置拦截器:
- 使用 Spring 的配置方式(XML、Java Config、注解)将自定义的拦截器配置到 Spring 容器中。
- 将拦截器与目标 bean 关联起来,可以通过切点表达式或其他方式指定在哪些方法上应用拦截器。
使用 Spring 的 Java Config 来配置拦截器和切面。
@Configuration
public class AppConfig {
/**
* 返回一个 UserService 实例
*/
@Bean
public UserService userService() {
return new UserServiceImpl();
}
/**
* 返回一个 CustomInterceptor 的实例,即自定义的拦截器
*/
@Bean
public CustomInterceptor customInterceptor() {
return new CustomInterceptor();
}
/**
* 返回一个 DefaultAdvisorAutoProxyCreator 实例,负责自动代理被 @AspectJ 注解标记的 Bean
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
return new DefaultAdvisorAutoProxyCreator();
}
/**
* 返回一个 DefaultPointcutAdvisor 实例,将拦截器和切点绑定在一起
*/
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor() {
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
// 将自定义的拦截器设置为 Advisor 的 advice,即在目标方法执行前后所执行的逻辑
advisor.setAdvice(customInterceptor());
// 设置切点,即确定在哪些方法上应用拦截器的条件
advisor.setPointcut(annotationMatchingPointcut());
return advisor;
}
/**
* 返回一个 AnnotationMatchingPointcut 实例,切点用于匹配带有 CustomAnnotation 注解的方法
*/
@Bean
public AnnotationMatchingPointcut annotationMatchingPointcut() {
return AnnotationMatchingPointcut.forMethodAnnotation(CustomAnnotation.class);
}
}
- 测试
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
// 在执行 addUser 方法之前,拦截器执行了自定义的前置逻辑,并在方法执行完毕后执行了自定义的后置逻辑
userService.addUser("Alice");
}
}
// 输出结果
Before invoking method: addUser
User added: Alice
After invoking method: addUser
- 调试和优化
二、Cglib代理
2.1 CGLIB 代理简介
CGLIB是一个强大的、高性能的代码生成库。它被广泛应用于AOP(面向切面编程)、ORM(对象关系映射)和其他一些框架中。
2.1.1 CGLIB代理的基本原理
- 创建代理类:CGLIB通过
ASM字节码操作框架,在运行时动态生成目标类的子类。这个子类会继承自目标类。 - 方法拦截:在生成的子类中,会覆盖所有非final的方法。覆盖的方法会委托给一个用户定义的拦截器(MethodInterceptor),拦截器中包含了增强的代码。
调用流程:当调用代理类的方法时,实际上是在调用被覆盖的方法。这些方法内部会调用拦截器,拦截器再去调用原始类的相应方法。
2.1.2 CGLIB代理的特点
- 无需接口:CGLIB代理不需要目标类实现任何接口,因为它是通过继承的方式来实现代理的。
- 性能:CGLIB生成的代理类是目标类的子类,相比于JDK动态代理(接口代理),CGLIB代理通常有更好的性能,因为它直接调用父类的方法,减少了反射调用的开销。
- 灵活性:由于CGLIB代理是通过继承实现的,它无法代理final类和方法。但是,它提供了比JDK代理更高的灵活性,因为它可以代理任何类,而不受接口限制。
- 复杂性:CGLIB代理的实现比JDK动态代理复杂,因为它涉及到字节码生成和类加载机制。
- 兼容性:CGLIB代理通常与Spring框架结合使用,Spring AOP默认使用JDK动态代理,但如果目标对象没有实现接口,Spring AOP会自动切换到CGLIB代理。
2.2 分析 CGLIB 如何通过字节码技术创建代理类
CGLIB通过操纵字节码,创建出目标类的子类,并在子类中覆盖非final的方法,从而实现方法拦截和增强。
2.2.1 CGLIB创建代理类的基本步骤
- 确定目标类:首先要确定需要被代理的目标类。CGLIB代理不需要目标类实现任何接口,因为它是通过继承的方式来实现代理的。
- 创建
Enhancer对象:Enhancer是CGLIB中的一个核心类,用于创建代理类。首先创建一个Enhancer实例,并设置其父类(即目标类)。 - 设置
Callback:Callback是一个接口,用于定义代理类中覆盖方法的逻辑。通常使用MethodInterceptor接口,它允许我们在调用原始方法之前和之后插入自定义代码。将实现的Callback对象设置给Enhancer。 - 创建代理类:调用Enhancer的
create()方法,CGLIB会使用ASM字节码操作框架来动态生成一个继承自目标类的子类。这个子类会覆盖所有非final的方法,并将调用委托给Callback对象。 - 使用代理类:create()方法返回的是一个代理类的实例,这个实例可以被当作目标类的实例来使用。当调用代理类的方法时,实际上会调用MethodInterceptor中的
intercept()方法。 - 方法调用流程:在intercept()方法中,可以调用Method对象的invoke()方法来执行原始方法。这样,我们就可以在原始方法执行前后插入自定义的逻辑,实现方法的拦截和增强。
2.2.2 深入分析 CglibAopProxy 类的结构
CglibAopProxy 类结构
- 成员变量:
AdvisedSupport advised:存储了AOP配置信息的数据结构,如目标对象、切面等。
Callback callback:CGLIB 回调对象,负责实现代理逻辑。 - 构造方法:
CglibAopProxy(AdvisedSupport config):构造方法接收一个 AdvisedSupport 参数,用于设置AOP配置信息。 - 核心方法:
getProxy(ClassLoader classLoader):生成代理对象的核心方法,接收一个 ClassLoader 参数用于加载代理类。
createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks):使用 CGLIB 的 Enhancer 创建代理类,并返回代理对象的实例。
proxy(ClassLoader classLoader, Callback[] callbacks):创建代理类并生成代理对象的实现逻辑, 使用 Enhancer 创建代理类,并指定 Callback 对象,完成代理类的生成和实例化。
createEnhancer():创建 Enhancer 对象,用于生成代理类, Enhancer 是 CGLIB 中负责生成代理类的核心类。
CglibAopProxy 类源码
class CglibAopProxy implements org.springframework.aop.framework.AopProxy, Serializable {
// Constants for CGLIB callback array indices
//表示aop代理类型
private static final int AOP_PROXY = 0;
//表示直接调用目标对象
private static final int INVOKE_TARGET = 1;
//表示不覆盖方法
private static final int NO_OVERRIDE = 2;
//表示分派到目标对象
private static final int DISPATCH_TARGET = 3;
//表示分派到通知器
private static final int DISPATCH_ADVISED = 4;
//表示调用equals方法
private static final int INVOKE_EQUALS = 5;
//表示调用hashCode方法
private static final int INVOKE_HASHCODE = 6;
private static final String COROUTINES_FLOW_CLASS_NAME = "kotlinx.coroutines.flow.Flow";
private static final boolean coroutinesReactorPresent = ClassUtils.isPresent(
"kotlinx.coroutines.reactor.MonoKt", CglibAopProxy.class.getClassLoader());
private static final GeneratorStrategy undeclaredThrowableStrategy =
new UndeclaredThrowableStrategy(UndeclaredThrowableException.class);
/** Logger available to subclasses; static to optimize serialization. */
protected static final Log logger = LogFactory.getLog(CglibAopProxy.class);
/** Keeps track of the Classes that we have validated for final methods. */
private static final Map<Class<?>, Boolean> validatedClasses = new WeakHashMap<>();
//用于存储aop的配置信息,如目标对象、切面等
/** The configuration used to configure this proxy. */
protected final org.springframework.aop.framework.AdvisedSupport advised;
//对象数组,用于存储构造器参数
protected Object @Nullable [] constructorArgs;
//类对象数组,用于存储构造器参数类型
protected Class<?> @Nullable [] constructorArgTypes;
/** Dispatcher used for methods on Advised. */
//用于分派通知器
private final transient AdvisedDispatcher advisedDispatcher;
private transient Map<Method, Integer> fixedInterceptorMap = Collections.emptyMap();
private transient int fixedInterceptorOffset;
/**
* Create a new CglibAopProxy for the given AOP configuration.
* @param config the AOP configuration as AdvisedSupport object
* @throws org.springframework.aop.framework.AopConfigException if the config is invalid. We try to throw an informative
* exception in this case, rather than let a mysterious failure happen later.
*/
public CglibAopProxy(org.springframework.aop.framework.AdvisedSupport config) throws org.springframework.aop.framework.AopConfigException {
Assert.notNull(config, "AdvisedSupport must not be null");
this.advised = config;
this.advisedDispatcher = new AdvisedDispatcher(this.advised);
}
/**
* Set constructor arguments to use for creating the proxy.
* @param constructorArgs the constructor argument values
* @param constructorArgTypes the constructor argument types
*/
public void setConstructorArguments(Object @Nullable [] constructorArgs, Class<?> @Nullable [] constructorArgTypes) {
if (constructorArgs == null || constructorArgTypes == null) {
throw new IllegalArgumentException("Both 'constructorArgs' and 'constructorArgTypes' need to be specified");
}
if (constructorArgs.length != constructorArgTypes.length) {
throw new IllegalArgumentException("Number of 'constructorArgs' (" + constructorArgs.length +
") must match number of 'constructorArgTypes' (" + constructorArgTypes.length + ")");
}
this.constructorArgs = constructorArgs;
this.constructorArgTypes = constructorArgTypes;
}
}
2.2.3 CGLIB 代理对象的创建过程
代理对象的创建过程: 检查是否可以使用缓存的代理对象 -> 准备 CGLIB Enhancer -> 配置 Enhancer -> 设置回调处理器(Callback) -> 生成代理类字节码 -> 创建代理对象实例 -> 将代理对象缓存起来
createHelper() 方法用来实际创建代理对象。
private Object createHelper() {
preValidate();
// SPRING PATCH BEGIN
Object key = new EnhancerKey((superclass != null ? superclass.getName() : null),
(interfaces != null ? Arrays.asList(ReflectUtils.getNames(interfaces)) : null),
(filter == ALL_ZERO ? null : new WeakCacheKey<>(filter)),
Arrays.asList(callbackTypes),
useFactory,
interceptDuringConstruction,
serialVersionUID);
// SPRING PATCH END
this.currentKey = key;
//创建代理对象
Object result = super.create(key);
return result;
}
AbstractClassGenerator 对象通过调用 create() 方法,根据给定的键值(key)创建对象实例。
protected Object create(Object key) {
try {
//获取当前类加载器
ClassLoader loader = getClassLoader();
Map<ClassLoader, ClassLoaderData> cache = CACHE;
ClassLoaderData data = cache.get(loader);
if (data == null) {
synchronized (AbstractClassGenerator.class) {
cache = CACHE;
data = cache.get(loader);
if (data == null) {
Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<>(cache);
data = new ClassLoaderData(loader);
newCache.put(loader, data);
CACHE = newCache;
}
}
}
this.key = key;
Object obj = data.get(this, getUseCache());
if (obj instanceof Class<?> clazz) {
//创建第一个实例
return firstInstance(clazz);
}
return nextInstance(obj);
}
catch (RuntimeException | Error ex) {
throw ex;
}
catch (Exception ex) {
throw new CodeGenerationException(ex);
}
}
2.2.4 实践与应用
编写自定义的 CGLIB 拦截器
假设有一个简单的服务类 UserService,其中包含一些方法,希望能够在调用这些方法之前和之后记录日志。使用CGLIB来实现一个拦截器,记录方法调用的开始和结束时间。
1.服务类 UserService,模拟创建和更新用户信息。
public class UserService {
public void createUser(String username) {
System.out.println("Creating user: " + username);
// 模拟创建用户的逻辑
}
public void updateUser(String username) {
System.out.println("Updating user: " + username);
// 模拟更新用户的逻辑
}
}
2.自定义的CGLIB拦截器,用于记录方法调用的开始和结束时间。
/**
* 创建 LoggingInterceptor 类,实现 MethodInterceptor 接口
*/
public class LoggingInterceptor implements MethodInterceptor {
/**
* 参数:obj 是被代理的对象实例
* method 是被调用的方法对象
* args 是方法的参数数组
* proxy 是用于调用父类(被代理类)方法的代理对象
*/
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 获取方法调用开始时的时间戳
long startTime = System.currentTimeMillis();
System.out.println("Method " + method.getName() + " start at: " + startTime);
// 调用被代理类的原始方法,而不是代理对象的方法,以避免循环调用
Object result = proxy.invokeSuper(obj, args);
// 获取方法调用结束时的时间戳
long endTime = System.currentTimeMillis();
System.out.println("Method " + method.getName() + " end at: " + endTime);
// 方法执行所花费的时间
System.out.println("Method " + method.getName() + " execution time: " + (endTime - startTime) + " milliseconds");
// 调用原始方法后的返回值
return result;
}
public static void main(String[] args) {
UserService userService = new UserService();
// 使用CGLIB的 Enhancer 类创建了 UserService 类的代理对象,并将拦截器设置为回调方法
Enhancer enhancer = new Enhancer();
// 设置了要代理的目标类是 UserService
enhancer.setSuperclass(UserService.class);
// 指定了在方法调用时应该执行的拦截逻辑
enhancer.setCallback(new LoggingInterceptor());
// 创建代理对象,将会在方法调用时执行我们定义的拦截逻辑
UserService userServiceProxy = (UserService) enhancer.create();
// 调用代理对象的 createUser 和 updateUser 方法来触发拦截器的拦截逻辑
userServiceProxy.createUser("John Doe");
userServiceProxy.updateUser("Jane Smith");
}
}
//输出结果:
Method createUser start at: 1621802728000
Creating user: John Doe
Method createUser end at: 1621802728000
Method createUser execution time: 0 milliseconds
Method updateUser start at: 1621802728000
Updating user: Jane Smith
Method updateUser end at: 1621802728000
Method updateUser execution time: 0 milliseconds
2.2.5 实现对非接口类的代理和增强功能
实现对非接口类的代理和增强功能通常使用 Spring AOP来实现,提供了一种便捷的方式来在方法执行前、执行后、方法抛出异常时等时机插入特定逻辑,而无需修改原始类的代码。
假设有一个订单管理系统,其中包含一个 OrderService 类,该类负责处理订单相关的业务逻辑,比如创建订单、更新订单状态等。希望在处理订单相关业务时,记录日志并统计方法执行时间。
- 切面类 OrderAspect。
@Aspect
@Component
public class OrderAspect {
/**
* 切面方法,用于实现切面的逻辑 -> 表示正在执行目标方法之前
* 接受一个 JoinPoint 参数,连接点 -> 被增强的目标方法
*/
@Before("execution(* com.example.service.OrderService.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before executing method: " + joinPoint.getSignature());
}
/**
* 切面方法,用于实现切面的逻辑 -> 表示目标方法执行完成后
* 接受一个 JoinPoint 参数,连接点 -> 被增强的目标方法
*/
@After("execution(* com.example.service.OrderService.*(..))")
public void logAfter(JoinPoint joinPoint) {
System.out.println("After executing method: " + joinPoint.getSignature());
}
}
-
配置类中启用 Spring AOP 功能。
-
OrderService 类
/**
* 调用 OrderService 类的 createOrder() 或 updateOrderStatus() 方法时,OrderAspect 切面中定义的增强逻辑会在方法执行前后生效,从而实现了对非接口类的代理和增强功能
*/
@Service
public class OrderService {
public void createOrder() {
// 模拟创建订单的业务逻辑
System.out.println("Creating order...");
}
public void updateOrderStatus() {
// 模拟更新订单状态的业务逻辑
System.out.println("Updating order status...");
}
}
// 输出结果:
Before executing method: public void com.example.service.OrderService.createOrder()
Creating order...
After executing method: public void com.example.service.OrderService.createOrder()
Before executing method: public void com.example.service.OrderService.updateOrderStatus()
Updating order status...
After executing method: public void com.example.service.OrderService.updateOrderStatus()
一条小咸鱼