文章目录
1. ProxyFactoryBean样例
业务接口ISubject.java:
package com.test.springaop;
public interface ISubject {
void request();
}
业务类 SubjectImpl.java:
package com.test.springaop;
public class SubjectImpl implements ISubject{
@Override
public void request() {
System.out.println("target request ..");
}
}
通知(Advice),TargetBeforeAdvice.java:
package com.test.springaop;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class TargetBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("target before");
}
}
Main方法,MainClass.java:
package com.test.springaop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainClass {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
//getBean的入参是代理bean,但是获得结果确实一个ISubject类型
ISubject subject = ((ISubject) context.getBean("proxyFactoryBean"));
subject.request();
}
}
spring配置aop.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.test.springaop.ISubject</value>
</property>
<property name="target">
<bean class="com.test.springaop.SubjectImpl"/>
</property>
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
<!--DefaultPointcutAdvisor 使用的切点为True单件,即匹配任何方法都为true,也就是为所有方法增强 -->
<bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice">
<bean class="com.test.springaop.TargetBeforeAdvice"/>
</property>
</bean>
</beans>
执行结果:
target before
target request ..
1.1 ProxyFactoryBean的缺陷
有2个缺陷:
-
如果我们只能指定单一的Bean的AOP, 如果多个Bean需要创建多个ProxyFactoryBean 。
target属性只能填一个目标对象,不支持多个
-
我们的拦截器的粒度只控制到了类级别,类中所有的方法都进行了拦截。
假设ISubject接口还有很多方法:public interface ISubject { void request(); void request2(); void request3(); }
ProxyFactoryBean会作用在所有的方法上,粒度太粗了,比如我不想在request3()上生效的话,则不支持。
2. Spring Aop
2.1 Spring 通知类型
通知(Advice)其实就是对目标切入点进行增强的内容,Spring AOP 为通知(Advice)提供了 org.aopalliance.aop.Advice 接口。
Spring 通知按照在目标类方法的连接点位置,可以分为以下五种类型,下表 所示:
名称 | 说明 |
---|---|
org.springframework.aop.MethodBeforeAdvice(前置通知) | 在方法之前自动执行的通知称为前置通知,可以应用于权限管理等功能。 |
org.springframework.aop.AfterReturningAdvice(后置通知) | 在方法之后自动执行的通知称为后置通知,可以应用于关闭流、上传文件、删除临时文件等功能。 |
org.aopalliance.intercept.MethodInterceptor(环绕通知) | 在方法前后自动执行的通知称为环绕通知,可以应用于日志、事务管理等功能。 |
org.springframework.aop.ThrowsAdvice(异常通知) | 在方法抛出异常时自动执行的通知称为异常通知,可以应用于处理异常记录日志等功能。 |
org.springframework.aop.IntroductionInterceptor(引介通知) | 在目标类中添加一些新的方法和属性,可以应用于修改旧版本程序(增强类)。 |
我们上面的例子用的是MethodBeforeAdvice
2.2 声明式 Spring AOP
Spring 创建一个 AOP 代理的基本方法是使用 org.springframework.aop.framework.ProxyFactoryBean,这个类对应的切入点和通知提供了完整的控制能力,并可以生成指定的内容。
ProxyFactoryBean 类中的常用可配置属性如下表 所示:
-
target 代理的目标对象
注意:只能填一个目标对象,不支持多个,这也是ProxyFactoryBean用法的致命问题,如果多个Bean需要创建多个ProxyFactoryBean。 -
proxyInterfaces 代理要实现的接口,如果有多个接口,则可以使用以下格式赋值:
<list> <value ></value> ... </list>
-
proxyTargetClass 是否对类代理而不是接口,设置为 true 时,使用 CGLIB 代理
-
interceptorNames 需要植入目标的 Advice,支持多个
-
singleton 返回的代理是否为单例,默认为 true(返回单实例)
-
optimize 当设置为 true 时,强制使用 CGLIB
3. ProxyFactoryBean
我们先来回顾一下Spring使用aop的过程:
-
配置好ProxyFactoryBean,给ProxyFactoryBean设置一个beanId
-
然后通过ac.getBean(beanId),就取得被ProxyFactoryBean代理的对象,不是ProxyFactoryBean自身
ac.getBean(
&beanId
),才能取得ProxyFactoryBean对象
顾名思义ProxyFactoryBean,本质上是一个FactoryBean,因此上述原因得以解释。
FactoryBean的特性可以参见 《beanFactory与FactoryBean的区别》
3.1 原理
参见《Spring深度解析-12、AOP代理对象创建原理-ProxyFactoryBean》中<ProxyFactoryBean创建AopProxy源码解析>章节