Spring 动态代理和静态代理模式以及扩展知识

代理模式

1.1、简介

定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。不改变原有业务的基础上,实现功能增强。

1.2、组成

  • 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
  • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
  • 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

1.3、静态代理

在这里插入图片描述
角色分析:

  • 抽象角色:一般会使用接口或抽象类来解决。
  • 真实角色:被代理的角色。
  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作。
  • 客户:访问代理对象的人。

1.3.1、案例一:房东租房

1、接口

    public interface UserService {
        void add();
        void delete();
        void update();
        void query();
    }

2、真实角色

    public class UserServiceImpl implements UserService{
        public void add() {
            System.out.println("add方法");
        }
    
        public void delete() {
            System.out.println("delete方法");
        }
    
        public void update() {
            System.out.println("update方法");
        }
    
        public void query() {
            System.out.println("query方法");
        }
    }
    

3、代理角色

    public class UserServiceProxy implements UserService {
        private UserServiceImpl userService;
    
        public void setUserService(UserServiceImpl userService) {
            this.userService = userService;
        }
    
        public void add() {
            log("add");
            userService.add();
        }
    
        public void delete() {
            log("delete");
            userService.delete();
        }
    
        public void update() {
            log("update");
            userService.update();
        }
    
        public void query() {
            log("query");
            userService.query();
        }
        public void log(String msg){
            System.out.println("【Debug】调用了"+msg+"方法");
        }
    }

4、客户

    public class Client {
        public static void main(String[] args) {
            UserServiceImpl userService = new UserServiceImpl();
            UserServiceProxy userServiceProxy = new UserServiceProxy();
            userServiceProxy.setUserService(userService);
            userServiceProxy.add();
        }
    }

1.3.2、案例二:学生写论文

1、接口

    public interface ThsisWriter {
        void write();
    }

2、学生类

    public class Student implements ThsisWriter {
        public void write() {
            System.out.println("========学生写论文");
        }
    }

3、代理类

    public class ThsisWriterProxy implements ThsisWriter {
        private ThsisWriter thsisWriter;
        public ThsisWriterProxy(ThsisWriter thsisWriter){
            this.thsisWriter = thsisWriter;
        }
        public void write() {
            //前置增强
            System.out.println("需求分析");
            thsisWriter.write();
            //后置增强
            System.out.println("提供源码");
        }
    }

4、测试类

    public class TestThsis {
        @Test
        public void test(){
            Student student = new Student();
            ThsisWriterProxy tw = new ThsisWriterProxy(student);
            tw.write();
        }
    }

静态代理模式的好处:

  • 可以使真实角色的操作更加纯粹!不用去关注一些公众的业务
  • 公共业务也就交给了代理角色,实现了业务的分工
  • 公共业务发生扩展的时候,方便集中管理!

缺点:

  • 一个真实角色就会产生一个代理角色;代码量会翻倍,开发效率会变低。

1.4、动态代理

在这里插入图片描述

  • 动态代理和静态代理角色一样

  • 动态代理的代理类是动态生成的,不是直接写好的!

  • 动态代理分为两大类:基于接口的动态代理;基于类的动态代理

  • 基于接口----JDK动态代理【我们在这里使用】

  • 基于类-------cglib

  • ava字节码实现:javasist

需要了解两个类

1)Proxy:提供了创建动态代理类和实例的静态方法。
2)InvocationHandler:是由代理实例的调用处理程序实现的接口

动态代理的好处:

  • 抽象角色:一般会使用接口或抽象类来解决

  • 真实角色:被代理的角色

  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作

  • 客户:访问代理对象的人

  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务

  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可

1.4.1、JDK代理

案例一

1、接口

    public interface Rent {
         void rent();
    }

2、真实角色

    public class Host implements Rent {
        public void rent() {
            System.out.println("房东要租房子");
        }
    }

3、自动生成代理类

    //用这个类自动生成代理类,继承该接口是为了回调
    public class ProxyInvocationHandler implements InvocationHandler {
    
        //被代理的接口
        private Rent rent;
    
        public void setRent(Rent rent) {
            this.rent = rent;
        }
    
        //生成得到代理类
        public Object getProxy(){
            return 	Proxy.newProxyInstance(this.getClass().getClassLoader(),
                                               rent.getClass().getInterfaces(),this);
        }
    
        //处理代理实例,并返回结果
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //动态代理的本质就是使用反射实现
            Object result = method.invoke(rent, args);
            return result;
        }
    
    }

