解析Jdk与Cglib代理

一、JDK动态代理

1.1 JDK动态代理简介

JDK动态代理是Java语言提供的一种实现动态代理的方式,其基本原理是利用反射机制在运行时动态生成代理类和代理对象。

1.1.1 基本原理

  1. 接口定义:定义一个接口(或者是一组接口),用于描述需要被代理的行为。
  2. InvocationHandler接口:编写一个实现了InvocationHandler接口的类,该类负责实际的代理逻辑。InvocationHandler接口只有一个方法invoke(Object proxy, Method method, Object[] args),当代理对象的方法被调用时,invoke方法会被调用,并在其中执行代理逻辑。
  3. Proxy类:使用Proxy类的newProxyInstance方法动态地创建代理对象。newProxyInstance方法接受三个参数:ClassLoader、一个接口数组和一个InvocationHandler对象。在运行时,Proxy类会动态生成一个实现了指定接口的代理类,并通过传入的InvocationHandler对象来调用实际的代理逻辑。
  4. 代理对象调用:当调用代理对象的方法时,实际上是调用了InvocationHandler接口的invoke方法,该方法会根据被调用的方法和传入的参数执行相应的代理逻辑。

1.1.2 使用场景

日志记录:通过代理可以在方法执行前后记录日志,实现日志记录的功能。
性能监控:可以在方法执行前后记录方法的执行时间,从而进行性能监控。
事务管理:在方法执行前后开启和提交事务,实现事务管理的功能。
权限控制:在方法执行前进行权限验证,实现权限控制的功能。
远程调用:可以通过代理在调用远程对象的方法时添加网络通信的逻辑,实现远程调用的功能。

1.2 Spring 如何利用动态代理实现 AOP

Spring AOP的实现基于代理模式装饰器模式,在目标方法执行前后或异常抛出时,通过代理对象来执行额外的逻辑,如日志记录、事务管理、权限控制等。通过配置切面通知,可以将这些额外逻辑统一地应用到多个目标类的方法中,从而实现横切关注点的分离和复用。

在Spring AOP中,主要利用了JDK动态代理和CGLIB动态代理两种方式。

  1. JDK动态代理:
  • 当被代理的目标对象实现了接口时,Spring会使用JDK动态代理。
  • Spring AOP利用java.lang.reflect.Proxy类来创建代理对象,该类要求被代理的类必须实现至少一个接口。
  • Spring在运行时动态生成了一个实现了相同接口的代理对象,代理对象中的方法会委托给InvocationHandler接口的实现类来执行增强逻辑。
  • JDK动态代理的优势在于它不需要引入额外的库,但缺点是被代理的类必须实现接口。
  1. 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 拦截器步骤

  1. 编写自定义拦截器:
  • 创建一个类,实现 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);
    }
}
  1. 配置拦截器:
  • 使用 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);
    }
}
  1. 测试
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
  1. 调试和优化

二、Cglib代理

2.1 CGLIB 代理简介

CGLIB是一个强大的、高性能的代码生成库。它被广泛应用于AOP(面向切面编程)、ORM(对象关系映射)和其他一些框架中。

2.1.1 CGLIB代理的基本原理

  1. 创建代理类:CGLIB通过ASM字节码操作框架,在运行时动态生成目标类的子类。这个子类会继承自目标类。
  2. 方法拦截:在生成的子类中,会覆盖所有非final的方法。覆盖的方法会委托给一个用户定义的拦截器(MethodInterceptor),拦截器中包含了增强的代码。
    调用流程:当调用代理类的方法时,实际上是在调用被覆盖的方法。这些方法内部会调用拦截器,拦截器再去调用原始类的相应方法。

2.1.2 CGLIB代理的特点

  1. 无需接口:CGLIB代理不需要目标类实现任何接口,因为它是通过继承的方式来实现代理的。
  2. 性能:CGLIB生成的代理类是目标类的子类,相比于JDK动态代理(接口代理),CGLIB代理通常有更好的性能,因为它直接调用父类的方法,减少了反射调用的开销。
  3. 灵活性:由于CGLIB代理是通过继承实现的,它无法代理final类和方法。但是,它提供了比JDK代理更高的灵活性,因为它可以代理任何类,而不受接口限制。
  4. 复杂性:CGLIB代理的实现比JDK动态代理复杂,因为它涉及到字节码生成和类加载机制。
  5. 兼容性:CGLIB代理通常与Spring框架结合使用,Spring AOP默认使用JDK动态代理,但如果目标对象没有实现接口,Spring AOP会自动切换到CGLIB代理。

2.2 分析 CGLIB 如何通过字节码技术创建代理类

CGLIB通过操纵字节码,创建出目标类的子类,并在子类中覆盖非final的方法,从而实现方法拦截和增强。

2.2.1 CGLIB创建代理类的基本步骤

  1. 确定目标类:首先要确定需要被代理的目标类。CGLIB代理不需要目标类实现任何接口,因为它是通过继承的方式来实现代理的。
  2. 创建Enhancer对象:Enhancer是CGLIB中的一个核心类,用于创建代理类。首先创建一个Enhancer实例,并设置其父类(即目标类)。
  3. 设置Callback:Callback是一个接口,用于定义代理类中覆盖方法的逻辑。通常使用MethodInterceptor接口,它允许我们在调用原始方法之前和之后插入自定义代码。将实现的Callback对象设置给Enhancer。
  4. 创建代理类:调用Enhancer的create()方法,CGLIB会使用ASM字节码操作框架来动态生成一个继承自目标类的子类。这个子类会覆盖所有非final的方法,并将调用委托给Callback对象。
  5. 使用代理类:create()方法返回的是一个代理类的实例,这个实例可以被当作目标类的实例来使用。当调用代理类的方法时,实际上会调用MethodInterceptor中的intercept()方法。
  6. 方法调用流程:在intercept()方法中,可以调用Method对象的invoke()方法来执行原始方法。这样,我们就可以在原始方法执行前后插入自定义的逻辑,实现方法的拦截和增强。

2.2.2 深入分析 CglibAopProxy 类的结构

CglibAopProxy 类结构

  1. 成员变量:
    AdvisedSupport advised:存储了AOP配置信息的数据结构,如目标对象、切面等。
    Callback callback:CGLIB 回调对象,负责实现代理逻辑。
  2. 构造方法:
    CglibAopProxy(AdvisedSupport config):构造方法接收一个 AdvisedSupport 参数,用于设置AOP配置信息。
  3. 核心方法:
    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 类,该类负责处理订单相关的业务逻辑,比如创建订单、更新订单状态等。希望在处理订单相关业务时,记录日志并统计方法执行时间。

  1. 切面类 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());
    }
}
  1. 配置类中启用 Spring AOP 功能。

  2. 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()

一条小咸鱼