Spring AOP 代理
1. Spring AOP 增强类型
AOP 联盟为通知 Advice 定义了 org.aopalliance.aop.Interface.Advice
。
Spring 按照通知 Advice 在目标类方法的连接点位置,可以分为 5 类
-
前置通知:
org.springframework.aop.MethodBeforeAdvice
在目标方法执行前实施增强
-
后置通知:
ogr.springframework.aop.AfterReturningAdvice
在目标方法执行后实施增强
-
环绕通知:
ogr.aopalliance.intercept.MethodInterceptor
在目标方法执行前后实施增强
-
异常抛出通知:
org.springframework.aop.ThrowsAdvice
在方法抛出异常后实施增强
-
引介通知:
org.springframework.aop.IntroductionInterceptor
在目标类中添加一些新的方法和属性(Spring 中不支持,只支持对方法增强)
2. Spring AOP 切面类型
- Advisor:代表一般切面,Advice 本身就是一个切面,对目标类所有方法进行拦截
- PointcutAdvisor:代表具有切点的切面,可以指定拦截目标类哪些方法(实现时需要配置切面信息,即类中要增强的方法设置bean)
- IntroductionAdvisor:代表引介切面,针对引介通知而使用切面(不要求掌握)
3. Advisor 切面案例 (xml 配置)
ProxyFactoryBean 常用可配置属性:
-
target:代理的目标对象
-
proxyInterfaces:代理要实现的接口
如果多个接口可以使用以下格式赋值:
<list> <value>xxx<value> ... </list>
-
proxyTargetClass:是否对类代理而不是接口,设置为 true 时,使用 CGLib 代理
-
interceptorNames:需要织入目标的 Advice
-
singleton:返回代理是否为单实例,默认为单例
-
optimize:当设置为 true 时,强制使用 CGLib(默认为 JDK 动态代理)
举个例子:
applicationContext.xml
:
<!-- 1. 配置目标类 -->
<bean id="studentDao" class="com.test.aop.demo3.StudentDaoImpl" />
<!-- 2. 前置通知类型 -->
<bean id="myBeforeAdvice" class="com.test.aop.demo3.MyBeforeAdvice" />
<!-- 3. Spring 的 AOP 产生代理对象 -->
<bean id="studentDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 配置目标类 -->
<property name="target" ref="studentDao" />
<!-- 实现的接口 -->
<property name="proxyInterfaces" value="com.test.aop.demo3.StudentDao" />
<!-- 采用拦截的名称 -->
<!-- 注意这个地方使用 value -->
<property name="interceptorNames" value="myBeforeAdvice" />
<!-- 默认使用 jdk 动态代理,使用下列语句更改为 CGLib 生成代理 -->
<property name="optimize" value="true" />
</bean>
通知类:
public class MyBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("前置增强========");
}
}
4. 演示(Advisor 方式)
首先要使用 AOP,我们需要导入两个包:
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
然后定义接口和实现类,再定义一个通知类(如前置通知),然后我们在 xml 文件中配置目标类和通知类,利用 Spring AOP 产生代理(通过bean配置),具体可参考上述例子,使用注解的方式在测试类中导入增强后的 bean:studentDaoProxy。
5. PointcutAdvisor 切点切面
使用普通 Advice 作为切面,将对目标类所有方法进行拦截,不够灵活,在实际开发中常采用带有切点的切面。
常用 PointcutAdvisor 实现类
DefaultPointcutAdvisor
最常用的切面类型,它可以通过任意Pointcut
和Advice
组合定义切面JdkRegexpMethodPointcut
构造正则表达式切点
这里以 JdkRegexpMethodPointcut
为例:
applicationContext.xml
:
<!-- 配置目标类 -->
<bean id="customerDao" class="com.test.aop.demo4.CustomerDao" />
<!-- 配置通知 -->
<bean id="myAroundAdvice" class="com.test.aop.demo4.MyAroundAdvice" />
<!-- 一般的切面是使用通知作为切面的,因为要对目标类的某个方法进行增强就需要配置一个带有切入点的切面 -->
<bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- pattern 中配置正则表达式:. 任意字符 * 任意次数-->
<!--<property name="pattern" value=".*save.*" />-->
<property name="patterns" value=".*save.*,.*delete.*" />
<property name="advice" ref="myAroundAdvice" />
</bean>
<!-- 配置产生代理 -->
<bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="customerDao" />
<!-- 代表使用 CGLib 方法 -->
<property name="proxyTargetClass" value="true" />
<property name="interceptorNames" value="myAdvisor" />
</bean>
通知类:
public class MyAroundAdvice implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("环绕前增强...");
// 执行目标方法
Object obj = methodInvocation.proceed();
System.out.println("环绕后增强...");
return obj;
}
}
6. 自动创建代理
前面的案例中,每个代理都是通过 ProxyFactoryBean
织入切面代理,在实际开发中,非常多的 Bean 每个都配置 ProxyFactoryBean
开发维护量巨大。
解决方案:自动创建代理。
自动创建代理的三种方式:
BeanNameAutoProxyCreator
根据 Bean 名称创建代理DefaultAdvisorAutoProxyCreator
根据 Advisor 本身包含信息创建代理AnnotationAwareAspectJAutoProxyCreator
基于 Bean 中的 AspectJ 注解进行自动代理(最主要方式)
7. BeanNameAutoProxyCreator
举例
对所有以 Dao 结尾 Bean 所有方法使用代理:
<!-- 配置目标类 -->
<bean id="studentDao" class="com.test.aop.demo5.StudentDaoImpl" />
<bean id="customerDao" class="com.test.aop.demo5.CustomerDao" />
<!-- 配置增强 -->
<bean id="myAroundAdvice" class="com.test.aop.demo5.MyAroundAdvice" />
<bean id="myBeforeAdvice" class="com.test.aop.demo5.MyBeforeAdvice" />
<!-- 自动创建代理不需要 id -->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="*Dao" />
<property name="interceptorNames" value="myBeforeAdvice" />
</bean>
8. DefaultAdvisorAutoProxyCreator
举例
配置环绕代理案例:
<!-- 配置目标类 -->
<bean id="studentDao" class="com.test.aop.demo6.StudentDaoImpl" />
<bean id="customerDao" class="com.test.aop.demo6.CustomerDao" />
<!-- 配置增强 -->
<bean id="myAroundAdvice" class="com.test.aop.demo6.MyAroundAdvice" />
<bean id="myBeforeAdvice" class="com.test.aop.demo6.MyBeforeAdvice" />
<!-- 配置切面 -->
<bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="pattern" value="com.test.aop.demo6.CustomerDao.save" />
<property name="advice" ref="myAroundAdvice" />
</bean>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />