精华内容
下载资源
问答
  • 代理模式

    千次阅读 热门讨论 2015-12-27 11:27:12
    为其他对象提供一种代理以控制对这个对象的访问(可以详细控制访问某个对象的方法, 在调用这个方法[前/后]做[前/后]置处理, 从而实现将统一流程放到代理类中处理).我们书写执行一个功能的函数时, 经常需要在其中写入...

    代理模式

    标签 : Java与设计模式


    代理模式

    为其他对象提供一种代理以控制对这个对象的访问(可以详细控制访问某个对象的方法, 在调用这个方法[前/后]做[前/后]置处理, 从而实现将统一流程放到代理类中处理).

    • 我们书写执行一个功能的函数时, 经常需要在其中写入与功能不是直接相关但很有必要的代码(如日志记录,事务支持等);这些枝节性代码虽然是必要的,但它会带来以下麻烦:

      1. 枝节性代码游离在功能性代码之外,它不是函数的目的,这是对OO是一种破坏;
      2. 枝节性代码会造成功能性代码对其它类的依赖,加深类之间的耦合,会造成功能性代码移植困难,可重用性降低, 这是OO系统所竭力避免的;
      3. 从正常角度来说: 枝节性代码应该监视着功能性代码,然后采取行动,而不是功能性代码通知枝节性代码采取行动,这好比吟游诗人应该是主动记录骑士的功绩而不是骑士主动要求诗人记录自己的功绩

    • Java代理分类

      1. 静态代理: 手动定义代理类
      2. 动态代理: 动态生成代理类
        • JDK自带的动态代理
        • JavaAssist字节码操作库实现
        • CGLib
        • ASM(底层使用指令, 可维护性差)

    • 代理中的角色

      1. 抽象接口:声明真实对象和代理对象的共同接口
      2. 代理对象:代理对象内部包含真实对象的引用,从而可以操作真实对象; 同时,代理对象与真实对象有相同的接口,能在任何时候代替真实对象,而且代理可以在真实对 象前后加入特定的逻辑以实现功能的扩展;
      3. 真实对象:代理对象所代表的对象;是我们最终要引用的对象

    静态代理

    我们模拟请明星唱歌这个过程,但大家都知道要请明星唱歌(比如周杰伦)是一件比较麻烦的事情, 比如唱歌前要签约, 唱歌之后还有收款, 而平时明星们都是比较忙的, 想签约, 收款这些事情一般都是由他的助手来代理完成的,而明星只负责唱歌就行了, 像签约收款这种事情就可以算作是明星的增强, 虽然这不是明星的主要目的, 但是这个流程是必须要有的.

    • 目标接口
    /**
     * 定义真实对象和代理对象的公共接口
     * Created by jifang on 15/12/20.
     */
    public interface Star {
        // 签约
        void signContract();
    
        // 唱歌
        void singSong();
    
        // 收款
        void collectMoney();
    }
    • 真实对象
    public class RealStar implements Star {
        /**
         * 由于这些事情都委托给代理来做了, 因此我们只是象征性实现就好了
         */
        @Override
        public void signContract() {
        }
    
        @Override
        public void collectMoney() {
        }
    
        /**
         * 但唱歌是要自己真唱的
         */
        @Override
        public void singSong() {
            System.out.println("周杰伦在唱歌");
        }
    }
    • 代理对象
      自己并未实现业务逻辑接口,而是调用真实角色来实现:
    public class StaticProxy implements Star {
    
        private Star star;
    
        public StaticProxy(Star star) {
            this.star = star;
        }
    
        @Override
        public void signContract() {
            System.out.println("代理签约");
        }
    
        /**
         * 代理可以帮明星做任何事, 但唯独唱歌这件事必须由Star自己来完成
         */
        @Override
        public void singSong() {
            star.singSong();
        }
    
        @Override
        public void collectMoney() {
            System.out.println("代理收钱");
        }
    }
    • Client
    public class Client {
    
        @Test
        public void client() {
            Star star = new StaticProxy(new RealStar());
            star.signContract();
            star.singSong();
            star.collectMoney();
        }
    }

    可以看出,客户实际想要调用的是RealStarsingSong方法,现在用StaticProxy来代理RealStar类,也可以达到同样的目的,同时还封装了其他方法(像singContract``collectMoney),可以处理一些其他流程上的问题.
    如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性;但是实际的Java应用中, 如果有一批真实对象, 而毎个代理对象只对应一个真实对象的话,会导致类的急剧膨胀;此外,如果我们事先并不知道真实角色,那么该如何使用编写代理类呢?这个问题可以通过java的动态代理机制来解决.


    动态代理

    所谓动态代理是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface.

    JDK对动态代理提供了以下支持:

    • java.lang.reflect.Proxy 动态生成代理类和对象
    • java.lang.reflect.InvocationHandler
      • 可以通过invoke方法实现对真实角色的代理访问;
      • 每次通过Proxy生成代理类对象时都要指定对象的处理器对象.

    首先, Star接口可以精简一下, 只做他该做的事情:

    • Star
    /**
     * Star只负责唱歌就行了
     * Created by jifang on 15/12/20.
     */
    public interface Star {
        // 唱歌
        void singSong();
    }
    • RealStar
    public class RealStar implements Star {
        /**
         * 唱歌是要自己真唱的
         */
        @Override
        public void singSong() {
            System.out.println("周杰伦在唱歌");
        }
    }

    当执行动态代理对象里的方法时, 实际上会替换成调用InvocationHandler中的invoke方法.

    • InvocationHandler: 用于实现代理
    /**
     * 相当于原先的代理需要执行的方法
     * Created by jifang on 15/12/20.
     */
    public class ProxyHandler implements InvocationHandler {
    
        private Star star;
    
        public ProxyHandler(Star star) {
            this.star = star;
        }
    
        /**
         * 代理对象的实现的所有接口中的方法, 内容都是调用invoke方法
         *
         * @param proxy  代理对象(Proxy.newProxyInstance返回的对象)
         * @param method 当前被调的方法
         * @param args   执行当前方法的参数
         * @return 执行方法method的返回值
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("签约");
            Object result = null;
            if (method.getName().equals("singSong")) {
                result = method.invoke(star, args);
            }
            System.out.println("收款");
            return result;
        }
    }
    • Client
    public class Client {
    
        @Test
        public void client() {
            /**
             * newProxyInstance方法会动态生成一个代理类, 他实现了Star接口, 然后创建该类的对象.
             *
             * 三个参数
             * 1. ClassLoader: 生成一个类, 这个类也需要加载到方法区中, 因此需要指定ClassLoader来加载该类
             * 2. Class[] interfaces: 要实现的接口
             * 3. InvocationHandler: 调用处理器
             */
            Star proxyStar = (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Star.class}, new ProxyHandler(new RealStar()));
            proxyStar.singSong();
        }
    }

    代理工厂实现动态代理

    • 动态代理虽然可以使得我们不用在手写代理对象的代码,但是InvocationHandler还是面向特定的抽象接口(如Star)的来写的; 而代理工厂可以让我们的代码写的更加抽象(而不必面向确定的抽象接口写代码).
    • 代理工厂的目标是目标对象和增强方法皆可改变, 这个模式在现实中的表现就是:
      a. 明星对代理并不一定是从一而终的, 明星随时都可能会换代理(助手);
      b. 明星不一定只会唱歌, 他还有可能会跳舞.
      c. 代理可能不只是为一个明星服务
      这样, 我们就实现一个代理工厂-可以随意更换代理所做的辅助性工作; 而目标对象也可以随时增加新的方法.

      可以看到, ProxyFactoryStart是没有任何关系的, 他们之间能够联系其他完全是靠Client来促成.


    • 代理工厂

    /**
     * Created by jifang on 15/12/21.
     */
    public class ProxyFactory {
    
        private BeforeAdvice beforeAdvice;
        private Object targetObject;
        private AfterAdvice afterAdvice;
    
        public ProxyFactory() {
        }
    
        public ProxyFactory(BeforeAdvice beforeAdvice, Object targetObject, AfterAdvice afterAdvice) {
            this.beforeAdvice = beforeAdvice;
            this.targetObject = targetObject;
            this.afterAdvice = afterAdvice;
        }
    
        private InvocationHandler handler = new InvocationHandler() {
    
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (beforeAdvice != null) {
                    beforeAdvice.before();
                }
    
                Object result = null;
                if (targetObject != null) {
                    result = method.invoke(targetObject, args);
                }
    
                if (afterAdvice != null) {
                    afterAdvice.after();
                }
    
                return result;
            }
        };
    
        public Object createProxy() {
            return Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), targetObject.getClass().getInterfaces(), handler);
        }
    }
    • Client
      StarRealStar同前
    /**
     * Created by jifang on 15/12/20.
     */
    public class Client {
    
        @Test
        public void client() {
            Star star = (Star) new ProxyFactory(new StarBeforeAdvice(), new RealStar(), new StarAfterAdvice()).createProxy();
            star.singSong();
        }
    
        /**
         * BeforeAdvice实现可定制化
         */
        private static class StarBeforeAdvice implements BeforeAdvice {
    
            @Override
            public void before() {
                System.out.println("签合约");
            }
        }
    
        /**
         * AfterAdvice实现可定制化
         */
        private static class StarAfterAdvice implements AfterAdvice {
    
            @Override
            public void after() {
                System.out.println("收款");
            }
        }
    }

    现在, 我们的对明星要求比较高了, 他不光要会唱歌, 还要会跳舞.

    public interface Star {
        // 唱歌
        void singSong();
    
        // 跳舞
        void dancing();
    }
    public class RealStar implements Star {
        //...
    
        @Override
        public void dancing() {
            System.out.println("周杰伦在跳舞...");
        }
    }

    此时, 我们的client什么都不需要改, 只是添加一个调用就可:

    public class Client {
    
        @Test
        public void client() {
            Star star = (Star) new ProxyFactory(new StarBeforeAdvice(), new RealStar(), new StarAfterAdvice()).createProxy();
            star.singSong();
            star.dancing();
        }
    
        // ...
    }

    而且在实际开发中, 这些增强类还可以从配置文件中读取(像Spring).
    这种代理在AOP(Aspect Orient Programming: 面向切面编程)中被成为AOP代理,AOP代理包含了目标对象的全部方法, 但AOP代理中的方法与目标对象的方法存在差异: 比如可以在执行目标方法之前/后插入一些通用的处理(增强).


    代理场景

    • 当Client需要调用某个对象时,客户端实际上也不关心是否准确得到该对象,Client要只是一个能提供该功能的对象而已,因此我们就可返回该对象的代理(Proxy).代理就是在访问对象时引入一定程度的间接性, 由于存在这种间接性, 我们就可以做很多工作:
      1. 远程代理: 为一个对象在不同的地址空间提供局部代表, 这样可以隐藏一个对象存在于不同地址空间的事实(Dubbo实现);
      2. 安全代理: 屏蔽对真实角色的访问, 用代理来控制对真实对象的访问权限;
      3. 延迟加载: 先加载轻量级代理对象,真正需要时再加载真实对象.

    参考:
    你应该知道的 RPC 原理
    大话设计模式
    高琪讲设计模式
    崔希凡讲动态代理
    java代理机制
    常用设计模式的应用场景
    展开全文
  • 服务器在线代理有哪些?

    千次阅读 2021-03-01 15:13:45
    如果大家使用过代理服务器,不难在QQ的设置上,看到想要启用代理服务器,可以有选择几种方式,如果没有看清楚,对应的几种代理,如HTTP、HTTPS、SOCKS代理是什么意思的话,可以跟着小编仔细阅读以下内容。...

    image.png

    如果大家使用过代理服务器,不难在QQ的设置上,看到想要启用代理服务器,可以有选择几种方式,如果没有看清楚,对应的几种代理,如HTTP、HTTPS、SOCKS代理是什么意思的话,可以跟着小编仔细阅读以下内容。

    代理分类详解:

    1、 HTTP代理:能够代理客户机的HTTP访问,我们在浏览网页,下载数据时就是用http代理。它的端口一般是80、8080、3128等。

    2、HTTPS代理:HTTP协议在传输的过程中是没有加密的,如果是传输隐私文件就有不安全的风险,为了保证安全,于是出现了HTTPS。其实,HTTPS协议就是SSL+HTTP协议建立的,可以对传输过程加密的网络协议,比HTTP协议传输文件更加安全。HTTP和HTTPS用的端口是不一样的,HTTP是80,HTTPS是443端口,当然连接方式也是截然不同。HTTPS和SSL支持使用X.509数字认证,如果需要的话用户可以确认发送者是谁。

    3、SOCKS代理:

    ①采用SOCKS协议的代理服务器就是SOCKS服务器,是一种通用的代理服务器。

    ②SOCKS代理又分为SOCKS4和SOCKS5,二者不同的是SOCKS4代理只支持TCP协议(即传输控制协议),而SOCKS5代理则既支持TCP协议又支持UDP协议(即用户数据包协议),还支持各种身份验证机制、服务器端域名解析等。

    以上就是关于代理的详细介绍了,大家感兴趣的话,不妨尝试使用下哦~

    展开全文
  • 动态代理和静态代理到底什么区别,好处在哪里?

    一、概念

     
      代理模式是常用的Java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。按照代理类的创建时期,代理类可分为两种。

    静态代理类:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
    动态代理类:在程序运行时,运用反射机制动态创建而成。
    静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
     静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
    动态代理是实现JDK里的InvocationHandler接口的invoke方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过Proxy里的newProxyInstance得到代理对象。
    还有一种动态代理CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行时,动态修改字节码达到修改类的目的。

    二、静态代理类

     
    如下, HelloServiceProxy类是代理类,HelloServiceImpl类是委托类,这两个类都实现了HelloService接口。其中HelloServiceImpl类是HelloService接口的真正实现者,而HelloServiceProxy类是通过调用HelloServiceImpl类的相关方法来提供特定服务的。HelloServiceProxy类的echo()方法和getTime()方法会分别调用被代理的HelloServiceImpl对象的echo()方法和getTime()方法,并且在方法调用前后都会执行一些简单的打印操作。
    由此可见,代理类可以为委托类预处理消息、把消息转发给委托类和事后处理消息等。

    例程1 HelloService.java
    package proxy;
    import java.util.Date;
    public interface HelloService{
      public String echo(String msg);
      public Date getTime();
    }

    例程2 HelloServiceImpl.java
    package proxy;
    import java.util.Date;
    public class HelloServiceImpl implements HelloService{
      public String echo(String msg){
        return "echo:"+msg;
      }
      public Date getTime(){
        return new Date();
      }
    }

    例程3 HelloServiceProxy.java
    package proxy;
    import java.util.Date;
    public class HelloServiceProxy implements HelloService{
      //表示被代理的HelloService 实例
      private HelloService helloService; 
      public HelloServiceProxy(HelloService helloService){
        this.helloService=helloService;
      }
      public void setHelloServiceProxy(HelloService helloService){
          this.helloService=helloService;
      }
      public String echo(String msg){
    	//预处理
        System.out.println("before calling echo()"); 
    	 //调用被代理的HelloService 实例的echo()方法
        String result=helloService.echo(msg);
    	//事后处理
        System.out.println("after calling echo()"); 
        return result;
      }
      public Date getTime(){
    	//预处理
        System.out.println("before calling getTime()"); 
    	 //调用被代理的HelloService 实例的getTime()方法
        Date date=helloService.getTime();
    	//事后处理
        System.out.println("after calling getTime()"); 
        return date;
        }
    }

    在Client1类的main()方法中,先创建了一个HelloServiceImpl对象,又创建了一个HelloServiceProxy对象,最后调用HelloServiceProxy对象的echo()方法。
    例程4 Client1.java
    package proxy;
    public class Client1{
      public static void main(String args[]){
        HelloService helloService=new HelloServiceImpl();
        HelloService helloServiceProxy=new HelloServiceProxy(helloService);
        System.out.println(helloServiceProxy.echo("hello"));
      }
    }

    运行Client1 类,打印结果如下:
    before calling echo()
    after calling echo()
    echo:hello
    例程3 的HelloServiceProxy类的源代码是由程序员编写的,在程序运行前,它的.class文件就已经存在了,这种代理类称为静态代理类。

    三、动态代理类

        与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler接口提供了生成动态代理类的能力。
    Proxy类提供了创建动态代理类及其实例的静态方法。
    (1) getProxyClass()静态方法负责创建动态代理类,它的完整定义如下:

    public static Class<?>getProxyClass(ClassLoader loader,Class<?>[] interfaces) throwsIllegalArgumentException


      参数loader 指定动态代理类的类加载器,参数interfaces指定动态代理类需要实现的所有接口。

    (2) newProxyInstance()静态方法负责创建动态代理类的实例,它的完整定义如下:

    public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler) throws
          IllegalArgumentException

        参数loader指定动态代理类的类加载器,参数interfaces 指定动态代理类需要实现的所有接口,参数handler 指定与动态代理类关联的InvocationHandler 对象。


    以下两种方式都创建了实现Foo接口的动态代理类的实例:

    //创建InvocationHandler对象
    InvocationHandler handler = new MyInvocationHandler(...);

    //创建动态代理类
    Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(),new Class[] { Foo.class });

    //创建动态代理类的实例
    Foo foo = (Foo) proxyClass.getConstructor(new Class[] {InvocationHandler.class }).
        newInstance(new Object[] {handler });



    //创建InvocationHandler对象
    InvocationHandler handler = new MyInvocationHandler(...);

    //直接创建动态代理类的实例
    Foo foo = (Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class[] {Foo.class }, handler);

    由Proxy类的静态方法创建的动态代理类具有以下特点:
      动态代理类是public、final和非抽象类型的;
     
    动态代理类继承了java.lang.reflect.Proxy类;
     
    动态代理类的名字以“$Proxy”开头;
     
    动态代理类实现getProxyClass()和newProxyInstance()方法中参数interfaces指定的所有接口;

    Proxy 类的isProxyClass(Class<?>cl)静态方法可用来判断参数指定的类是否为动态代理类。只有通过Proxy类创建的类才是动态代理类;

    动态代理类都具有一个public 类型的构造方法,该构造方法有一个InvocationHandler 类型的参数。

    由Proxy类的静态方法创建的动态代理类的实例具有以下特点:
    1. 假定变量foo 是一个动态代理类的实例,并且这个动态代理类实现了Foo 接口,那么“foo instanceofFoo”的值为true。把变量foo强制转换为Foo类型是合法的:
    (Foo) foo //合法

    2.每个动态代理类实例都和一个InvocationHandler 实例关联。Proxy类的getInvocationHandler(Objectproxy)静态方法返回与参数proxy指定的代理类实例所关联的InvocationHandler 对象。

    3.假定Foo接口有一个amethod()方法,那么当程序调用动态代理类实例foo的amethod()方法时,该方法会调用与它关联的InvocationHandler对象的invoke()方法。

    InvocationHandler 接口为方法调用接口,它声明了负责调用任意一个方法的invoke()方法:
    Object invoke(Object proxy,Method method,Object[] args) throwsThrowable

    参数proxy指定动态代理类实例,参数method指定被调用的方法,参数args指定向被调用方法传递的参数,invoke()方法的返回值表示被调用方法的返回值。
    动态代理例程
     
       
     
       
     
       
    java 使用动态代理 和ThreadLocal实现事务管理实例

    以下信息来源于另一篇博客

    Java设计模式-代理模式之静态代理

    概念

    为另一个对象提供一个替身或占位符以提供对这个对象的访问,使用代理模式创建代表对象,让代表对象控制某对象的访问,被代理对象可以是远程的对象、创建开销大的对象或需要安全控制的对象

    • 远程代理控制访问远程对象
    • 虚拟代理控制访问创建开销大的资源
    • 保护代理基于权限控制对资源的访问

      看如下的类图:

      proxy1.png

      仔细看上面的类图,首先是Subject它为RealSubject和Proxy提供了接口,通过实现同一个接口,Proxy在RealSubject出现的地方取代它,这点和 适配器模式有比较大的区别。

      RealSubject是真正做事情的对象,它被proxy代理控制访问的对象,Proxy持有RealSubject的引用,在某些例子中Proxy还会负责RealSubject对象的创建和销毁。客户和RealSubject的交互都必须通过Proxy。因为Proxy和RealSubject实现了相同的接口Subject,所以任何用到RealSubject的地方,都可以用Proxy代替。Proxy也控制了对RealSubject的访问,在某些情况下面,我们可能需要:权限保护,远程访问,创建开销控制。


      代理模式分类

      代理模式分为

    • 静态代理
      - 由程序员创建或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。上面的类图很好的表示了这种关系,

    • 动态代理
      - 在程序运行时运用反射机制动态创建而成。动态代理在代理ProxySubject和RealSubject之间增加了InvocationHandler这一层,这是一种通信间接化,增加了灵 性性

      一个关于静态代理的例子

      警匪片大家一定都不会陌生,一些有钱的人看那个不顺眼,就想着找黑帮的帮忙杀人,黑帮就帮他们做一些坏事。这里的老板就变成了RealSubject,黑帮就变成了(Proxy),这里的real和proxy只是针对杀人是谁指使的(即幕后黑手是那个)
      • 首先定义一个共同的接口,使得RealSubject出现的地方Proxy都可以出现

      package ProxyMode;
      
      /*
       * 抽象接口,对应类图中的Subject
       * 
       */
      
      public interface Subject {
      
          public void SujectShow();
      
      }

      • 然后定义一个RealSubject,真正的幕后黑手

      package ProxyMode;
      
      
      public class RealSubject implements Subject{
      
          @Override
          public void SujectShow() {
              // TODO Auto-generated method stub
              System.out.println("杀人是我指使的,我是幕后黑手!By---"+getClass());
      
          }
      
      }
      
      
          然后定义一个代理类,黑帮,拿钱办事,但不是幕后黑手
      
      
      package ProxyMode;
      
      import proxy.RealeSubject;
      
      public class ProxySubject implements Subject{
      
          private Subject realSubject;//代理类中有 老板的引用。
      
          public Subject TakeCall() //通过电话联系
          {
              return new RealSubject();
          }
      
          public void Before()
          {
              System.out.println("我只是一个代理类,在做事情之前我先声明,接下来的事情跟我无关,我只是受人指使!By---"+getClass());
          }
      
          public void After()
          {
              System.out.println("正如事情还没有发生之前讲的一样,我只是个路人,上面做的事情跟我无关,我是受人指使的! By---"+getClass());
          }
      
          @Override
          public void SujectShow() {
              // TODO Auto-generated method stub
      
              Object o=TakeCall();  //代理类接到了一个电话
      
              if(checked(o)) //检查这个电话是不是老板打过来的
              {
                  Before();
      
                  this.realSubject=(Subject)o;
                  realSubject.SujectShow();
      
                  After();
              }
              else {
                  System.out.println("不好意思,你权限不够,我帮不了你!");
              }
      
      
          }
          boolean checked(Object o)  //权限检查,这年头不是谁都可以冒充老板的
          {
              if(o instanceof RealSubject )
                  return true;
              return false;
          }
      
      }
      
      
      

      • 测试

      package ProxyMode;
      
      public class ProxyTest {
      
          public static void main(String[] args)
          {
              ProxySubject proxy=new ProxySubject();
      
              proxy.SujectShow();
          }
      
      }
      

      执行结果:

    我只是一个代理类,在做事情之前我先声明,接下来的事情跟我无关,我只是受人指使!By---class ProxyMode.ProxySubject
    杀人是我指使的,我是幕后黑手!By---class ProxyMode.RealSubject
    正如事情还没有发生之前讲的一样,我只是个路人,上面做的事情跟我无关,我是受人指使的! By---class ProxyMode.ProxySubject

    Java设计模式-代理模式之动态代理(附源码分析)

    动态代理概念及类图

    动态代理跟静态代理一个最大的区别就是:动态代理是在运行时刻动态的创建出代理类及其对象。上篇中的静态代理是在编译的时候就确定了代理类具体类型,如果有多个类需要代理,那么就得创建多个。还有一点,如果Subject中新增了一个方法,那么对应的实现接口的类中也要相应的实现这些方法。

    动态代理的做法:在运行时刻,可以动态创建出一个实现了多个接口的代理类。每个代理类的对象都会关联一个表示内部处理逻辑的InvocationHandler接 口的实现。当使用者调用了代理对象所代理的接口中的方法的时候,这个调用的信息会被传递给InvocationHandler的invoke方法。在 invoke方法的参数中可以获取到代理对象、方法对应的Method对象和调用的实际参数。invoke方法的返回值被返回给使用者。这种做法实际上相 当于对方法调用进行了拦截。

    类图如下所示:

    proxy2.png

    上面类图中使用的JDK中的Proxy类,所以是需要要办法来告诉Proxy类需要做什么,不能像静态代理一样,将代码放到Proxy类中,因为现在Proxy不是直接实现的。既然这样的代码不能放在Proxy类中,那么就需要一个InvocationHandler,InvocationHandler的工作就是响应代理的任何调用。


    动态代理实现过程

    具体有如下四步骤:
    • 通过实现 InvocationHandler 接口创建自己的调用处理器;
    • 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
    • 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
    • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

      一个具体的例子

      接着上面的类图和静态代理中的例子,我们分别创建Subject和RealSubject
    • Subject

    package ProxyMode;
    
    /*
     * 抽象接口,对应类图中的Subject
     * 
     */
    
    public interface Subject {
    
        public void SujectShow();
    
    }
    


    • RealSubject

    package ProxyMode;
    
    
    public class RealSubject implements Subject{
    
        @Override
        public void SujectShow() {
            // TODO Auto-generated method stub
            System.out.println("杀人是我指使的,我是幕后黑手!By---"+getClass());
    
        }
    
    }


    • 建立InvocationHandler用来响应代理的任何调用

    package ProxyMode;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class ProxyHandler implements InvocationHandler {
    
        private Object proxied;   
    
          public ProxyHandler( Object proxied )   
          {   
            this.proxied = proxied;   
          }   
    
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
    
            System.out.println("准备工作之前:");
    
            //转调具体目标对象的方法
              Object object=   method.invoke( proxied, args);
    
             System.out.println("工作已经做完了!");
             return object;
        }
    
    }


    • 动态代理类测试,这个代理类中再也不用实现Subject接口,可以动态的获得RealSubject接口中的方法
      package ProxyMode;
      
      
      import java.lang.reflect.Proxy;
      
      public class DynamicProxy  {
      
          public static void main( String args[] )   
            {   
              RealSubject real = new RealSubject();   
              Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(), 
               new Class[]{Subject.class}, 
               new ProxyHandler(real));
      
              proxySubject.SujectShow();;
      
            }   
      }

    测试结果
    
    准备工作之前:
    杀人是我指使的,我是幕后黑手!By---class ProxyMode.RealSubject
    工作已经做完了!
        Proxy和InvocationHandler重要部分源码分析
        java.lang.reflect.Proxy:这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
    清单 1. Proxy 的静态方法
    // 方法 1: 该方法用于获取指定代理对象所关联的调用处理器,比如上面代码中的ProxyHandler
    static InvocationHandler getInvocationHandler(Object proxy) 
    
    // 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
    static Class getProxyClass(ClassLoader loader, Class[] interfaces) 
    
    // 方法 3:该方法用于判断指定类对象是否是一个动态代理类
    static boolean isProxyClass(Class cl) 
    
    // 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
    static Object newProxyInstance(ClassLoader loader, Class[] interfaces, 
        InvocationHandler h)
    下面重点看看newProxyInstance方法:
       public static Object newProxyInstance(ClassLoader loader, 
                Class<?>[] interfaces, 
                InvocationHandler h) 
                throws IllegalArgumentException { 
    
        // 检查 h 不为 空,否则抛异常
        if (h == null) { 
            throw new NullPointerException(); 
        } 
    
        // 获得与制定类装载器和一组接口相关的代理类类型对象
        Class cl = getProxyClass(loader, interfaces); 
    
        // 通过反射获取构造函数对象并生成代理类实例
        try { 
            Constructor cons = cl.getConstructor(constructorParams); 
            return (Object) cons.newInstance(new Object[] { h }); 
        } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); 
        } catch (IllegalAccessException e) { throw new InternalError(e.toString()); 
        } catch (InstantiationException e) { throw new InternalError(e.toString()); 
        } catch (InvocationTargetException e) { throw new InternalError(e.toString()); 
        } 
    }
    看这个方法的三个参数
    public static Object newProxyInstance(ClassLoader loader,
    Class<?>[] interfaces,
    InvocationHandler h) throws IllegalArgumentException

    loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
    interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),
    h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler
    从上面JDK源码中可以看出getProxyClass方法才是newProxyInstance方法中最重要的,该方法负责为一组接口动态地生成代理类类型对象。下面开始解析proxy中的getProxyClass方法

    该方法总共可以分为四个步骤:
    对这组接口进行一定程度的安全检查,包括检查接口类对象是否对类装载器可见并且与类装载器所能识别的接口类对象是完全相同的,还会检查确保是 interface 类型而不是 class 类型。

    这个步骤通过一个循环来完成,检查通过后将会得到一个包含所有接口名称的字符串数组,记为 String[] interfaceNames
     for (int i = 0; i < interfaces.length; i++) {
    
                // 验证类加载程 序 解 析 该接口到同一类对象的名称。
                String interfaceName = interfaces[i].getName();
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(interfaceName, false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != interfaces[i]) {
                    throw new IllegalArgumentException(
                        interfaces[i] + " is not visible from class loader");
                }
    
                // 验证类对象真正代表一个接口
    
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
    
                //验证这个接口是不是重复的
                if (interfaceSet.contains(interfaceClass)) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
                interfaceSet.add(interfaceClass); //interfaceset是一个hashset集合
    
                interfaceNames[i] = interfaceName;
            }
    从 loaderToCache 映射表中获取以类装载器对象为关键字所对应的缓存表,如果不存在就创建一个新的缓存表并更新到 loaderToCache。

    缓存表是一个 HashMap 实例,正常情况下它将存放键值对(接口名字列表,动态生成的代理类的类对象引用)。当代理类正在被创建时它会临时保存(接口名字列表,pendingGenerationMarker)。标记 pendingGenerationMarke 的作用是通知后续的同类请求(接口数组相同且组内接口排列顺序也相同)代理类正在被创建,请保持等待直至创建完成。
     synchronized (cache) {
    do { 
        // 以接口名字列表作为关键字获得对应 cache 值
        Object value = cache.get(key); 
        if (value instanceof Reference) { 
            proxyClass = (Class) ((Reference) value).get(); 
        } 
        if (proxyClass != null) { 
            // 如果已经创建,直接返回,这里非常重要,如果已经创建过代理类,那么不再创建
            return proxyClass; 
        } else if (value == pendingGenerationMarker) { 
            // 代理类正在被创建,保持等待
            try { 
                cache.wait(); 
            } catch (InterruptedException e) { 
            } 
            // 等待被唤醒,继续循环并通过二次检查以确保创建完成,否则重新等待
            continue; 
        } else { 
            // 标记代理类正在被创建
            cache.put(key, pendingGenerationMarker); 
            // break 跳出循环已进入创建过程
            break; 
    } while (true);
    }
    动态创建代理类的类对象。
    首先是确定代理类所在的包,其原则如前所述,如果都为 public 接口,则包名为空字符串表示顶层包;如果所有非 public 接口都在同一个包,则包名与这些接口的包名相同;如果有多个非 public 接口且不同包,则抛异常终止代理类的生成。确定了包后,就开始生成代理类的类名,同样如前所述按格式“$ProxyN”生成。

    // 动态地生成代 理类的字节码数组
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); 
    try { 
        // 动态地定义新生成的代理类
        proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, 
            proxyClassFile.length); 
    } catch (ClassFormatError e) { 
        throw new IllegalArgumentException(e.toString()); 
    } 
    
    // 把生成的代理类的类对象记录进 proxyClasses 表
    proxyClasses.put(proxyClass, null);
    到了这里,其实generateProxyClass方法也是一个重点,但是generateProxyClass的方法代码跟踪不了,位于并未公开的 sun.misc 包,有若干常量、变量和方法以完成这个神奇的代码生成的过程,但是 sun 并没有提供源代码以供研读
    结尾部分
    根据结果更新缓存表,如果成功则将代理类的类对象引用更新进缓存表,否则清楚缓存表中对应关键值,最后唤醒所有可能的正在等待的线程。
    synchronized (cache) {
                    if (proxyClass != null) {
                        cache.put(key, new WeakReference<Class<?>>(proxyClass));
                    } else {
                        cache.remove(key);
                    }
                    cache.notifyAll();
                }
    java.lang.reflect.InvocationHandler:这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。
    InvocationHandler 的核心方法,我们最关心的是Invoke方法为什么会被调用,见下面分析:

    Object invoke(Object proxy, Method method, Object[] args)

    // 该方法负责集中处理动态代理类上的所 有方法调用。
    //第一个参数既是代理类实例,
    //第二个参数是被调用的方法对象
    // 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行

    每次生成动态代理类对象时都需要指定一个实现了该接口的调用处理器对象(参见 newProxyInstance 的第三个参数)。
    很多人肯定跟我一样,我们在Handler中调用的method.invoke方法中并没有显示的调用invoke方法,只是在newProxyInstance中应用了一个handler对象,有了上面关于newProxyInstance的源码分析,我们知道了 newproxyinstance生成了一个$Proxy0类代理。当调用Subjectshow()方法时,其实调用的$Proxy0的SubjectShow()方法,从而调用父类Proxy中传进来第三个参数(h)的的Invoke方法。

    //这个方法是 Proxy源码中的
      protected Proxy(InvocationHandler h) {
            this.h = h;
        }
    
    来看NewProxyInstance方法生成的$Proxy0代理类的源码
    public final class $Proxy0 extends Proxy implements Subject {
        private static Method m1;
        private static Method m0;
        private static Method m3;
        private static Method m2;
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals",
                        new Class[] { Class.forName("java.lang.Object") });
    
                m0 = Class.forName("java.lang.Object").getMethod("hashCode",
                        new Class[0]);
    
                m3 = Class.forName("***.RealSubject").getMethod("request",
                        new Class[0]);
    
                m2 = Class.forName("java.lang.Object").getMethod("toString",
                        new Class[0]);
    
            } catch (NoSuchMethodException nosuchmethodexception) {
                throw new NoSuchMethodError(nosuchmethodexception.getMessage());
            } catch (ClassNotFoundException classnotfoundexception) {
                throw new NoClassDefFoundError(classnotfoundexception.getMessage());
            }
        } //static
    
        public $Proxy0(InvocationHandler invocationhandler) {
            super(invocationhandler);
        }
    
        @Override
        public final boolean equals(Object obj) {
            try {
                return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
            } catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        @Override
        public final int hashCode() {
            try {
                return ((Integer) super.h.invoke(this, m0, null)).intValue();
            } catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        public final void SubjectShow() {
            try {
                super.h.invoke(this, m3, null); //就是这个地方  调用h.invoke()
                return;
            } catch (Error e) {
            } catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        @Override
        public final String toString() {
            try {
                return (String) super.h.invoke(this, m2, null);
            } catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    }
    从上面的$Proxy0中找到方法SubjectSHow()方法,我们可以看到中间调用了父类Proxy的参数Handler h的invoke方法,也就调用了ProxyHandler中的invoke()方法,还可以看到¥Proxy0还代理了equals()、hashcode()、tostring()这三个方法,至此动态代理实现机制就很清楚了













    http://blog.sina.com.cn/s/blog_72893c860100puzg.html


    展开全文
  • 静态代理代理模式实现方式之一,比较简单,主要分为三个角色:客户端,代理类,目标类;而代理类需要与目标类实现同一个接口,并在内部维护目标类的引用,进而执行目标类的接口方法,并实现在不改变目标类的情况下...

    在这里插入图片描述

    前言

    近期在研究Hook技术,需要用到动态代理,说到动态代理就会聊到它的兄弟静态代理,那它们到底是怎么一回事呢?实现方式有哪些呢?一起来看下

    代理模式

    代理在我们生活中随处可见,比如我们生活中的各种中介公司,以买一辆二手车为例:如果我买车,我可以自己去网上找车源,然后做质量检测,车辆过户等一系列的流程,但是这太费时间和精力了,我就是想花钱买个车而已,最后我居然吭哧吭哧的干了这么多活;于是我可以通过中介买车,我只用给钱,选车就行了,其它活都交给中介干,这就是代理的一种实现;还有网络代理(不管是正向还是反向代理),房屋租赁公司等都是代理的具体实现
    在Java编程里就有一种设计模式,即代理模式,提供了一种对目标对象的访问方式,即通过代理对象访问目标对象,代理对象是指具有与被代理对象相同的接口的类,客户端必须通过代理对象与被代理的目标类进行交互

    为啥使用代理模式:

    • 中间隔离:某些情况下,客户端不想或者不能直接引用一个目标对象,而代理类可以在客户端和目标类之前起到中介作用
    • 开闭原则,扩展功能:代理类除了是客户类和目标类的中介,还可以通过给代理类增加额外的功能来扩展目标类的功能,这样我们只需要修改代理类而不需要再修改目标类,
      符合代码设计的开闭原则(对扩展开放,对修改关闭)。代理类主要负责为目标类预处理消息、过滤消息、把消息转发给目标类,以及事后对返回结果的处理等。
      代理类本身并不真正实现服务,而是同过调用目标类的相关方法,来提供特定的服务。真正的业务功能还是由目标类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的目标类。

    代理实现方式:
    如果按照代理创建的时期来进行分类的话, 可以分为静态代理、动态代理

    • 静态代理是由程序员创建或特定工具自动生成代理类,再对其编译,在程序运行之前,代理类.class文件就已经被创建了
    • 动态代理是在程序运行时通过反射机制动态创建代理对象

    如图:
    在这里插入图片描述

    开源框架应用:
    Spring框架是时下很流行的Java开源框架,Spring之所有如此流行,跟它自身的特性是分不开的,一个是IOC,一个是AOP

    • IOC是Inverse Of Control,即控制反转,也有人把IOC称作依赖注入。我觉得依赖注入这种说法很好理解,但不完全对;依赖注入是Dependency Injection的缩写,是实现IOC的一种方法,但不等同于IOC,IOC是一种思想,DI只是一种实现

    • AOP是Aspect Oriented Programming的缩写,即面向切面编程;与面向过程和面向对象的编程方式相比,面向切面编程提供了一种全新的思路,解决了OOP编程过程中的一些痛点。

    • IOC的实现原理是利用了JAVA的反射技术,那么AOP的实现原理是什么呢?就是动态代理技术,目前动态代理技术主要分为Java自己提供的JDK动态代理技术和CGLIB技术

    AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

    Retrofit框架应该是时下Android端最流行的网络访问框架了,而动态代理技术在里面就运用的非常多,对接口进行解析,获取到接口里的注解信息,参数等,然后封装成Http请求,最后再由底层网络请求框架OKHttp执行

    静态代理

    静态代理是代理模式实现方式之一,比较简单,主要分为三个角色:客户端,代理类,目标类;而代理类需要与目标类实现同一个接口,并在内部维护目标类的引用,进而执行目标类的接口方法,并实现在不改变目标类的情况下前拦截,后拦截等所需的业务功能。

    假设现在项目经理给你提了一个需求:在项目所有类的有关用户操作的方法前后打印日志;这种情况下就可以通过静态代理实现

    做法就是:为每个相关类都编写一个代理类(业务重合的可以共用一个代理类),并且让它们实现相同的接口

    定义接口:

    public interface ILogin {
        void userLogin();
    }
    

    定义目标类:

    public class UserLogin implements ILogin {
    
        @Override
        public void userLogin() {
            System.out.print("用户登录");
        }
    }
    

    定义代理类:

    public class UserLoginProxy implements ILogin {
    
        private UserLogin mLogin;
        public UserLoginProxy() {
            mLogin = new UserLogin();
        }
    
        @Override
        public void userLogin() {
            System.out.print("登录前。。。");
            mLogin.userLogin();
            System.out.print("登录后。。。");
        }
    }
    

    客户端:

    public class Test {
        public static void main(String[] args){
            ILogin loginProxy = new UserLoginProxy();
            loginProxy.userLogin();
        }
    }
    

    这样我们就在尽量不修改现有类的基础上实现了这个需求,同时客户端只用跟代理类打交道,完全不用了解代理类与目标类的实现细节,也不需要知道目标类是谁;当然你如果想指定目标类,将在外部创建目标类,传给代理类
    这里只是在同步情况下的一种实现,如果是异步操作,就需要进行一些改动

    静态代理总结:

    • 优点:在符合开闭原则的情况下,对目标对象功能进行扩展和拦截
    • 缺点:需要为每个目标类创建代理类和接口,导致类的数量大大增加,工作量大;接口功能一旦修改,代理类和目标类也得相应修改,不易维护

    动态代理

    从静态代理的实现过程可以知道工作量太大,如果是在项目早期同步做还好,要是在接手老项目或者项目晚期再做,你可能要为成百上千个类创建对应的代理对象,那真的挺让人崩溃的;所以我们就需要想有没有别的方法:如何少写或者不写代理类却能完成这些功能

    做Java的都知道一个.java文件会先被编译成.class文件,然后被类加载器加载到JVM的方法区,存在形式是一个Class对象(所谓的Class对象就是Class文件在内存中的实例);我们构造出的任何对象实例是保存在Heap中,而实例是由其Class对象创建的(可以通过任意实例的getClass方法获取对应的Class对象),比如平时使用new关键字加构造方法Person p = new Person()就是将这个Class对象在Heap中创建一个实例;可以看出要创建一个实例,最关键的是得到对应的Class对象,而得到Class对象追根溯源需要一个Java文件;那么我们要做的就是不写Java文件,而是直接得到代理Class对象,然后根据它通过反射创建代理实例

    Class对象里面包含了一个类的所有信息,比如构造器,方法,字段等;那我们怎么在不写代理类的前提下获取到这些信息呢?从静态代理的实现知道目标类和代理类实现了同一个接口,这是为了尽可能保证代理对象的内部结构和目标对象一致,这样代理对象只需要专注于代码增强部分的编写,所以接口拥有代理对象和目标对象共同的类信息,那么我们就可以从接口获取本来应该由代理类提供的信息,但是要命的是接口是不能实例化的啊

    这就要讲到动态代理了:在动态代理中,不需要我们再手动创建代理类,只需要编写一个动态处理器及指定要代理的目标对象实现的接口,真正的代理对象由JDK在运行时为我们创建;JDK提供了java.lang.reflect.InvocationHandler和java.lang.reflect.Proxy来实现动态代理

    JDK代理

    Proxy.getProxyClass(ClassLoader, interfaces)方法只需要接收一个类加载器和一组接口就可以返回一个代理Class对象,然后就可以通过反射创建代理实例;其原理就是从传入的接口Class中,拷贝类结构信息到一个新的Class对象中,并继承Proxy类,拥有构造方法;站在我们的角度就是通过接口Class对象创建代理类Class对象

    这里通过一个很骚的比喻来说明下:一个东厂太监(接口Class对象)有一家子财产,但是为了侍奉皇帝这一伟大事业,毅然决然的割了DD(没有构造方法),虽然实现了自己的理想,但是不能繁育下一代(不能构造器创建对象),也就没有后人继承自己的家业;但是好在华佗在世,江湖上有一个高人(Proxy),发明了一个克隆大法(getProxyClass),不仅克隆出了几乎和太监一样的下一代(新的Class),还拥有自己的小DD(构造方法),这样这个下一代就能继承太监的家产(类结构信息,其实是实现了该接口),同时还能娶妻生子,传给下一代(创建实例)

    用一副图展示动态代理和静态代理实现思路:

    在这里插入图片描述

    那这样就很简单了

        public static Object loadProxy(Object target) throws Exception {
            //通过接口Class对象创建代理Class对象
            Class<?> proxyClass = Proxy.getProxyClass(target.getClass().getClassLoader(), target.getClass().getInterfaces());
            //拿到代理Class对象的有参构造方法
            Constructor<?> constructors = proxyClass.getConstructor(InvocationHandler.class);
            //反射创建代理实例
            Object proxy = constructors.newInstance(new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("执行前日志..."+"\n");
                    //执行目标类的方法
                    Object result = method.invoke(target, args);
                    System.out.println("执行后日志..."+"\n");
                    return result;
                }
            });
            return proxy;
        }
        
        public static void main(String[] args) throws Exception {
            ILogin proxy = (ILogin) loadProxy(new UserLogin());
            proxy.userLogin();
        }
    

    看看打印结果
    在这里插入图片描述
    这样无论系统有多少目标类,通过传进来的目标类都可以获取到对应的代理对象,就达到我们在执行目标类前后加日志的效果了,同时还不需要编写代理类

    Proxy类还有个更简单的方法newProxyInstance,直接返回代理对象,如下

        public static Object loadProxy(Object object) {
            return Proxy.newProxyInstance(
                    object.getClass().getClassLoader(), //和目标对象的类加载器保持一致
                    object.getClass().getInterfaces(), //目标对象实现的接口,因为需要根据接口动态生成代理对象
                    new InvocationHandler() { //事件处理器,即对目标对象方法的执行
    
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("执行前日志...");
    
                            Object result = method.invoke(object, args);
    
                            System.out.println("执行后日志...");
                            return result;
                        }
                    });
        }
    

    JDK动态代理总结:

    • 优点:相对于静态代理,极大的减少类的数量,降低工作量,减少对业务接口的依赖,降低耦合,便于后期维护;同时在某些情况下是最大的优势,即可以统一修改代理类的方法逻辑,而不需要像静态代理需要修改每个代理类
    • 缺点:因为使用的是反射,所以在运行时会消耗一定的性能;同时JDK代理只支持interface的动态代理,如果你再继续深究源码,会发现,所有动态生成的代理对象都有一个共同的父类,即都继承于Proxy;Java的单继承机制决定了无法支持class的动态代理,也就意味着你拿到动态生成的代理对象,只能调用其实现的接口里的方法,无法像静态代理中的代理类可以在内部扩展更多的功能

    动态生成代理对象原理

    在动态代理的过程中,我们不能清晰的看到代理类的实际样子,而且被代理对象和代理对象是通过InvocationHandler来完成的代理过程,其中代理对象是如何生成的,具体什么样,为什么代理对象执行的方法都会走到InvocationHandler中的invoke方法,要想了解这些就需要对Java是如何动态生成代理对象源码进行分析,继续往下看

    在这里插入图片描述
    你们有没有好奇getProxyClass这个方法是怎么通过【目标类实现的接口】生成代理Class对象的呢?

    如果你在第一个方法输入如下代码

    System.out.println("执行日志..."+(proxy instanceof Proxy)+"\n");
    System.out.println("执行日志..."+(proxyClass.getName())+"\n");
    System.out.println("执行日志..."+(proxyClass.getSuperclass().getName())+"\n");
    System.out.println("执行日志..."+(proxyClass.getSuperclass().getSimpleName())+"\n");
    System.out.println("执行日志..."+(proxyClass.getInterfaces()[0].getSimpleName())+"\n");
    

    得到的结果是
    在这里插入图片描述

    • 第一个结果为true,说明代理对象属于Proxy类型
    • 第二个结果是代理对象的全限定类名
    • 第三个结果是代理对象的父类的全限定名,第四个结果是父类名
    • 第四个结果是代理对象实现的接口名

    从这四点是不是能知道点啥了:动态生成的代理对象的父类是Proxy,实现了ILogin接口;这也就是为什么能将代理对象强转成ILogin,从而调用其接口方法;也说明了为什么只能支持动态生成接口代理,不能动态生成class代理,因为最终生成的代理对象肯定会继承Proxy类,如果我们提供的类已经继承了其它类,那就不能继承Proxy类了,动态代理也就无从谈起了

    接下来再从源码分析下其原理,鉴于篇幅原因,这里只分析重点代码

    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
            proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
     /**
     * 如果存在实现给定接口的给定加载器定义的代理类,则只返回缓存副本; 否则,它将通过ProxyClassFactory创建代理类
     */
     private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {
         if (interfaces.length > 65535) {
             throw new IllegalArgumentException("interface limit exceeded");	
         }
         return proxyClassCache.get(loader, interfaces);
     }
        
    private static final class ProxyClassFactory
            implements BiFunction<ClassLoader, Class<?>[], Class<?>>
        {
            // 所有代理类名称的前缀
            private static final String proxyClassNamePrefix = "$Proxy";
    
            // 用于生成唯一代理类名称的下一个数字
            private static final AtomicLong nextUniqueNumber = new AtomicLong();
    
            @Override
            public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    
                Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
                for (Class<?> intf : interfaces) {//通过传入的接口clone后的接口
    
                ......
    
                String proxyPkg = null;     // 定义代理class的包名
                int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    
    
                for (Class<?> intf : interfaces) {
                    int flags = intf.getModifiers();//获取接口修饰符
                    if (!Modifier.isPublic(flags)) {//我们定义的接口修饰符都是public,所以这里不会进去
                        accessFlags = Modifier.FINAL;
                        String name = intf.getName();
                        int n = name.lastIndexOf('.');
                        String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                        if (proxyPkg == null) {
                            proxyPkg = pkg;
                        } else if (!pkg.equals(proxyPkg)) {
                            throw new IllegalArgumentException(
                                "non-public interfaces from different packages");
                        }
                    }
                }
    
                if (proxyPkg == null) {
                    // 如果没有非public代理接口,请使用com.sun.proxy包
                    proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
                }
    
                /*
                 * 生成代理class名称
                 * 比如com.sun.proxy.$Proxy0
                 */
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
                /*
                 * 通过接口和class名称生成代理class数据
                 * 如果继续看generateProxyClass,会发现里面是生成class数据,包括写入Object的三个初始方法、写入实现的接口、写入继承Proxy类等到ByteArrayOutputStream中,然后转换成byte数组返回,至于是否会将Byte数据写到class文件保存在本地,视情况而定(默认不保存到硬盘)   
                 */
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces, accessFlags);
                try {
                    //将动态生成的class数据加载到内存中 生成一个代理class对象
                    return defineClass0(loader, proxyName,
                                        proxyClassFile, 0, proxyClassFile.length);
                } catch (ClassFormatError e) {
                    throw new IllegalArgumentException(e.toString());
                }
            }
        }
    

    生成class数据源码

    ProxyGenerator.generateProxyClass()方法属于sun.misc包下,Oracle并没有提供源代码,但是我们可以使用JD-GUI这样的反编译软件打开jre\lib\rt.jar来一探究竟,以下是其核心代码的分析:

    
    public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
            ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
            final byte[] var4 = var3.generateClassFile();
            //是否将生成的class数据保存到硬盘,默认不保存
            if (saveGeneratedFiles) {
                ......
            }
            return var4;
        }
        
    private byte[] generateClassFile() {
            //添加hashCode方法
            this.addProxyMethod(hashCodeMethod, Object.class);
            //添加equals方法
            this.addProxyMethod(equalsMethod, Object.class);
            //添加toString方法
            this.addProxyMethod(toStringMethod, Object.class);
            Class[] var1 = this.interfaces;
            int var2 = var1.length;
    
            int var3;
            Class var4;
            //遍历接口数组
            for(var3 = 0; var3 < var2; ++var3) {
                var4 = var1[var3];
                Method[] var5 = var4.getMethods();
                int var6 = var5.length;
                //添加接口里的方法,此时方法体还为空
                for(int var7 = 0; var7 < var6; ++var7) {
                    Method var8 = var5[var7];
                    this.addProxyMethod(var8, var4);
                }
            }
    
            Iterator var11 = this.proxyMethods.values().iterator();
    
            List var12;
            while(var11.hasNext()) {
                var12 = (List)var11.next();
                checkReturnTypes(var12);
            }
    
            Iterator var15;
            try {
                //添加一个带有InvocationHandler的构造方法
                this.methods.add(this.generateConstructor());
                var11 = this.proxyMethods.values().iterator();
                //生成方法体代码
                while(var11.hasNext()) {
                    var12 = (List)var11.next();
                    var15 = var12.iterator();
    
                    while(var15.hasNext()) {
                        ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                        this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                        //方法体里生成调用InvocationHandler的invoke方法代码
                        this.methods.add(var16.generateMethod());
                    }
                }
    
                this.methods.add(this.generateStaticInitializer());
            } catch (IOException var10) {
                throw new InternalError("unexpected I/O Exception", var10);
            }
    
            if (this.methods.size() > 65535) {
                throw new IllegalArgumentException("method limit exceeded");
            } else if (this.fields.size() > 65535) {
                throw new IllegalArgumentException("field limit exceeded");
            } else {
                this.cp.getClass(dotToSlash(this.className));
                this.cp.getClass("java/lang/reflect/Proxy");
                var1 = this.interfaces;
                var2 = var1.length;
                //生成实现接口,继承Proxy类代码
                for(var3 = 0; var3 < var2; ++var3) {
                    var4 = var1[var3];
                    this.cp.getClass(dotToSlash(var4.getName()));
                }
    
                this.cp.setReadOnly();
                ByteArrayOutputStream var13 = new ByteArrayOutputStream();
                DataOutputStream var14 = new DataOutputStream(var13);
    
                try {
                    var14.writeInt(-889275714);
                    var14.writeShort(0);
                    var14.writeShort(49);
                    this.cp.write(var14);
                    var14.writeShort(this.accessFlags);
                    var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
                    var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                    var14.writeShort(this.interfaces.length);
                    Class[] var17 = this.interfaces;
                    int var18 = var17.length;
    
                    for(int var19 = 0; var19 < var18; ++var19) {
                        Class var22 = var17[var19];
                        var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
                    }
    
                    var14.writeShort(this.fields.size());
                    var15 = this.fields.iterator();
    
                    while(var15.hasNext()) {
                        ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                        var20.write(var14);
                    }
    
                    var14.writeShort(this.methods.size());
                    var15 = this.methods.iterator();
    
                    while(var15.hasNext()) {
                        ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                        var21.write(var14);
                    }
    
                    var14.writeShort(0);
                    return var13.toByteArray();
                } catch (IOException var9) {
                    throw new InternalError("unexpected I/O Exception", var9);
                }
            }
        }
    

    从源码可以得出:

    • JDK帮我们生成了这样一个class数据,它继承了Proxy类,添加了一个带InvocationHandler参数的构造方法,这样也就明白了为什么使用构造方法反射创建代理对象的时候传入了一个InvocationHandler参数,因为默认会调用到Proxy类的构造方法,其参数正好是InvocationHandler,赋值给内部的成员变量h
      protected Proxy(InvocationHandler h) {
          Objects.requireNonNull(h);
          this.h = h;
      }
      
    • 实现了我们指定的接口,并实现了接口里的方法,同时接口中的方法调用了InvocationHandler的invoke方法
    • 当代理对象执行方法的时候,方法里面都会执行InvocationHandler的invoke方法,我们就可以在这里执行目标类方法

    这样可以说动态生成代理class对象其实是动态生成代理class数据

    动态代理类真身

    使用如下代码将动态生成的class数据保存到硬盘

        public static void write(){
            byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", UserLogin.class.getInterfaces());
            String path = "D:/Workspaces/com/sun/proxy/LoginProxy.class";
            try(FileOutputStream fos = new FileOutputStream(path)) {
                fos.write(classFile);
                fos.flush();
                System.out.println("代理类class文件写入成功");
            } catch (Exception e) {
                System.out.println("写文件错误");
            }
        }
    

    接下来将这个class文件反编译成Java文件看看:如果你没有jad工具,可以通过JAD Java Decompiler下载,使用如下命令

    /**
    * -d :用户指定输出文件保存目录
    * d:\  :具体目录目录
    * -sjava :输出文件扩展名 这里保存成Java文件
    * D:\Workspaces\com\sun\proxy\LoginProxy.class :class文件
    */
    jad -d d:\ -sjava D:\Workspaces\com\sun\proxy\LoginProxy.class
    /**
    * 上面的命令没有指定保存的Java文件名,下面的命令可以指定保存文件名
    * -p 输出详细文件信息
    */
    jad -p D:\Workspaces\com\sun\proxy\LoginProxy.class > D:\Workspaces\com\sun\proxy\LoginProxy.java
    

    代理类:

    public final class $Proxy0 extends Proxy implements ILogin
    {
    
        public $Proxy0(InvocationHandler invocationhandler)
        {
            super(invocationhandler);
        }
    
        public final boolean equals(Object obj)
        {
            try
            {
                return ((Boolean)super.h.invoke(this, m1, new Object[] {
                    obj
                })).booleanValue();
            }
            catch(Error _ex) { }
            catch(Throwable throwable)
            {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        public final String toString()
        {
            try
            {
                return (String)super.h.invoke(this, m2, null);
            }
            catch(Error _ex) { }
            catch(Throwable throwable)
            {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        public final void userLogin()
        {
            try
            {
                super.h.invoke(this, m3, null);
                return;
            }
            catch(Error _ex) { }
            catch(Throwable throwable)
            {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        public final int hashCode()
        {
            try
            {
                return ((Integer)super.h.invoke(this, m0, null)).intValue();
            }
            catch(Error _ex) { }
            catch(Throwable throwable)
            {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        private static Method m1;
        private static Method m2;
        private static Method m3;
        private static Method m0;
    
        static 
        {
            try
            {
                m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
                    Class.forName("java.lang.Object")
                });
                m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
                m3 = Class.forName("com.mango.demo.proxy.ILogin").getMethod("userLogin", new Class[0]);
                m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            }
            catch(NoSuchMethodException nosuchmethodexception)
            {
                throw new NoSuchMethodError(nosuchmethodexception.getMessage());
            }
            catch(ClassNotFoundException classnotfoundexception)
            {
                throw new NoClassDefFoundError(classnotfoundexception.getMessage());
            }
        }
    }
    
    • JDK为我们生成了一个$Proxy0(这个名字后面的0是编号,有多个代理类会一次递增)的代理类
    • 代理类实现了ILogin接口,继承了Proxy类,并有一个带InvocationHandler参数的构造方法,使用super调用Proxy类的构造方法,证实了上面的分析
    • 实现了接口的userLogin方法,并在其内部调用InvocationHandler的invoke方法,其h正是Proxy类定义的成员变量
    • 最下面是通过反射拿到类中的几个方法,作为参数传递到InvocationHandler.invoke方法中,即调用动态代理对象的任何方法,最终都是走到InvocationHandler.invoke方法中(所以在invoke方法中写日志需要判断下,是否是调用代理对象指定的方法走到这里)

    至此就解答了这一节【动态生成代理对象原理】刚开始提出的几个疑问了

    总结

    JDK动态代理是实现AOP编程的一种途径,可以在执行指定方法前后贴入自己的逻辑,像Spring、Struts2就有用到该技术(拦截器设计就是基于AOP的思想);只不过JDK动态代理只能实现基于接口的动态代理,也算是一个遗憾,还有这种实现方式也不能避免类数量增加,因为你必须要为每个业务类编写业务接口;那么有没有不用写代理类、也不用写业务接口的代理方法呢?

    方法是有的,需要用到CGLib了:

    CGLIB(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口
    CGLIB比JDK的代理更加强大,不只可以实现接口,还可以扩展类,通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势植入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理
    CGLIB底层封装了ASM,通过对字节码的操作来生成类,具有更高的性能,但是CGLIB创建代理对象时所花费的时间却比JDK多;ASM是一套JAVA字节码生成架构,能够动态生成.class文件并在加载进内存之前进行修改
    使用CGLIB需要引用jar包cglib-nodep-3.2.5.jar(如果引入cglib.jar,还需要引入asm的jar包)

    像Spring框架都支持这两种动态代理方式,但Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。

    至于在我们自己的业务里需要选择哪种方式实现AOP,还是需要视情况而定,两种方式组合使用或许能支持更多的业务需求

    展开全文
  • 代理服务器实际上是一台服务器,介于浏览器和你要访问的网站...代理服务器按用途分类可以分成以下几种类型。 1、Http代理:最常用的代理代理客户机的http访问,主要代理浏览器访问网页,它的端口一般为80、8080、31.
  • Java 动态代理详解

    千次阅读 2018-12-21 01:56:25
    博客原文:http://laijianfeng.org/2018/12/Java-动态代理详解/ 动态代理在Java中有着广泛的应用,比如Spring AOP、Hibernate数据...本文主要介绍Java中两种常见的动态代理方式:JDK原生动态代理和CGLIB动态代理。...
  • 代理ip

    千次阅读 2018-10-11 12:03:36
    通过上图可以看出: 正向代理:对于浏览器知道服务器的真实地址,例如VPN 反向代理:浏览器不知道服务器的真实地址,例如nginx 是否知道服务器的真实地址是正向代理和反向代理的主要区别 4 代理的使用 用法: ...
  • ubuntu设置代理 的三种方式

    万次阅读 2016-09-07 20:54:18
    转载自:... ubuntu下设置代理 2010-01-11 20:13:20|分类:ubuntu总结|标签:|字号大中小订阅 一、代理上网: 方法一 这是一种临时的手段,如果您仅仅是暂时需要通过http代理使用apt-get,您可以使用
  • 代理IP调研报告

    千次阅读 2017-10-27 10:26:08
    主要的用途有以下3种: 1. 突破自身IP访问限制; 2. 提高访问速度; 3. 隐藏真实IP。 在我们的业务场景里主要关心第三种用途。2.分类按IP协议分 HTTP代理 能够代理客户机的HTTP访问,主要是代理浏览器访问网页...
  • 模式的秘密——代理模式

    千次阅读 2016-09-03 17:10:22
    分类:远程代理、智能引用代理、保护代理、虚拟代理。 远程代理:为不同地理的对象提供局域网代表对象。 虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候进行创建。 保护代理:控制对一个对象的...
  • 【SSH系列】静态代理&&动态代理

    千次阅读 2016-07-06 00:01:34
    代理模式是二十三中设计模式中的一种,代理模式就是指由一个代理主题来操作真实的主题,真实的主题执行具体的业务操作,而代理主题负责其她相关业务,简而言之,代理模式可以以下三个部分组成: a、抽象角色:通过...
  • 代理服务器基本知识

    万次阅读 多人点赞 2018-01-12 17:21:21
    代理服务器系列(一):基础知识 2005 年 9 月 10 日 世界网络教研组 编 一、什么是代理服务器? 代理服务器英文全称是 Proxy Server,其功能就是代理网络用户去取得网络信息。 形象的说:它是网络信息的中转站...
  • 【设计模式】一文学透代理模式

    千次阅读 2021-04-03 11:18:43
    4 代理模式的分类:2、静态代理1.)首先新建一个买车的接口2.)声明一个要买车的客户,实现买车接口3.)声明一个买车代理汽车4S店,同样也实现买车接口,必须接受客户下单4.) 创建一个客户端,模拟一次买车5.)通过...
  • http代理是高匿名的吗?

    千次阅读 2021-10-15 15:34:48
    实际上,http代理可以根据匿名程度分类,本文将为大家详细介绍一下http代理根据匿名度可分哪几类。 匿名HTTP代理分为以下几种: 1、透明代理声明您正在使用代理并在HTTP标头中传递用户的真实IP地址。透明代理的使用...
  • 防火墙的分类

    千次阅读 2020-09-04 11:49:11
    防火墙的分类方法,主要有以下6种: 1、软、硬件形式分类:软件防火墙、硬件防火墙、芯片级防火墙。 2、防火墙技术分类:包过滤型防火墙、应用代理型防火墙 。 3、防火墙结构分类:单一主机防火墙、路由器集成式...
  • 详解设计模式之 - 代理模式

    千次阅读 2019-07-01 15:16:53
    代理模式什么作用? 代理模式 为真实对象提供一个代理,从而控制对真实对象的访问 代理模式的作用 通过代理,控制对对象的访问! 可以详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用...
  • 一、代理服务器概述1.1什么是代理服务器 在TCP/IP...代理服务器分析该请求,先查看自己缓存中是否请求数据,如果就直接传送给客户端,如果没有就代替客户端向该服务器发出请求。服务器响应以后,代理服务器将响应
  • squid代理原理与配置

    千次阅读 2012-08-29 15:24:02
    Linux下架设代理服务器 一、代理服务器概述   1.1什么是代理服务器  在TCP/IP网络中,传统...代理服务器分析该请求,先查看自己缓存中是否请求数据,如果就直接传送给客户端,如果没有就代替客户端向该服务
  • 代理服务器常用端口号

    千次阅读 热门讨论 2016-10-16 20:57:33
    代理服务器常用以下端口: (1). HTTP协议代理服务器常用端口号:80/8080/3128/8081/9080 (2). SOCKS代理协议服务器常用端口号:1080 (3). FTP(文件传输)协议代理服务器常用端口号:21 (4). Telnet(远程...
  • 商品分类

    千次阅读 2020-02-13 21:37:39
    会使用nginx进行反向代理 实现商品分类查询功能 掌握cors解决跨域 实现品牌查询功能 1.搭建后台管理前端 1.1.导入已资源 后台项目相对复杂,为了有利于教学,我们不再从0搭建项目,而是直接使用课前资料中给大家...
  • 胡说八道设计模式—代理模式

    万次阅读 2020-12-03 21:33:10
    Java 设计模式值代理模式,本文主要讲解Java 中两种常见的动态代理方式:JDK原生动态代理`和`CGLIB动态代理
  • Spring系列(2)--为什么需要动态代理

    千次阅读 2011-08-11 20:21:19
    理由看起来有以下几点: 1、的时候我们需要为一些类的方法增加一些额外的责任,因为这些责任是额外的,去改动这些类当然是不好的。  对于这点,大家可以很快的想到用装饰模式或者代理模式去实现。当然,如果...
  • 代理服务器(代理IP)的入门与简介

    千次阅读 2011-02-01 23:36:00
    代理服务器(代理IP)的入门与简介
  • IOS开发之协议和代理

    万次阅读 2013-09-29 17:32:37
    【iOS开发技术分享】协议与代理 ...代理是一个概念,很难用一个名词去定义(如我们可以说协议其实就是一个方法列表)。它更像是一种关系,我要做某一个事情,但我自己不想去做这件事,我委托其他人
  • 设计模式----代理模式

    千次阅读 2018-07-30 17:18:38
    代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。 分类 按照代理的创建时期划分:静态代理和动态...其中动态代理根据实现方式两种一种是JDK内置Proxy实现,一种是cglib实现 静态代理 ...
  • 分类: 远程代理(Remote Proxy)—为不同地理的对象提供局域网代表对象。(类似于客户端和服务器端) 虚拟代理(Virtual Proxy)—根据需要将资源消耗很大的对象进行延迟,真正需要的时候才进行创建。(网页中
  • 设计模式---代理模式

    千次阅读 2018-12-25 18:48:44
    代理模式是一个比较重要的设计模式在Spring框架中经常用到,对这个模式的学习助于自己去理解Spring,舒服!
  • Linux下架设代理服务器

    万次阅读 2011-10-12 11:11:40
    再次感谢,本文的作者bye2000。  Linux下架设代理服务器  作者:bye2000  ...在引入了代理服务器以后,这一过程变成了这样:客户端向服务器发起请求,该请求被送到代理服务器;代理服务器分析该请求,先查

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 54,998
精华内容 21,999
关键字:

代理可以有以下分类