Spring 4.3 源码分析之 配置式Aop (一)

1. Spring Aop 配置式Aop Demo

上篇中通过手动写代码, 将 Pointcut, MethodInterceptor, ProxyFactory, TargetBean 组装起来, 最终通过 ProxyFactory.getProxy() 来获取代理的对象; 而本篇将结合 Spring 中的 FactoryBean 结合起来, 只需要在 xml 中配置少许 信息就能对目标对象生成代理对象, 配置的信息如下:

<!-- Simple target -->
<bean id="kk" class="com.lami.foodie.aop.aop1.KK"></bean>

<!-- MethodInterceptor -->
<bean id="simpleMethodInterceptor" class="com.lami.foodie.aop.aop1.SimpleMethodInterceptor"/>

<!-- ProxyFactoryBean -->
<bean id="test1" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="interfaces"><value>com.lami.foodie.aop.aop1.KK</value></property>
    <property name="target"><ref bean="kk" /></property>
    <property name="interceptorNames"><value>simpleMethodInterceptor</value></property>
</bean>

PS: 上面配置文件对应的类在上篇中已存在

2. Spring Aop 配置式Aop 组件 ProxyFactoryBean

ProxyFactoryBean 是通过 FactoryBean 来针对指定的 targetBean 来创建动态代理对象 (PS: FactoryBean 是 Spring IOC 中针对创建指定对象的抽象工厂, 毕竟有些类的创建过程是很复杂的, 所以出现了 FactoryBean 这个角色), 其主要有一下属性:

/**
 * This suffix in a value in an interceptor list indicates to expand globals.
 */
public static final String GLOBAL_SUFFIX = "*";

protected final Log logger = LogFactory.getLog(getClass());

// 从字面意思上, 下面是拦截器, 其实不然, 下面支持获取指定前缀的 advice/MethodInterceptor, 当然也可以在下面的数组的最后一位设置 target 的名字 (PS: 这在checkInterceptorNames中解析)
private String[] interceptorNames;

// 代理的目标类 target, 这个 String 主要是通过xml 设置, 或通过 checkInterceptorNames 方法上
private String targetName;

// 自动获取 target 上的 interface
private boolean autodetectInterfaces = true;

// PS: 这里的 singleton 指的是 advice 是否是 singleton
private boolean singleton = true;

// 前置 Spring aop 中主要使用的是 MethodInterceptor, 而在 xml 中配置的 <aop> 标签中解析出来的都是 advice, 这时需要对应的适配器 -> 将 advice 适配成 MethodInterceptor
// 这里的 advisorAdapterRegistry 中主要是将 advice, MethodInterceptor 包装成 Advisor, 与将 Advisor 转成 MethodInterceptor
private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();

// 设置是否 动态代理的配置信息是否变化
private boolean freezeProxy = false;

// 创建动态代理时使用的 classLoader
private transient ClassLoader proxyClassLoader = ClassUtils.getDefaultClassLoader();

// 配置是否已经配置了 classLoader
private transient boolean classLoaderConfigured = false;

// 创建动态时使用的  BeanFactory
private transient BeanFactory beanFactory;

// 标示动态代理中的 advisorChain 链是否已经创建好
/** Whether the advisor chain has already been initialized */
private boolean advisorChainInitialized = false;

// 这个类其实就是动态代理创建的最终对象
/** If this is a singleton, the cached singleton proxy instance */
private Object singletonInstance;

以上这些属性中最重要的还是 interceptorNames(拦截器链), targetName(代理的类), 将 advice/advisor 转换成 MethodInterceptor 的适配器注册工厂 advisorAdapterRegistry, 有了以上的属性, 则再通过下面的的两个方法就能创建代理对象:

public Object getObject() throws BeansException {
    // 这里初始化通知器链
    initializeAdvisorChain();
    // 这里对 Singleton prototype 的类型进行区分, 生成对应的 Proxy
    if (isSingleton()) {
        return getSingletonInstance();
    }
    else { // prototype 模式则每次创建一个类
        if (this.targetName == null) {
            logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
                    "Enable prototype proxies by setting the 'targetName' property.");
        }
        return newPrototypeInstance();
    }
}

