快上网专注成都网站设计 成都网站制作 成都网站建设
成都网站建设公司服务热线:028-86922220

网站建设知识

十年网站开发经验 + 多家企业客户 + 靠谱的建站团队

量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决

如何理解AOP中JDK代理实现的原理

本篇内容主要讲解“如何理解AOP中JDK代理实现的原理”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何理解AOP中JDK代理实现的原理”吧!

在静宁等地区,都构建了全面的区域性战略布局,加强发展的系统性、市场前瞻性、产品创新能力,以专注、极致的服务理念,为客户提供网站设计制作、成都网站设计 网站设计制作按需定制,公司网站建设,企业网站建设,品牌网站制作,成都全网营销,成都外贸网站制作,静宁网站建设费用合理。

???? 动态代理

动态代理技术在Spring AOP中分为两种:

???? 基于JDK原生的动态代理.

提供一种在运行时创建一个实现了一组接口的新类由于Java是不支持实例化接口的,因此JDK会在运行期间生成一个代理类对给定的接口进行实现在调用该代理类接口的时候,将实现逻辑转发到调用处理器中(Invocation handler)

  • JDK进行动态代理的类必须实现接口

  • 代理类是java.lang.reflect.Proxy子类,类名以$Proxy开始


???? 基于CGLIB的动态代理.

CGLIB(Code Generation Library)是基于ASM(对Java字节码进行操作的框架)的类库

  • Spring AOP中,如果被代理类(targetObject)没有实现接口,即无法通过JDK的动态代理生成代理类,那么就会选择CGLIB来进行代理

  • CGLIB动态代理的原理:创建一个targetObject的子类,覆盖掉需要父类的方法,在覆盖的方法中对功能进行增强

注意,由于是采用继承覆盖的方式,所以由final方法修饰的类无法使用CGLIB进行代理。


???? 前提小结

  • JDK动态代理要求被代理类实现接口,切面类需要实现InvocationHandler。

  • CGLIB采用继承+方法覆盖实现切面,重写方法将逻辑委托给MethodInterceptor#intercept

  • CGLIB对代理类基本没有限制,但是需要注意被代理的类不可以被final修饰符和private修饰因为Java无法重写final类/private的方法


???? Spring的AOP实现原理

???? @EnableAspectJAutoProxy
  • @EnableAspectJAutoProxy注解是Spring AOP开启的标志,在启动类标记此注解,即启用可加载对应的切面类逻辑。此注解的ElementType为TYPE,表示标记在类上。

  • 同时用 @Retention(RetentionPolicy.RUNTIME)声明了注解在运行时得到保留。此外最重要的是使用了 @Import(AspectJAutoProxyRegistrar.class)来注册AOP的。

???? 实现方案

@EnableAspectJAutoProxy注解正是通过@Import的方式来将 AspectJAutoProxyRegistrar类注册成Spring的Bean,以便在容器解析切面类时派上用场。那么AspectJAutoProxyRegistrar类的作用是什么?

@Import(AspectJAutoProxyRegistrar.class)
???? proxyTargetClass

@EnableAspectJAutoProxy支持处理标有AspectJ的@Aspect批注的组件,用户可以主动声明proxyTargetClass来指定Spring AOP使用哪种动态代理方式来创建代理类(默认使用基于实现接口的JDK动态代理方式)。

  • 使用CGLIB动态代理来创建代理类

  @Configuration
  @EnableAspectJAutoProxy(proxyTargetClass=true)
  @ComponentScan("com.libo")
  public class AppConfig {
      // ...
  }
???? exposeProxy
  • 为了解决一些由于代理引发的切面失效问题,Spring AOP在Spring 4.3.1后引入了AopContext类来将代理类的引用存储在ThreadLocal中,通过AopContext可以快速获取当前类的代理类。

  • 默认为不支持,如果声明为true,即可使用AopContext获取代理类,同时,为了使用AspectJ,需要确保当前jar仓库存在aspectjweaver

???? AspectJAutoProxyRegistrar

通过@Import注册AspectJAutoProxyRegistrar,通常情况下,我们的启动类本身也是一个Bean,Spring支持使用 @Import来导入一个没有标记任何Spring注解的类来将该Java类注册成Spring的Bean。

