服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - Spring源码学习之动态代理实现流程

Spring源码学习之动态代理实现流程

2021-08-30 11:05黄智霖-blog Java教程

这篇文章主要给大家介绍了关于Spring源码学习之动态代理实现流程的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

注:这里不阐述Spring和AOP的一些基本概念和用法,直接进入正题。

流程

  Spring所管理的对象大体会经过确定实例化对象类型、推断构造方法创建对象(实例化)、设置属性、初始化等等步骤。在对象初始化阶段,Spring为开发者提供了一个BeanPostProcessor接口,它会在对象初始化之前和初始化之后被调用(初始化,不是实例化,对应实例化的是InstantiationAwareBeanPostProcessor接口)。

  1. public interface BeanPostProcessor {
  2. //初始化之前
  3. Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
  4. //初始化之后
  5. Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
  6.  
  7. }

  在对象初始化之后会调用postProcessAfterInitialization方法,该方法返回一个Object。如果成功返回了一个对象,那么容器中相应beanName对应的实例就将会是这个对象。

  本文主要分析动态代理,我们着重看AnnotationAwareAspectJAutoProxyCreator。先来看一下它的继承关系:

Spring源码学习之动态代理实现流程

  AnnotationAwareAspectJAutoProxyCreator最终实现了BeanPostProcessor接口(也实现了InstantiationAwareBeanPostProcessor接口),可以看到继承关系比较复杂。当前我们关注的postProcessAfterInitialization方法实现在它的父类AbstractAutoProxyCreator中(只保留了部分代码):

  1. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  2. if (bean != null) {
  3. Object cacheKey = getCacheKey(bean.getClass(), beanName);
  4. if (!this.earlyProxyReferences.contains(cacheKey)) {
  5. return wrapIfNecessary(bean, beanName, cacheKey);
  6. }
  7. }
  8. return bean;
  9. }

  这里主要看看wrapIfNecessary方法(只保留了部分代码):

  1. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
  2. ......
  3. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
  4. if (specificInterceptors != DO_NOT_PROXY) {
  5. this.advisedBeans.put(cacheKey, Boolean.TRUE);
  6. Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
  7. this.proxyTypes.put(cacheKey, proxy.getClass());
  8. return proxy;
  9. }
  10. ......
  11. }

  其中核心的是两个方法调用,分别是getAdvicesAndAdvisorsForBean和createProxy。getAdvicesAndAdvisorsForBean会返回一个对象数组,包含aop相关的一些对象,如果是一个普通的不需要代理的对象会返回一个空Object数组,也就是DO_NOT_PROXY;createProxy方法则是创建代理类。

  先看看getAdvicesAndAdvisorsForBean方法:

  1. protected abstract Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource customTargetSource) throws BeansException;

  getAdvicesAndAdvisorsForBean方法在当前类(AbstractAutoProxyCreator)中是一个抽象方法,由子类AbstractAdvisorAutoProxyCreator实现:

  1. public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {
  2. @Override
  3. protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
  4. List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
  5. if (advisors.isEmpty()) {
  6. return DO_NOT_PROXY;
  7. }
  8. return advisors.toArray();
  9. }
  10. }

  代码很清晰,我们进入findEligibleAdvisors方法,看方法名也知道它会完成寻找Advisor的工作:

  1. protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
  2. //寻找Advisor
  3. List<Advisor> candidateAdvisors = findCandidateAdvisors();
  4. //针对指定的bean,过滤可用的Advisor,比如根据注解匹配
  5. List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
  6. extendAdvisors(eligibleAdvisors);
  7. if (!eligibleAdvisors.isEmpty()) {
  8. eligibleAdvisors = sortAdvisors(eligibleAdvisors);
  9. }
  10. return eligibleAdvisors;
  11. }

  首先进入findCandidateAdvisors方法:

  1. protected List<Advisor> findCandidateAdvisors() {
  2. // Add all the Spring advisors found according to superclass rules.
  3. List<Advisor> advisors = super.findCandidateAdvisors();
  4. // Build Advisors for all AspectJ aspects in the bean factory.
  5. advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
  6. return advisors;
  7. }

  我们这里主要看看aspectj的逻辑,所以看看aspectJAdvisorsBuilder.buildAspectJAdvisors方法(只保留了主要代码):

  1. public List<Advisor> buildAspectJAdvisors() {
  2. List<String> aspectNames = null;
  3. ......
  4. synchronized (this) {
  5. aspectNames = this.aspectBeanNames;
  6. if (aspectNames == null) {
  7. //获取所有管理的beanName
  8. String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
  9. //遍历每个beanName
  10. for (String beanName : beanNames) {
  11. //从beanFactory获取Class
  12. Class<?> beanType = this.beanFactory.getType(beanName);
  13. //检查对应的Class是否实现Aspect注解
  14. if (this.advisorFactory.isAspect(beanType)) {
  15. //说明这个beanName对应的类是一个切面
  16. aspectNames.add(beanName);
  17. AspectMetadata amd = new AspectMetadata(beanType, beanName);
  18. if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
  19. MetadataAwareAspectInstanceFactory factory =
  20. new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
  21. //获取Advisor,主要是解析对象中关于AOP的注解,比如Pointcut
  22. List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
  23. if (this.beanFactory.isSingleton(beanName)) {
  24. //就放入缓存,后面就不用重新解析了
  25. this.advisorsCache.put(beanName, classAdvisors);
  26. }
  27. advisors.addAll(classAdvisors);
  28. }
  29. }
  30. }
  31. this.aspectBeanNames = aspectNames;
  32. return advisors;
  33. }
  34. }
  35. ......
  36. }

  会从beanFactory中寻找所有管理的beanName,返回一个String数组,然后遍历数组,从beanFactory中根据beanName获取对应的Class,然后再看对应的Class是否有Aspect注解,如果有对应的注解,那么就表示这个对象是一个切面。接下来就需要进行解析,生成真正的Advisor对象,最后放入缓存。

  可以看看isAspect方法是如何判断的:

  1. @Override
  2. public boolean isAspect(Class<?> clazz) {
  3. return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
  4. }
  5. private boolean hasAspectAnnotation(Class<?> clazz) {
  6. return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
  7. }

  逻辑很清晰,主要就是看有没有Aspect注解。 但是这里要注意,这个buildAspectJAdvisors方法通常不是在这里调用的(”这里“的意思是postProcessAfterInitialization的流程)。回到AnnotationAwareAspectJAutoProxyCreator继承关系图中,它也实现了InstantiationAwareBeanPostProcessor接口,同样在其父类AbstractAutoProxyCreator中实现了postProcessBeforeInstantiation方法,这个方法会在对象实例化(不是初始化)之前调用,在该方法的逻辑里通常会首先触发buildAspectJAdvisors方法的执行,执行之后会把结果缓存起来。

  好了,再回到findEligibleAdvisors方法,上面代码已经贴了,这里就不贴了。获取到Advisor列表之后,要从中找到能用于指定类的Advisor列表,然后返回。接下来就要为指定的对象创建代理对象了,也就是AbstractAutoProxyCreator类的createProxy方法:

  1. protected Object createProxy(
  2. Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
  3.  
  4. ProxyFactory proxyFactory = new ProxyFactory();
  5. proxyFactory.copyFrom(this);
  6.  
  7. if (!proxyFactory.isProxyTargetClass()) {
  8. if (shouldProxyTargetClass(beanClass, beanName)) {
  9. proxyFactory.setProxyTargetClass(true);
  10. }
  11. else {
  12. evaluateProxyInterfaces(beanClass, proxyFactory);
  13. }
  14. }
  15.  
  16. Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
  17. for (Advisor advisor : advisors) {
  18. proxyFactory.addAdvisor(advisor);
  19. }
  20.  
  21. proxyFactory.setTargetSource(targetSource);
  22. customizeProxyFactory(proxyFactory);
  23.  
  24. proxyFactory.setFrozen(this.freezeProxy);
  25. if (advisorsPreFiltered()) {
  26. proxyFactory.setPreFiltered(true);
  27. }
  28.  
  29. return proxyFactory.getProxy(getProxyClassLoader());
  30. }

  代理对象是由ProxyFactory代理工厂创建的,我们先看看这个工厂是如何创建代理对象的,也就是proxyFactory.getProxy方法:

  1. public Object getProxy(ClassLoader classLoader) {
  2. return createAopProxy().getProxy(classLoader);
  3. }

  createAopProxy方法会返回一个AopProxy,该方法定义在ProxyFactory的父类ProxyCreatorSupport中:

  1. public class ProxyCreatorSupport extends AdvisedSupport {
  2. private AopProxyFactory aopProxyFactory;
  3. public ProxyCreatorSupport() {
  4. //设置默认的代理工厂DefaultAopProxyFactory
  5. this.aopProxyFactory = new DefaultAopProxyFactory();
  6. }
  7. public AopProxyFactory getAopProxyFactory() {
  8. //获取代理工厂,默认就是DefaultAopProxyFactory
  9. return this.aopProxyFactory;
  10. }
  11.  
  12. protected final synchronized AopProxy createAopProxy() {
  13. //先获取代理工厂,然后调用工厂的createAopProxy方法创建AopProxy
  14. return getAopProxyFactory().createAopProxy(this);
  15. }
  16. }

  上面贴出了关键代码,getAopProxyFactory默认返回的是一个DefaultAopProxyFactory工厂类,来看看DefaultAopProxyFactory的createAopProxy方法:

  1. public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  2. if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
  3. Class<?> targetClass = config.getTargetClass();
  4. if (targetClass.isInterface()) {
  5. return new JdkDynamicAopProxy(config);
  6. }
  7. return new ObjenesisCglibAopProxy(config);
  8. }
  9. else {
  10. return new JdkDynamicAopProxy(config);
  11. }
  12. }

  代码中有一些代理配置的判断,这里不用关心。可以看到它提供了两个AopProxy,分别是基于JDK的JdkDynamicAopProxy和基于cglib的ObjenesisCglibAopProxy。由于JDK提供的动态代理实现最终生成的代理类默认会继承Proxy类,实现被代理类实现的接口,因为Java是单继承,所以只能通过接口实现,也就限制了要使用JDK提供的动态代理,必须要基于接口。而使用cglib基于字节码的改造则没有这个限制,所以Spring提供了这两种方式,根据被代理类的实际情况来选择。

  关于每个AopProxy是如何创建代理类的,这里就先不跟了~

总结

  总的来说,动态代理是实现AOP的重要手段,Spring提供的动态代理主要依靠其提供的BeanPostProcessor,也称之为后置处理器。除了BeanPostProcessor之外,还有InstantiationAwareBeanPostProcessor(也继承了BeanPostProcessor),它们会在bean的生命周期的特定阶段被调用,以开放给开发者处理和调整对象的入口或者手段。动态代理依托后置处理器,在后置处理器的逻辑中使用AopProxy创建了被代理对象的代理类,然后代替原有类存入Spring的bean工厂中,之后根据beanName获取的实例对象就不再是原对象实例,而是代理类。而AopProxy是由AopProxyFactory接口生成,目前该接口只有DefaultAopProxyFactory实现类,其提供了两种AopProxy,分别基于原生JDK提供的动态代理和cgib,根据实际情况选择。

Spring源码学习之动态代理实现流程

到此这篇关于Spring源码学习之动态代理实现流程的文章就介绍到这了,更多相关Spring动态代理实现内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/huangzhilin2015/article/details/115255075

延伸 · 阅读

精彩推荐
  • Java教程升级IDEA后Lombok不能使用的解决方法

    升级IDEA后Lombok不能使用的解决方法

    最近看到提示IDEA提示升级,寻思已经有好久没有升过级了。升级完毕重启之后,突然发现好多错误,本文就来介绍一下如何解决,感兴趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java BufferWriter写文件写不进去或缺失数据的解决

    Java BufferWriter写文件写不进去或缺失数据的解决

    这篇文章主要介绍了Java BufferWriter写文件写不进去或缺失数据的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java8中Stream使用的一个注意事项

    Java8中Stream使用的一个注意事项

    最近在工作中发现了对于集合操作转换的神器,java8新特性 stream,但在使用中遇到了一个非常重要的注意点,所以这篇文章主要给大家介绍了关于Java8中S...

    阿杜7472021-02-04
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    这篇文章主要介绍了Java使用SAX解析xml的示例,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程小米推送Java代码

    小米推送Java代码

    今天小编就为大家分享一篇关于小米推送Java代码,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...

    富贵稳中求8032021-07-12
  • Java教程20个非常实用的Java程序代码片段

    20个非常实用的Java程序代码片段

    这篇文章主要为大家分享了20个非常实用的Java程序片段,对java开发项目有所帮助,感兴趣的小伙伴们可以参考一下 ...

    lijiao5352020-04-06
  • Java教程Java实现抢红包功能

    Java实现抢红包功能

    这篇文章主要为大家详细介绍了Java实现抢红包功能,采用多线程模拟多人同时抢红包,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙...

    littleschemer13532021-05-16
  • Java教程xml与Java对象的转换详解

    xml与Java对象的转换详解

    这篇文章主要介绍了xml与Java对象的转换详解的相关资料,需要的朋友可以参考下...

    Java教程网2942020-09-17