Spring boot为什么默认不使用jdk动态代理呢?深入Aop源码讲解

深入理解Spring Aop源码

五大组件

Advised

However you create AOP proxies, you can manipulate them BY using the org.springframework.aop.framework.Advised interface. Any AOP proxy can be cast to this interface, no matter which other interfaces it implements.

任何的Aop代理对象都实现了 Advised这个接口

所以任何的Aop代理对象都可以进行进行强转然后调用其中的方法。

image-20210412165859271

Advice

每个Advice都是一个Spring Bean,这个advice实例能够被所有的Advised对象所共享或者,它在每个Advised对象中是唯一的。advice能够做到通知每个Advised对象。这个接口其实就是当我们想改变某个对象的行为时,通过advice去实现我们想要的,而且每个Advised能够感知到这个advice,进而实现代理。

PointCut

切面我的理解其实就是规则,能够满足规则的就走代理逻辑

当我们想要实现某个代理时,但是不能所有的类都实现我们的代理,而且每个类需要满足什么样的条件(切点),才能够走代理。

Advisor

这个advisor是什么呢?

In Spring, an Advisor is an aspect that contains only a single advice object associated with a pointcut expression.

其实它就是一个切面,并且这个切面只包含一个advice 和一个切入点,所以我们想要自定义我们自己的切面,我们去实现这个类就可以了。

AbstractAutoProxyCreator

这个类实现了BeanPostProcessor 这个类的作用其实就是去根据我们配置的Advisor去创建代理类对象。

这里我们以定义一个日志切面为例

  1. 定义一个方法上只要标注的Log注解的规则的切面,只要满足这个规则的对象那么就会被创建成这个切面所对应的代理对象。

    package com.example.demo;
    
    import org.springframework.aop.Pointcut;
    import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;
    import org.springframework.aop.support.StaticMethodMatcherPointcut;
    import org.springframework.core.annotation.AnnotatedElementUtils;
    import org.springframework.core.annotation.AnnotationAttributes;
    
    import java.lang.reflect.Method;
    
    /**
     * @author fpp
     * @version 1.0
     * @date 2021/4/12 9:27
     */
    public class BeanFactoryLogAdvisor extends AbstractBeanFactoryPointcutAdvisor {
        static class LogPointCut extends StaticMethodMatcherPointcut {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
                        method, Log.class, false, false);
                return attributes!=null;
            }
        }
        @Override
        public Pointcut getPointcut() {
            return new LogPointCut();
        }
    }
    
  2. 定义一个通知类,也就是具体的代理处理逻辑

    package com.example.demo;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    /**
     * @author fpp
     * @version 1.0
     * @date 2021/4/12 9:25
     */
    public class LogInterceptor implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            System.out.println("invoke before");
            Object invoke = null;
            try {
                invoke = methodInvocation.proceed();
            }catch (Exception e){
                e.printStackTrace();
                System.out.println("invoke error "+e.getMessage());
            }
            System.out.println("invoke after");
            return invoke;
        }
    }
    
  3. 将我们配置好的切面暴露出去

    package com.example.demo;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    /**
     * @author fpp
     * @version 1.0
     * @date 2021/4/12 9:32
     */
    @Configuration
    @EnableTransactionManagement
    @Import(ImportProxyCreator.class)
    public class BeanCommon {
        @Bean
        public BeanFactoryLogAdvisor advisor(LogInterceptor logInterceptor){
            BeanFactoryLogAdvisor beanFactoryLogAdvisor=new BeanFactoryLogAdvisor();
            beanFactoryLogAdvisor.setAdvice(logInterceptor);
            return beanFactoryLogAdvisor;
        }
        @Bean
        public LogInterceptor logInterceptor(){
            return new LogInterceptor();
        }
    }
    
    
  4. 定义我们自己的代理类构件器

    package com.example.demo;
    
    import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    import org.springframework.stereotype.Component;
    
    /**
     * @author fpp
     * @version 1.0
     * @date 2021/4/12 9:40
     */
    @Component
    public class LogProxyCreator extends DefaultAdvisorAutoProxyCreator {
    }
    

    声明这个代理类构建器使用Cglib来构建,为啥不用jdk的下面再说

    package com.example.demo;
    
    import org.springframework.aop.config.AopConfigUtils;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.type.AnnotationMetadata;
    
    /**
     * @author fpp
     * @version 1.0
     * @date 2021/4/12 10:12
     */
    public class ImportProxyCreator implements ImportBeanDefinitionRegistrar {
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            BeanDefinition definition = registry.getBeanDefinition("logProxyCreator");
            definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
        }
    }
    
  5. 业务处理类和最终运行结果

    package com.example.demo;
    
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    /**
     * @author fpp
     * @version 1.0
     * @date 2021/4/12 9:34
     */
    @Service
    public class A implements D{
        @Override
        @Log
        @Transactional
        public void doService(){
            System.out.println("do service");
        }
    }
    

    image-20210412173922111

深入源码中

AbstractAutoProxyCreator

266行
@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
		Object cacheKey = getCacheKey(beanClass, beanName);

		if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
			if (this.advisedBeans.containsKey(cacheKey)) {
				return null;
			}
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
				this.advisedBeans.put(cacheKey, Boolean.FALSE);
				return null;
			}
		}

		// Create proxy here if we have a custom TargetSource.
		// Suppresses unnecessary default instantiation of the target bean:
		// The TargetSource will handle target instances in a custom fashion.
		TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
		if (targetSource != null) {
			if (StringUtils.hasLength(beanName)) {
				this.targetSourcedBeans.add(beanName);
			}
            //获取该Bean满足条件的Advisors 然后通过这些Advisor来构建代理对象
			Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
			Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		return null;
	}

紧接着我们看到它的子类AbstractAdvisorAutoProxyCreator具体实现的方法

protected List<Advisor> findAdvisorsThatCanApply(
			List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

		ProxyCreationContext.setCurrentProxiedBeanName(beanName);
		try {
			return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
		}
		finally {
			ProxyCreationContext.setCurrentProxiedBeanName(null);
		}
	}

然后看到 Aop工具类中的实现 ,然后关键看到 canApply这个方法

/**
 * Can the given pointcut apply at all on the given class?
 * <p>This is an important test as it can be used to optimize
 * out a pointcut for a class.
 * @param pc the static or dynamic pointcut to check
 * @param targetClass the class to test
 * @param hasIntroductions whether or not the advisor chain
 * for this bean includes any introductions
 * @return whether the pointcut can apply on any method
 */
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
   Assert.notNull(pc, "Pointcut must not be null");
   if (!pc.getClassFilter().matches(targetClass)) {
      return false;
   }
   //获取规则
   MethodMatcher methodMatcher = pc.getMethodMatcher();
   if (methodMatcher == MethodMatcher.TRUE) {
      // No need to iterate the methods if we're matching any method anyway...
      return true;
   }

   IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
   if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
      introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
   }

   Set<Class<?>> classes = new LinkedHashSet<>();
    //如果不是jdk动态代理,那么就根据cglib的类名获取到根被代理对象的class 防止漏掉。
   if (!Proxy.isProxyClass(targetClass)) {
      classes.add(ClassUtils.getUserClass(targetClass));
   }
    //获取类所有实现的接口
   classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

   for (Class<?> clazz : classes) {
      Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
       //遍历所有的方法 找到符合切点规则的方法  返回true表示当前这个clazz符合这个切面,需要通过这个切面来构建代理对象。
      for (Method method : methods) {
         if (introductionAwareMethodMatcher != null ?
               introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
               methodMatcher.matches(method, targetClass)) {
            return true;
         }
      }
   }

   return false;
}

jdk动态代理实现和Cglib大同小异,来我们来看精华点

            // 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.
            //查找符合方法的拦截器或者advice
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			// Check whether we have any advice. If we don't, we can fallback 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.
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                //回调里面一层级的代理对象的方法。
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// We need to create a method invocation...
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// Proceed to the joinpoint through the interceptor chain.
				retVal = invocation.proceed();
			}

为什么不使用jdk来生成Aop代理对象呢?

jdk动态代理的性能要高于cglib啊,为什么spring boot 默认不使用jdk动态代理呢?而默认使用Cglib呢?

接下来我们来看如何根据我们配置的切面来生成代理对象呢?

Spring Aop代理 AopProxy接口 的实现有 CglibAopProxy,JdkDynamicAopProxy

JdkDynamicAopProxy的实现

@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
        //自己作为InvocationHandler
		return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
	}

1.jdk动态代理需要使用到接口,必须要实现一个接口,当多级嵌套层层代理时,将会导致漏掉Advisor中的Intercept或者Advice

当一个类没有实现任何接口时,如果spring用jdk动态代理那么将会报错

2.jdk不是特别灵活 例如:不能够自定义最终类的名字 默认$Proxy+数字

当在调用 获取bean所匹配的Advisor时,

Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

AopUtils.canApply方法中有这样一段代码

如果不是JDK代理 就通过 类名获取根代理对象 A E n h a n c e r B y S p r i n g C G L I B EnhancerBySpringCGLIB EnhancerBySpringCGLIBf2e0c7b7 可以通过这种命名获取根被代理的对象A,当多级嵌套时 层层代理时,jdk动态代理 不能够识别**不能找到根代理对象A(其实根本原因还是因为jdk动态代理不能够继承一个类来生成代理,而必须实现一个接口才能生成代理) ** ,导致漏掉Advisor.。单个层次的代理是没有问题的。

image-20210413154956549

jdk生成的代理

image-20210413155059651

image-20210413155302131

特别注意m3这个方法 ,这个 h就是JdkDynamicAopProxy,调用 JdkDynamicAopProxy运行 invoke 这段代码时 m3是 接口的方法

// Get the interception chain for this method. 查找符合这个方法的拦截器或者advice  
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

但是接口 D方法上是没有 @Log 这个注解的 这时又会漏掉符合的拦截器和advice

cglib生成的代理 就不同了

image-20210413155919284

可以看到 CGLIB$doService 0 0 0Method 这个方法时**A这个根类的方法,调用时是通过这个方法去调用,就不会漏掉 **。

总结

1.jdk动态代理不是很灵活,必须依赖接口。

2.当单个层次的代理jdk动态代理能够胜任 ,当多级代理层层代理时,jdk代理不能够满足,因为不能够找到被代理的根对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值