java设计模式之代理设计模式(Spring核心思想AOP的底层设计模式)

简介: 代理设计模式🍅 作者:程序员小王🍅 程序员小王的博客:程序员小王的博客🍅 扫描主页左侧二维码,加我微信 一起学习、一起进步🍅 欢迎点赞 👍 收藏 ⭐留言 📝🍅 如有编辑错误联系作者,如果有比较好的文章欢迎分享给我,我会取其精华去其糟粕🍅java自学的学习路线:java自学的学习路线

代理设计模式文章目录:

一、结构型模式

结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。


由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。


结构型模式分为以下 7 种:


  • 代理模式
  • 适配器模式
  • 装饰者模式
  • 桥接模式
  • 外观模式
  • 组合模式
  • 享元模式


二、现有开发中存在的问题

问题:在JavaEE分层开发开发中,那个层次对于我们来讲最重要?

DAO ---> Service --> Controller 
JavaEE分层开发中,最为重要的是Service层

1、定义业务层接口

public interface EmpService {
    /**
     * 分页
     * @return 当前页的员工
     */
    public List<Emp> selectEmpByPage(int pageNumber);
    /**
     * 总的页数
     * @return 数据库有多少条员工数据
     */
    public int selectTotalPage();
    /**
     * 添加员工
     * @param emp
     */
    public void addEmp(Emp emp);
    /**
     * 根据id删除员工
     * @param id
     */
    public void dropEmp(Integer id);
    /**
     * 根据id修改员工信息
     * @param emp
     */
    public void update(Emp emp);
    /**
     * 批量删除员工
     * @param ids
     */
    public void plDeleteEmp(List<Integer> ids);
    /**
     * 更具id查询员工,用于数据回显
     * @param id
     * @return
     */
    public Emp queryEmp(Integer id);
}

2、实现业务接口

public class EmpServiceImpl implements EmpService {
    @Override
    public List<Emp> selectEmpByPage(int pageNumber) {
        EmpDao mapper = (EmpDao) MybatisUtil.getMapper(EmpDao.class);
        //设置每页展示的页数
        int rows = 3;
        //设置第几页开始的起始条数
        int begin = (pageNumber - 1) * rows;
        List<Emp> emps = mapper.selectEmpByPage(begin, rows);
        MybatisUtil.close();
        return emps;
    }
    @Override
    public int selectTotalPage() {
        EmpDao mapper = (EmpDao) MybatisUtil.getMapper(EmpDao.class);
        int i = mapper.selectTotalCount();
        int rows = 3;
        int page = 0;
        if (i % rows == 0) {
            page = i / rows;
        } else {
            page = i / rows + 1;
        }
        MybatisUtil.close();
        return page;
    }
    @Override
    public void addEmp(Emp emp) {
        EmpDao mapper = (EmpDao) MybatisUtil.getMapper(EmpDao.class);
        mapper.insertEmp(emp);
        MybatisUtil.commit();
    }
    @Override
    public void dropEmp(Integer id) {
        EmpDao mapper = (EmpDao) MybatisUtil.getMapper(EmpDao.class);
        mapper.deleteEmp(id);
        MybatisUtil.commit();
    }
    @Override
    public void update(Emp emp) {
        EmpDao mapper = (EmpDao) MybatisUtil.getMapper(EmpDao.class);
        mapper.update(emp);
        MybatisUtil.commit();
    }
    @Override
    public void plDeleteEmp(List<Integer> ids) {
        EmpDao mapper = (EmpDao) MybatisUtil.getMapper(EmpDao.class);
        mapper.plDeleteEmp(ids);
        MybatisUtil.commit();
    }
    @Override
    public Emp queryEmp(Integer id) {
        EmpDao mapper = (EmpDao) MybatisUtil.getMapper(EmpDao.class);
        Emp emp = mapper.queryEmp(id);
        MybatisUtil.close();
        return emp;
    }
}

3、Service层中包含了哪些代码?

Service层中 = 核心功能(几十行 上百代码) + 额外功能(附加功能)
1. 核心功能
   业务运算
   DAO调用
2. 额外功能 
   1. 不属于业务
   2. 可有可无
   3. 代码量很小 
   事务、日志、性能...

从下图中可以看出,现有业务层中控制事务代码出现了大量的冗余,如何解决现有业务层出现的冗余问题?

0.png



