• Spring中的静态代理和动态代理


    一、什么是代理?

    代理是一种设计模式,提供了对目标对象另外的访问方式,即通过代理对象访问目标对象。可以不修改目标对象,对目标对象功能进行拓展。在我们学习Spring的时候就会发现,AOP(面向切面编程)的底层就是代理。

    代理的实现可以分为静态代理和动态代理。动态代理又根据实现的方式分为:基于JDK接口的动态实现和基于Cglib类的动态实现

    举例:假如说你要租房子,那么有两种方式,第一种方式:可以直接找房主,和房主进行协商;第二种方式:直接找中介,那么这里的中介就相当于是房主的一个代理。

    在这里插入图片描述

    二、为什么需要代理?

    代理的目的是为了使被代理类的功能更加纯粹,不用考虑其他的问题,其他的一些操作都可以在代理类中完成。

    在javaweb中就用一个典型的例子,我们可以将service层看做是dao层的代理,dao层需要专注于纯粹的数据库交互。

    代理的好处

    • 可以使真实角色的操作更加纯粹,不用关注一些公共的业务。
    • 公共的业务直接交给代理角色,实现了业务的分工。
    • 公共业务发生扩展时,可以集中操作。

    三、静态代理

    所谓的静态代理,就是对于一个实体类。我们需要为每一个实体类创建一个代理类。

    静态代理对于有一定java基础的人而言,还是很简单的。

    在这里插入图片描述

    应用案例

    接口

    public interface PersonDao {
        // 增加方法
        void addPerson();
    }
    
    • 1
    • 2
    • 3
    • 4

    目标类【接口的实现类】

    public class PersonDaoImpl implements PersonDao {
        @Override
        public void addPerson() {
            System.out.println("增加成功");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    代理工厂【代理类】 实现和目标类相同的接口

    声明接口的实现类

    public class PersonDaoProxy implements PersonDao {
        private PersonDao personDao;
    
        public PersonDaoProxy() {
        }
    
        public PersonDaoProxy(PersonDao personDao) {
            this.personDao=personDao;
        }
    
        @Override
        public void addPerson() {
            System.out.println("代理类调用PersonDao接口的实现类");
            personDao.addPerson();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    测试:过调用代理类中的方法 来实现最目标类方法的增强

    public class PersonTest {
        private PersonDao personDao = new PersonDaoImpl();
        
        @Test
        public void test(){
            // 通过调用代理类中的方法 来实现最目标类方法的增强
            PersonDaoProxy personDaoProxy = new PersonDaoProxy(personDao);
            personDaoProxy.addPerson();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    四、动态代理

    什么是动态代理?

    使用jdk的反射机制,创建对象的能力,创建的是代理类的对象。而不用你创建类文件。不用写java文件。

    **动态:**在程序执行时,调用JDK提供的方法才能创建代理类的对象。

    jdk动态代理,必须有接口,目标类必须实现接口,没有接口时,需要使用cylib动态代理。

    知道动态代理能做什么?

    可以在不改变原来目标方法功能的前提下,可以在代理中增强自己的功能代码。

    程序开发中的意思。

    实例:

    比如:你所在的项目中,有一个功能是其他人(公司的其他部门,其他小组的人)写好的,你可以使用。

    有一个功能,需要使用 学生类的实例化对象的一个print()方法。你发现这个功能,现在还缺点,不能完全满足我项目的需要,我需要在gn.print()执行前,需要增加判断权限,得自己在写判断逻辑的代码。

    用动态代理实现print()调用时,可以在代理工厂类中的invoke()方法里调用print()之前编写判断权限代码,而不用去改变原来学生类.class文件。

    理解:

    代理模式是设计模式的其中一种**,也是Java相关框架中的重要应用。我也是初学者, 个人见解, 不喜勿喷, 简单的说就是需要进行功能增强的代理类原本真实对象的被代理类会实现同样的接口,代理类的字节码文件是在jvm运行的时候动态生成该类(下面进行的介绍),但是代理类会多去继承一个Java中的Proxy 类,代理类负责为代理类(也就是生成真是对象的java类)预处理信息、增强信息、过滤信息最终把已经增强的转发给代理类。然而,回想之后,代理类又是谁生成的呢? 因此,还需要一个类去动态的生成代理类,这个类在编写的时候还需要用到一个Java中的invocationhandler类,这个类是用于增强被代理类中的方法,也就是谁继承了invocationhandler,谁就要去实现该接口对需要增强的类的方法(该接口中的invoke方法), 并且通过调用代理类生成器的生成代理类方法**,就会去调用该实现类的invoke方法, 这是个人自己的理解,所说的动态生成器类就是在这个生成动态代理类的Java类中,不能有别的自己编写的Java类的引用(可以在该类中看是否有import导入自己编写的类),万事俱备, 只欠东风, 那就是搞一个测试, 去看下按照自己的理解是否可行。

    使用Proxy.newProxyInstance() 要求被代理类最少实现一个接口

    return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
    
    • 1

    三个参数:

    • loader :

    一个类的对象,用于加载代理对象的字节码,(写死)写的是被代理对象的类加载器

    通过targetObject.getClass().getClassLoader()来对要生成的代理对象进行类的加载,通常用的就是目标类的类加载器

    • interfaces:

    一个Interface对象的数组,就是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了, 通俗的讲就是被代理类所实现的接口,而代理类也需要这个接口,否则它怎么知道你要用到哪个方法呢

    • h:

    一个InvocationHandler对象,就是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上,也就是需要怎么实现对被代理对象的加强。

    InvocationHandler接口:

    作用:内部写增强代码

    内部的invoke()方法:

    invoke()方法(调用):目标类中的接口方法都会经过该方法

    此方法内部写对方法的增强部分
    注意使用匿名内部类需要调用外部成员变量,外部成员变量要设置为final

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    }
    
    • 1
    • 2
    • 3
    • proxy:  所代理的那个真实对象
    • method: 所要调用真实对象的某个方法的Method对象,通过method.invoke(目标类, 参数)来调用目标类中的方法。
    • args:  调用真实对象某个方法时接受的参数,为method.invoke()方法提供参数,参数就是目标类中被调用方法上需要的参数。

    应用案例

    接口

    public interface PersonDao {
        // 添加
        void addPerson();
    }
    
    • 1
    • 2
    • 3
    • 4

    目标类【实现了接口的类】

    public class PersonDaoImpl implements PersonDao {
        @Override
        public void addPerson() {
            System.out.println("添加成功!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    动态代理类工厂【根据目标类 直接生成 代理类对象的类】

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    // 代理类【通过目标类来生成代理类对象】
    public class ProxyFactory implements InvocationHandler {
        // 声明目标类对象
        private Object object;
        // 创建 一个 可以根据目标类生成代理类的对象 的方法
        public Object getProxyInstance(Object targetObject){
            this.object = targetObject;
            // java.lang.reflect.Proxy.newProxyInstance()函数的作用是:返回某个对象的代理对象
            return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(proxy.getClass().toString()+"---------------");
            System.out.println("前置通知----增强代码段-----");
            Object result = method.invoke(object, args); // 被调用的目标方法
            System.out.println("后置通知----增强代码段-----");
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    测试:

     @Test
        public void test(){
            // 创建目标类
            PersonDaoImpl personDaoImpl = new PersonDaoImpl();
            // 创建动态代理类工厂
            ProxyFactory proxyFactory = new ProxyFactory();
            // 调用代理工厂中的方法 根据目标类生成 代理类对象
            PersonDao proxyInstance = (PersonDao) proxyFactory.getProxyInstance(personDaoImpl);
            // 用代理类对象直接调用目标类的方法
            proxyInstance.addPerson();
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    效果:
    在这里插入图片描述

  • 相关阅读:
    一篇详解,Postman设置token依赖步骤
    Eclipse跌落神坛,逐渐被Visual Studio Code等新兴IDE占据市场
    软考高级信息系统项目管理师系列之:信息系统项目管理师历年论文真题汇总
    鲜花商城系统设计与实现(Java+Web+MySQL)
    秋招入职阿里腾讯大厂,阿里淘系内传322页“Java并发编程核心讲义”学习笔记,轻松拿下大厂offer
    农村人口房屋管理系统(VB+access)
    Guava类库——BiMap和Multimap详解
    技术分享:国民远控向日葵如何通过BBR算法提升远控体验?
    还没用熟 TypeScript 社区已经开始抛弃了
    鹰潭高通量测序建设细节概述
  • 原文地址:https://blog.csdn.net/PIKapikaaaa/article/details/125627189