AOP增强类型
Spring按照通知Advice在目标类的方法的连接位置,可以分为5类:
- 前置通知:在目标方法执行前实施增强(MethodBeforeAdvice)
- 后置通知:在目标方法执行后实施增强(AfterReturningAdvice)
- 环绕通知:在目标方法执行前后实施增强(MethodInterceptor)
- 异常抛出通知:在方法抛出异常后实施增强(ThrowsAdvice)
- 引介通知:在目标类中添加新的方法和属性(IntroductionInterceptor)
AOP切面类型
Advisor
Advice本身就是一个切面,可以对目标类的所有方法进行拦截成为一般切面(Advisor)
示例:
首先需要引入jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.13.RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.13.RELEASE</version>
</dependency>
spring也集成了测试的方法,可以简化测试类的写法
创建一个类继承MethodBeforeAdvice
接口,实现接口中的before()
方法作为前置增强类
package com.spring.aop;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* @author admin
*/
public class MyBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("前置类型增强...");
}
}
创建接口类和实现类(省略),在spring配置文件中创建bean并设置代理类
<?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="userDao" class="com.spring.aop.dao.impl.UserDaoImpl"/>
<!-- 引入前置增强类型 -->
<bean id="myBeforeAdvice" class="com.spring.aop.MyBeforeAdvice"/>
<!-- 设置代理类 -->
<bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 设置目标类 -->
<property name="target" ref="userDao"/>
<!-- 设置接口 -->
<!-- 如果需要实现对个接口,可以使用list标签 -->
<property name="proxyInterfaces" value="com.spring.aop.dao.UserDao"/>
<!-- 设置拦截后需要增强的方法 -->
<property name="interceptorNames" value="myBeforeAdvice"/>
</bean>
</beans>
测试类:
package com.spring.aop;
import com.spring.aop.dao.UserDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
// 通过注解加载配置文件
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestSpring {
@Resource(name = "userDaoProxy")
private UserDao userDao;
@Test
public void test1() {
userDao.save();
userDao.update();
userDao.delete();
userDao.select();
}
}
除了上述常用的属性还有其他一些其他常用属性,例如:
- proxyTargetClass:是否对类代理,设置为true时使用CGLib代理
- interceptorNames:需要织入的目标的Advice
- singleton:返回代理是否为单例模式,默认为单例模式
- optimize:设置为true时,强制使用CGLib
PointcutAdvisor
只对指定切入点进行增强的切面被成为切点切面(PointcutAdvisor)。对于一般切面来说,对所有方法进行拦截不够灵活,所以一般开发过程中以切点切面为主
实现切点切面主要有两种方法,DefaultPointcutAdvisor是最常见的切面类型,他可以任意组合PointCut和Advice形成切面。另一种为使用JdkRegexpMethodPointcut构造正则表达式指定切点。
示例:
<?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="customerDao" class="com.imooc.aop.demo4.CustomerDao"/>
<!-- 配置通知 -->
<bean id="myAroundAdvice" class="com.imooc.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"/>
<property name="proxyTargetClass" value="true"/>
<property name="interceptorNames" value="myAdvisor"/>
</bean>
</beans>
IntroductionAdvsior
针对引介通知而使用的切面
自动代理
如果项目较为复杂需要代理的bean很多,使用上述的方法就需要每一个bean都创建一个ProxyFactoryBean,此时维护就十分麻烦,因此Spring提供了自动代理的功能。
自动代理重要提供了三种方案
- 根据bean名称创建代理(BeanNameAutoProxyCreator)
- 根据Advisor本身包含信息创建代理(DefualtAdvisorAutoProxyCreator)
- 基于Bean中的AspectJ注解进行自动代理(AnnotationAwareAspectJAutoProxyCreator)
示例:
使用名称进行代理:
<?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="userDao" class="com.spring.aop.dao.impl.UserDaoImpl"/>
<bean id="productDao" class="com.spring.aop.dao.ProductDao"/>
<!-- 引入前置增强类型 -->
<bean id="myBeforeAdvice" class="com.spring.aop.MyBeforeAdvice"/>
<!-- 设置代理类 -->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!-- 配置需要拦截的bean,这里是对所有的以Dao结尾的类的所有方法进行拦截 -->
<property name="beanNames" value="*Dao"/>
<property name="interceptorNames" value="myBeforeAdvice"/>
</bean>
</beans>
BeanNameAutoProxyCreator是不需要指定id的,在Spring中会自动调用bean
包含信息创建代理:
<?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="userDao" class="com.spring.aop.dao.impl.UserDaoImpl"/>
<bean id="productDao" class="com.spring.aop.dao.ProductDao"/>
<!-- 引入前置增强类型 -->
<bean id="myBeforeAdvice" class="com.spring.aop.MyBeforeAdvice"/>
<bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="pattern" value="com\.spring\.aop\.dao\.ProductDao\.save"/>
<property name="advice" ref="myBeforeAdvice"/>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
</beans>