AOP原理

简介

AOP(Aspect-OrientedProgramming)面向切面编程,可以理解为OOP的改进和完善,其编程思想是把散布于不同业务但功能相同的代码从业务逻辑中抽取出来,封装成独立的模块,这些独立的模块被称为切面,切面的具体功能方法被称为关注点。在业务逻辑执行过程中,AOP会把分离出来的切面和关注点动态切入到业务流程中,这样做的好处是提高了功能代码的重用性和可维护性。

  • AOP的相关术语:

Joinpoint:连接点,可以被拦截到的点

Pointcut:切入点,真正被拦截到的点

Advice:通知、增强(方法层面)

Introduction:引介(类层面的增强)

Target:目标(被增强的对象)

Weaving:织入,讲通知应用到目标的过程

Proxy:代理对象

Aspect:切面

  • 通知方法:

前置通知:在要执行的方法之前执行

后置通知:在要执行的方法之后执行

返回通知:在要执行的方法有返回值的时候执行

异常通知:在要执行的方法报异常的时候执行

环绕通知:在通知的内部执行要执行的方法,方法之前的内容就相当于前置通知,以此类推,形成环绕效果

原理

谈到AOP大家肯定首先想到的就是代理模式,那么代理模式是什么?

代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。

在代理中,还可以分为静态代理与动态代理。

静态代理

在我的理解中,静态代理十分简单,只需要继承或者实现它,然后实现它要代理的方法,在调用的时候直接调用代理类,从而实现代理功能。(记得之前看视频学习的时候听到过一句话,可以说是十分精辟巧妙:是你还是你,我中包含你)。具体实现方法如下:

  • 被代理类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private String name;
    private String age;
    public void doWork(){
        System.out.println("我是一个学生,我正在写作业!");
    }
    public void toTask(){
        System.out.println("作业写完了,去交作业啦!");
    }
}
  • 代理类
public class StudentProxy extends Student {
    @Override
    public void doWork() {
        super.doWork();
        System.out.println("帮你写完了作业,开心吧");
    }
    @Override
    public void toTask() {
        super.toTask();
    }
}
  • 测试
@Test
public void TestDemo1(){
    StudentProxy studentProxy = new StudentProxy();
    studentProxy.doWork();
    studentProxy.toTask();
}
/** OUTPUT **
我是一个学生,我正在写作业!
帮你写完了作业,开心吧
作业写完了,去交作业啦!
*/
  • 总结

在Student中定义了两个方法,一个做作业方法,一个写作业方法,当你写不完作业怎么办呢?很简单,写一个代理类,代理你写作业,然后就写完了。

在这个测试例子中,代理类StudentProxy代理了Student类,实现了静态代理。

代理模式就是在访问实际对象时引入一定程度的间接性,这里的间接性就是指不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途。

动态代理

动态代理与静态代理不同,静态代理是在程序运行之前就已经创建好了的,而动态代理是在程序运行的过程中动态的进行创建的。所以说动态代理可以很方便的对被代理类进行管理,而不用去直接修改代理方法。

动态代理现在用两种比较常用的实现:CGLIB动态代理JDK动态代理。两者的区别如下:

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 。
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP 。
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换。

  • JDK动态代理

还是之前的例子,那么代理类就可以变成下边这样:

public class JDKStudenProxy implements InvocationHandler {
    private Object target = null;
    public Object bind(Object object) {
        this.target=object;
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK正在帮你快速的做作业,马上就做完了,开心吧!");
        method.invoke(target, args);
        return null;
    }
}

因为JDK动态代理针对的是方法,所以需要被代理类实现一个方法,才能被代理,所以要实现的方法如下:

public interface Peroson {
    void doWork();
}

测试类:

@Test
public void tesetDemo2(){
    Student student = new Student();
    JDKStudenProxy jdkStudenProxy = new JDKStudenProxy();
    Peroson bind = (Peroson) jdkStudenProxy.bind(student);
    bind.doWork();
    student.toTask();
}
/** OUTPUT **
JDK正在帮你快速的做作业,马上就做完了,开心吧!
我是一个学生,我正在写作业!
作业写完了,去交作业啦!
*/
  • CGLIB动态代理

同样,代理类如下:

public class CGLIBStudentProxy implements MethodInterceptor {
    private Object targetObject;

    public Object createProxyObject(Object obj) {
        this.targetObject = obj;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(this);
        Object proxyObj = enhancer.create();
        return proxyObj;
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args,
                            MethodProxy methodProxy) throws Throwable {
        if(method.getName().equals("doWork")){
            System.out.println("CGLIB告诉你你得写作业了!");
        }else
            System.out.println("CGLIB帮你做完作业啦!");
        Object obj = null;
        obj = method.invoke(targetObject, args);
        return obj;
    }
}

因为CGLIB动态代理对代理类没有要求,所以被代理类没有变化,测试类如下:

@Test
public void testDemo3(){
    Student student = new Student();
    CGLIBStudentProxy cglibStudentProxy = new CGLIBStudentProxy();
    Student stu = (Student) cglibStudentProxy.createProxyObject(student);
    stu.doWork();
    stu.toTask();
}
/** OUTPUT **
CGLIB告诉你你得写作业了!
我是一个学生,我正在写作业!
CGLIB帮你做完作业啦!
作业写完了,去交作业啦!
*/