Spring源码学习(3) —— CglibAopProxy实现AOP

前面我们解读了jkd动态代理方式实现aop的源码,今天我们重点来看下cglib方式实现aop的原理。与JdkDynamicAopProxy一样,CglibAopProxy实现了AopProxy接口,主要是需要用到其getProxy方法来生成代理对象。

1.生成代理对象

getProxy()方法核心逻辑如下:

 

public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
        }

        try {
            Class<?> rootClass = this.advised.getTargetClass();
            Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

            Class<?> proxySuperClass = rootClass;
            // 这里判断rootClass是否是Cglib代理所产生的类(内部判断rootClass的className是否包含$$)
            if (ClassUtils.isCglibProxyClass(rootClass)) {
                proxySuperClass = rootClass.getSuperclass();
                Class<?>[] additionalInterfaces = rootClass.getInterfaces();
                for (Class<?> additionalInterface : additionalInterfaces) {
                    this.advised.addInterface(additionalInterface);
                }
            }

            // 方法校验,final方法不能被代理,记录日志
            validateClassIfNecessary(proxySuperClass, classLoader);

            // 配置Enhancer属性,这里ClassLoader需要再看下
            Enhancer enhancer = createEnhancer();
            if (classLoader != null) {
                enhancer.setClassLoader(classLoader);
                if (classLoader instanceof SmartClassLoader &&
                        ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                    enhancer.setUseCache(false);
                }
            }
            enhancer.setSuperclass(proxySuperClass);
            // 这里为啥要设置接口???
            enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
            enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
            enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class));
            // 通过callbacks设置拦截器链
            Callback[] callbacks = getCallbacks(rootClass);
            Class<?>[] types = new Class<?>[callbacks.length];
            for (int x = 0; x < types.length; x++) {
                types[x] = callbacks[x].getClass();
            }
            // 设置回调过滤器,修正后的拦截器map和偏移量在这里传进去
            enhancer.setCallbackFilter(new ProxyCallbackFilter(
                    this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
            enhancer.setCallbackTypes(types);

            // 生成代理类实例
            return createProxyClassAndInstance(enhancer, callbacks);
        }
        catch (CodeGenerationException ex) {
            throw new AopConfigException("Could not generate CGLIB subclass of class [" +
                    this.advised.getTargetClass() + "]: " +
                    "Common causes of this problem include using a final class or a non-visible class",
                    ex);
        }
        catch (IllegalArgumentException ex) {
            throw new AopConfigException("Could not generate CGLIB subclass of class [" +
                    this.advised.getTargetClass() + "]: " +
                    "Common causes of this problem include using a final class or a non-visible class",
                    ex);
        }
        catch (Exception ex) {
            // TargetSource.getTarget() failed
            throw new AopConfigException("Unexpected AOP exception", ex);
        }
    }

在创建代理对象的过程中需要对方法的修饰符进行校验,对不能进行代理的方法,也只是打印了日志,并无特殊处理。Modifier类值得一看,方法的各种修饰符是通过一个16进制常量表示的,判断某个方法的修饰符属性时可以通过位运算来得到。

 

    private void doValidateClass(Class<?> proxySuperClass, ClassLoader proxyClassLoader) {
        if (Object.class != proxySuperClass) {
            Method[] methods = proxySuperClass.getDeclaredMethods();
            for (Method method : methods) {
                int mod = method.getModifiers();
                // 静态方法直接跳过
                if (!Modifier.isStatic(mod)) {
                    // final方法不能被代理
                    if (Modifier.isFinal(mod)) {
                        logger.info("Unable to proxy method [" + method + "] because it is final: " +
                                "All calls to this method via a proxy will NOT be routed to the target instance.");
                    }
                    // 目标方法方位修饰符为default(意味着该方法仅包内可见),且目标类的类加载器与默认类加载器不相等
                    else if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod) && !Modifier.isPrivate(mod) &&
                            proxyClassLoader != null && proxySuperClass.getClassLoader() != proxyClassLoader) {
                        logger.info("Unable to proxy method [" + method + "] because it is package-visible " +
                                "across different ClassLoaders: All calls to this method via a proxy will " +
                                "NOT be routed to the target instance.");
                    }
                }
            }
            doValidateClass(proxySuperClass.getSuperclass(), proxyClassLoader);
        }
    }

