• Spring-05 AOP入门( 原理解析)


    AOP基本介绍

    概念

    OOP > 面向对象

    AOP > 面向切面 (切面 切入点 - 面)

    AOP 一般适用于, 具有横切逻辑的场合, 比如访问控制,事务管理,性能检测等等

    ​ Spring框架的AOP机制采用一种称为“横切”的技术,可以让开发者把业务流程中的通用功能抽取出来,单独编写功能代码。在业务流程执行过程中,Spring框架会根据业务流程要求,自动把独立编写的功能代码切入到流程的合适位置。

    厨师  核心   打荷 辅助
    打荷  备料 (鸡蛋 , 西红柿 )
    大厨 开始炒菜 
    摆盘  
    
    代码  必须由程序员来操作完成的    核心 
    没有必要由程序要来手动编写的  辅助 
    程序员 书写两套 系统   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    以往接触过的类似逻辑

    junit 的前置后置注解
    原始方式

    @Test
    public void m1(){
        // 前置功能(类似于配菜的工作)
        long starttime;
        long endtime;
        starttime = System.currentTimeMillis();
    
        String s = "";
        for (int i = 0; i < 10000; i++) {
            s += i;
        }
    
        // 后置功能(类似于洗碗的工作)
        endtime = System.currentTimeMillis();
        System.out.println("程序运行所耗时间为: "+ (endtime - starttime));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    优化之后

    //  计算程序运行所耗时间
    public long starttime;
    public long endtime;
    
    @Before
    public void start(){
        starttime = System.currentTimeMillis();
    }
    @After
    public void end(){
        endtime = System.currentTimeMillis();
        System.out.println("程序运行所耗时间为: "+ (endtime - starttime));
    //        使用StringUtil 需要导入 飞龙依赖
    //        String s = StringUtil.format("程序运行所耗时间为: %s毫秒",(endtime - starttime));
    //        System.out.println(s);
    }
    
    @Test
    public void m1(){
        String s = "";
        for (int i = 0; i < 10000; i++) {
            s += i;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    AOP编程思想及术语

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

    ● Aspect

    表示切面。切入业务流程的一个独立模块。一个应用程序可以拥有任意数量的切面。

    ● Join point

    表示连接点。也就是业务流程在运行过程中需要插入切面的具体位置。

    ● Advice

    表示通知。是切面的具体实现方法。可分为前置通知(Before)、后置通知(AfterReturning)、异常通知(AfterThrowing)、最终通知(After)和环绕通知(Around)五种。实现方法具体属于哪类通知,是在配置文件和注解中指定的。

    ● Pointcut

    表示切入点。用于定义通知应该切入到哪些连接点上,不同的通知通常需要切入到不同的连接点上。

    ● Target

    表示目标对象。被一个或者多个切面所通知的对象。

    ● Proxy

    表示代理对象。将通知应用到目标对象之后被动态创建的对象。可以简单地理解为,代理对象为目标对象的业务逻辑功能加上被切入的切面所形成的对象。

    ● Weaving

    表示切入,也称为织入。将切面应用到目标对象从而创建一个新的代理对象的过程。这个过程可以发生在编译期、类装载期及运行期。

    在这里插入图片描述

    辅助理解:
    周星驰的功夫场景!
    卖给周星驰如来神掌的老乞丐(袁祥仁), 这本《如来神掌》就是一个增强
    所有小孩就是普通类,收到这本树的就是目标类了, 像那些学会了神功的小孩就是所谓的代理类

    AOP 实现原理解析

    案例准备

    用户查询案例

    用户登陆系统之后, 去查询数据库的某一条信息
    (一个公司的网站, 员工需要查询 客户资料, 员工是否有权限查询)
    核心业务逻辑  查询
    辅助性工作  权限验证 , 日志功能
    
    • 1
    • 2
    • 3
    • 4

    java JDK 的动态代理,只支持接口方式增强

    Select (普通查询类 连接点 需要被增强的类 )
    SelectInterface 接口

    public interface SelectInterface {
        public void select();
        public void add();
    }
    
    • 1
    • 2
    • 3
    • 4

    Select 实现类 (目标类, 其中的方法, 需要被增强)

    public class Select  implements SelectInterface {
        public void select(){
            System.out.println("员工查询客户数据");
        }
        public void add(){
            System.out.println("员工添加客户数据");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    SelectAdvice (增强查询功能类 切面类, 增强类) 每一个方法称为(关注点)

    public class SelectAdvice {
    
        public void before(){
            System.out.println("验证权限功能!");
        }
    
        public void after(){
            System.out.println("日志记录功能");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    JDK 动态代理方式

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /*
    相当于一个饭店, 将打荷的员工, 和核心技能的厨师组合到一起工作
     */
    public class MyProxy implements InvocationHandler {
        // 声明目标接口
        private SelectInterface selectInterface;
    
        // 如何对 selectInterface 进行增强()
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            SelectAdvice sa = new SelectAdvice();
            // 前置方法 (摘菜洗菜切菜)
            sa.before();
            // 被增强方法的核心功能(炒菜)
            Object obj = method.invoke(selectInterface, args);
            // 后置方法 (装盘, 摆盘)
            sa.after();
            return obj;
        }
    
        // 如何生成 增强的代理类
        public Object createProxy(SelectInterface selectInterface){
            this.selectInterface = selectInterface;
    
            // 获取当前类的类加载器
            ClassLoader classLoader = MyProxy.class.getClassLoader();
            // 被代理对象实现的所有接口列表
            Class<?>[] classes = selectInterface.getClass().getInterfaces();
            // 使用Java 的JDK 的动态代理功能, 获取该类的代理类对象
            return  Proxy.newProxyInstance(classLoader,classes,this);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    测试

    @Test
    public void jdkTest(){
        // 创建代理工厂
        MyProxy myProxy = new MyProxy();
        // 创建被代理类对象
        Select select = new Select();
        // 获取 被代理对象的增强类
        SelectInterface proxySelect = (SelectInterface)myProxy.createProxy(select);
        proxySelect.select();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    CGLib 动态代理方式

    JDK 动态代理存在缺陷, 他只能为接口创建代理对象, 当需要为没有接口的类创建代理对象时, 就需要使用CGLib(Code Generration Library) 技术了。CGLib 代理不要求目标类是否有接口, 它采用底层的字节码技术, 通过继承的方式动态创建代理对象。
    另外 Spring核心包中已经集成了CGLib 依赖, 所以开发时不需要再引入其他依赖。

    public class CglibProxy implements MethodInterceptor {
    
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            // Cglib 不需要使用目标类的接口, 就可以生成目标类的代理对象, 实际上是该对象的子类
            // 创建增强类的对象, 用于增强
            SelectAdvice sa = new SelectAdvice();
            // 前置增强
            sa.before();
            Object obj = methodProxy.invokeSuper(proxy, args);
            // 后置增强
            sa.after();
            return obj;
        }
        // 返回增强类
        public Object createProxy(Object target){
            //创建一个动态代理类对象
            Enhancer enhancer = new Enhancer();
            // 确定需要增强的类
            enhancer.setSuperclass(target.getClass());
            // 配置回调函数(返回该代理类时, 配置其去执行增强功能)
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    测试功能

    @Test
    public void cglibTest(){
        // 创建代理工厂
        CglibProxy myProxy = new CglibProxy();
        // 创建被代理类对象
        Select select = new Select();
        // 获取 被代理对象的增强类
        SelectInterface proxySelect = (SelectInterface)myProxy.createProxy(select);
        proxySelect.select();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 相关阅读:
    你的RPCvs佬的RPC
    [附源码]Python计算机毕业设计Django基于微信小程序的网络办公系统
    顺序表的(增删查改)实现
    2022.11.15 英语背诵
    吃鸡高手必备!这些技巧帮你提高战斗力!
    防火墙---双机热备技术
    stm32 - 中断
    开源在线课堂知识付费源码系统 PHP+MySQL组合开发 支持视频、音频、图文章节 带完整的搭建教程
    2022 年前端工程师学习路线图(完整版)
    字符串中数据排序
  • 原文地址:https://blog.csdn.net/linyuancsdn/article/details/126819553