Spring AOP

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..*.*(..))

  

原文地址:https://www.cnblogs.com/qmillet/p/12541116.html