0.10、Spring 源码学习 —— ProxyBeanFactory

前言

Spring 源码学习 FactoryBean 这篇文章中,我们了解了一下 Spring 中 FactoryBean 接口的基本使用,事实上,该接口在 Spring 中有着诸多的扩展应用,比如本文介绍的 ProxyBeanFactory ,其就是作为 Spring AOP 的一种实现方式。

Spring AOP 的实现方式

Spring AOP 通过切面实现了 Spring功能的横线扩展。
Spring AOP 的具体实现上,既可以结合第三方组件 AspectJ,也可以独立实现。
就一般而言Spring 结合AspectJ 会更常见一些 ,这个实现上又可以分为编程式注解式
而本文介绍的 ProxyBeanFactory 是 Spring 依靠自身代码实现的 Spring AOP 功能。

进入正题:ProxyBeanFactory 实现 SpringAOP 例子

源码路径

org.springframework.aop.framework.ProxyBeanFactory

ProxyBeanFactory 实现了 BeanFactory 接口

ProxyBeanFactory 实现了 BeanFactory 接口,这意味着,ProxyBeanFactory 通过 覆盖重写 getObject() 方法来进行代理对象的创建工作。当然,如果你有兴趣可以看一下 ProxyBeanFactory.getObject() ,本文仅仅介绍一下 ProxyBeanFactory 的使用。
在这里插入图片描述

向 ProxyBeanFactory 注入 切面、目标类

切面需要 通知、切点来完成具体的功能,当然,SpringAOP 是通过动态代理的方式来完成这个过程的,所以
我们需要先定义被代理类、切面。
和AspectJ不同的是,ProxyBeanFactory 是通过将切面、被代理类(目标类)注入到自己的属性内,然后再将自己注册为 Spring Bean ,在调用时,需要通过 这个 SpringBean 来进行调用。

当然,Spring 在实现动态代理的时候支持 JDK动态代理和Cglib 动态代理,所以这也会体现在属性的配置上。

代码

定义被代理类 SimpleBean2

这个 类会被注册为 SpringBean

package com.bestcxx.test2;

/**
 * @Title: 普通 bean
 * @Description: 自定义的一个普通 bean,正常而言我们会将该类注册为 SpringBean2
 */
public class SimpleBean2 {
    /**自定义参数:时间戳*/
    private Long timestamp;
    public void printTimestamp(){
        System.out.println("timestamp="+this.timestamp);
    }
    public Long getTimestamp() {
        return timestamp;
    }
    public void setTimestamp(Long timestamp) {
        this.timestamp = timestamp;
    }
}
定义 切面 MyAdvisor

ProxyBeanFactory 和 AspectJ 的一大区别在于,ProxyBeanFactory 定义切面的不如AspectJ灵活,这体现在你必须先集成某一个接口,用于实现某一个功能。本文只是举例子,仅实现 MethodBeforeAdvice接口作为切面类。

名称说明
org.springframework.aop.MethodBeforeAdvice(前置通知)在方法之前自动执行的通知称为前置通知,可以应用于权限管理等功能。
org.springframework.aop.AfterReturningAdvice(后置通知)在方法之后自动执行的通知称为后置通知,可以应用于关闭流、上传文件、删除临时文件等功能。
org.aopalliance.intercept.MethodInterceptor(环绕通知)在方法前后自动执行的通知称为环绕通知,可以应用于日志、事务管理等功能。
org.springframework.aop.ThrowsAdvice(异常通知)在方法抛出异常时自动执行的通知称为异常通知,可以应用于处理异常记录日志等功能。
org.springframework.aop.IntroductionInterceptor(引介通知)在目标类中添加一些新的方法和属性,可以应用于修改旧版本程序(增强类)。
package com.bestcxx.test2;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
 * @Title: Advisor
 * @Description: Advisor
 */
public class MyAdvisor implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("前置增强代码");
    }
}

定义配置类,并向 ProxyBeanFactory 注入 切面、目标类

这里通过 Java 代码来完成相关 SpringBean 的注册声明,你可以采取 xml 的方式.
本类做了三件事,注册被代理类、切面类为 SpringBean,将二者注入到 ProxyBeanFactory,并将其注册为 Spring Bean。
事实上,被代理类没有必要再次被注册为 SpringBean,因为在 SpringAOP 场景下,我们可以直接通过 BeanFactory 工厂类(ProxyBeanFactory)来对其进行调用,当然仅仅是没必要,不是说不可以声明。
因为每一个被代理类都需要进行类似的声明,所以特别要注意,每一次 ProxyBeanFactory 进行代理设置都是一对一的,所以要注意 beanName 不要重复。

package com.bestcxx.test2;

import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Title: ProxyFactoryBean 测试
 * @Description: MyProxyFactoryBean
 */
@Configuration
public class MyProxyFactoryBean{

    @Bean(name = "proxyFactoryBean")
    public ProxyFactoryBean getProxyFactoryBean(){
        ProxyFactoryBean p=new ProxyFactoryBean();
        p.setTarget(new SimpleBean2());
        p.setInterceptorNames("myAdvisor");
        p.setProxyTargetClass(true);
        return p;
    }

    @Bean(name="simpleBean2")
    public SimpleBean2 getSimpleBean2(){
        SimpleBean2 simpleBean2=new SimpleBean2();
        return  simpleBean2;
    }

    @Bean(name = "myAdvisor")
    public MyAdvisor getMyAdvisor(){
        MyAdvisor myAdvisor=new MyAdvisor();
        return  myAdvisor;
    }

}
xml 配置

如果你是 SpringBoot 环境,本步骤也可以免去,只要设置扫描 MyProxyFactoryBean 这个类即可。

<?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.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.bestcxx.test2"/>
</beans>
测试代码
package com.bestcxx.test2;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Title: 测试类
 * @Description: MyProxyFactoryBeanTest
 */
public class MyProxyFactoryBeanTest {

    @Test
    public void testMyBeanFactoryBean(){

        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:test/applicationContext-test2.xml");
        //SimpleBean2 simpleBean=applicationContext.getBean("simpleBean2",SimpleBean2.class);
        SimpleBean2 simpleBean=applicationContext.getBean("proxyFactoryBean",SimpleBean2.class);
        simpleBean.printTimestamp();

    }
}

测试结果
前置增强代码
timestamp=null

参考资料

[1]、https://www.cnblogs.com/lowerma/p/11759251.html
[2]、https://www.cnblogs.com/zhangzongle/p/5944906.html

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页