默认类加载器通过ClassUtils.getDefaultClassLoader()获得:

 

public static ClassLoader getDefaultClassLoader() {
    ClassLoader cl = null;
    try {
        // 优先获取线程上下文类加载器
        cl = Thread.currentThread().getContextClassLoader();
    }
    catch (Throwable ex) {
        // Cannot access thread context ClassLoader - falling back...
    }
    if (cl == null) {
        // 线程上下文类加载器为空,则获取加载ClassUtils的类加载器
        cl = ClassUtils.class.getClassLoader();
        if (cl == null) {
            // getClassLoader() 为空则意味着是启动类加载器
            try {
                cl = ClassLoader.getSystemClassLoader();
            }
            catch (Throwable ex) {
                // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
            }
        }
    }
    return cl;
}

生成代理对象过程中,最重要的一步就是如何产生回调数组callbacks了,spring在这里考虑了很多种情况,将所有callback放到一个数组中。而在某些特定场景下,spring还对回调进行了优化,生成新的回调函数,追加到callbacks数组中。

 

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
        // Parameters used for optimisation choices...
        boolean exposeProxy = this.advised.isExposeProxy();
        boolean isFrozen = this.advised.isFrozen();
        boolean isStatic = this.advised.getTargetSource().isStatic();

        // 我们最常用的就是这个拦截器DynamicAdvisedInterceptor
        Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

        // Choose a "straight to target" interceptor. (used for calls that are
        // unadvised but can return this). May be required to expose the proxy.
        Callback targetInterceptor;
        if (exposeProxy) {
            targetInterceptor = isStatic ?
                    new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
                    new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
        }
        else {
            targetInterceptor = isStatic ?
                    new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
                    new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
        }

        // Choose a "direct to target" dispatcher (used for
        // unadvised calls to static targets that cannot return this).
        Callback targetDispatcher = isStatic ?
                new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();

        // 原始的7种callback
        Callback[] mainCallbacks = new Callback[]{
            aopInterceptor, // for normal advice
            targetInterceptor, // invoke target without considering advice, if optimized
            new SerializableNoOp(), // no override for methods mapped to this
            targetDispatcher, this.advisedDispatcher,
            new EqualsInterceptor(this.advised),
            new HashCodeInterceptor(this.advised)
        };

        Callback[] callbacks;

        // 如果目标类是不可变的(getTarget()方法返回的对象是否一直是同一个,也就是说对单例对象而言,isStatic都返回true),
        // 且代理对象被冻结(isFrozen属性设置为true),那么spring将会采取优化,在callbacks数组中增加了新的回调,
        // 使用修正后的增强链把aop调用直接转发到目标方法来进行一些优化,也就是说此时代理将不生效
        if (isStatic && isFrozen) {
            Method[] methods = rootClass.getMethods();
            Callback[] fixedCallbacks = new Callback[methods.length];
            this.fixedInterceptorMap = new HashMap<String, Integer>(methods.length);

            // TODO: small memory optimisation here (can skip creation for methods with no advice)
            for (int x = 0; x < methods.length; x++) {
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
                fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
                        chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
                this.fixedInterceptorMap.put(methods[x].toString(), x);
            }

            // 把默认的7种callback和修正后的callback合并到一个数组中返回
            callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
            System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
            System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
            this.fixedInterceptorOffset = mainCallbacks.length;
        }
        else {
            callbacks = mainCallbacks;
        }
        return callbacks;
    }

2.拦截器

