动态代理 过渡 AOP

提问:你可以按照网上教程写一个动态代理的案例;也可以写一个AOP的案例。也常听到AOP的底层就是动态代理,是否能解释的清楚它两之间的关系呢?

目录

  • 一 无代理
  • 二、静态代理改造
  • 三 动态代理改造
  • 四 spring AOP的实现
  • 总结

一 无代理

public interface UserService {
    void save(String name);
    void delete(String id);
    void update();
    String findAll(String name);
    String findOne(String id);
}
// 原始业务逻辑对象
public class UserServiceImpl implements UserService{
    // 开启事务   处理业务   调用dao
    @Override
    public void save(String name) {
        try {
            System.out.println("开启事务");
            System.out.println("处理事务逻辑,调用DAO~~~");
            System.out.println("提交事务");
        } catch (Exception e) {
            System.out.println("回滚事务");
            e.printStackTrace();
        }
    }

    @Override
    public void delete(String id) {
        try {
            System.out.println("开启事务");
            System.out.println("处理事务逻辑,调用DAO~~~");
            System.out.println("提交事务");
        } catch (Exception e) {
            System.out.println("回滚事务");
            e.printStackTrace();
        }
    }

    @Override
    public void update() {
        try {
            System.out.println("开启事务");
            System.out.println("处理事务逻辑,调用DAO~~~");
            System.out.println("提交事务");
        } catch (Exception e) {
            System.out.println("回滚事务");
            e.printStackTrace();
        }
    }

    @Override
    public String findAll(String name) {
        try {
            System.out.println("开启事务");
            System.out.println("处理事务逻辑,调用DAO~~~");
            System.out.println("提交事务");
        } catch (Exception e) {
            System.out.println("回滚事务");
            e.printStackTrace();
        }
        return name;
    }

    @Override
    public String findOne(String id) {
        try {
            System.out.println("开启事务");
            System.out.println("处理事务逻辑,调用DAO~~~");
            System.out.println("提交事务");
        } catch (Exception e) {
            System.out.println("回滚事务");
            e.printStackTrace();
        }
        return id;
    }
}
    public static void main(String[] args) {
        UserService  userService = new UserServiceImpl();
        userService.save("1");
     }

在这里插入图片描述
可以发现UserServiceImpl 中存在重复代码 和业务代码

重复代码
System.out.println("开启事务");
System.out.println("提交事务");
System.out.println("回滚事务");

业务代码
System.out.println("处理事务逻辑,调用DAO~~~");

我们希望将重复的非业务代码拆分出来,UserServiceImpl 专心写业务代码,创建一个代理类就负责重复的非业务代码
更新后的代码

二、静态代理改造

public interface UserService {
    void save(String name);
    void delete(String id);
    void update();
    String findAll(String name);
    String findOne(String id);
}
// 原始业务逻辑对象
public class UserServiceImpl implements UserService{
    // 开启事务   处理业务   调用dao
    @Override
    public void save(String name) {
        System.out.println("处理事务逻辑,调用DAO~~~");
    }

    @Override
    public void delete(String id) {
      System.out.println("处理事务逻辑,调用DAO~~~");
    }

    @Override
    public void update() {
       System.out.println("处理事务逻辑,调用DAO~~~");
    }

    @Override
    public String findAll(String name) {
        System.out.println("处理事务逻辑,调用DAO~~~");
        return name;
    }

    @Override
    public String findOne(String id) {
        System.out.println("处理事务逻辑,调用DAO~~~");
        return id;
    }
}
//静态代理类
//开发原则:代理类和目标类实现相同接口,依赖于真正的目标类
public class UserServiceStaticProxy implements UserService{

    // 依赖原始业务逻辑对象 // Target:目标对象|被代理对象称之为目标对象 原始业务逻辑对象
    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void save(String name) {
        try {
            System.out.println("静态代理:开启事务");
            // 调用原始业务逻辑对象的方法
            userService.save(name);
            System.out.println("静态代理:提交事务");
        } catch (Exception e) {
            System.out.println("静态代理:回滚事务");
            e.printStackTrace();
        }
    }

