Java Spring-传统AOP开发
2017-11-10 17:25:48
Spring中通知Advice类型(增强代码):
- 前置通知,org.springframework.aop.MethodBeforeAdvice:方法前
- 后置通知,org.springframework.aop.AfterReturningAdvice:方法后
- 环绕通知,org.aopalliance.intercept.MethodInterceptor:方法前后
- 异常抛出通知,org.springframework.aop.ThrowsAdvice:异常抛出后的增强
- 引介通知,org.springframework.aop.IntroductionInterceptor:类的增强
Spring中切面的类型:
- Advisor:AOP中的传统切面,Advice本身就是一个切面,对所有方法进行增强 -- 也被称为不带切点的切面
- PointcutAdvisor:针对某些方法进行增强 -- 也被称为带有切点的切面
- IntroductionAdvisor:代表引介的切面,针对类的切面
Spring中AOP开发的几个步骤:
- 第一步:导入相应jar包.
* spring-aop-3.2.0.RELEASE.jar
* com.springsource.org.aopalliance-1.0.0.jar - 第二步:编写被代理对象
- 第三步:编写增强的代码,编写具体类来实现前置增强,后置增强以及环绕增强
- 第四步:配置生成代理
一、不带切点的切面:Advisor
// 接口 public interface Person { public void add(); public void delete(); } // 实现类 public class PersonImpl implements Person { @Override public void add() { System.out.println("添加方法..."); } @Override public void delete() { System.out.println("删除方法..."); } } // 测试 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-config.xml") public class Demo { @Resource(name = "proxy") private Person p; @Test public void fun(){ p.add(); } }
通过xml进行配置代理:
生成代理Spring基于ProxyFactoryBean类,底层自动选择使用JDK的动态代理还是CGLIB的代理.
属性:
- target : 代理的目标对象
- proxyInterfaces : 代理要实现的接口
如果多个接口可以使用以下格式赋值
<list>
<value></value>
....
</list>
- proxyTargetClass : 是否对类代理而不是接口,设置为true时,使用CGLib代理
- interceptorNames : 需要织入目标的Advice
- singleton : 返回代理是否为单实例,默认为单例
- optimize : 当设置为true时,强制使用CGLib
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--定义目标对象--> <bean id="person" class="spring1.PersonImpl"/> <!--定义增强--> <bean id="before" class="spring1.MethodBef"/> <!--Spring支持配置生成代理--> <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 设置目标对象 --> <property name="target" ref="person"/> <!-- 设置实现的接口 ,value中写接口的全路径 --> <property name="proxyInterfaces" value="spring1.Person"/> <!-- 需要使用的切面,这里增强即是切面--> <property name="interceptorNames" value="before"/> </bean> </beans>
注意:
- 使用注解的时候不仅需要导入Junit4.12,还需要导入hamcrest-core
- 在使用接口方式生成代理的时候,返回的类的类型必须是接口类型。
- 这种方法有两个弊端,一是每个类都需要手动进行代理的配置,显得比较麻烦;二是由于是不带切点的,导致灵活性变差,所以并不常用。
二、带有切点的切面:PointcutAdvisor
PointcutAdvisor接口有三种实现:DefaultPointcutAdvisor , RegexpMethodPointcutAdvisor 和 NameMatchMethodPointcutAdvisor,它们都在org.springframework.aop.support包中。
- RegexpMethodPointcutAdvisor是通过正则表达式来匹配拦截的方法
- NameMatchMethodPointcutAdvisor通过直接指定那些方法是需要拦截的,它也可以用*作为通配符
- DefaultPointcutAdvisor则需要自定义切入点类
// 具体类 public class PersonImpl implements Person { @Override public void add() { System.out.println("添加方法..."); } @Override public void delete() { System.out.println("删除方法..."); } } // 自定义增强 import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class AroundAdvice implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("环绕前增强..."); // 执行目标对象的方法 Object res = methodInvocation.proceed(); System.out.println("环绕后增强..."); return res; } } // 测试 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:spring-config.xml") public class Demo { @Resource(name = "proxy2") private Person p; @Test public void fun(){ p.add(); } }
通过配置来生成代理:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--定义目标对象--> <bean id="person" class="spring1.PersonImpl"/> <!--定义增强--> <bean id="before" class="spring1.MethodBef"/> <bean id="around" class="spring1.AroundAdvice"/> <!--Spring支持配置生成代理--> <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 设置目标对象 --> <property name="target" ref="person"/> <!-- 设置实现的接口 ,value中写接口的全路径 --> <property name="proxyInterfaces" value="spring1.Person"/> <!-- 需要使用的增强 --> <property name="interceptorNames" value="before"/> </bean> <!--定义带有切点的切面代理--> <!--定义切点切面--> <bean id="mypointcut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!--定义正则表达式,来限定切点--> <property name="pattern" value=".*"/> <!--添加增强--> <property name="advice" ref="around"/> </bean> <bean id="proxy2" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 设置目标对象 --> <property name="target" ref="person"/> <!-- 设置实现的接口 ,value中写接口的全路径 --> <property name="proxyInterfaces" value="spring1.Person"/> <!-- 需要使用的增强 --> <property name="interceptorNames" value="mypointcut"/> </bean> </beans>
三、自动代理
前面的案例中,每个代理都是通过ProxyFactoryBean织入切面代理,在实际开发中,非常多的Bean每个都配置ProxyFactoryBean开发维护量巨大。
自动创建代理(基于后处理Bean,在Bean创建的过程中完成的增强,生成Bean就是代理 .)
- BeanNameAutoProxyCreator :根据Bean名称创建代理
- DefaultAdvisorAutoProxyCreator :根据Advisor本身包含信息创建代理
- AnnotationAwareAspectJAutoProxyCreator :基于Bean中的AspectJ 注解进行自动代理
基于后处理Bean的都不用配置id。
* BeanNameAutoProxyCreator: 按名称生成代理
// 实现类 public class PersonImpl implements Person { @Override public void add() { System.out.println("添加方法..."); } @Override public void delete() { System.out.println("删除方法..."); } } //增强 import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class AroundAdvice implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("环绕前增强..."); // 执行目标对象的方法 Object res = methodInvocation.proceed(); System.out.println("环绕后增强..."); return res; } } public class MethodBef implements MethodBeforeAdvice{ /** * method:执行的方法 * args:参数 * o:目标对象 */ @Override public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("前置增强..."); } } // 测试 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:config.xml") public class Demo { // 这里直接使用person进行注入就可以了 @Resource(name = "person") private Person p; @Test public void fun(){ p.add(); } }
使用XML进行配置(如果想对特定方法,只需要配置一个有切点的切面就可以了):
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--定义目标对象--> <bean id="person" class="spring1.PersonImpl"/> <!--定义增强--> <bean id="before" class="spring1.MethodBef"/> <bean id="around" class="spring1.AroundAdvice"/> <!-- 自动代理:按名称的代理,基于后处理Bean,后处理Bean,不需要配置id--> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <!--设置需要代理的beanname,支持正则--> <property name="beanNames" value="person"/> <property name="interceptorNames" value="around"/> </bean> </beans>
* DefaultAdvisorAutoProxyCreator:根据切面中定义的信息生成代理
// 实现类 public class PersonImpl implements Person { @Override public void add() { System.out.println("添加方法..."); } @Override public void delete() { System.out.println("删除方法..."); } } // 增强 public class AroundAdvice implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("环绕前增强..."); // 执行目标对象的方法 Object res = methodInvocation.proceed(); System.out.println("环绕后增强..."); return res; } } // 测试 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:config2.xml") public class Demo { // 这里直接使用person进行注入就可以了 @Resource(name = "person") private Person p; @Test public void fun(){ p.add(); p.delete(); } }
使用配置进行自动代理:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--定义目标对象--> <bean id="person" class="spring1.PersonImpl"/> <!--定义增强--> <bean id="before" class="spring1.MethodBef"/> <bean id="around" class="spring1.AroundAdvice"/> <!-- 定义一个带有切点的切面 --> <bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="pattern" value=".*add.*"/> <property name="advice" ref="around"/> </bean> <!-- 自动生成代理 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean> </beans>