我们主要来看看DynamicAdvisedInterceptor,这种callback与jdk动态代理的实现方式很类似。其intercept()方法如下:

 

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;
    Class<?> targetClass = null;
    Object target = null;
    try {
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
        // May be null. Get as late as possible to minimize the time we
        // "own" the target, in case it comes from a pool...
        target = getTarget();
        if (target != null) {
            targetClass = target.getClass();
        }
        // 这里获取拦截器链的方式与jdk动态代理方式调用了同一个方法
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        Object retVal;

        // 拦截器链为空,这里为啥还要求必须是公有方法???
        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
            // 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.
            retVal = methodProxy.invoke(target, args);
        }
        else {
            // CglibMethodInvocation继承自ReflectiveMethodInvocation,没有重写proceed()方法,因此获取代理对象后的执行逻辑与jdk动态代理方式完全一样
            retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
        }
        retVal = processReturnType(proxy, target, method, retVal);
        return retVal;
    }
    finally {
        if (target != null) {
            releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

3.Enhancer

接下来具体了解一下Enhancer, 它有两个重要属性:

 

private CallbackFilter filter;
private Callback[] callbacks;

callback是一个空接口,只是作为回调入口,有了它,我们可以对被代理类的不同方法采用不同的回调,这是jdk动态代理方式所不具备的,它有7个子接口:

 

/** 
 * All callback interfaces used by {@link Enhancer} extend this interface. 
 * @see MethodInterceptor  满足了所有的代理需求,但对于某些特定场景它可能使用起来不太方便。
 * @see NoOp 什么操作也不做,代理类直接调用被代理的方法不进行拦截
 * @see LazyLoader 适用于被代理对象需要懒加载的场景
 * @see Dispatcher 每次调用都会重新加载被代理的对象
 * @see ProxyRefDispathcher 与Dispatcher类似,但是它支持传入代理对象
 * @see InvocationHandler 
 * @see FixedValue 锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值
 */ 
public interface Callback {
}

image.png

 

而CallbackFilter作为回调过滤器,其核心方法为accept(),返回值为int类型,代表了回调入口在callbacks数组中的位置。假设目标类只有一个方法,如下图所示:

 

public class Target {

    public void exeTarget() {
        System.out.println("execute target.");
    }
}

此时我们得到的callbacks数组如下:

 

回调数组.png

Enhancer对象中对应的对调类型如下:

 

enhancer部分属性.png

可以看到,由于不满足优化条件,回调过滤器中的拦截器map为空,对应的偏移量也为0.
accept()方法核心逻辑如下:

 

// Constants for CGLIB callback array indices
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;
private static final int INVOKE_EQUALS = 5;
private static final int INVOKE_HASHCODE = 6;

@Override
public int accept(Method method) {
    // finalize()方法,直接调用目标类方法,不对其进行增强
    if (AopUtils.isFinalizeMethod(method)) {
        logger.debug("Found finalize() method - using NO_OVERRIDE");
        return NO_OVERRIDE;
    }
    if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() &&
            method.getDeclaringClass().isAssignableFrom(Advised.class)) {
        if (logger.isDebugEnabled()) {
            logger.debug("Method is declared on Advised interface: " + method);
        }
        return DISPATCH_ADVISED;
    }
    // We must always proxy equals, to direct calls to this.
    if (AopUtils.isEqualsMethod(method)) {
        logger.debug("Found 'equals' method: " + method);
        return INVOKE_EQUALS;
    }
    // We must always calculate hashCode based on the proxy.
    if (AopUtils.isHashCodeMethod(method)) {
        logger.debug("Found 'hashCode' method: " + method);
        return INVOKE_HASHCODE;
    }
    Class<?> targetClass = this.advised.getTargetClass();
    // Proxy is not yet available, but that shouldn't matter.
    List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    boolean haveAdvice = !chain.isEmpty();
    boolean exposeProxy = this.advised.isExposeProxy();
    boolean isStatic = this.advised.getTargetSource().isStatic();
    boolean isFrozen = this.advised.isFrozen();
    if (haveAdvice || !isFrozen) {
        // If exposing the proxy, then AOP_PROXY must be used.
        if (exposeProxy) {
            if (logger.isDebugEnabled()) {
                logger.debug("Must expose proxy on advised method: " + method);
            }
            return AOP_PROXY;
        }
        String key = method.toString();
        // 如果我们对这个方法的拦截器进行了优化,那么需要使用修正后的拦截器
        if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(key)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Method has advice and optimisations are enabled: " + method);
            }
            // 返回修正后的回调函数在callbacks数组中的位置,需要加上偏移量
            int index = this.fixedInterceptorMap.get(key);
            return (index + this.fixedInterceptorOffset);
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Unable to apply any optimisations to advised method: " + method);
            }
            // 如果没有进行优化,直接使用原始的回调函数,我们最常用的就是这个DynamicAdvisedInterceptor,在上面那个例子中,返回的也是这个
            return AOP_PROXY;
        }
    }
    else {
        // See if the return type of the method is outside the class hierarchy
        // of the target type. If so we know it never needs to have return type
        // massage and can use a dispatcher.
        // If the proxy is being exposed, then must use the interceptor the
        // correct one is already configured. If the target is not static, then
        // cannot use a dispatcher because the target cannot be released.
        if (exposeProxy || !isStatic) {
            return INVOKE_TARGET;
        }
        Class<?> returnType = method.getReturnType();
        if (targetClass == returnType) {
            if (logger.isDebugEnabled()) {
                logger.debug("Method " + method +
                        "has return type same as target type (may return this) - using INVOKE_TARGET");
            }
            return INVOKE_TARGET;
        }
        else if (returnType.isPrimitive() || !returnType.isAssignableFrom(targetClass)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Method " + method +
                        " has return type that ensures this cannot be returned- using DISPATCH_TARGET");
            }
            return DISPATCH_TARGET;
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Method " + method +
                        "has return type that is assignable from the target type (may return this) - " +
                        "using INVOKE_TARGET");
            }
            return INVOKE_TARGET;
        }
    }
}