Registers an AnnotationAwareAspectJAutoProxyCreator against the current BeanDefinitionRegistry as appropriate based on a given @EnableAspectJAutoProxy annotation.

根据当前BeanDefinitionRegistry在适当的位置注册AnnotationAwareAspectJAutoProxyCreator。

如何理解AOP中JDK代理实现的原理

???? ImportBeanDefinitionRegistrar
  • 用来导入一些特殊的BeanDefinitionSpring在处理 @Configuration时,会去扫描是否有通过 @Import标签导入的类,对ImportBeanDefinitionRegistrar这类接口,还会执行其中的registerBeanDefinitions方法。

AspectJAutoProxyRegistrar:实现了ImportBeanDefinitionRegistrar接口,用来注册AspectJAnnotationAutoProxyCreator,也就是支持注解驱动(同时兼容XML)解析的AspectJ自动代理创建器。

???? AspectJAutoProxyRegistrar源码分析
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    
	@Override
    public void registerBeanDefinitions(
         AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
         // 向容器注册AspectJAnnotationAutoProxyCreator
        AopConfigUtils.
		    registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
        AnnotationAttributes enableAspectJAutoProxy =
        AnnotationConfigUtils.attributesFor(importingClassMetadata,
													EnableAspectJAutoProxy.class);
        // 如果@EnableAspectJAutoProxy上存在标签内容
        if (enableAspectJAutoProxy != null) {
            // proxyTargetClass为true,则强制指定AutoProxyCreator使用CGLIB进行代理
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            // 是否开启exposeProxy特性
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }
}
  • 向容器注册AspectJAnnotationAutoProxyCreator

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

  • 随后解析 @EnableAspectJAutoProxy注解上的元数据来决定是否开启上述我们讲到的proxyTargetClassexposeProxy特性.

  • 为了了解registerBeanDefinitions方法的执行链路和调用时机,我们使用IDE的debug来查看调用栈分析执行流程。

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass(configClass.getImportBeanDefinitionRegistrars())

这行代码,从代码的语义上我们可以大致可以猜出来,这是解析当前配置类上是否存在通过@Import导入的实现了ImportBeanDefinitionRegistrar的类

如何理解AOP中JDK代理实现的原理

最终会调用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,注册一个AnnotationAwareAspectJAutoProxyCreator,该类属于AOP的核心类。

???? registerBeanDefinitions

执行AspectJAutoProxyRegistrar#registerBeanDefinitions方法。

  • AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        BeanDefinitionRegistry registry, @Nullable Object source) {
    return      registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
???? 查找切面

从上面的代码可以看出来AnnotationAwareAspectJAutoProxyCreator这个类作为实际操作者,查看该类的继承关系图。(省略一些不必要的类)。

如何理解AOP中JDK代理实现的原理

首先看第一个接口,BeanPostProcessor,这个接口作为顶层接口,肯定不会被外部直接调用,所以大概率是底下的几个具体实现类被调用,然后通过判断是不是InstantiationAwareBeanPostProcessor接口的类型,再执行相应逻辑,带着这个疑惑,来看源码。

???? AbstractAutoProxyCreator

AbstractAutoProxyCreator通过postProcessAfterInitialization实现AOP功能

   // 在实例化之后进行操作容器对象
    @Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String 
		beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

	protected Object wrapIfNecessary(Object bean, String beanName, Object 
		cacheKey) {
    // beanName不为空,并且存在于targetSourcedBeans中,也就是自定义的
	// TargetSource被解析过了
    if (StringUtils.hasLength(beanName) && 
		this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    // 如果Bean为advisedBeans,也不需要被代理
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    // isInfrastructureClass和shouldSkip的作用:
    // 识别切面类,加载切面类成advisors
    // 为什么又执行一次是因为存在循环依赖的情况下无法加载advisor
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), 
		beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // Create proxy if we have advice.
    // 返回匹配当前Bean的所有Advice、Advisor、Interceptor
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), 
		beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 创建Bean对应的代理,SingletonTargetSource用于封装实现类的信息
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new 
				SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
    // 下次代理不需要重复生成了
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}
  • 判断缓存中是否存在当前Bean或者是当前Bean已经被代理过了,那么直接返回bean.

  • 尝试再次加载advisor,避免由于循环依赖导致advisor加载不完整.

  • 获取当前bean符合的advisor数组.

  • 创建代理类.

    • 本文来分析getAdvicesAndAdvisorsForBean方法是如何在所有的advisors中找到匹配的advisor的.