4、额外功能书写在Service层中好不好?

Service层的调用者的角度(Controller):需要在Service层书写额外功能。
                         软件设计者:Service层不需要额外功能


5、现实生活中的解决方式

1.住酒店不一定需要亲自到酒店去,还可以通过微信支付下的同程艺龙来订酒店。


2.我们可以通过中介去找房子,不用直接跟房东沟通(现实生活中,我们更希望直接跟房东沟通)


3.春运买票买不到,我们可以找黄牛替我们抢票


4.想访问国外的网站,可以使用代理服务器进行访问。


三、代理设计模式

单词 proxy [ˈprɑːksi] 代理


1、概述

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。


1.png


Java中的代理按照代理类生成时机不同 又分为静态代理和动态代理。


1."静态代理"代理类在编译期就生成


2."动态代理"代理类则是在Java运行时动态生成。动态代理又有JDK代理和CGLib代理两种。


2、结构

代理(Proxy)模式分为三种角色:


抽象主题(Subject)类: 通过接口或抽象类 声明真实主题和代理对象实现的业务方法。


真实主题(Real Subject)类: 实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。


代理(Proxy)类 : 提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。


3.png


1、什么是代理? 中介

代理是java中的一种设计模式:代理设计模式


2、为什么需要代理?

有没有代理都可以完成当前的功能,但是存在代理之后,更加专注于核心的业务功能,额外功能可以交给代理去做,实现功能的解耦合


生活案例:小红正在做饭,但是男朋友小李给他发消息,他如果边做饭边发消息,影响做饭的效率,他就可以口述给她的闺蜜让她闺蜜和男朋友聊天,自己认真做饭,闺蜜就是代理,"如花"就是一个接口

4.png


3、代理的好处?

  保证核心功能完成的前提下,同时兼顾额外功能


4、怎么开发代理对象?

1、代理对象一定依赖于核心业务对象

2、代理对象和核心业务对象一定要实现相同的接口


5.png


四、静态代理

1、什么是静态代理

静态代理:手工为某个类开发一个代理类,一个类对应一个代理类


通过代理类,为原始类(目标)增加额外的功能


好处:利于原始类(目标)的维护


2、名词解释

目标类 原始类 被代理的对象

指的是 业务类 (核心功能 --> 业务运算 DAO调用)


目标方法,原始方法

目标类(原始类)中的方法 就是目标方法(原始方法)


额外功能 (附加功能)

日志,事务,性能,数据清洗


3、代理开发的核心要素

代理类 = 目标类(原始类) + 额外功能 + 原始类(目标类)实现相同的接口

房东 ---> public interface UserService{
               m1
               m2
          }
          UserServiceImpl implements UserService{
               m1 ---> 业务运算 DAO调用
               m2 
          }
          UserServiceProxy implements UserService
               m1
               m2


4、编码(静态代理的开发)

开发代理的原则: 代理类和目标类功能一致且实现相同的接口,同时代理类中依赖于目标类对象

(1)UserService接口

public interface UserService {
    void save(String name);
    void delete(String id);
    void update();
    String findAll(String name);
    String findOne(String id);
}

(2)开发静态代理类

/**
 * @author 王恒杰
 * @version 1.0
 * @date 2021/11/11 11:14
 * @email 1078993387@qq.com
 * @Address 天津
 * @Description:开发原则:代理类和目标类实现相同接口,依赖于真正的目标类
 */
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) {
        String all=null;
        try {
            System.out.println("开启事务");
           all = userService.findAll(name);
            System.out.println("提交事务");
        }catch (Exception e){
            System.out.println("回滚事务");
            e.printStackTrace();
        }
        return all;
    }
    @Override
    public String findOne(String id) {
        String one=null;
        try {
            System.out.println("开启事务");
            one= userService.findOne(id);
            System.out.println("提交事务");
        }catch (Exception e){
            System.out.println("回滚事务");
            e.printStackTrace();
        }
        return one;
    }
}

(3)修改目标实现类

public class UserServiceImpl implements UserService {
    @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;
    }
}

(4)配置静态代理类

    <!--配置目标类-->
    <bean id="userService" class="StaticProxy.UserServiceImpl"></bean>
    <!--配置代理类-->
    <bean id="userServiceStaticProxy" class="StaticProxy.UserServiceStaticProxy">
        <!--setter入-->
        <property name="userService" ref="userService"></property>
     </bean>