    @Override
    public void delete(String id) {
        try {
            System.out.println("静态代理:开启事务");
            // 调用原始业务逻辑对象的方法
            userService.delete(id);
            System.out.println("静态代理:提交事务");
        } catch (Exception e) {
            System.out.println("静态代理:回滚事务");
            e.printStackTrace();
        }
    }

    @Override
    public void update() {
        try {
            System.out.println("静态代理:开启事务");
            // 调用原始业务逻辑对象的方法
            userService.update();
            System.out.println("静态代理:提交事务");
        } catch (Exception e) {
            System.out.println("静态代理:回滚事务");
            e.printStackTrace();
        }
    }

    @Override
    public String findAll(String name) {
        try {
            System.out.println("静态代理:开启事务");
            // 调用原始业务逻辑对象的方法
            String all = userService.findAll(name);
            System.out.println("静态代理:提交事务");
            return all;
        } catch (Exception e) {
            System.out.println("静态代理:回滚事务");
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String findOne(String id) {
        try {
            System.out.println("静态代理:开启事务");
            // 调用原始业务逻辑对象的方法
            String one = userService.findOne(id);
            System.out.println("静态代理:提交事务");
            return one;
        } catch (Exception e) {
            System.out.println("静态代理:回滚事务");
            e.printStackTrace();
        }
        return null;
    }
}

    public static void main(String[] args) {
		UserService  userService = new UserServiceImpl();
        UserServiceStaticProxy proxy = new UserServiceStaticProxy();
        proxy.setUserService(userService);
        proxy.save("2");
     }

在这里插入图片描述

可以发现,UserServiceImpl中只写了业务代码,其他的写在了静态代理中。

静态代理是无法满足实际场景的,需要使用动态代理才行,具体原因参考此文

三 动态代理改造

public interface UserService {
    void save(String name);
    void delete(String id);
    void update();
    String findAll(String name);
    String findOne(String id);
}
// 原始业务逻辑对象
public class UserServiceImpl implements UserService{
    // 开启事务   处理业务   调用dao
    @Override
    public void save(String name) {
        System.out.println("处理事务逻辑,调用DAO~~~");
    }

    @Override
    public void delete(String id) {
      System.out.println("处理事务逻辑,调用DAO~~~");
    }

    @Override
    public void update() {
       System.out.println("处理事务逻辑,调用DAO~~~");
    }

    @Override
    public String findAll(String name) {
        System.out.println("处理事务逻辑,调用DAO~~~");
        return name;
    }

    @Override
    public String findOne(String id) {
        System.out.println("处理事务逻辑,调用DAO~~~");
        return id;
    }
}
public class UserServiceStaticSynamicProxy implements InvocationHandler {
    private Object target;

    public void setUserService(Object target) {
        this.target = target;
    }

    public Object getProxy(){
        Object o = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        return o;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理:开启事务");
        Object result = method.invoke(target, args);
        System.out.println("代理:提交事务");
        return result;
    }
}

    public static void main(String[] args) {
        UserService  userService = new UserServiceImpl();
        UserServiceStaticSynamicProxy proxy = new UserServiceStaticSynamicProxy();
        proxy.setUserService(userService);
        UserService proxy2 = (UserService)proxy.getProxy();
        proxy2.findAll("3");
    }

在这里插入图片描述

四 spring AOP的实现

这里提供一个伪代码:
我们自己写一个@Before,@After注解,被该注解标记的方法中就写非业务代码。
如被@Before标记的方法,实现”spring aop 动态代理:开启事务“
被@After标记的方法,实现”spring aop 动态代理:提交事务“

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class Test {
    public static void main(String[] args) {
        ApplicationContext.registerBean(Config.class);
        Config o = (Config)ApplicationContext.map.get(Config.class.getName());
        o.pointCut();
    }


}

class Config {

	// 自定义注解Before
    @Before
    public void before() {
        System.out.println("before");
        System.out.println("spring aop 动态代理:开启事务");
    }

	// 自定义注解After
    @After
    public void before() {
        System.out.println("after");
        System.out.println("spring aop 动态代理:提交事务");
    }

	// 被代理的方法
    public void pointCut() {
        System.out.println("pointCut");
         System.out.println("处理事务逻辑,调用DAO~~~");
    }



}

class ApplicationContext {

    public static Map<String, Object> map = new HashMap<>();

	// 参数传递Class字节码:如Config.class
    public static void registerBean(Class clazz) {
        Method beforeMethod = null;
        Method afterMethod = null;
        // 获取Config类全部的public属性方法
        for (Method declaredMethod : clazz.getDeclaredMethods()) {
        
        	// 找到具有@Before注解的方法
            Before annotation = declaredMethod.getAnnotation(Before.class);
            if (annotation != null) {
                beforeMethod = declaredMethod;
            }
			// 找到具有@After注解的方法
			After annotation = declaredMethod.getAnnotation(After.class);
            if (annotation != null) {
                afterMethod = declaredMethod;
            }
        }

        Set<String> proxyMethName = new HashSet<>();
        for (Method declaredMethod : clazz.getDeclaredMethods()) {
            Before annotation = declaredMethod.getAnnotation(Before.class);
            if (annotation != null) {
                continue;
            }
			After annotation = declaredMethod.getAnnotation(After.class);
            if (annotation != null) {
                continue;
            }
            proxyMethName.add(declaredMethod.getName());
        }

// 这里使用cjlib的方式实现动态代理(没有接口的动态代理实现),之前实例的是jdk的动态代理实现(基于接口实现)
        Enhancer enhancer = new Enhancer();
        // 设置enhancer对象的父类
        enhancer.setSuperclass(clazz);
        // 设置enhancer的回调对象
        Method finalBeforeMethod = beforeMethod;
        Method finalAfterMethod = afterMethod;
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                if (proxyMethName.contains(method.getName()) && finalBeforeMethod != null) {
                	// 这一行打印:spring aop 动态代理:开启事务
                    finalBeforeMethod.invoke(o, objects);
                }
                // 调用被代理的方法(这一行打印:处理事务逻辑,调用DAO~~~)
                Object invoke = methodProxy.invokeSuper(o, objects);
                
				if (proxyMethName.contains(method.getName()) && finalAfterMethod != null) {
					// 这一行打印:spring aop 动态代理:提交事务
                    finalAfterMethod .invoke(o, objects);
                }
                return invoke;
            }
        });
        // 创建代理对象
        Object o = enhancer.create();
        map.put(clazz.getName(), o);
    }
}

