一、Spring AOP
AOP 开发规范 : AOP联盟为通知Advice定义了org.aopalliance.aop.Interface.Advice
Spring AOP 实现 AOP联盟定义 规范
1、传统Spring AOP 提供 五类 Advice
前置通知(代码增强) org.springframework.aop.MethodBeforeAdvice
* 在目标方法执行前实施增强
后置通知 org.springframework.aop.AfterReturningAdvice
* 在目标方法执行后实施增强
环绕通知 org.aopalliance.intercept.MethodInterceptor
* 在目标方法执行前后实施增强
异常抛出通知 org.springframework.aop.ThrowsAdvice
* 在方法抛出异常后实施增强
引介通知 org.springframework.aop.IntroductionInterceptor
* 在目标类中添加一些新的方法和属性
2、 Spring 的切面 Advisor
Advisor 就是对PointCut 应用 Advice (通常所说Advisor 指只有一个Point 和 一个 Advice )
类型:
Advisor : 代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截 (没有切点)
PointcutAdvisor : 代表具有切点的切面,可以指定拦截目标类哪些方法
IntroductionAdvisor : 代表引介切面,针对引介通知而使用切面
3、案例一(不带切点的切面) : 使用普通Advisor, 使用Advice作为一个切面 ,不定义切点,拦截目标类 所有方法
1) 导入jar包
导入 aop联盟的规范 : com.springsource.org.aopalliance-1.0.0.jar
导入 spring aop实现 : spring-aop-3.2.0.RELEASE.jar
2) 编写被代理 接口和实现类
package lsq.spring.aop.advisor;
/**
* 客户管理接口
*
* @author lishanquan
*
*/
public interface CustomerDao {
public void add();
public void search();
public void update();
public void delete();
}
package lsq.spring.aop.advisor;
public class CustomerDaoImpl implements CustomerDao{
@Override
public void add() {
System.out.println("客户添加……");
}
@Override
public void search() {
System.out.println("客户查询……");
}
@Override
public void update() {
System.out.println("客户更新……");
}
@Override
public void delete() {
System.out.println("客户删除……");
}
}
3) 编写前置通知 (在目标方法前执行...)
package lsq.spring.aop.advisor;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
/**
* 自定义前置增强方法
*
* @author lishanquan
*
*/
public class MyMethodBeforeAdvice implements MethodBeforeAdvice{
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("前置增强~~~~~~~~~~~~~~~~~~");
}
}
4) 为目标对象创建 , 配置applicationContext.xml
使用ProxyFactoryBean 为目标对象创建代理,ProxyFactoryBean会根据属性设置自动选择这两种方式进行动态代理。
<!-- 被代理对象 -->
<bean id="customerDao" class="lsq.spring.aop.advisor.CustomerDaoImpl"></bean>
<!-- 增强 -->
<bean id="myBeforeAdvice" class="lsq.spring.aop.advisor.MyMethodBeforeAdvice"></bean>
<!-- 创建代理 -->
<bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 目标 -->
<property name="target" ref="customerDao"></property>
<!-- 针对接口代理 -->
<property name="proxyInterfaces" value="lsq.spring.aop.advisor.CustomerDao"></property>
<!-- 增强
interceptorNames 表示可以运用多个 Advice, 必须写value
-->
<property name="interceptorNames" value="myBeforeAdvice"></property>
</bean>
5)测试
package lsq.spring.aop.advisor;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* 测试不带切点的切面
*
* @author lishanquan
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class SpringTest {
//对测试对象进行自动注入
@Autowired
@Qualifier("customerDaoProxy") //☆注意:这里一定要注入增强后的代理对象,不能注入原来的Bean
private CustomerDao customerDao;
@Test
public void demo(){
customerDao.add();
customerDao.search();
customerDao.update();
customerDao.delete();
}
}
☆注意:在编程时,应该使用 ProxyFactoryBean 创建后代理对象(CustomerDAOProxy ), 不要引入原来Bean (CustomerDAO)
4、案例二 (带有切点的切面): 定义单独切点切面,指定被代理对象 哪些方法 会被增强
* JdkRegexpMethodPointcut 构造正则表达式切点
* 使用正则表达式 切点切面 org.springframework.aop.support.RegexpMethodPointcutAdvisor
1)创建被代理对象 (没有接口 的类 )
package lsq.spring.aop.pointcutadvisor;
/**
* 订单操作类
*
* @author lishanquan
*
*/
public class OrderDao {
public void save(){
System.out.println("添加订单……");
}
public void edit(){
System.out.println("编辑订单……");
}
public void update(){
System.out.println("修改订单……");
}
public void delete(){
System.out.println("删除订单……");
}
}
2) 增强 (编写环绕通知)
package lsq.spring.aop.pointcutadvisor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* 自定义环绕增强
*
* @author lishanquan
*
*/
public class MyMethodInterceptor implements MethodInterceptor{
@Override
//进行增强方法,methodInvocation调用目标方法
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("环绕增强 方法前增强……");
Object result = methodInvocation.proceed();//目标方法执行
System.out.println("环绕增强 方法后增强……");
return result;
}
}
3) 通过配置 ProxyFactory 为目标对象创建代理
package lsq.spring.aop.pointcutadvisor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* 自定义环绕增强
*
* @author lishanquan
*
*/
public class MyMethodInterceptor implements MethodInterceptor{
@Override
//进行增强方法,methodInvocation调用目标方法
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("环绕增强 方法前增强……");
Object result = methodInvocation.proceed();//目标方法执行
System.out.println("环绕增强 方法后增强……");
return result;
}
}
4)测试
package lsq.spring.aop.pointcutadvisor;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class SpringTest {
//自动注入测试对象
@Autowired
@Qualifier("orderDaoProxy")
private OrderDao orderDao;
@Test
public void demo(){
orderDao.save();
orderDao.edit();
orderDao.update();
orderDao.delete();
}
}
☆☆☆注意:
正则表达式案例
lsq\.spring\.aop\.pointcutadvisor\.OrderDao\.save.* ---- 执行OrderDAO 的save方法
.*save.* ----- 包含save方法
<property name="patterns" value=".*save.*,.*delete.*"></property> ---- 同时增强save和delete方法
5、 自动代理
使用ProxyFactoryBean 创建代理,需要为每个Bean 都配置一次 ,非常麻烦
自动代理原理: 根据xml中配置advisor的规则,得知切面对哪个类的哪个方法进行代理 (切面中本身就包含 被代理对象信息) ,就不需要ProxyFactoryBean ,使用 BeanPostProcessor 完成自动代理
BeanNameAutoProxyCreator 根据Bean名称创建代理
DefaultAdvisorAutoProxyCreator 根据Advisor本身包含信息创建代理
* AnnotationAwareAspectJAutoProxyCreator 基于Bean中的AspectJ 注解进行自动代理
1) BeanNameAutoProxyCreator
<!-- 被代理对象 -->
<bean id="customerDao" class="lsq.spring.aop.advisor.CustomerDaoImpl"></bean>
<bean id="orderDao" class="lsq.spring.aop.pointcutadvisor.OrderDao"></bean>
<!-- 增强 -->
<bean id="myBeforeAdvice" class="lsq.spring.aop.advisor.MyMethodBeforeAdvice"></bean>
<bean id="myMethodInterceptor" class="lsq.spring.aop.pointcutadvisor.MyMethodInterceptor"></bean>
<!-- 第一种BeanName自动代理 -->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!-- 对所有以Dao结尾的Bean进行代理 -->
<property name="beanNames" value="*Dao"></property>
<!-- 增强 -->
<property name="interceptorNames" value="myMethodInterceptor"></property>
</bean>
package lsq.spring.aop.autoproxy;
import lsq.spring.aop.advisor.CustomerDao;
import lsq.spring.aop.pointcutadvisor.OrderDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext2.xml")
public class SpringTest {
@Autowired
@Qualifier("customerDao")
private CustomerDao customerDao;
@Autowired
@Qualifier("orderDao")
private OrderDao orderDao;
@Test
public void demo(){
customerDao.add();
orderDao.save();
}
}
效果:
*** 自动代理和ProxyFactoryBean 本质区别 :
ProxyFactoryBean, 先有被代理对象, 传递ProxyFactoryBean,创建代理
自动代理 , 在Bean构造过程中, 使用后处理Bean 创建代理,返回构造完成对象就是代理对象
2) DefaultAdvisorAutoProxyCreator
基于切面信息进行代理
<!-- 被代理对象 -->
<bean id="customerDao" class="lsq.spring.aop.advisor.CustomerDaoImpl"></bean>
<bean id="orderDao" class="lsq.spring.aop.pointcutadvisor.OrderDao"></bean>
<!-- 增强 -->
<bean id="myBeforeAdvice" class="lsq.spring.aop.advisor.MyMethodBeforeAdvice"></bean>
<bean id="myMethodInterceptor" class="lsq.spring.aop.pointcutadvisor.MyMethodInterceptor"></bean>
<!-- 切面 -->
<bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 切点拦截信息 -->
<property name="patterns" value="lsq\.spring\.aop\.pointcutadvisor\.OrderDao.save.*"></property>
<!-- 增强 -->
<property name="advice" ref="myBeforeAdvice"></property>
</bean>
<!-- 第二种 基于切面信息自动代理 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
package lsq.spring.aop.autoproxy;
import lsq.spring.aop.pointcutadvisor.OrderDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext3.xml")
public class SpringTest2 {
@Autowired
@Qualifier("orderDao")
private OrderDao orderDao;
@Test
public void demo(){
orderDao.save();
orderDao.delete();
}
}
效果: