文章目录
前言
在 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 实现了 FactoryBean 接口
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