4、客户

  public class Client {
        public static void main(String[] args) {
            //真实角色
            Host host = new Host();
    
            //代理角色:现在没有
       ProxyInvocationHandler pih = new ProxyInvocationHandler();
    
            //通过调用程序处理角色来处理我们要调用的接口对象!
            pih.setRent(host);
    
            //这里的proxy就是动态生成的,我们并没有写代理类!
            Rent proxy = (Rent) pih.getProxy();
            proxy.rent();
    
        }
    }
案例二

1、学生类

    public class Student implements ThsisWriter {
        public void write() {
            System.out.println("========学生写论文");
        }
    }

2、动态代理类

    /**
     * 动态代理类
     * 根据接口在运行期动态创建代理对象
     */
    public class ProxyBean implements InvocationHandler {
        private Object service;
        //括号中是logger的名字
        private Logger logger = Logger.getLogger("");
        // 创建代理对象
        public Object newProxy(Object service){
            this.service = service;
            //代理类对象
            // ------1.把this传入目的 为了回调invoke()
            // ------2.类加载器和接口  为了依据接口创建对应的代理对象  创建代理的业务对象的方法method。
            return Proxy.newProxyInstance(service.getClass().getClassLoader(),service.getClass().getInterfaces(),this);
        }
        /**
         *
         * @param proxy  代理对象
         * @param method 被代理对象执行的方法
         * @param args   执行方法传入的参数
         * @return
         * @throws Throwable
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            logger.info("市场需求分析");
            //执行原有业务对象方法,method 是原有业务对象的方法
            Object result = method.invoke(service, args);
            logger.info("加钱提供源码");
            return result;
    
        }
    }

3、测试类

    public class TestProxyBean {
        @Test
        public void testProxyBean(){
            //原有的业务对象
            Student s = new Student();
            //动态代理对象
            ProxyBean proxyBean = new ProxyBean();
            //使用动态代理对象,动态生成代理对象,返回的是接口类型,不是原有业务类型
            ThsisWriter thsisWriter = (ThsisWriter)proxyBean.newProxy(s);
            //执行代理对象的方法
            thsisWriter.write();
        }
    }

1.4.2、cglib代理

cglib介绍:

CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,

它可以在运行期扩展Java类与实现Java接口。Hibernate用它来实现PO(Persistent Object 持久化对象)字节码的动态生成。

CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP为他们提供

方法的interception(拦截)。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。

除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM(ASM是一种通用Java字节码操作和分析框架。它可以用于修改现有的class文件或动态生成class文件)来生成java的字节码。

案例:

  • 使用cglib实现代理需要先导入pom依赖
     <dependency>
         <groupId>cglib</groupId>
         <artifactId>cglib</artifactId>
         <version>3.3.0</version>
     </dependency>

该类为业务类的子类,即两个类是继承关系。
该类是根据业务对象,动态创建代理对象。

  • 代理类需要实现MethodInterceptor类,为了回调

自定义代理类

    //该类是用来创建代理类的类,需要实现一个MethodInterceptor接口,用来回调。
    //该类和业务类的关系是父子关系,即该类继承了业务类。
    
    /**
     *  配置pom.xml依赖 cglib
     *  动态代理类
     *  1.动态创建代理对象------- 依据业务对象
     *  2.业务类和代理对象是父子继承关系
     *  3.实现 MethodInterceptor 为了回调
     */
    public class MyCglibProxy implements MethodInterceptor {
    
        //创建一个代理类
        public Object newMyCglibInterceptor(Object service){
          //引入依赖之后才会有此类
            Enhancer enhancer = new Enhancer();
    
            //设置业务类为代理类父类
            enhancer.setSuperclass(service.getClass());
    
            //设置回调为当前对象intercept方法
            enhancer.setCallback(this);
    
            //创建代理对象
            return enhancer.create();
        }
        /**
         *
         * @param o             代理类对象
         * @param method        业务类的方法,即被代理对象的方法
         * @param objects       参数
         * @param methodProxy   代理类对象的方法
         */
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
          /**
          * m(){
          *	 super.m();
          *  }
          */
            Object result = methodProxy.invokeSuper(o,objects);
         
            return result;
        }
    }

注意:jdk代理和cglib代理是有区别的.

  • cglib代理是将原有的业务类当作自己的父类,也就是说它和原有业务类是父子关系。可以从中直接调用原有业务类的方法。
  • jdk代理是和原有业务类继承相同的接口,从而将接口注入到代理类中,从而达到调用原有业务类方法的目的。

以上就是本人我总结的一些代理类知识点,希望对大家有帮助!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值