(5)调用代理方法

 

    @Test
    public void StaticProxyText() {
        ClassPathXmlApplicationContext context =
                 new ClassPathXmlApplicationContext("ApplicationContext.xml");
         UserService userService = 
                     (UserService) context.getBean("userServiceStaticProxy");
        userService.delete("1");
    }
}


a.开发静态代理类


5、静态代理存在的问题

存在问题:造成程序中存在过多的代理类,不利于后期额外功能的维护


静态类文件数量过多,不利于项目管理

   UserServiceImpl——》 UserServiceProxy


   OrderServiceImpl——》 OrderServiceProxy


额外功能维护性差

   代理类中 额外功能修改复杂(麻烦)


五、动态代理

1、什么动态态代理

动态代理:在程序运行的过程中,JVM动态的为某个类生成代理对象


概念:通过代理类为原始类(目标类)增加额外功能


好处:利于原始类(目标类)的维护


常用的动态代理技术


1.JDK 代理 : 基于接口的动态代理技术


2.cglib 代理:基于父类的动态代理技术


6.png


DK动态代理是利用反射机制生成一个实现代理接口的匿名类 ,在调用具体方法前调用InvokeHandler (调用处理程序的意思)来处理。


而cglib动态代理是利用asm开源包 ,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。


使用场景


   1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP

   2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP

   3、如果目标对象没有实现了接口,必须采用CGLIB库 ,spring会自动在JDK动态代理和CGLIB之间转换


DK动态代理和CGLIB字节码生成的区别?

   (1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类

   (2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法


   因为是继承,所以该类或方法最好不要声明成final


2、名词解释

目标对象:被代理的对象,称之为目标对象


目标方法: 核心业务对象中实现的核心方法


额外功能 (附加功能)

日志处理,事务,性能


3、搭建开发环境

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>5.1.14.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.8.8</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.3</version>
</dependency>


4、JDK动态代理的开发

(1)目标类接口

public interface EmpService {
    public  void method();
}

(2)目标类

public class EmpServiceImpl implements EmpService{
    @Override
    public void method() {
        System.out.println("Service方法被执行了");
    }
}


(3)JDK动态代理代码并调用代理对象的方法测试

    @Test
    public void JDKDynamicProxyTest() {
        //生成动态代理对象   1、目标对象和代理对象实现相同的接口  2、代理对象一定依赖目标对象
        final EmpService empService = new EmpServiceImpl();
        //获取动态代理前的对象
        System.out.println(empService.getClass());
        //   创建代理对象
        InvocationHandler invocationHandler;
        //动态代理 o就是静态代理对象
        /**
         * 参数1: 类加载器(class.getClassLoad()) 动态代理JVM依据字节码技术完成 加载.class文件
         * Thread.currentThread():当前的线程
         * Thread.currentThread().getContextClassLoader()等价于DynamicProxyTest.class.getClassLoader(),
         * 参数2:目标对象实现的接口的类对象 数组 目标对象实现的所有的接口类型的数组
         * 参数3:额外功能 目标对象中的目标方法需要执行的额外方法
         */
        EmpService empService1 = (EmpService) Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[]{EmpService.class},
                new InvocationHandler() {
                    /**
                     * 参数1 proxy: 当前的动态代理对象
                     * 参数2 method: 当前代理对象执行的方法的对象
                     * 参数3 args: 当前代理对象执行的方法的参数
                     *
                     * @param proxy
                     * @param method
                     * @param args
                     * @return
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("这是EmpServiceImpl目标对象的动态代理");
                        //调用目标方法  参数1:目标对象  参数2:目标方法执行时传递的参数
                        Object invoke = method.invoke(empService, args);
                        return null;
                    }
                }
        );
        empService1.method();
        //动态代理后的对象
        System.out.println(empService1.getClass());
    }

7.png


5、cglib 动态代理

需要导入两个jar(maven依赖)包,asm.jar,cglib.jar。版本自行选择·


所需相关依赖

 

   <!--asmasm开源包-->
    <dependency>
      <groupId>asm</groupId>
      <artifactId>asm</artifactId>
      <version>3.3.1</version>
    </dependency>
    <!--cglib包-->
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.3.0</version>
    </dependency>


(1)目标类

  public void method() {
            System.out.println("Target 运行了....");
        }


(2)动态代理代码及调用代理对象的方法测试

8.png

  /**
     * CjLib动态代理
     * 如果目标对象没有实现了接口,
     * 必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
     */
    @Test
    public void CjLibTest() {
        //创建目标对象
        final Target target = new Target();
        //创建增强器
        Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(Target.class);
        //设置回调
        enhancer.setCallback(new MethodInterceptor() {
            /**
             * 参数1 proxy: 当前的动态代理对象
             * 参数2 method: 当前代理对象执行的方法的对象
             * 参数3 args: 当前代理对象执行的方法的参数
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("这是Target目标对象的动态代理");
                //调用目标方法  参数1:目标对象  参数2:目标方法执行时传递的参数
                Object invoke = method.invoke(target, objects);
                return invoke;
            }
        });
        //创建代理对象
        Target t = (Target) enhancer.create();
        t.method();
    }


六、三种代理的对比

1、jdk代理和CGLIB代理的区别

   使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在JDK1.6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的类或者方法进行代理,因为CGLib原理是动态生成被代理类的子类 。


   在JDK1.6、JDK1.7、JDK1.8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLib代理效率 ,只有当进行大量调用的时候,JDK1.6和JDK1.7比CGLib代理效率低一点,但是到JDK1.8的时候,JDK代理效率高于CGLib代理 。所以如果有接口使用JDK动态代理,如果没有接口使用CGLIB代理。


2、动态代理和静态代理的区别?

   动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。


   如果接口增加一个方法,静态代理模式除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。而动态代理不会出现该问题


七、代理模式的优缺点

1、代理模式的优点:

代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;


代理对象可以扩展目标对象的功能;


代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;


2、代理模式的缺点:

增加了系统的复杂度;

八、代理模式使用场景

1、远程(Remote)代理

   本地服务通过网络请求远程服务。为了实现本地到远程的通信,我们需要实现网络通信,处理其中可能的异常。为良好的代码设计和可维护性,我们将网络通信部分隐藏起来,只暴露给本地服务一个接口,通过该接口即可访问远程服务提供的功能,而不必过多关心通信部分的细节。


2、防火墙(Firewall)代理

   当你将浏览器配置成使用代理功能时,防火墙就将你的浏览器的请求转给互联网;当互联网返回响应时,代理服务器再把它转给你的浏览器。


3、保护(Protect or Access)代理

   控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。


相关文章
|
8天前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
29天前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
35 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
14天前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
27 1
|
22天前
|
设计模式 Java 程序员
[Java]23种设计模式
本文介绍了设计模式的概念及其七大原则,强调了设计模式在提高代码重用性、可读性、可扩展性和可靠性方面的作用。文章还简要概述了23种设计模式,并提供了进一步学习的资源链接。
36 0
[Java]23种设计模式
|
6天前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
|
10天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
22 0
|
1月前
|
Java 编译器 Spring
Spring AOP 和 AspectJ 的区别
Spring AOP和AspectJ AOP都是面向切面编程(AOP)的实现,但它们在实现方式、灵活性、依赖性、性能和使用场景等方面存在显著区别。‌
66 2
|
1月前
|
设计模式 监控 算法
Java设计模式梳理:行为型模式(策略,观察者等)
本文详细介绍了Java设计模式中的行为型模式,包括策略模式、观察者模式、责任链模式、模板方法模式和状态模式。通过具体示例代码,深入浅出地讲解了每种模式的应用场景与实现方式。例如,策略模式通过定义一系列算法让客户端在运行时选择所需算法;观察者模式则让多个观察者对象同时监听某一个主题对象,实现松耦合的消息传递机制。此外,还探讨了这些模式与实际开发中的联系,帮助读者更好地理解和应用设计模式,提升代码质量。
Java设计模式梳理:行为型模式(策略,观察者等)
|
1月前
|
设计模式 Java
Java设计模式
Java设计模式
28 0
|
1月前
|
XML Java 数据格式
Spring的IOC和AOP
Spring的IOC和AOP
45 0

热门文章

最新文章

  • 1
    C++一分钟之-设计模式:工厂模式与抽象工厂
    42
  • 2
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    46
  • 3
    C++一分钟之-C++中的设计模式:单例模式
    54
  • 4
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    38
  • 5
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    62
  • 6
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    57
  • 7
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    41
  • 8
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    50
  • 9
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    106
  • 10
    Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
    78