Spring AOP学习
Posted 记录点点滴滴
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring AOP学习相关的知识,希望对你有一定的参考价值。
sSpring Aop术语
连接点(Joinpoint)
连接点指的是程序执行的特点位置,如 类初始化前,类初始化后,某个方法调用的前后,方法抛出异常后等等。Spring 仅仅支持方法的连接点,即能在方法调用前,方法调用后,方法抛出异常时及方法调用前后 这些程序执行点植入增强逻辑。
切点(Pointcut)
切点描述的是一系列连接点的集合。
增强(Advice)
增强指的是植入目标连接点上的一段代码,增强除了描述一段代码还包含连接点的方位信息,如BeforeAadvice,ThrowsAdvice,AfterReturnAdvice等等
目标对象(Target)
指的是增强逻辑植入的目标类
引介(Introdution)
引介是一种特殊的增强(Advice),它为类添加一些属性和方法,这样 即使一个类没有实现某个接口,aop引介也可以动态的加入接口和接口实现方法。让类成为某个接口的实现类。
织入(weaving)
织入是将增强(Advice)加入到具体连接点的过程,aop有3种织入方式,编译期织入,类加载织入,动态代理织入。Spring aop采用动态代理织(JDK动态代理,CGLib动态代理)入 在运行期为目标类添加增强生成子类的方式,而AspectJ采用编译期织入和类加载织入。
代理(Proxy)
一个类被aop织入增强后,就产生了一个结果类,这个结果类融合的原类和增强逻辑的代理类。
切面(Aspect)
切面是由切点和增加组成,它既包含横切逻辑,也包含连接点的定义,Spring Aop就是负责实施切面的工作。
JDK动态代理
jdk动态代理是java在1.3的时候提供的技术,它允许开发者在运行时创建接口的代理实例,jdk动态代理主要涉及到Proxy类和 InvocationHandler接口,
下面使用动态代理实现在被代理对象执行的每一个方法前后都输出1和2,
public interface Car {
public void run();
public void whistle();
}
public class SmallCar implements Car{
@Override
public void run() {
System.out.println("我是小车,我在执行run方法");
}
@Override
public void whistle() {
System.out.println("我是小车,我在执行whistle方法");
}
}
public class BigCar implements Car{
@Override
public void run() {
System.out.println("我是大车,我在执行run方法");
}
@Override
public void whistle() {
System.out.println("我是大车,我在执行run方法");
}
}
public class CarHandler<T> implements InvocationHandler{
private T target;//这个是被代理对象目标
public CarHandler(T target) {
super();
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//proxy 是最终生成的代理对象, 一般我们不会用到。method是目标的方法,args 是方法入参
System.out.println(1);
Object result = method.invoke(target, args);//调用用反射调用目标的方法
System.out.println(2);
return result;
}
}
public class TestCar {
public static void main(String[] args) {
Car smallCar = new SmallCar();
CarHandler<Car> handle = new CarHandler<Car>(smallCar);
Car proxyCar = (Car) Proxy.newProxyInstance(smallCar.getClass().getClassLoader(), smallCar.getClass().getInterfaces(), handle);
proxyCar.run();
proxyCar.whistle();
Car bigCar = new BigCar();
CarHandler<Car> handle1 = new CarHandler<Car>(bigCar);
Car proxyCar1 = (Car) Proxy.newProxyInstance(bigCar.getClass().getClassLoader(), bigCar.getClass().getInterfaces(), handle1);
proxyCar1.run();
proxyCar1.whistle();
}
}
输出结果
1
我是小车,我在执行run方法
2
1
我是小车,我在执行whistle方法
2
1
我是大车,我在执行run方法
2
1
我是大车,我在执行run方法
2
CGLib代理
jdk动态代理有一个限制,它只能为接口创建代理实例,从Proxy.newProxyInstance就能看出来,CGLib采用底层字节码技术,可以为一个类创建子类,在子类使用方法拦截拦截所有父类方法并顺势加入增强代码
public class SmallCar implements Car{
@Override
public void run() {
System.out.println("我是小车,我在执行run方法");
}
@Override
public void whistle() {
System.out.println("我是小车,我在执行whistle方法");
}
}
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println(1);
Object result = proxy.invokeSuper(obj, args);
System.out.println(2);
return result;
}
}
public class CglibProxyTest {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
SmallCar smallCar = (SmallCar) cglibProxy.getProxy(SmallCar.class);
smallCar.run();
smallCar.whistle();
}
}
输出结果
1
我是小车,我在执行run方法
2
1
我是小车,我在执行whistle方法
2
动态代理和CGLib代理小结
研究表明CGLib所创建的对象的性能比jdk动态代理创建的对象性能高很多(大概10倍),但是CGLib创建代理对象所花费的时间比jdk动态代理多(大概8倍),
所以对于singleton的代理对象或者具有实例池的代理对象,因为无需频繁的创建对象,所以应该采用CGLib技术,反之应该采用jdk动态代理技术,此外CGLib技术也有一定的限制:不能对目标中的final或者private方法进行代理。
上面两个代理的例子存在3个问题
1 目标类的所有方法都被增加了我们的增强业务,而有时候我们只需要对特定的方法添加横向逻辑,
2 通过硬编码的方式在目标业务方法前后加入增强业务
3 代理的创建过程是手写的,在为不同类创建代理时 无法做到通用性。
Spring AOP的主要工作就是围绕以上3点来开展的
AOP联盟为增强Advice,以下aoppalliance为aop联盟定义的接口,spring为spring定义扩展的增强接口,Spring 支持5种类型的增强。
前置增强:MethodBeforeAdvice,在目标方法执行前实施增强
后置增强:AfterReturningAdvice,在目标方法执行后实施增强
环绕增强:MethodInterceptor,在目标方法执行前后实施增强
异常增强:ThrowsAdvice,在目标方法抛出异常后实施增强
引介增强:IntroductionInterceptor,在目标类添加一些新的属性和方法
前置增强,
public interface Waiter { public void greetTo(String name); public void serverTo(String name); }
public class NaiveWaiter implements Waiter{ public void greetTo(String name) { System.out.println("欢迎"+name); } public void serverTo(String name) { System.out.println("服务"+name); } }
import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class GreetingBeforeAdvice implements MethodBeforeAdvice { //在目标方法执行前执行 @Override public void before(Method method, Object[] args, Object target) throws Throwable { String clinetName = (String) args[0]; System.out.println("您好,"+clinetName); } }
public class BeforeTest { public static void main(String[] args) { Waiter target = new NaiveWaiter(); BeforeAdvice advice = new GreetingBeforeAdvice(); //Spirng 提供的代理工厂 ProxyFactory pf = new ProxyFactory(); // pf.setInterfaces(target.getClass().getInterfaces()); //这里是指定使用接口代理(jdk动态代理) pf.setOptimize(true);//启用代理优化 如果这里开启那么ProxyFactory还有可以使用Cglib进行代理 //设置代理目标 pf.setTarget(target); //为代理目标增强 这里也可以添加多个增强,形成增强链,他们的调用顺序和添加顺序保持一致,当然也可以指定添加顺序 pf.addAdvice(advice); //生成代理实例 Waiter proxy = (Waiter) pf.getProxy(); proxy.greetTo("张三"); proxy.serverTo("李四"); } }
输出结果
您好,张三
欢迎张三
您好,李四
服务李四
后置增强
public class GreetingAfterAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("请享用您的实物"); } }
public static void main(String[] args) { Waiter target = new NaiveWaiter(); AfterReturningAdvice advice = new GreetingAfterAdvice(); //Spirng 提供的代理工厂 ProxyFactory pf = new ProxyFactory(); // pf.setInterfaces(target.getClass().getInterfaces()); //这里是指定使用接口代理(jdk动态代理) pf.setOptimize(true);//启用代理优化 如果这里开启那么ProxyFactory还有可以使用Cglib进行代理 //设置代理目标 pf.setTarget(target); //为代理目标增强 这里也可以添加多个增强,形成增强链,他们的调用顺序和添加顺序保持一致,当然也可以指定添加顺序 pf.addAdvice(advice); //生成代理实例 Waiter proxy = (Waiter) pf.getProxy(); proxy.serverTo("李四"); }
环绕增强
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class GreetingMehodInterceptor implements MethodInterceptor{ @Override public Object invoke(MethodInvocation invocation) throws Throwable { Object[] args = invocation.getArguments();//目标方法入参 String clientName = (String) args[0]; System.out.println(clientName+":吃饭前,请擦嘴"); Object result = invocation.proceed();//使用反射机制调用目标类的方法 System.out.println(clientName+":吃饭后,请擦嘴"); return result; } }
public static void main(String[] args) { Waiter target = new NaiveWaiter(); GreetingMehodInterceptor advice = new GreetingMehodInterceptor(); //Spirng 提供的代理工厂 ProxyFactory pf = new ProxyFactory(); // pf.setInterfaces(target.getClass().getInterfaces()); //这里是指定使用接口代理(jdk动态代理) pf.setOptimize(true);//启用代理优化 如果这里开启那么ProxyFactory还有可以使用Cglib进行代理 //设置代理目标 pf.setTarget(target); //为代理目标增强 这里也可以添加多个增强,形成增强链,他们的调用顺序和添加顺序保持一致,当然也可以指定添加顺序 pf.addAdvice(advice); //生成代理实例 Waiter proxy = (Waiter) pf.getProxy(); proxy.serverTo("李四"); }
输出结果
李四:吃饭前,请擦嘴
服务李四
李四:吃饭后,请擦嘴
异常抛出增强
异常增强适合事物管理,发生异常时触发事物回滚
public class OrderServiceImpl { public void testTransationManager()throws Exception { System.out.println("-----开始业务操作--------"); System.out.println("插入数据1"); throw new SQLException("模拟的sql异常出现"); } }
public class TransationManager implements ThrowsAdvice{ public void afterThrowing(Method method,Object[] args,Object target,Exception e) { System.out.println("----------------"); System.out.println("method:"+method.getName()); System.out.println("抛出异常:"+e.getMessage()); System.out.println("事物已经被回滚"); } }
public static void main(String[] args) throws Exception{ OrderServiceImpl target = new OrderServiceImpl(); TransationManager advice = new TransationManager(); //Spirng 提供的代理工厂 ProxyFactory pf = new ProxyFactory(); // pf.setInterfaces(target.getClass().getInterfaces()); //这里是指定使用接口代理(jdk动态代理) pf.setOptimize(true);//启用代理优化 如果这里开启那么ProxyFactory还有可以使用Cglib进行代理 //设置代理目标 pf.setTarget(target); //为代理目标增强 这里也可以添加多个增强,形成增强链,他们的调用顺序和添加顺序保持一致,当然也可以指定添加顺序 pf.addAdvice(advice); //生成代理实例 OrderServiceImpl proxy = (OrderServiceImpl) pf.getProxy(); proxy.testTransationManager(); }
输出结果
-----开始业务操作-------- 插入数据1 ---------------- method:testTransationManager 抛出异常:模拟的sql异常出现 事物已经被回滚 Exception in thread "main" java.sql.SQLException: 模拟的sql异常出现 at com.bujiang.magic.springAOP.OrderServiceImpl.testTransationManager(OrderServiceImpl.java:10) at com.bujiang.magic.springAOP.OrderServiceImpl$$FastClassBySpringCGLIB$$7ad45694.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invoke(ThrowsAdviceInterceptor.java:127) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) at com.bujiang.magic.springAOP.OrderServiceImpl$$EnhancerBySpringCGLIB$$d3d9cdd2.testTransationManager(<generated>) at com.bujiang.magic.springAOP.BeforeTest.main(BeforeTest.java:69)
引介增强
引介增强是一种特殊的增强,他与上面的几种都不一样。他是为目标类建立新的属性和方法,通过引介增强,原来的目标类未实现某个接口,引介增强可以为目标类实现创建实现类的接口代理,这种功能非常富有吸引力,因为能在横向的定义接口的实现方法。Spring 定义了引介增强接口
public interface Monitorable { //这个方法控制监控的开关 void setMonitorActive(boolean active); }
import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.support.DelegatingIntroductionInterceptor; public class ControllableMonitor extends DelegatingIntroductionInterceptor implements Monitorable{ //ThreadLocal这里是保存监控开关的状态, //之所以是使用ThreadLocal 是因为使代理类非线程安全的类变成线程安全的类 通过ThreadLocal使每个线程都单独使用一个状态,因此是线程安全的 private ThreadLocal<Boolean> MonitorStatusMap = new ThreadLocal<>(); @Override public void setMonitorActive(boolean active) { MonitorStatusMap.set(active); } public Object invoke(MethodInvocation mi) throws Throwable{ Object result =null; //根据监控状态,来确定是否开启监控行为 if(MonitorStatusMap.get()!=null&&MonitorStatusMap.get()) { System.out.println("开始监控"); result = super.invoke(mi); System.out.println("监控完毕"); }else { result = super.invoke(mi); } return result; } }
<?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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd" default-autowire="byName"> <bean id="pmonitor" class="com.bujiang.magic.t1.ControllableMonitor" /> <bean id="carTarget" class="com.bujiang.magic.proxy.BigCar" /> <bean id="car" class="org.springframework.aop.framework.ProxyFactoryBean" p:interfaces="com.bujiang.magic.t1.Monitorable" p:target-ref= "carTarget" p:interceptorNames="pmonitor" p:proxyTargetClass="true" /> </beans>
public class Test { public static void main(String[] args) { String path = "com/bujiang/magic/beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(path); Car car = (Car) applicationContext.getBean("car"); car.run(); System.out.println("-----------------开关开启,再次执行-----------------"); Monitorable monitor = (Monitorable) car; monitor.setMonitorActive(true); car.run(); } }
输出结果
我是大车,我在执行run方法 -----------------开关开启,再次执行----------------- 开始监控 我是大车,我在执行run方法 监控完毕
切面
Spring 提供的切面使用起来比较繁琐,需要实现专门的接口,并进行复杂的配置,这里不做介绍,我们可以使用基于@AspectJ实现的AOP的功能
public class Waiter { public void greetTo(String name) { System.out.println("欢迎"+name); } public void serverTo(String name) { System.out.println("服务"+name); } }
import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; //标注为切面类 @Aspect public class PreGreetAspect { //前置增强 Aspect有自己的语法,这里不做介绍,具体可百度 @Before("execution(* greetTo(..))") public void beforeGreetingAspect() { System.out.println("你好"); } //后置增强 @After("execution(* serverTo(..))") public void afterGreetingAspect() { System.out.println("再见"); } }
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory; public class Test { public static void main(String[] args) { Waiter target = new Waiter(); AspectJProxyFactory factory = new AspectJProxyFactory(); factory.setTarget(target);//设置目标对象 factory.addAspect(PreGreetAspect.class);//添加切面 Waiter proxy = factory.getProxy();//生成代理对象 proxy.greetTo("张三"); System.out.println("--------------------"); proxy.serverTo("李四"); } }
输出结果
你好 欢迎张三 -------------------- 服务李四 再见
其他 如:@AfterReturning @Around @AfterThrowing @After 等等与前面的spring AOP作用一致,这里不做介绍,引介增强 @DeclareParents
283页
以上是关于Spring AOP学习的主要内容,如果未能解决你的问题,请参考以下文章