Spring AOP
一、引述
与OOP(面向对象)相比,传统的的OOP开发的代码逻辑是自上而下的,但是在这些自上而下的过程中会产生横切性的问题(例如日志、权限、事务),而这些横切性的问题由于我们的主业务逻辑关系不大,会散落在代码的各个地方,造成难以维护的问题。
AOP的编程思想就是把这些横切性的问题和主业务逻辑进行分离,从而达到解耦的目的。主要用于事务管理、性能监视、安全检查、缓存、日志等应用。
AspectJ是一个基于Java语言的AOP框架。
二、实现原理(代理)
AOP底层采用代理机制进行实现。如果接口+实现类,那么spring采用jdk动态代理Proxy。如果实体类没有实现接口,那么spring采用chlib字节码增强。
JDK代理利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
Cglib利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。实现CGLIB动态代理必须实现MethodInterceptor(方法拦截器)接口。
【为什么JDK代理需要实现接口】
1、JDK代理生成的代理类继承了Proxy,由于java是单继承,所以只能实现接口,通过接口实现
2、从代理模式的设计来说,充分利用了java的多态特性,也符合基于接口编码的规范
1. AOP术语
① target:目标类,需要被代理的类。——Userservice
② jionPoint:连接点:可能被拦截到的方法。——addUser()、delUser()、updateUser()
③ pointCut:切入点,已经被增强的连接点。—— addUser()
④ advice:通知,增强代码。—— after() 、before()
⑤ weaving:织入,指把增强advice应用到target来创建新的代理对象proxy的过程。——可以看成工厂类
⑥ proxy:代理类—— 生成之后的代理类
⑦ aspect:切面,是指切入点pointCut和通知advice的结合。—— MyAspect与切入点结合
2. JDK代理
① 接口 IUserService
public interface IUserService { public void addUser(); public void delUser(); public void updateUser(); }
② 目标类UserService
//target目标类 public class UserService implements IUserService { //三个方法都是连接点,可能被拦截的方法 @Override public void addUser() { System.out.println("增加用户"); } @Override public void delUser() { System.out.println("删除用户"); } @Override public void updateUser() { System.out.println("更新用户"); } }
④ 切面类
//切面类aspect public class MyAspect { //通知advice public void before(){ System.out.println("开启事务"); } public void after(){ System.out.println("提交事务"); } }
④ 工厂类--提供一个创建代理类的方法
public class MyBeanFactory { public static IUserService createUserService(){ //target目标类 final IUserService userService = new UserService(); System.out.println(userService); //aspect切面类 final MyAspect aspect = new MyAspect(); //proxy 代理类 IUserService proxyService = (IUserService) Proxy.newProxyInstance(MyBeanFactory.class.getClassLoader(),userService.getClass().getInterfaces(),new InvocationHandler(){ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //执行前 aspect.before();; //放行 Object obj = method.invoke(userService,args); //执行后 aspect.after(); return obj; } }); return proxyService; } }
⑤ 测试
public static void main(String[] args) { IUserService userService = MyBeanFactory.createUserService(); //切入点,已经被增强的方法 userService.addUser(); }
3. Cglib代理
① 目标类 UserService(不实现接口)
//target目标类,不实现接口 public class Userervice { //三个方法都是连接点,可能被拦截的方法 public void addUser() { System.out.println("增加用户"); } public void delUser() { System.out.println("删除用户"); } public void updateUser() { System.out.println("更新用户"); } }
② 切面类(与jdk代理相同)
//切面类 public class MyAspect { //通知 public void before(){ System.out.println("开启事务"); } public void after(){ System.out.println("提交事务"); } }
③ 工厂类(与JDK不同,使用了enhancer,字节码增强)
public class MyFactory { public static Userervice createUserService(){ //1. 目标类 final Userervice userervice = new Userervice(); //2. 切面类 final MyAspect aspect = new MyAspect(); //3. cglib核心类 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(userervice.getClass()); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { aspect.before(); //执行代理类的方法,目标类和代理类是父子关系 Object obj = methodProxy.invokeSuper(o,objects); aspect.after(); return obj; } }); Userervice proxy = (Userervice) enhancer.create(); return proxy; } }
④ 测试(效果相同)
public static void main(String[] args) { Userervice userervice = MyFactory.createUserService(); userervice.addUser(); }
4. Aop通知类型
前置通知 before:在目标方法执行前实施增强
后置通知 afterReturning:在目标方法执行后实施增强,如果出现异常无法执行
环绕通知 around:在目标方法执行前后实施增强
异常抛出通知 afterThrowing:方法抛出异常后实施增强
引介通知 declare-parents:在目标类中添加一些新的方法和属性
最终通知 after : 在目标方法执行后实施增强,无论是否出现异常
三、AspectJ
AspectJ是一个基于Java语言的AOP框架,使用注解开发。
常用注解:
1. 通知注解
@Before 前置
@AfterReturning 后置
@Around 环绕
@AfterThrowing 抛出异常
@After 最终
2. 切入点 @PointCur
3. 声明切面 @Aspect
4. 切入表达式
最重要的一个表达式——execution():用于描述方法,execution(* com.crm.*.service..*.*(..))