06-Spring与AOP

Posted zhy0720

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了06-Spring与AOP相关的知识,希望对你有一定的参考价值。

Spring与AOP思想

1. AOP简介

AOP(Aspect Orient Programming),面向切面编程,是面向对象编程OOP的一种补充。面向对象编程是从静态角度考虑程序的结构,而面向切面编程是从动态角度考虑程序运行过程。
通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP底层,就是采用动态代理模式实现的。采用了两种代理:JDK的动态代理与CGLIB的动态代理。
面向切面编程,就是将交叉业务逻辑封装成切面,利用AOP容器的功能将切面织入到主业务逻辑中。所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、事务、日志等。
若不使用AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样,会使主业务逻辑变得混杂不清。
例如:转账,在真正转账业务逻辑前后,需要权限控制、日志记录、加载事务、结束事务等交叉业务逻辑,而这些业务逻辑与主业务逻辑间并无直接关系。但,它们的代码量所占必重能达到总代码两的一半甚至更多。它们的存在,不仅产生了大量的冗余代码,还大大干扰了主业务逻辑--转账。
技术图片
技术图片
技术图片

总结:

  • 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  • AOP底层,就是采用动态代理模式实现的。采用了两种代理:JDK的动态代理与CGLIB的动态代理。

2. 为什么学习AOP

对程序进行增强:不修改源码的情况下,AOP可以进行权限校验,日志记录,性能监控,事务控制。

3. AOP的由来

AOP最早由AOP联盟的组织提出的,制定了一套规范,Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范。

4. AOP的底层实现

代理机制:Spring的AOP底层用到两种代理机制。

  1. JDK的动态代理:针对实现了接口的类产生代理。
  2. CGLIB的动态代理:针对没有实现接口的类产生代理,应用的是底层的字节码增强的技术,生成当前的子类对象。
    具体详解动态代理机制:

    JDK动态代理【优先】

    被代理对象必须要实现接口,才能产生代理对象.如果没有接口将不能使用动态代理技术。
    JDK的动态代理,是使用反射技术获取类的加载器并且创建实例,根据类执行的方法,在执行方法的前后发送通知
    在代理对象Proxy的新建代理实例方法中,必须要获得类的加载器,类所实现的接口,还有一个拦截方法的句柄。在句柄的invoke中如果不调用method.invoke则方法不会执行。在invoke前后添加通知,就是对原有类进行功能扩展了
    创建好代理对象之后,proxy可以调用接口中定义的所有方法,因为它们实现了同一个接口,并且接口的方法实现类的加载器已经被反射框架获取到了
    (1)java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入目标接口的类加载器,目标接口数组以及InvocationHandler接口便可为目标接口生成代理类及代理对象
    (2)java.lang.reflect.InvocationHandler:该接口包含一个invoke方法,通过该方法实现对委托类的代理的访问,是代理类完整逻辑的集中体现,包括要切入的增强逻辑和进行反射执行的真实业务逻辑
    (3)java.lang.ClassLoader:类加载器类,负责将类的字节码装载到Java虚拟机中并为其定义类对象,然后该类才能被使用。Proxy静态方法生成动态代理类同样需要通过类加载器来进行加载才能使用,它与普通类的唯一区别就是其字节码是由JVM在运行时动态生成的而非预存在于任何一个.class 文件中
    通过JDK的java.lang.reflect.Proxy类实现动态代理,会使用其静态方法newProxyInstance(),依据目标对象,业务接口及业务增强逻辑三者。自动生成一个动态代理对象
    InvocationHadnler接口:实现了InvocationHandler接口的类用于加强目标类的主业务逻辑。这个接口中由一个方法invoke(),具体加强的代码逻辑就是定义在该方法中的,程序在调用主业务逻辑时,会自动调用invoke()方法。由于该方法是由代理对象自动调用的,所以这三个参数的值不用程序员给出
    技术图片
    技术图片
    技术图片
    技术图片

public interface UserService{
    void save();
    void delete();
    void update();
    void find();
}
public class UserServiceImpl{
    @Override
    public void save(){System.out.println("保存用户!")};
    @Override
    public void delete(){System.out.println("删除用户!")};
    @Override
    public void update(){System.out.println("更新用户!")};
    @Override
    public void find(){System.out.println("查找用户!")};
}
public class UserServiceProxyFactory implments InvocationHandler{
    // 需要代理的对象类,并通过构造方法对该对象进行初始化
    private UserService us;
    public UserServiceProxyFactory(UserService us){
        super();
        this.us = us;
    }
    public UserService get
    public UserService getUserServiceProxy(){
        // 生成动态代理
    UserService usProxy = (UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(),
        UserService.class.gegtInterfaces(),this);
        // 返回代理对象
        return usProxy;
    }
    @Override
    // 第一个参数是当前代理对象;第二个参数是当前调用的方法;第三个代理对象是:当前调用方法执行的参数数组
    public Object invoke(Object proxy,Method method,Object[] args)throws Throwable(){
        System.out.println("打开事务!");
        Object invoke = method.invoke(us,args);
        System.out.println("提交事务!");
        return invoke;
    }
}

public class Demo{
    @Test
    public void jdkProxyTest(){
        UserService us = new UserServiceImpl();
        UserServiceProxyFactor factory = new UserServiceProxyFactory(us);
        UserService usProxy = factory.getUserServiceProxy();
        usFactory.save();
    }
}

CGLIB代理【没有接口】

第三方代理技术,cglib代理.可以对任何类生成代理.代理的原理是对目标对象进行继承代理. 如果目标对象被final修饰.那么该类无法被cglib代理。
JDK动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用CGLIB实现。
使用JDK动态代理,要求目标类与代理类实现相同的接口,若目标类不存在接口,则无法使用该方式实现。但对于无接口的类,要为其创建动态代理,就要使用CGLIB来实现。
CGLIB代理的生成原理是通过生成目标类的子类,而子类是增强过的,这个子类对象就是代理对象。所以,使用CGLIB生成动态代理,要求目标类必须能够被继承,即不能是final的类。
CGLIB底层是通过使用一个小而快的字节码处理框架ASM来转换字节码并生成新的类。CGLIB是通过对字节码进行增强来生成代理的。

// MethodInterceptor是Callback的一个子接口
public class UserServiceProxyFactory implements MethodInterceptor{
    public UserService getUserServiceProxy(){
        // Enhancer:Spring帮我们集成好了,不需要自己找包,它会帮我们生成代理对象
        Enhancer en = new Enhancer();
        // 设置对谁进行代理
        en.setSuperclass(UserServiceImpl.class);
        // 代理要做什么
        en.setCallback(this);
        // 创建代理对象
        UserService us = en.create();
        return us;
    }
    @Override
    // proxy:被代理的原始对象;Method:被代理对象的方法;args:运行的参数;methodProxy:代理方法传递过来的
    public Object intercept(Object proxy,Method method,Object[] args MethodProxy methodProxy)throws Throwable(){
        System.out.println("打开事务!");
        Object invoke = methodProxy.invokeSuper(proxy,args);
        System.out.println("提交事务!");
        return invoke;
    }
}
public class Demo{
    @Test
    public void cglibProxyTest(){
        UserServiceProxyFactor factory = new UserServiceProxyFactory();
        UserService usProxy = factory.getUserServiceProxy();
        usFactory.save();
        // 判断代理对象是否属于被代理对象类型
        System.out.println(usProxy instanceof UserServiceImpl);
        
    }
}

5. Spring中的AOP【AspectJ】

AOP思想最早由AOP联盟组织提出的。而Spring是使用这种思想最好的框架。
Spring的AOP最开始有自己实现的方式,但方式很繁琐。后来出现了AspectJ【是一个AOP框架】,Spring引入Aspect J作为自身AOP的开发。
Spring有两套AOP的开发方式:
Spring的传统方式(弃用)
Spring基于AspectJ的AOP开发(使用)

6. AOP编程术语

以上是关于06-Spring与AOP的主要内容,如果未能解决你的问题,请参考以下文章

[Java复习06] Spring IoC

06Spring源码-分析篇-ApplicationContext

spring aop中this和target区别

阿里四面:你知道Spring AOP创建Proxy的过程吗?

AOP框架Dora.Interception 3.0 [3]: 拦截器设计

Spring的AOP面向切面编程