到此我们已经大概讲明白了在cglib方式实现aop过程中,代理对象是如何产生的,拦截器是如何工作的以及spring是如何定位到具体的回调函数的,至于代理对象产生的具体细节,且听下回Enhancer源码解析。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOPSpring框架中的一个重要模块,它提供了面向切面编程(AOP)的支持。AOP是一种编程思想,它可以在不改变原有代码的情况下,通过在程序运行时动态地将代码“织入”到现有代码中,从而实现对原有代码的增强。 Spring AOP提供了基于注解的AOP实现,使得开发者可以通过注解的方式来定义切面、切点和通知等相关内容,从而简化了AOP的使用。 下面是一个基于注解的AOP实现的例子: 1. 定义切面类 ```java @Aspect @Component public class LogAspect { @Pointcut("@annotation(Log)") public void logPointcut() {} @Before("logPointcut()") public void beforeLog(JoinPoint joinPoint) { // 前置通知 System.out.println("执行方法:" + joinPoint.getSignature().getName()); } @AfterReturning("logPointcut()") public void afterLog(JoinPoint joinPoint) { // 后置通知 System.out.println("方法执行完成:" + joinPoint.getSignature().getName()); } @AfterThrowing(pointcut = "logPointcut()", throwing = "ex") public void afterThrowingLog(JoinPoint joinPoint, Exception ex) { // 异常通知 System.out.println("方法执行异常:" + joinPoint.getSignature().getName() + ",异常信息:" + ex.getMessage()); } } ``` 2. 定义业务逻辑类 ```java @Service public class UserService { @Log public void addUser(User user) { // 添加用户 System.out.println("添加用户:" + user.getName()); } @Log public void deleteUser(String userId) { // 删除用户 System.out.println("删除用户:" + userId); throw new RuntimeException("删除用户异常"); } } ``` 3. 在配置文件中开启AOP ```xml <aop:aspectj-autoproxy/> <context:component-scan base-package="com.example"/> ``` 在这个例子中,我们定义了一个切面类LogAspect,其中通过@Aspect注解定义了一个切面,通过@Pointcut注解定义了一个切点,通过@Before、@AfterReturning和@AfterThrowing注解分别定义了前置通知、后置通知和异常通知。 在业务逻辑类中,我们通过@Log注解标注了需要增强的方法。 最后,在配置文件中,我们通过<aop:aspectj-autoproxy/>开启了AOP功能,并通过<context:component-scan>扫描了指定包下的所有组件。 这样,当我们调用UserService中的方法时,就会触发LogAspect中定义的通知,从而实现对原有代码的增强。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值