Spring学习十五----------Spring AOP API的Pointcut、advice及 ProxyFactoryBean相关内容
© 版权声明:本文为博主原创文章,转载请注明出处
实例:
1.项目结构
2.pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.spring</groupId> <artifactId>Spring-AOP-API</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>Spring-AOP-API Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <spring.version>4.3.8.RELEASE</spring.version> </properties> <dependencies> <!-- junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- Spring Core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> </dependencies> <build> <finalName>Spring-AOP-API</finalName> </build> </project>
3.BizLogic.java
package org.spring.aop.api; public interface BizLogic { String save(); String saveEx(); }
4.BizLogicImpl.java
package org.spring.aop.api; public class BizLogicImpl implements BizLogic { public String save() { System.out.println("BizLogicImpl:BizLogicImpl save."); return "BizLogicImpl save"; } public String saveEx() { System.out.println("BizLogicImpl:BizLogicImpl save."); throw new RuntimeException(); } }
5.CustomBeforeAdvice
package org.spring.aop.api; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class CustomBeforeAdvice implements MethodBeforeAdvice { public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("CustomBeforeAdvice:" + method.getName() + " " + target.getClass().getName()); } }
6.CustomAfterReturnAdvice.java
package org.spring.aop.api; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class CustomAfterReturnAdvice implements AfterReturningAdvice { public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("CustomAfterReturnAdvice:" + method.getName() + " " + target.getClass().getName() + " " + returnValue); } }
7.CustomThrowsAdvice.java
package org.spring.aop.api; import java.lang.reflect.Method; import org.springframework.aop.ThrowsAdvice; public class CustomThrowsAdvice implements ThrowsAdvice { public void afterThrowing(Exception ex) throws Throwable { System.out.println("CustomThrowsAdvice afterThrowing 1"); } public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable { System.out.println("CustomThrowsAdvice afterThrowing 2:" + method.getName() + " " + target.getClass().getName()); } }
8.CustomMethodInterceptor.java
package org.spring.aop.api; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class CustomMethodInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("CustomMethodInterceptor 1:" + invocation.getMethod().getName() + " " + invocation.getStaticPart().getClass().getName()); Object obj = invocation.proceed(); System.out.println("CustomMethodInterceptor 2:" + obj); return obj; } }
9.Lockable.java
package org.spring.aop.api.introduction; /** * 接口 * */ public interface Lockable { void lock(); void unlock(); boolean locked(); }
10.LockMixin.java
package org.spring.aop.api.introduction; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.support.DelegatingIntroductionInterceptor; /** * 实现类 * */ public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable { private static final long serialVersionUID = 1L; private boolean locked; public void lock() { this.locked = true; } public void unlock() { this.locked = false; } public boolean locked() { return this.locked; } /** * 被锁定后不能使用setter方法改变值 */ @Override public Object invoke(MethodInvocation invocation) throws Throwable { if(locked && invocation.getMethod().getName().indexOf("set") == 0){ throw new RuntimeException(); } return super.invoke(invocation); } }
11.LockMixinAdvisor.java
package org.spring.aop.api.introduction; import org.springframework.aop.support.DefaultIntroductionAdvisor; /** * Introduction,在不改变代码的情况下,添加一个父类 * */ public class LockMixinAdvisor extends DefaultIntroductionAdvisor { private static final long serialVersionUID = 1L; public LockMixinAdvisor() { super(new LockMixin(), Lockable.class); } }
12.Spring-aop-api.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"> <!-- Before advice -一个简单的通知类型 -只是进方法之前被调用,不需要MethodInvocation对象 -前置通知可以在连接点执行之前插入自定义行为,但不能改变返回值 --> <bean id="customBeforeAdvice" class="org.spring.aop.api.CustomBeforeAdvice"/> <!-- Throws advice -如果连接点抛出异常,throws advice在连接点返回后被调用 -如果throws-advice的方法抛出异常,那么它将覆盖原有异常 -接口org.springframework.aop.ThrowsAdvice不包含任何方法,仅仅是一个声明,实现类需要实现类似下面的方法 -void afterThrowing([Method, args, target], ThrowableSubclass) --> <bean id="customThrowsAdvice" class="org.spring.aop.api.CustomThrowsAdvice"/> <!-- After Returning advice -后置通知必须实现org.springframework.aop.AfterReturningAdvice几口 -可以访问返回值(但不能进行修改)、被调用的方法、方法的参数和目标 -如果抛出异常,将会抛出拦截器链,替代返回值 --> <bean id="customAfterReturnAdvice" class="org.spring.aop.api.CustomAfterReturnAdvice"/> <!-- Interception around advice -Spring的切入点模型使得切入点可以独立与advice重用,以针对不同的advice可以使用相同的切入点 --> <bean id="customMethodInterceptor" class="org.spring.aop.api.CustomMethodInterceptor"/> <!-- Introduction advice -Spring把引入通知作为一种特殊的拦截通知 -需要IntroductionAdvisor和IntroductionInterceptor -仅适用于类,不能和任何切入点一起使用 -实例:如果调用lock()方法,希望所有的setter方法抛出LockedException异常。 -代码:Lockalbe.java LockMixin.java LockMixinAdvisor.java --> <!-- Advisor API -Advisor是仅包含一个切入点表达式关联的单个通知的方面 -除了introductions,advisor可以用于任何通知 -org.springframework.aop.support.DefaultIntroductionAdvisor是最常用的advisor类, 它可以与MethodInterceptor,BeforeAdvice或者ThrowsAdvice一起使用 -它可以混合在Spring同一个AOP代理的advisor和advice --> <!-- 当使用方式三时,屏蔽该bean的定义 --> <!-- <bean id="bizLogicImplTarget" class="org.spring.aop.api.BizLogicImpl"/> --> <!-- ###########################方式一:使用pointcutBean开始############################# --> <!-- 根据方法名匹配切入点pointcut --> <!-- <bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut"> <property name="mappedNames">成员变量,匹配的方法名集合 <list> <value>sa*</value> </list> </property> </bean> <bean id="defaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="advice" ref="customBeforeAdvice"/>接入点通知,若非接入点,则该通知不生效 <property name="pointcut" ref="pointcutBean"/>接入点 </bean> --> <!-- ProxyFactoryBean -创建Spring AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean -这可以完全控制切入点和通知(advice)以及它们的顺序 -ProxyFactoryBean实现里getObject()方法创建一个AOP代理包装一个目标对象 -使用ProxyFactoryBean或者其它IOC相关类来创建AOP代理的最重要的好处是通知和切入点也可以由IOC来管理 -被代理类没有实现任何接口,使用CGLIB代理,否则JDK代理 -通过设置proxyTargetClass为true,可强制使用CGLIB -如果目标类实现了一个(或者多个)接口,那么创建代理的类型将依赖ProxyFactoryBean的配置 -如果ProxyFactoryBean的proxyInterfaces属性设置为一个或多个全限定接口名,基于JDK的代理将被创建 -如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类实现了一个(或者更多)接口,那么proxyInterfaces 将自动检测到这个目标类已经实现了至少一个接口,创建一个基于JDK的代理 --> <!-- <bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target">为该类创建代理,由ProxyFactoryBean自己去判断是否实现了接口 <ref bean="bizLogicImplTarget"/> </property> <property name="interceptorNames">执行该代理的时候执行的interceptor <list> <value>defaultAdvisor</value> <value>customAfterReturnAdvice</value> <value>customMethodInterceptor</value> <value>customThrowsAdvice</value> </list> </property> </bean> --> <!-- ###############################方式一结束######################################### --> <!-- ############################方式二:使用pointcutBean开始############################ --> <!-- <bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces">直接指定接口 <value>org.spring.aop.api.BizLogic</value> </property> <property name="target">为该类创建代理,因为直接指定了接口,所以肯定是使用JDK代理 <ref bean="bizLogicImplTarget"/> </property> <property name="interceptorNames">执行该代理的时候执行的interceptor <list> <value>customBeforeAdvice</value> <value>customAfterReturnAdvice</value> <value>customMethodInterceptor</value> <value>customThrowsAdvice</value> </list> </property> </bean> --> <!-- ###############################方式二结束######################################### --> <!-- ##############################方式三:使用匿名内部Bean############################### --> <bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"><!-- 直接指定接口 --> <value>org.spring.aop.api.BizLogic</value> </property> <property name="target"><!-- 上面可以直接使用bean id获取bean对象,此时该bean对象没有被代理,配置的advice不会被执行 --> <bean class="org.spring.aop.api.BizLogicImpl"/> </property> <property name="interceptorNames"><!-- 执行该代理的时候执行的interceptor --> <list> <value>customBeforeAdvice</value> <value>customAfterReturnAdvice</value> <value>customMethodInterceptor</value> <value>customThrowsAdvice</value> </list> <!-- 使用global advisors -用*做通配,匹配所有拦截器加入通知链 此处只会执行customMethodInterceptor,因为只有该advice实现了Interceptor接口 --> <!-- <list> <value>custom*</value> </list> --> </property> </bean> <!-- ###############################方式三结束######################################### --> <!-- ##############################方式四:简化的proxy定义############################### --> <!-- 简化的proxy定义 -使用父子bean定义以及内部bean定义,可能会带来更清洁和更简洁的代理定义 --> <!-- <bean id="baseProxyBean" class="org.springframework.aop.framework.ProxyFactoryBean" lazy-init="true" abstract="true"/> <bean id="bizLogicImpl" parent="baseProxyBean"> <property name="proxyInterfaces">直接指定接口 <value>org.spring.aop.api.BizLogic</value> </property> <property name="target">上面可以直接使用bean id获取bean对象,此时该bean对象没有被代理,配置的advice不会被执行 <bean class="org.spring.aop.api.BizLogicImpl"/> </property> <property name="interceptorNames">执行该代理的时候执行的interceptor <list> <value>customBeforeAdvice</value> <value>customAfterReturnAdvice</value> <value>customMethodInterceptor</value> <value>customThrowsAdvice</value> </list> </property> </bean> --> <!-- ###############################方式四结束######################################### --> </beans>
13.TestBase.java
package org.spring.aop.api.test; import org.junit.After; import org.junit.Before; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.util.StringUtils; public class TestBase { private ClassPathXmlApplicationContext context; private String springXmlPath; /** * 无参构造器 */ public TestBase() { } /** * 含参构造器,初始化spring配置文件路径 * * @param springXmlPath * spring配置文件路径 */ public TestBase(String springXmlPath) { this.springXmlPath = springXmlPath; } /** * 初始化spring配置文件并加载到IOC容器中 */ @Before public void before() { if(StringUtils.isEmpty(springXmlPath)) { springXmlPath = "classpath:spring-*.xml"; } context = new ClassPathXmlApplicationContext(springXmlPath.split("[,\\s]+")); context.start(); } /** * 销毁IOC容器 */ @After public void after() { if(context != null){ context.destroy(); } } /** * 根据bean ID获取bean对象 * * @param beanId * bean ID * @return */ public Object getBean(String beanId) { return context.getBean(beanId); } }
14.TestSpringAOPAPI.java
package org.spring.aop.api.test; import org.junit.Test; import org.spring.aop.api.BizLogic; public class TestSpringAOPAPI extends TestBase { /** * 通过构造器初始化spring配置文件 */ public TestSpringAOPAPI() { super("classpath:spring-aop-api.xml"); } /** * 测试正常方法 */ @Test public void testSave() { BizLogic logic = (BizLogic) super.getBean("bizLogicImpl"); logic.save(); } /** * 测试存在异常的方法 */ @Test public void testsaveEx() { BizLogic logic = (BizLogic) super.getBean("bizLogicImpl"); logic.saveEx(); } }
15.效果预览
15.1 执行testSave方法
15.2 执行testsaveEx方法
说明:其他几种方式已在spring-aop-api.xml中注明,可自行测试。
参考:http://www.imooc.com/video/4597
http://www.imooc.com/video/4598
http://www.imooc.com/video/4599
© 版权声明:本文为博主原创文章,转载请注明出处