您的位置:首页 > 编程语言 > ASP

学习Java框架的笔记(Spring AOP)简介、动态代理、基于代理类的AOP实现、AspectJ开发

2019-03-27 11:33 1281 查看

1.Spring AOP简介

1.1 什么是AOP

  • AOP的全称是:Aspect - Oriented - Programming 即 面向切面编程(也称之为:面向方面编程)
  • 在传统的业务处理代码中,通常都会进行事物处理、日志记录等操作。如果使用传统的OOP方式(通过组合或者继承)的方式来达到代码的重用,会使同样的代码分散到各个方法中。如果想要关闭这个操作(功能),就需要对相关的所有方法进行修改,十分麻烦,增加了代码的耦合性,不灵活,不符合(高内聚低耦合的要求)。
  • 为解决这一问题,AOP思想出现,AOP采用横向抽取机制,将分散在各个方法中的代码提取出来(例如日志记录,需要在main方法的前后进行执行),然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方,这种就是采用横向抽取机制的方式。AOP是一种新的编程思想,不是OOP的替代品,它是OOP的延伸和补充。
  • 目前最流行的AOP框架有两个,Spring AOPAspectJ
  • 区别:
    • Spring AOP: 使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式想目标类织入增强代码(扩充的功能代码)。
    • AspectJ: 是基于Java语言的AOP框架,从Spring 2.0开始,Spring AOP 引用了对AspectJ的支持,AspectJ扩展了Java语言,提供了 一个专门的编译器,在编译时提供横向代码的织入。

    1.2 AOP术语

    AOP术语:包括Aspect(切面)、Joinpoint(连接点)、Pointcut(切入点)、Advice(通知/增强处理)、Target Object(目标对象)、 Proxy(代理)、Weaving(织入)

    • Aspect(切面):通常指封装的用于横向插入系统功能(事务,日志)的类。该类要被Spring容器识别为切面,需要在配置文件中通过< bean >元素指定
    • Joinpoint(连接点):在程序执行过程中的某个阶段点,实际上是对象的一个操作(例如:方法的调用或者异常的抛出)。
    • Pointcut(切入点):切面与程序流程的交叉点,即那些需要处理的连接点。在通常程序中,切入点指的是类/方法名。(连接点包涵切入点且更多)。
    • Advice(通知/增强处理):AOP框架在特定的切入点执行的增强处理,即在定义好的切入点执行的代码。
    • Target Object(目标对象):是指所有被通知的对象,也称为被增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象就是一个被代理对象
    • Proxy(代理):将通知应用到目标对象之后被动态创建的对象。
    • Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程。

    2. 动态代理

    AOP中的代理就是由AOP框架动态生成的 一个对象,该对象可以作为目标对象使用。

    增强方法的意思就是:调用切面类,将切面类中的方法加到目标类中,扩充其中方法数量,就是增强方法。

    2.1 JDK动态代理

    例图:

    代码:
    (1)创建一个web项目,导入Spring框架所需要的JAR包到项目的lib目录下,并发布到类路径下。
    (2)在src目录下:创建一个com.itheima.jdk包,在该包下创建接口UserDao ,并在其中编写添加和删除的方法

    package com.itheima.jdk;
    public interface UserDao {
    public void addUser();		//添加
    public void deleteUser();		//删除
    }

    (3)在com.itheima.jdk包中,创建UserDao接口的实现类UserDaoImpl ,分别实现接口中的方法。

    package com.itheima.jdk;
    
    import org.springframework.stereotype.Repository;
    
    // 目标类(被代理对象:房主)
    @Repository("userDao")
    public class UserDaoImpl implements UserDao {
    public void addUser() {
    System.out.println("添加用户");
    }
    public void deleteUser() {
    System.out.println("删除用户");
    }
    }

    (4)在src目录下,创建一个com.itheima.aspect包,并且在该包下创建切面类MyAspect ,在该类中定义一个模拟权限检查的方法,这两个方法就表示切面中的通知

    package com.itheima.aspect;
    //切面类:可以存在多个通知Advice(即增强的方法)
    public class MyAspect {
    public void check_Permissions(){
    System.out.println("模拟检查权限...");
    }
    public void log(){
    System.out.println("模拟记录日志...");
    }
    }

    (5)在com.itheima.jdk包下,创建代理类JdkProxy,该类需要实现InvocationHandler接口,并编写代理方法。在代理方法中,需要通过Proxy类实现动态代理
    反射:通过对象名/类名,获取class文件,获取该类的所有
    通过对象: .getClass ; 通过类名: .class

    package com.itheima.jdk;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import com.itheima.aspect.MyAspect;
    /**
    * JDK代理类
    */
    public class JdkProxy implements InvocationHandler{   //InvocationHandler接口实现invoke()方法
    // 声明目标类接口(房主)
    private UserDao userDao;
    // 创建代理方法
    public  Object createProxy(UserDao userDao) {
    this.userDao = userDao;
    // 1.类加载器(通过类名反射得到当前类.getClassLoader()再获取类加载器上的容器)
    ClassLoader classLoader = JdkProxy.class.getClassLoader();
    // 2.被代理对象实现的所有接口,通过userDao对象实例,通过反射getInterfaces获取该类所有方法
    Class[] clazz = userDao.getClass().getInterfaces();
    // 3.使用代理类,进行增强,返回的是代理后的对象(动态生成一个代理类,参数:一个加载器,userDao的所有方法,该类)
    return  Proxy.newProxyInstance(classLoader,clazz,this);
    }
    /*
    * 所有动态代理类的方法调用,都会交由invoke()方法去处理
    * 执行被代理对象中的方法的反射
    * proxy 被代理后的对象
    * method 将要被执行的方法信息(反射)
    * args 执行方法时需要的参数,几个方法
    */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable {
    // 声明切面
    MyAspect myAspect = new MyAspect();
    // 前增强
    myAspect.check_Permissions();
    // 在目标类上调用方法,并传入参数
    Object obj = method.invoke(userDao, args);
    // 后增强
    myAspect.log();
    return obj;
    }
    }

    (6)在com.itheima.jdk包下,创建测试类JdkTest,在该类中的main方法中创建代理对象和目标对象,然后从代理对象中获取目标对象userDao增强后的对象,最后调用该对象的添加个删除方法

    package com.itheima.jdk;
    public class JdkTest{
    public static void main(String[] args) {
    // 创建代理对象
    JdkProxy jdkProxy = new JdkProxy();
    // 创建目标对象
    UserDao userDao= new UserDaoImpl();
    // 从代理对象中获取增强后的目标对象
    UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);
    // 执行方法
    userDao1.addUser();
    userDao1.deleteUser();
    }
    }

    实验结果截图:

    2.2 CGLIB代理

    相比于JDK动态代理而言,CGLIB代理有一个最大优势是:JDK动态代理对象必须实现一个或多个接口。而CGLIB代理可以对没有接口的类进行代理。

    (1)同上UserDao目标类一样:

    package com.itheima.cglib;
    //目标类
    public class UserDao {
    public void addUser() {
    System.out.println("添加用户");
    }
    public void deleteUser() {
    System.out.println("删除用户");
    }
    }

    (2)在com.itheima.cglib包中,创建代理类CglibProxy ,该代理类需要实现MethodInterceptor接口,并实现接口中的intercept()方法

    package com.itheima.cglib;
    import java.lang.reflect.Method;
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    import com.itheima.aspect.MyAspect;
    // 代理类
    public class CglibProxy implements MethodInterceptor{
    // 代理方法
    public  Object createProxy(Object target) {
    // 创建一个动态类对象
    Enhancer enhancer = new Enhancer();
    // 确定需要增强的类,设置其父类
    enhancer.setSuperclass(target.getClass());
    // 添加回调函数(自己调用自己)
    enhancer.setCallback(this);    //this代表的就是代理类CglibProxy 本身
    // 返回创建的代理类
    return enhancer.create();
    }
    /**
    * proxy CGlib根据指定父类生成的代理对象
    * method 拦截的方法
    * args 拦截方法的参数数组
    * methodProxy 方法的代理对象,用于执行父类的方法
    */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args,
    MethodProxy methodProxy) throws Throwable {
    // 创建切面类对象
    MyAspect myAspect = new MyAspect();
    // 前增强
    myAspect.check_Permissions();
    // 目标方法执行
    Object obj = methodProxy.invokeSuper(proxy, args);
    // 后增强
    myAspect.log();
    return obj;
    }
    }

    (3)在com.itheima.cglib包中,创建测试类CglibTest 。

    package com.itheima.cglib;
    // 测试类
    public class CglibTest {
    public static void main(String[] args) {
    // 创建代理对象
    CglibProxy cglibProxy = new CglibProxy();
    // 创建目标对象
    UserDao userDao = new UserDao();
    // 获取增强后的目标对象
    UserDao userDao1 = (UserDao)cglibProxy.createProxy(userDao);
    // 执行方法
    userDao1.addUser();
    userDao1.deleteUser();
    }
    }

    实验结果截图:

  • 内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: