zoukankan      html  css  js  c++  java
  • 代理模式

    在学习Spring Aop时,使用了动态代理,所以学习了代理模式,静态代理,动态代理,Cglib动态代理,整理blog记录自己的学习笔记

    静态代理

    1.定义

    为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。通过代理对象访问目标对象,防止直接访问目标对象造成系统复杂性提升.

    2.原理

    Subject : 接口,为RealSubject和ProxySubject提供一致性的接口

    RealSubject : 目标访问类,实际的主体,实现Subject接口的类

    ProxySubject : 代理类,处理Client的请求,只有当代理类无法回复请求时,才会调用目标访问类

    Client : 请求者

    3.适用场景

    Virtual Proxy : 对于一些占用系统资源较多或者加载时间较长的对象,可以给这些对象提供一个虚拟代理。在真实对象创建成功之前虚拟代理扮演真实对象的替身,而当真实对象创建之后,虚拟代理将用户的请求转发给真实对象。

    Remote Proxy : 远程代理使得客户端可以访问远程主机上的对象,远程代理可以把网络的细节隐藏起来,使得客户端不必考虑网络的存在,客户端完全可以认为调用的远程代理对象在本地,而不是在远程

    Cache Proxy : 某一个操作的结果提供临时的缓存存储空间,以便在后续使用中能够共享这些结果,从而可以避免某些方法的重复执行,优化系统性能。 

    Access Proxy : 可以在调用RealSubject时做访问限制,通过代理过滤不可以访问的对象 

    4.实现

    Printable.java

    public interface Printable {
        void setPrintName(String name);
    
        String getPrintName();
    
        void print(String str);
    }  

    Printer.java

    public class Printer implements Printable {
        private String name;
    
        public Printer() {
            heavyJob("正在生成Printer的实例");
        }
    
        public Printer(String name) {
            this.name = name;
            heavyJob("正在生成(Printer)" + name + "的实例");
        }
    
    
        @Override
        public void setPrintName(String name) {
            this.name = name;
        }
    
        @Override
        public String getPrintName() {
            return this.name;
        }
    
        @Override
        public void print(String str) {
            System.out.println("==" + name + "==");
            System.out.println(str);
        }
    
        public void heavyJob(String msg) {
            System.out.println(msg);
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.print(".");
            }
            System.out.println("over");
        }
    }  

    PrintProxy.java

    public class PrintProxy implements Printable {
        private String name;
        private Printer real;
    
        public PrintProxy(String name) {
            this.name = name;
        }
    
        @Override
        public synchronized void setPrintName(String name) {
            if (real != null) {
                real.setPrintName(name);
            }
            this.name = name;
        }
    
        @Override
        public String getPrintName() {
            return this.name;
        }
    
        @Override
        public void print(String str) {
            realize();
            real.print(str);
        }
    
        public synchronized void realize() {
            if (real == null) {
                real = new Printer(name);
            }
        }
    }  

    Main.java

    public class Main {
        public static void main(String[] args) {
            Printable p = new PrintProxy("kristin");
            System.out.println("name: " + p.getPrintName());
            p.setPrintName("kkk");
            System.out.println("new name: " + p.getPrintName());
            p.print("hello world");
        }
    }
    

      

    Printable相当于Subject

    Printer相当于RealSubject

    PrinterProxy相当于ProxySubject

    Main相当于Client

    5.优缺点

    优点:

    协调调用者与被调用者,降低耦合度

    作为客户端对象与目标对象的中介,可以有效地保护目标对象

    缺点:

    在客户端与目标对象之间增加了代理对象,处理请求的速度可能会变慢

    如果代理的实现复杂,可能会增加系统实现的复杂性

    如果想要为多个类进行代理,需要创建多个代理类,维护难度加大 

    JDK动态代理

    1.定义

    代理类在程序运行时被创建,并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的.

    2.原理

    在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

    3.实现

    UserDao.java

    public interface UserDao {
        void add();
    }

    UserDaoImpl.java

    public class UserDaoImpl implements UserDao {
        @Override
        public void add() {
            System.out.println("------------add------------");
        }
    }

    MyInvocationHandler.java

    public class MyInvocationHandler implements InvocationHandler {
        private Object target;
    
        public void setProxy(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("-------------before-------------");
            Object result = method.invoke(target, args);
            System.out.println("-------------after-------------");
            return result;
        }
    
    }

    MyProxy.java

    public class MyProxy {
        public static void main(String[] args) {
            UserDao user = new UserDaoImpl();
            MyInvocationHandler handler = new MyInvocationHandler();
            handler.setProxy(user);
    //        UserDao userDao = (UserDao) Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), handler);
            UserDao userDao = (UserDao) Proxy.newProxyInstance(UserDao.class.getClassLoader(), new Class[]{UserDao.class}, handler);
            userDao.add();
        }
    }

    Output

    -------------before-------------
    ------------add------------
    -------------after-------------
    

      

    4.应用 

    如果想对代理类的所有方法都加上日志,可以通过动态代理可以对代理类的所有方法进行统一的处理,而不用一一更改每一个方法. 

    Cglib动态代理

    1.定义

     cglib是针对类来实现代理的,它的原理是对指定的目标类生成一个类,并覆盖其中方法实现增强

    2.原理

    代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理.

    3.应用

    UserDao.java

    public interface UserDao {
        void add();
    }  

    UserDaoImpl.java

    public class UserDaoImpl implements UserDao {
        public void add() {
            System.out.println("the method is running");
        }
    }
    

    Interceptor.java

    public class Interceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("-------------------before------------------");
            proxy.invokeSuper(obj, args);
            System.out.println("--------------------after-------------------");
            return null;
        }
    }
    

    Main.java

    public class Main {
        @org.junit.jupiter.api.Test
        public void test() {
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\code");
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(UserDaoImpl.class);
            enhancer.setCallback(new Interceptor());
            UserDao userDao = (UserDao) enhancer.create();
            userDao.add();
        }
    }
    

    Output

    CGLIB debugging enabled, writing to 'E:code'
    -------------------before------------------
    the method is running
    --------------------after-------------------
    

      

      

      

      

  • 相关阅读:
    刚开发的游戏《天黑请闭眼》
    用手机控制服务器
    专业网站打包/解包asp工具(E文精装版本)!
    令我爱慕的女子(转自7di.net)
    8088 汇编速查手册
    Asp调用函数是否会影响性能?
    文档管理器
    ubuntu install xxx.deb
    Java线程池的原理及几类线程池的介绍
    ubuntu download file path
  • 原文地址:https://www.cnblogs.com/Hangtutu/p/9024969.html
Copyright © 2011-2022 走看看