// 动态代理对象 singletonInstance
private synchronized Object getSingletonInstance() {
    if (this.singletonInstance == null) {
        this.targetSource = freshTargetSource();                // 返回对应的 target
        // 自动获取需要动态代理的接口
        if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
            // Rely on AOP infrastructure to tell us what interfaces to proxy.
            Class<?> targetClass = getTargetClass();
            if (targetClass == null) {
                throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
            }
            // 这里设置代理对象的接口
            setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));     // 获取 targetClass 的所有的 interface
        }
        // Initialize the shared singleton instance.
        super.setFrozen(this.freezeProxy);
        // 注意这里的方法将会使用 ProxyFactory 来生成
        // 通过 createAopProxy 返回 合适的 AopProxy(JdkDynamicAopProxy, CglibAopProxy), 并将 AopProxy 传如 getProxy 方法中进行动态代理
        this.singletonInstance = getProxy(createAopProxy());
    }
    return this.singletonInstance;
}
3. Spring Aop 配置式Aop ProxyFactoryBean 创建对象流程

整个创建代理对象的整个过程是在 ProxyFactoryBean 的 getObject 方法中触发的, 大体流程如下:

1. 初始化 AdvisorChain
    1.1 循环遍历 interceptorNames, 从 BeanFactory 中获取对应 Advice, MethodInterceptor, 通过 AdvisorAdaptorRegistry 包装成 Advisor 加入到 AdvisorChain 里面
2. 调用 getSingletonInstance方法, 创建动态代理对象
    2.1 根据 targetName 从 BeanFactory 中拿到对应对象
    2.2 获取 target 类实现的所有接口 <- 为代理这些接口准备数据
    2.3 DefaultAopProxyFactory 根据 AdvisedSupport 来创建合适的 AopProxy (JdkDynamicAopProxy, CglibAopProxy) , 判断的条件主要是 optimize, targetClass, hasNoUserSuppliedProxyInterfaces
    2.4 Aop 根据已经获取到的 Advisor, Target 等信息创建代理对象
(PS: 其实理解了上篇的编程式 Aop, 则这里的 ProxyFactoryBean 就很好理解了; 另外插一下 aop 很重要, Spring 里面的很多特性都是基于 aop, 比如 事务, 缓存, Hystrix 等)
4. 总结:

本篇讲述通过 ProxyFactoryBean 来创建代理对象, 整个创建过程其实和上篇差不多, 也就是说 ProxyFactoryBean 只是做了 MethodInterceptor 的自动获取, 以及 通过 ProxyFactory 创建代理对象, 而问题也在 ProxyFactoryBean 中, 从这个类中我们发现, 只要需要创建一个动态代理对象, 则就需要配置一个 ProxyFactoryBean <-- 而在一般项目中需要创建很多动态代理对象, 可想而知这是多么可怕的事情, 所以就出现了 AbstractAutoProxyCreator(自动获取 BeanFactory 中的所有 Advice/MethodInterceptor, 并针对所有符合 Pointcut 的对象创建代理对象, 这也是下篇将叙述的内容)。

5. 参考:

Spring Aop核心源码分析
Spring技术内幕
Spring 揭秘
Spring 源码深度分析
开涛 Spring 杂谈
伤神 Spring 源码分析
Spring源码情操陶冶

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,477评论 19 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 47,080评论 6 342
  • 1. Spring Aop 编程式Aop Demo 整个 "编程式Aop"的参与者如下: 被代理的对象, 方法拦截...
    爱吃鱼的KK阅读 4,699评论 0 4
  • 事先说明此文章并不是针对《互赞好么?不好!》而写,我只是发表我自己的看法。 常年深居居与简书的书友们有时会收到其他...
    不懂wz阅读 4,030评论 28 8
  • (文∕尔雅图南) 你走后静湖所有的荷花都开了,它们婀娜妩媚,令人沉醉。我想,如果你还在,我一定会有勇气约你出来陪我...
    蒲柳人家宝龙陈鹏阅读 3,437评论 1 2