最原始的方式,我们是手写了一个最标准的动态代理模式。现在我们改造了一下,模仿spring实现动态代理。表面看是只是用了@Before
注解,还有什么切面概念,其实还是代理的逻辑。

这个时候你再看spring aop的实现过程,是不是特别清晰

spring利用注解实现aop

spring利用xml实现aop

总结

目的:将非业务代码抽离出来,我们自定义了动态代理解决;也利用了spring提供的方案解决了。而在 ”利用xml实现aop“ 中提供的示例中,作者表面未使用动态代理的写法,最内层确实是动态代理
在这里插入图片描述

回顾一下前面的问题:为什么说aop的底层就是动态代理。我是这样理解的:
在没有spring之前,我们可以使用自定义的动态代理方式将业务代码前后的非业务代码抽离出来(例如前面将非业务代码抽离到代理类中)。后来spring一琢磨,把这种行为定义为切面编程的思想,取了个英文名AOP。并把将我们之前首先动态代理过程改造了一下,套了一壳,包装了一下,换成了更简单的写法。
所以,当你想将业务代码前后的非业务代码抽离出来时,你可以考虑自己手写一个动态代理,也可以使用spring提供的写法。根据实际需求出发。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/513671.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

redis之主从复制、哨兵模式

一 redis群集有三种模式 主从复制&#xff1a; 主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可用的。 主从复制主要实现了数据的多机备份&#xff0c;以及对于读操作的负载均衡和简单的故障恢复。 缺陷&#xff1a; 故障恢复无法自动化&…

