专栏首页软件开发-青出于蓝简析Spring aop的BeanNameAutoProxyCreator如何对目标类生成代理

简析Spring aop的BeanNameAutoProxyCreator如何对目标类生成代理

    注意:我们分析的版本是SpringFramework-3.2.8.RELEASE。

    先上一张图,如下图1所示,后面会用到:

                                 图1 BeanNameAutoProxyCreator间接继承了BeanPostProcessor

1.铺垫

类Seller、Waiter、GreetingBeforeAdvice的代码如下,先做下铺垫:

public class Seller {

    public void greetTo(String name) {
        LOG.info("Seller greet to: {}", name);
    }
    
}
public class Waiter {

    public void greetTo(String name){
        LOG.info("waiter greet to: {}",name);
    }

    public void serveTo(String name){
        LOG.info("waiter serving to: {}",name);
    }
    
}
import org.springframework.aop.MethodBeforeAdvice;

import com.mjduan.project.log.LOG;

/**
 * @author mjduan@yahoo.com 2018-05-03 13:20
 * @version 0.0.1
 * @since 0.0.1
 */
public class GreetingBeforeAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        LOG.info("Greeting before advice: {}", args[0]);
    }
}

spring的xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
	http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd">

    <context:component-scan base-package="com.mjduan.project"/>

    <bean id="seller" class="com.mjduan.project.example4.Seller"/>
    <bean id="waiter" class="com.mjduan.project.example4.Waiter"/>
    <bean id="greetingAdvice" class="com.mjduan.project.example4.GreetingBeforeAdvice"/>

    <bean id="beanNameAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="beanNames" value="*er"/>
        <property name="interceptorNames" value="greetingAdvice"/>
        <property name="optimize" value="true"/>
    </bean>
</beans>

 单元测试代码如下:

@Test
public void test1(){
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring4.xml");
    Waiter waiter = applicationContext.getBean("waiter", Waiter.class);
    Seller seller = applicationContext.getBean("seller", Seller.class);

    waiter.greetTo("John");
    seller.greetTo("Tom");
}

    到这里,铺垫基本是完成了,下面开始分析。

    上面的单元测试代码中waiter、seller其实是Cglib生成的代理对象,这个可以自己打断点查看。

2.代理类是如何生成的

    1.BeanPostProcessor的postProcessAfterInitialization作用

    先说明下BeanPostProcessor的postProcessAfterInitialization(...)的作用,因为这个是后面内容的铺垫。假设Spring容器中beanNameX对应的bean是A,那么经过某个类(这个类实现了BeanPostProcessor接口)的postProcessAfterInitialization(A,beanNameX)处理后返回B,则此时Spring容器中beanNameX对应的bean是B,而不是A。

    在AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsAfterInitialization中,会调用BeanPostProcessor的postProcessAfterInitialization。

    2.Waiter代理类的是如何生成的

    AbstractAutowireCapableBeanFactory调用BeanNameAutoProxyCreator的图如下图2所示,有些步骤被我省略了,主要集中与主流程,因为分支太多了,难以全部遍及。

                         图2 AbstractAutowireCapableBeanFactory调用BeanNameAutoProxyCreator

   applicationContext.getBean("waiter", Waiter.class)时,会调用BeanNameAutoProxyCreator的postProcessAfterInitialization,如图2的步骤1,这个方法返回的对象就是用Cglib生成的代理对象waiter,所以我们从Spring中拿到的是代理类,而不是waiter对象。如下所示,wrapIfNecessary方法里面的就是我们重点分析的内容。

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
	if (bean != null) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (!this.earlyProxyReferences.containsKey(cacheKey)) {
			return wrapIfNecessary(bean, beanName, cacheKey);
		}
	}
	return bean;
}

     步骤3中的getAdvicesAndAdvisorsForBean方法,就是判断是否需要被代理的逻辑代码,这里不再分析。

     步骤4中,通过ProxyFactory的getProxy(),来生成代理类。

从Spring容器中拿到的Seller,是由Cglib生成的代理对象,同上述的Waiter。

3.思考

    3.1.思考1    

  GreetingBeforeAdvice在waiter.greetTo()和seller.greetTo()的前调用是如何实现的?这个与ProxyFactory的advisor有关,图2的步骤4中有涉及。

    3.2.思考2

    我们在spring xml配置文件中定义了BeanNameAutoProxyCreator,并未对其设置什么,Spring是如何调用它的方法postProcessAfterInitialization? 来看下AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsAfterInitialization实现,如下所示:

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
		throws BeansException {
	Object result = existingBean;
	for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
		result = beanProcessor.postProcessAfterInitialization(result, beanName);
		if (result == null) {
			return result;
		}
	}
	return result;
}

    首先从容器中取出所有的BeanPostProcessor,逐个对其调用postProcessAfterInitialization,而我们的BeanNameAutoProxyCreator恰好是BeanPostProcessor,所以在这里BeanNameAutoProxyCreator的postProcessAfterInitialization就会被调用到。

4.Reference

  • SpringFramework-3.2.8.RELEASE.
  • 精通Spring4.x企业应用开发实践,个人不是很喜欢用精通二字,但此书确实相比其它,写的稍微好多了。

(adsbygoogle = window.adsbygoogle || []).push({});

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • SpringFramework之ServletContext

                                                            图1 ServletContext接口的方法

    克虏伯
  • Springboot之AbstractApplicationEventMulticaster

      Springboot的版本2.0.9.release,对应的SpringFramework值5.0.x.release。

    克虏伯
  • Springboot中@Ehable**学习 原

        Springboot中使用EnableAutoConfiguration注解,如下所示:

    克虏伯
  • 【设计模式】——工厂方法FactoryMethod

      前言:【模式总览】——————————by xingoo   模式意图   工厂方法在MVC中应用的很广泛。   工厂方法意在分离产品与创建的两个层次,使用...

    用户1154259
  • Spring 注解大全

    @Autowired标记Spring将要解析和注入的依赖项。我们可以使用构造函数、setter或字段注入来使用这个注释。

    终身幼稚园
  • 我通过三个故事终于学明白了三种工厂模式

    接下来创建一个工厂类,功能是提供给用户的出行的方式,用户只需要告诉工厂类他需要什么方式就好了。

    TrueDei
  • 设计模式~简单工厂模式

    从上图可以看出,简单工厂模式涉及到工厂角色、抽象产品角色以及具体产品角色等三个角色:

    Vincent-yuan
  • Spring @Transactional踩坑记

    @Transactional踩坑记 总述 ​ Spring在1.2引入@Transactional注解, 该注解的引入使得我们可以简单地通过在方法或者类上添加@...

    SecondWorld
  • 10(01)总结形式参数,包,修饰符,内部类

    类,抽象类,接口的综合小练习 /* 教练和运动员案例(学生分析然后讲解) 乒乓球运动员和篮球运动员。 乒乓球教练和篮球教练。 为了出国交流,跟乒乓球相关...

    Java帮帮
  • 用LOL获得BUFF场景来看待Guava之事件总线

    每次都new AsyncEventBus();这种方式为免有点太low,可以使用@Bean注解构建一个Bean对象

    诺浅

扫码关注云+社区

领取腾讯云代金券