???? AbstractAdvisorAutoProxyCreator
    @Override
    @Nullable
    protected Object[] getAdvicesAndAdvisorsForBean(
            Class beanClass, String beanName, @Nullable TargetSource 
			targetSource) {
        List advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        return advisors.toArray();
    }
	//这里调用了findEligibleAdvisors来寻找合适的advisors,如果返回的集合为空,那么    // 最后返回null.
   // 如果返回了advisors,将其数组化返回.

    protected List findEligibleAdvisors(Class beanClass, String 
			beanName) {
        //BeanFactory 中所有 Advisor 的实现
        List candidateAdvisors = findCandidateAdvisors();
        // 有资格的 Advisor
        List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, 
		beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }
	
//首先获取之前解析过的advisors列表-candidateAdvisors,这里是所有的切面类解析成的advisors.
//在candidateAdvisors中找到当前Bean匹配的advisor-findAdvisorsThatCanApply.
//将获取到的eligibleAdvisors进行排序.

    protected List findCandidateAdvisors() {
        Assert.state(this.advisorRetrievalHelper != null, "No 
					 BeanFactoryAdvisorRetrievalHelper available");
        return this.advisorRetrievalHelper.findAdvisorBeans();
    }

    protected List findAdvisorsThatCanApply(
            List candidateAdvisors, Class beanClass, String beanName) {
        	ProxyCreationContext.setCurrentProxiedBeanName(beanName);
			try {
				return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
			}
			finally {
				ProxyCreationContext.setCurrentProxiedBeanName(null);
			}
    }
	
	public static List findAdvisorsThatCanApply(List 	candidateAdvisors, Class clazz) {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    // 存储最终匹配的Advisor集合
    List eligibleAdvisors = new ArrayList<>();
    for (Advisor candidate : candidateAdvisors) {
        // 当前advisor对象是否实现了IntroductionAdvisor接口
        if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
            eligibleAdvisors.add(candidate);
        }
    }
    boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor) {
            // already processed
            continue;
        }
        // canApply->判断当前的advisor的pointcut表达式是否匹配当前class
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    return eligibleAdvisors;
}
	
	//最终是调用了AopUtils.findAdvisorsThatCanApply来筛选匹配Bean的Advisors.
???? 关于createProxy

ProxyFactory 对象中有要代理的bean和这个Bean上的advisor

Bean使用哪种代理

当Bean实现接口时,Spring就会用JDK的动态代理。 当Bean没有实现接口时,Spring会自动使用CGlib实现,但是前提是项目中导入了CGlib的相关依赖,否则Spring只能使用JDK来代理那些没有实现接口的类,这样生成的代理类会报错。 AopProxy有两个实现类JdkDynamicAopProxy和CglibAopProxy。都是构造 ReflectiveMethodInvocation.proceed()。

JdkDynamicAopProxy

invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();

CglibAopProxy

// CglibMethodInvocation 继承于 ReflectiveMethodInvocation
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

ReflectiveMethodInvocation.proceed()

public Object proceed() throws Throwable {
    // 当所有拦截器都执行后,调用目标类的目标方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // 动态拦截器
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            // Dynamic matching failed.
            // Skip this interceptor and invoke the next in the chain.
            return proceed();
        }
    }
    else {
        // MethodInterceptor的实现类在处理完自己的逻辑后,还是会调用procee(),传入this就是为了达到这个目的
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

Advisor

如何理解AOP中JDK代理实现的原理

Advice

如何理解AOP中JDK代理实现的原理

Joinpoint

如何理解AOP中JDK代理实现的原理


????AnnotationAwareAspectJAutoProxyCreator

Spring用来处理应用上下文中被@AspectJ注解标记的类的。继续进入registerOrEscalateApcAsRequired方法中看看注册流程.

org.springframework.aop.config.AopConfigUtils#registerOrEscalateApcAsRequired
private static BeanDefinition registerOrEscalateApcAsRequired(
        Class cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    // 当前容器是否包含 org.springframework.aop.config.internalAutoProxyCreator
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = 
			registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        return null;
    }
	
    // 将传入的class包装成BeanDefinition,然后注册到容器中,并讲其order的执行顺序
	//调整为最优。
    // 在aop中,这里会注册AnnotationAwareAspectJAutoProxyCreator.class这个类
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", 
										  Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME,     
									beanDefinition);
    return beanDefinition;
}
  1. 首先查看当前容器中是否包含