Python搭建编程环境—安装Python3解释器

✅作者简介&#xff1a;CSDN内容合伙人、阿里云专家博主、51CTO专家博主、新星计划第三季python赛道Top1&#x1f3c6; &#x1f4c3;个人主页&#xff1a;hacker707的csdn博客 &#x1f525;系列专栏&#xff1a;零基础学Python &#x1f4ac;个人格言&#xff1a;不断的翻越一…

数据结构(二)----线性表(顺序表,链表)

目录 1.线性表的概念 2.线性表的基本操作 3.存储线性表的方式 &#xff08;1&#xff09;顺序表 •顺序表的概念 •顺序表的实现 静态分配&#xff1a; 动态分配&#xff1a; 顺序表的插入&#xff1a; 顺序表的删除&#xff1a; 顺序表的按位查找&#xff1a; 顺序…

搭建跨境电商电商独立站如何接入1688平台API接口|通过1688API接口采集商品通过链接搜索商品下单

接口设计|接口接入 对于mall项目中商品模块的接口设计&#xff0c;大家可以参考项目的Swagger接口文档&#xff0c;以Pms开头的接口就是商品模块对应的接口。 参数说明 通用参数说明 参数不要乱传&#xff0c;否则不管成功失败都会扣费url说明……d.cn/平台/API类型/ 平台&…

LLM大模型可视化-以nano-gpt为例

内容整理自&#xff1a;LLM 可视化 --- LLM Visualization (bbycroft.net)https://bbycroft.net/llm Introduction 介绍 Welcome to the walkthrough of the GPT large language model! Here well explore the model nano-gpt, with a mere 85,000 parameters. 欢迎来到 GPT 大…

AI复活:商业新风口还是情感禁区?

随着人工智能技术的飞速发展&#xff0c;AI已经渗透到我们生活的方方面面&#xff0c;其中&#xff0c;“AI复活”服务作为新兴的技术应用&#xff0c;正逐渐走进大众视野。然而&#xff0c;这一技术带来的不仅是商业机会&#xff0c;更伴随着伦理和情感的争议。 “AI复活”服务…

HDLbits 刷题 --Conditional

学习: Verilog has a ternary conditional operator ( ? : ) much like C: (condition ? if_true : if_false) This can be used to choose one of two values based on condition (a mux!) on one line, without using an if-then inside a combinational always block. …

什么是智慧驿站?智慧驿站主要应用有哪些?新型智慧公厕解说

智慧驿站是一种融合了创意设计和多项功能的新型智慧公厕&#xff0c;它在信息化公共厕所的基础上&#xff0c;以创意的外观设计、全金属结构用材、快速制作整体运输、快速部署落地使用等价值特点&#xff0c;所打造了一个集购物、互动、休憩等多种功能于一体的城市基础设施。无…

redis 集群 (主从复制 哨兵模式 cluster)

目录 一 主从复制 &#xff08;一&#xff09;相关理论 1&#xff0c;主从复制定义 2&#xff0c;主从复制的作用 3&#xff0c;主从复制架构图 4 sync 同步过程 5&#xff0c;主从复制流程 &#xff08;二&#xff09; 实验模拟 1&#xff0c; 实验环境 2, 修…

Java | Leetcode Java题解之第5题最长回文子串

题目&#xff1a; 题解&#xff1a; class Solution {public String longestPalindrome(String s) {int start 0, end -1;StringBuffer t new StringBuffer("#");for (int i 0; i < s.length(); i) {t.append(s.charAt(i));t.append(#);}t.append(#);s t.to…

