Spring系列学习之Spring AOP
1.何谓AOP?
aop是开发中面向切面编程的一种思想
AOP与OOP的对比:OOP是面向对象编程的一种思想。即模拟现实世界模式一切皆对象;AOP是面前切面,即将过程横切处理的思想。
2.AOP的作用:
1.提供了声明式的服务
2.允许用户定义自己的切面
3.AOP常见的实现:
1.Proxy方式实现AOP方式:
2.CGLib方法实现AOP
3.Spring AOP实现:注解aspect方式实现;基于配置文件方式实现
4.Spring AOP的实现:
1.纯Java实现,无需特殊编译过程,不需要控制类加载层次;
2.只支持方法执行连接点
3.不是为了提供最完整的AOP实现,而是侧重提供一种AOP实现与Spring IOC容器之间的整合,用于帮助企业级应用提供常见问题的解决途径。
4.Spring AOP默认使用标准的JavaSE动态代理作为AOP的代理器,使得所用的接口都可以被代理
5.Spring AOP代理也可以使用CGLib代理
5.Spring AOP实现之基于注解aspect方式实现:
1.代理对象业务代码:
package aop.aspect; public class Student { public String print(String name) { System.out.println(name + "print"); return "hello word"; } }
2.注解实现的切面代码
package aop.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class StudnetInterceptor { /** * 打印方法AOP */ @Pointcut("execution(* aspect.Student.print(..))") // @Pointcut("execution(* // com.jike.spring.chapter09.aop.aspect.Student.*(..))") public void printMethod() { } @Before("printMethod()") public void printBeforeAdvice() { System.out.println("printBeforeAdvice()!"); } @AfterReturning(pointcut = "printMethod()", returning = "flag") public void printAfterAdvice(String flag) { System.out.println("printAfterAdvice()! " + flag); } @After("printMethod()") public void finallyAdvice() { System.out.println("finallyAdvice()!"); } @Around("printMethod() && args(name)") public Object printAroundAdvice(ProceedingJoinPoint pjp, String name) throws Throwable { Object result = null; if (name.equals("whc")) pjp.proceed(); else System.out.println("print()方法以及被拦截..."); return result; } }
3.测试代码:
public class Test { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("config/conf-aspect.xml"); Student stu = (Student) ctx.getBean("stu"); stu.print("whc"); } }
6.Spring AOP实现之aspect配置文件方式实现:
1.被代理对象的业务事务代码
package aop.xml; public class Student { public String print(String name) { System.out.println(name + "print"); return "hello word"; } }
2.基于配置文件实现的切面代码
package aop.xml; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; /** * Xml方式实现AOP切面 * * @author lilin * @time 2016年5月27日 下午11:46:46 * @email [email protected] * @blog http://gaosililin.iteye.com * @school USC * @team Geowind */ public class StudnetInterceptor { /** * 打印方法AOP */ public void printMethod() { } public void printBeforeAdvice() { System.out.println("printBeforeAdvice()!"); } public void printAfterAdvice(String name) { System.out.println("printAfterAdvice()! " + name); } public void finallyAdvice() { System.out.println("finallyAdvice()!"); } public Object printAroundAdvice(ProceedingJoinPoint pjp, String name) throws Throwable { Object result = null; if (name.equals("whc")) pjp.proceed(); else System.out.println("print()方法以及被拦截..."); return result; } }
3.测试代码:
package aop.xml; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("config/conf-xml.xml"); Student stu = (Student) ctx.getBean("stu"); stu.print("whc1"); } }
4.配置文件:
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"> <aop:aspectj-autoproxy /> <bean id="stu" class="xml.Student"></bean> <bean id="interceptor" class="xml.StudnetInterceptor"></bean> <aop:config> <aop:aspect id="stuInterceptor" ref="interceptor"> <aop:before pointcut="execution(* xml.Student.print(..))" method="printBeforeAdvice" /> <aop:after-returning pointcut="execution(* xml.Student.print(..))" method="printAfterAdvice" /> <aop:after pointcut="execution(* xml.Student.print(..))" method="finallyAdvice" /> <aop:around pointcut="execution(* .xml.Student.print(..)) and args(name)" method="printAroundAdvice" /> </aop:aspect> </aop:config> </beans>
7.Spring pointCut:
8.aop通知的执行的顺序:
1.before:方法执行前
2.around:环绕,方法执行之前,但是该方法第一个参数必须是ProceedingJoinPoint
3.after-throwing:方法抛出异常之后
4.after:方法执行之后
5.after-returning:方法返回值之后
示例代码:
1.目标接口代码
package aop.aop; public interface Foo { void play(String name); }
2.目标接口实现类代码
package aop.aop; public class FooImpl implements Foo { @Override public void play(String name) { System.out.println("paling with " + name); throw new RuntimeException(); } }
3.Interceptor类代码
package aop.aop; import org.aspectj.lang.ProceedingJoinPoint; /** * Xml配置实现通知 * * @author lilin * @time 2016年5月31日 下午12:36:52 * @email [email protected] * @blog http://gaosililin.iteye.com * @school USC * @team Geowind */ public class FooInterceptor { /** * 前置通知 */ public void beforePaly() { System.out.println("FooInterceptor.beforePlay"); } /** * 后置通知 */ public void afterPlay() { System.out.println("FooInterceptor.afterPlay"); } /** * 返回返回值后通知 */ public void afterReturnning() { System.out.println("FooInterceptor.afterReturnning"); } /** * 抛出异常后通知 */ public void throwExcetion() { System.out.println("FooInterceptor.throwExcetion"); } /** * 环绕通知 。第一个参数必须是ProceedingJoinPoint * * @param pjp * @param name * @return * @throws Throwable */ public Object printAroundAdvice(ProceedingJoinPoint pjp, String name) throws Throwable { Object result = null; if (name.equals("jing")) { try { result = pjp.proceed(); } catch (Exception e) { System.out.println("诶呀,异常了"); } } else { System.out.println("print()方法以及被拦截..."); } return result; } }
4.测试类代码
package aop.aop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AopTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("config/introduce-introduce.xml"); Foo foo = (Foo) context.getBean("foo"); foo.play("jing"); } }
5.配置文件
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"> <aop:aspectj-autoproxy /> <bean id="foo" class="aop.aop.FooImpl"></bean> <bean id="foointerceptor" class="aop.aop.FooInterceptor"></bean> <aop:config> <aop:aspect id="fooInterceptor" ref="foointerceptor"> <aop:before method="beforePaly" pointcut="execution(* aop.aop.Foo.play(..) )" /> <aop:after-returning method="afterReturnning" pointcut="execution(* aop.aop.Foo.play(..))" /> <aop:after method="afterPlay" pointcut="execution(* aop.aop.Foo.play(..))" /> <aop:around pointcut="execution(* aop.aop.Foo.play(..)) and args(name)" method="printAroundAdvice" /> <aop:after-throwing method="throwExcetion" pointcut="execution(* aop.aop.Foo.play(..))" /> </aop:aspect> </aop:config> </beans>
6.结果排序
9.Advice AOP增强
Aop Advice中除了Introdutions引介增强外,其他的都是方法级别的增强,Introdutions引介增强是类级别的增强
实现代码示例:
1.增强对象的接口
package advice.advice; /** * 增强对象的接口 * * @author lilin * @time 2016年5月31日 上午11:22:19 * @email [email protected] * @blog http://gaosililin.iteye.com * @school USC * @team Geowind */ public interface Foo { void play(String name); }
2.增强的实现类
package advice.advice; /** * 增强对象的实现类 * * @author lilin * @time 2016年5月31日 上午11:22:45 * @email [email protected] * @blog http://gaosililin.iteye.com * @school USC * @team Geowind */ public class FooImpl implements Foo { @Override public void play(String name) { System.out.println("paling with " + name); // throw new RuntimeException(); } }
3.前置增强
package advice.advice; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; /** * 前置增强 * * @author lilin * @time 2016年5月31日 上午11:27:42 * @email [email protected] * @blog http://gaosililin.iteye.com * @school USC * @team Geowind */ public class BeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("BeforeAdvice.before:method " + method.getName() + " aug:" + args[0]); } }
5.后置增强
package advice.advice; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; /** * 后置增强 * * @author lilin * @time 2016年5月31日 上午11:23:42 * @email [email protected] * @blog http://gaosililin.iteye.com * @school USC * @team Geowind */ public class AfterAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("AfterAdvice.afterReturning. method:" + method.getName() + " arg:" + args[0]); } }
6.环绕增强
package advice.advice; import java.lang.reflect.Method; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; /** * 环绕增强 * * @author lilin * @time 2016年5月31日 上午11:28:23 * @email [email protected] * @blog http://gaosililin.iteye.com * @school USC * @team Geowind */ public class AroundAdvice implements MethodInterceptor { @Override public Object invoke(MethodInvocation arg0) throws Throwable { Method method = arg0.getMethod(); Object[] arguments = arg0.getArguments(); // 环绕增强 前 System.out.println("AroundAdvice.invoke.before method:" + method.getName()); // 放射方式调用目标方法 Object proceed = arg0.proceed(); // 环绕增强 后 System.out.println("AroundAdvice.invoke.after arg:" + arguments[0]); return proceed; } }
7.配置实现的配置文件
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- 前置增强 --> <bean id="beforeAdvice" class="advice.advice.BeforeAdvice"></bean> <!-- 后置增强 --> <bean id="afterAdvice" class="advice.advice.AfterAdvice"></bean> <!-- 环绕增强 --> <bean id="aroundAdvice" class="advice.advice.AroundAdvice"></bean> <!-- 增强的对象 --> <bean id="target" class="advice.advice.FooImpl"></bean> <bean id="foo" class="org.springframework.aop.framework.ProxyFactoryBean" p:proxyInterfaces="advice.advice.Foo" p:target-ref="target" p:interceptorNames="beforeAdvice,afterAdvice,aroundAdvice" /> </beans>
8.测试代码
package advice.advice; import org.aopalliance.intercept.MethodInterceptor; import org.springframework.aop.AfterReturningAdvice; import org.springframework.aop.BeforeAdvice; import org.springframework.aop.framework.ProxyFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainTest { public static void main(String[] args) { System.out.println("------xml配置风格实现之前增强-------"); // ----------xml文件方式实现前置增强 // 文件路径 String path = "config/advice-advice.xml"; // 加载配置文件 ApplicationContext context = new ClassPathXmlApplicationContext(path); // 获取Bena对象 Foo fooXml = (Foo) context.getBean("foo"); // 调用play方法,查看是否实现前置通知 fooXml.play("jing"); System.out.println("------java风格实现之前增强-------"); // ----- 实现接口方式实现前置增强 // 创建增强的兑仓 Foo foo = new FooImpl(); // 创建前置增强的接口,切入点 // 这里可以实现多个增强的切入点 BeforeAdvice beforeAdvice = new advice.advice.BeforeAdvice(); // 创建增强的代理器 ProxyFactory proxyFactory = new ProxyFactory(); // 设置代理目标 proxyFactory.setTarget(foo); // 添加代理切口,可以添加多个切口 proxyFactory.addAdvice(beforeAdvice); // 获取代理的器放回的对象 Foo fooAdvice = (Foo) proxyFactory.getProxy(); // 调用play方法,查看是否实现前置通知 fooAdvice.play("gaosi"); System.out.println("------后置增强增强-------"); // -------后置增强 // 切入后置增强的切入点 AfterReturningAdvice afterReturningAdvice = new AfterAdvice(); // 增加切入点 proxyFactory.addAdvice(afterReturningAdvice); // 获取添加增强后的对象 Foo afterAdviceFoo = (Foo) proxyFactory.getProxy(); afterAdviceFoo.play("lg"); System.out.println("-----环绕增强------"); // 创建环绕增强的切面 MethodInterceptor around = new AroundAdvice(); // 添加环绕增强的切点 proxyFactory.addAdvice(around); // 获取增加切点的对象 Foo aroundFoo = (Foo) proxyFactory.getProxy(); aroundFoo.play("jl"); } }
运行结果
10.Introdutions引介增强:
Introdutions是允许一个切面声明一个实现指定的接口的通知对象,并且提供一个接口实现来代理这些对象。与advice的区别在于,advice是方法级别的增强,Introdutions是类级别的增强。
可以参考 introdutions增强