org.springframework.aop.config.internalAutoProxyCreator的BeanDefiniton.

  1. 如果没有,将传入的class(在此处传入了AnnotationAwareAspectJAutoProxyCreator.class)包装成RootBeanDefinition,然后注册到容器中.

  2. 设置proxyTargetClass与exposeProxy

我们看一下如何设置proxyTargetClass即可,大体上设置proxyTargetClass与exposeProxy的逻辑都是相通的.

// 如果@EnableAspectJAutoProxy上存在标签内容
if (enableAspectJAutoProxy != null) {
    // 如果proxyTargetClass为true,则强制指定AutoProxyCreator使用CGLIB进行代理
    if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
        AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
    }
    // 是否开启exposeProxy特性
    if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
        AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
    }
}
进入AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);

AopConfigUtils#forceAutoProxyCreatorToUseClassProxying
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {  
    // 如果容器中包含 org.springframework.aop.config.internalAutoProxyCreator
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {  
        // 取出 org.springframework.aop.config.internalAutoProxyCreator的BeanDefinition
        BeanDefinition definition =     
		 registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);  
        // 设置proxyTargetClass为true
        definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
    }
}
  • 如容器中包含名org.springframework.aop.config.internalAutoProxyCreator,那么取出该BeanDefinition设置proxyTargetClass为true

  • Spring为了兼容不同的BeanDefinition持有不同的属性值,将它们都抽象成了MutablePropertyValues,definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE),就跟我们平时JavaBean中的set方法是一样的.


简单理解 @ImportImportBeanDefinitionRegistrar,下面我们通过两个用例来理解@Import和ImportBeanDefinitionRegistrar

通过@Import导入类让Spring进行管理

public class NeedImportBean {
    public void doSomething(){
        Logger.getGlobal().info("Through @Import registry to a bean ");
    }
}

在启动类中将NeedImportBean导入

/**
 * @author jaymin
 * 2020/11/30 20:13
 */
@Configuration
@ComponentScan(value = "com.xjm")
@Import(NeedImportBean.class)
@EnableAspectJAutoProxy
public class ApplicationConfig {
    public static AnnotationConfigApplicationContext getApplicationContext() {
        return new AnnotationConfigApplicationContext(ApplicationConfig.class);
    }
}

//对NeedImportBean做getBean

public class BeanFactoryDemo {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext applicationContext = 
				ApplicationConfig.getApplicationContext();
               NeedImportBean needImportBean = 
			   applicationContext.getBean(NeedImportBean.class);
    }
}
???? 实现ImportBeanDefinitionRegistrar

NeedImportBean实现ImportBeanDefinitionRegistrar接口,然后验证两个点:

  • 是否会回调registerBeanDefinitions方法。

  • 通过getBean是否能获取NeedImportBean

public class NeedImportBean implements ImportBeanDefinitionRegistrar{

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Logger.getGlobal().info("Through implements ImportBeanDefinitionRegistrar and @Import to callback me.");
    }

    public void doSomething(){
        Logger.getGlobal().info("Through @Import registry to a bean ");
    }

}
  • refresh中激活后置处理器ConfigurationClassPostProcessor加载@Configuration上的元数据

???? refresh

首先容器会加载refresh方法。

如何理解AOP中JDK代理实现的原理

  • 执行invokeBeanFactoryPostProcessors(beanFactory)激活工厂级别的后置处理器。

  • 由于启动类都是被 @Configuration标记的,Spring会使用ConfigurationClassPostProcessor来解析被 @Configuration的类。

  • 使用ConfigurationClassBeanDefinitionReader加载配置类解析成BeanDefinition

如何理解AOP中JDK代理实现的原理

到此,相信大家对“如何理解AOP中JDK代理实现的原理”有了更深的了解,不妨来实际操作一番吧!这里是创新互联网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!


网站题目:如何理解AOP中JDK代理实现的原理
标题链接:http://6mz.cn/article/ieeoop.html

其他资讯