jsp实现增删改查——(三)用Echarts图表统计学生信息

学生信息CRUD——Echarts显示生活费 目录结构 创建一个js文件夹&#xff0c;将echarts.min.js放到里面。 功能实现 与之前我们写的jsp文件&#xff08;含有html代码、Java代码&#xff09;不同的是&#xff0c;实现Echarts对生活费的显示&#xff0c;需要调用echarts.min.js…

Java中线程详解

文章目录 相关概念多线程概念实现方式继承Thread类实现Runnable接口比较 常用方法线程安全产生的原因解决思想同步同步代码块同步方法Lock锁机制 死锁概念避免 状态线程间的通讯介绍方法 相关概念 并行&#xff1a;在同一时刻&#xff0c;有多个任务在多个CPU上同时执行并发&a…

为什么拼音命名在编程中不被推荐?

为什么拼音命名在编程中不被推荐? 在学习编程的过程中&#xff0c;命名变量、函数和类等是一个重要的环节。然而&#xff0c;专业的编程教材和经验都强烈建议不要使用拼音来命名&#xff0c;并且拼音命名常常被教育和经验严厉禁止。本文将探讨为何学编程时不推荐使用拼音命名&…

Spring注解@EventListener实现监听原理

文章目录 EventListener使用方式EventListener实现原理1.引入时机-获取bean定义2 实例化时机-new对象3 作用时机->将加了EventListener注解的方法识别出来&#xff0c;并封装为监听器&#xff0c;加载spring容器中 总结 EventListener使用方式 package com.cyl.listener;im…

嵌入式中常见的面试题分享

1.关键字static的作用是什么&#xff1f;为什么static变量只初始化一次&#xff1f; 1&#xff09;修饰局部变量&#xff1a;使得变量变成静态变量&#xff0c;存储在静态区&#xff0c;存储在静态区的数据周期和程序相同&#xff0c; 在main函数开始前初始化&#xff0c;在退…

权限提升技术:攻防实战与技巧

本次活动赠书1本&#xff0c;包邮到家。参与方式&#xff1a;点赞收藏文章即可。获奖者将以私信方式告知。 网络安全已经成为当今社会非常重要的话题&#xff0c;尤其是近几年来&#xff0c;我们目睹了越来越多的网络攻击事件&#xff0c;例如公民个人信息泄露&#xff0c;企业…

ContEA论文翻译

Facing Changes: Continual Entity Alignment for Growing Knowledge Graphs 面对变化&#xff1a;不断增长的知识图谱的持续实体对齐 Abstract 实体对齐是知识图谱(KG)集成中一项基本且重要的技术。多年来&#xff0c;实体对齐的研究一直基于知识图谱是静态的假设&#xff…

CLIP 图文检索,相似度计算

CLIP 是OpenAI提出的神经网络&#xff0c;它可以从自然语言监督中有效地学习视觉概念。 CLIP 可以应用于任何视觉分类基准&#xff0c;只需提供要识别的视觉类别的名称&#xff0c;类似于 GPT-2 和 GPT-3 的“零样本”功能。 相关paper 用法可以参考github 这里举几个使用CLI…

EDM邮件推广营销是什么意思?

在当今瞬息万变的商业环境中&#xff0c;数字化营销手段已成为企业与消费者建立有效连接、提升品牌影响力的关键途径。其中&#xff0c;EDM&#xff08;Electronic Direct Mail&#xff09;邮件推广营销作为一种成熟且极具性价比的策略&#xff0c;正以其精准投放、实时追踪、成…

dhcp和dhcp中继代理

简单说就是各个pc机的ip自动获取&#xff0c;不用手动设置 配置思路 1.使能dhcp功能 2.创建全局地址池 ip pool &#xff0c;配置可用网络地址 network mask和网关地址刚刚忘记了&#xff0c;租约期 lease day hour 3.配置端口的网关地址&#xff08;各个网络地址的第二位…