精华内容
下载资源
问答
  • Java 动态代理作用是什么? 如果不懂动态代理就无法深入理解当下最流行的诸多框架的原理,如spring。 如果不懂动态代理大厂的offer很可能和你插肩而过。 很早就想写一篇动态代理的文章,正好看到这个问题,在回答...

    Java 动态代理作用是什么?

    如果不懂动态代理就无法深入理解当下最流行的诸多框架的原理,如spring。

    如果不懂动态代理大厂的offer很可能和你插肩而过。

    要理解动态代理首先要理解代理模式

    什么是代理模式?

    有一个打印机的类

    public class Printer {
        public void print(){
            System.out.println("打印!");
        }
    }
    

    我想在打印之前先记录一下日志怎么做?

    最简单的方法:在打印的功能前面直接加上记录日志的功能。

    public class Printer {
        public void print(){
            System.out.println("记录日志!");
            System.out.println("打印!");
        }
      }
    

    看上去好像没有问题,但是我们修改了打印机的源代码,破坏了面向对象的开闭原则,有可能影响到其它功能。怎么解决呢?很容易可以想到,既然不能修改原来的代码,那我新建一个类吧。

    public class LogPrinter extends Printer {
        public void print(){
            System.out.println("记录日志!");
            System.out.println("打印!");
        }
    }
    

    这个类继承了打印机的类,重写了打印机的print方法,提供了记录日志的功能,以后需要打印机的时候使用这个类就好。问题似乎得到了解决,我们可以在这个解决方案的基础上进一步的优化:

    先抽象出一个接口:

    public interface IPrinter {
        void print();
    }
    

    打印机类实现这个接口:

    public class Printer implements IPrinter {
           public void print(){
           System.out.println("打印!");
        }
    }
    

    创建打印机代理类也实现该接口,在构造函数中将打印机对象传进去,实现接口的打印方法时调用打印机对象的打印方法并在前面加上记录日志的功能:

    public class PrinterProxy implements IPrinter {
        private IPrinter printer;
        public PrinterProxy(){
            this.printer = new printer();
        }
        @Override
        public void print() {
            System.out.println("记录日志");
            printer.print();
        }
    }
    

    试一把吧:

    public class Test {
        public static void main(String[] args) {
            PrinterProxy proxy = new PrinterProxy();
            proxy.print();
        }
    }
    

    结果出来了:

    记录日志
    打印
    

    以后我们就可以直接实例化PrinterProxy对象调用它的打印方法了,这就是静态代理模式,通过抽象出接口让程序的扩展性和灵活性更高了。

    静态代理是完美无缺的吗?

    考虑一下,如果我的打印机类中还有别的方法,也需要加上记录日志的功能,就不得不将记录日志的功能写n遍。进一步如果我还有电视机,电冰箱的类里面的所有方法也需要加上记录日志的功能,那要重复的地方就更多了。

    怎么办?

    动态代理闪亮登场:

    要想不重复写记录日志的功能,针对每一个接口实现一个代理类的做法肯定不可行了,可不可以让这些代理类的对象自动生成呢?

    Jdk提供了invocationHandler接口和Proxy类,借助这两个工具可以达到我们想要的效果。

    invocationHandler接口上场:

    //Object proxy:被代理的对象 
    //Method method:要调用的方法 
    //Object[] args:方法调用时所需要参数 
    public interface InvocationHandler {
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    }
    

    接口里只有一个方法invoke,这个方法非常重要,先混个脸熟,稍后解释。

    Proxy类上场,它里面有一个很重要的方法 newProxyInstance:

    //CLassLoader loader:被代理对象的类加载器 
    //Class<?> interfaces:被代理类全部的接口 
    //InvocationHandler h:实现InvocationHandler接口的对象 
     public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
    

    调用Proxy的newProxyInstance方法可以生成代理对象

    一切准备就绪动态代理模式千呼万唤始出来:

    接口IPrinter 和 该接口的实现类 Printer的代码同前。

    *实现一个类,该类用来创建代理对象,*它实现了InvocationHandler接口

    作者:夏昊
    链接:https://www.zhihu.com/question/20794107/answer/811250346
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    public class ProxyHandler implements InvocationHandler {
        private Object targetObject;//被代理的对象
        //将被代理的对象传入获得它的类加载器和实现接口作为Proxy.newProxyInstance方法的参数。
        public  Object newProxyInstance(Object targetObject){
            this.targetObject = targetObject;
            //targetObject.getClass().getClassLoader():被代理对象的类加载器
            //targetObject.getClass().getInterfaces():被代理对象的实现接口
            //this 当前对象,该对象实现了InvocationHandler接口所以有invoke方法,通过invoke方法可以调用被代理对象的方法
            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("记录日志");
            return method.invoke(targetObject,args);
        }
    }
    

    被代理的对象targetObject可以通过方法参数传进来:

    public Object newProxyInstance(Object targetObject){
           this.targetObject=targetObject;
    

    我们重点来分析一下这段代码:

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

    动态代理对象就是通过调用这段代码被创建并返回的

    方法有三个参数:

    第一个参数:

    targetObject.getClass().getClassLoader():targetObject对象的类加载器。

    第二个参数:

    targetObject.getClass().getInterfaces():targetObject对象的所有接口

    第三个参数:

    this:也就是当前对象即实现了InvocationHandler接口的类的对象,在调用方法时会调用它的invoke方法。

    再来看一下这段代码:

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          //在这里可以通过判断方法名来决定执行什么功能
          System.out.println("记录日志");
          //调用被代理对象的方法
          return method.invoke(targetObject, args);
    }
    

    这个方法就是生成的代理类中的方法被调用时会去自动调用的方法,可以看到在这个方法中调用了被代理对象的方法: method.invoke(targetObject, args);

    我们可以在这里加上需要的业务逻辑,比如调用方法前记录日志功能.

    见证奇迹的时刻到了:

    public class Test {
       public static void main(String[] args){
       ProxyHandler proxyHandler=new ProxyHandler();
       IPrinter printer=(IPrinter) proxyHandler.newProxyInstance(new Printer());
       printer.print();
     }
    }
    

    打印结果:

    记录日志
    打印
    

    当执行printer.print();时会自动调用invoke方法,很多初学者不理解为什么能调用这个方法,回忆一下创建代理对象的时候是通过

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

    来创建的,方法的第三个参数this是实现了 InvocationHandler 接口的对象, InvocationHandler 接口有invoke方法。现在有点思路了吧~

    将被代理的对象作为参数传入就可以执行里面的任意方法,所有的方法调用都通过invoke来完成。不用对每个方法进行处理,动态代理是不是很简洁。

    代理模式的定义:
    代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用,通俗的来讲代理模式就是我们生活中常见的中介
    动态代理和静态代理的区别在于静态代理我们需要手动的去实现目标对象的代理类,而动态代理可以在运行期间动态的生成代理类。

    参考链接:https://www.zhihu.com/question/20794107/answer/811250346

    展开全文
  • 作者:Intopass ...来源:知乎 著作权归作者所有。商业转载请联系作者获得...① 首先你要明白静态代理的作用 我们有一个字体提供类,有多种实现(从磁盘,从网络,从系统) public interface FontProvider { Font...

    作者:Intopass
    链接:https://www.zhihu.com/question/20794107/answer/75164285
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    ① 首先你要明白静态代理的作用
    我们有一个字体提供类,有多种实现(从磁盘,从网络,从系统)

    public interface FontProvider {
        Font getFont(String name);
    }
    
    public abstract class ProviderFactory {
        public static FontProvider getFontProvider() {
            return new FontProviderFromDisk();
        }
    }
    
    public class Main() {
        public static void main(String[] args) {
            FontProvider fontProvider = ProviderFactory.getFontProvider();
            Font font = fontProvider.getFont("微软雅黑");
            ......
        }
    }
    

    现在我们希望给他加上一个缓存功能,我们可以用静态代理来完成

    public class CachedFontProvider implements FontProvider {
        private FontProvider fontProvider;
        private Map<String, Font> cached;
    
        public CachedFontProvider(FontProvider fontProvider) {
            this.fontProvider = fontProvider;
        }
    
        public Font getFont(String name) {
            Font font = cached.get(name);
            if (font == null) {
                font = fontProvider.getFont(name);
                cached.put(name, font);
            }
            return font;
        }
    }
    
    
    /* 对工厂类进行相应修改,代码使用处不必进行任何修改。
       这也是面向接口编程以及工厂模式的一个好处 */
    public abstract class ProviderFactory {
        public static FontProvider getFontProvider() {
            return new CachedFontProvider(new FontProviderFromDisk());
        }
    }
    

    当然,我们直接修改FontProviderFromDisk类也可以实现目的,但是我们还有FontProviderFromNet, FontProviderFromSystem等多种实现类,一一修改太过繁琐且易出错。
    况且将来还可能添加日志,权限检查,异常处理等功能显然用代理类更好一点。

    ② 然而为什么要用动态代理?
    考虑以下各种情况,有多个提供类,每个类都有getXxx(String name)方法,每个类都要加入缓存功能,使用静态代理虽然也能实现,但是也是略显繁琐,需要手动一一创建代理类。

    public abstract class ProviderFactory {
        public static FontProvider getFontProvider() {...}
        public static ImageProvider getImageProvider() {...}
        public static MusicProvider getMusicProvider() {...}
        ......
    }
    

    使用动态代理怎么完成呢?

    public class CachedProviderHandler implements InvocationHandler {
        private Map<String, Object> cached = new HashMap<>();
        private Object target;
    
        public CachedProviderHandler(Object target) {
            this.target = target;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
            Type[] types = method.getParameterTypes();
            if (method.getName().matches("get.+") && (types.length == 1) &&
                    (types[0] == String.class)) {
                String key = (String) args[0];
                Object value = cached.get(key);
                if (value == null) {
                    value = method.invoke(target, args);
                    cached.put(key, value);
                }
                return value;
            }
            return method.invoke(target, args);
        }
    }
    
    public abstract class ProviderFactory {
        public static FontProvider getFontProvider() {
            Class<FontProvider> targetClass = FontProvider.class;
            return (FontProvider) Proxy.newProxyInstance(targetClass.getClassLoader(),
                new Class[] { targetClass },
                new CachedProviderHandler(new FontProviderFromDisk()));
        }
    }
    

    ③ 这也是为什么Spring这么受欢迎的一个原因
    Spring容器代替工厂,Spring AOP代替JDK动态代理,让面向切面编程更容易实现。
    在Spring的帮助下轻松添加,移除动态代理,且对源代码无任何影响。

    展开全文
  • ...商业转载请联系作者获得授权,非商业转载请注明出处。...① 首先你要明白静态代理的作用我们有一个字体提供类,有多种实现(从磁盘,从网络,从系统)public interface FontProvider { Font getFo...

    作者:Intopass
    链接:https://www.zhihu.com/question/20794107/answer/75164285
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    ① 首先你要明白静态代理的作用我们有一个字体提供类,有多种实现(从磁盘,从网络,从系统)public interface FontProvider {
        Font getFont(String name);
    }

    public abstract class ProviderFactory {
        public static FontProvider getFontProvider() {
            return new FontProviderFromDisk();
        }
    }

    public class Main() {
        public static void main(String[] args) {
            FontProvider fontProvider = ProviderFactory.getFontProvider();
            Font font = fontProvider.getFont("微软雅黑");
            ......
        }
    }
    现在我们希望给他加上一个缓存功能,我们可以用静态代理来完成public class CachedFontProvider implements FontProvider {
        private FontProvider fontProvider;
        private Map<String, Font> cached;

        public CachedFontProvider(FontProvider fontProvider) {
            this.fontProvider = fontProvider;
        }

        public Font getFont(String name) {
            Font font = cached.get(name);
            if (font == null) {
                font = fontProvider.getFont(name);
                cached.put(name, font);
            }
            return font;
        }
    }


    /* 对工厂类进行相应修改,代码使用处不必进行任何修改。
       这也是面向接口编程以及工厂模式的一个好处 */
    public abstract class ProviderFactory {
        public static FontProvider getFontProvider() {
            return new CachedFontProvider(new FontProviderFromDisk());
        }
    }
    当然,我们直接修改FontProviderFromDisk类也可以实现目的,但是我们还有FontProviderFromNet, FontProviderFromSystem等多种实现类,一一修改太过繁琐且易出错。况且将来还可能添加日志,权限检查,异常处理等功能显然用代理类更好一点。② 然而为什么要用动态代理?考虑以下各种情况,有多个提供类,每个类都有getXxx(String name)方法,每个类都要加入缓存功能,使用静态代理虽然也能实现,但是也是略显繁琐,需要手动一一创建代理类。public abstract class ProviderFactory {
        public static FontProvider getFontProvider() {...}
        public static ImageProvider getImageProvider() {...}
        public static MusicProvider getMusicProvider() {...}
        ......
    }
    使用动态代理怎么完成呢?public class CachedProviderHandler implements InvocationHandler {
        private Map<String, Object> cached = new HashMap<>();
        private Object target;

        public CachedProviderHandler(Object target) {
            this.target = target;
        }

        public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
            Type[] types = method.getParameterTypes();
            if (method.getName().matches("get.+") && (types.length == 1) &&
                    (types[0] == String.class)) {
                String key = (String) args[0];
                Object value = cached.get(key);
                if (value == null) {
                    value = method.invoke(target, args);
                    cached.put(key, value);
                }
                return value;
            }
            return method.invoke(target, args);
        }
    }

    public abstract class ProviderFactory {
        public static FontProvider getFontProvider() {
            Class<FontProvider> targetClass = FontProvider.class;
            return (FontProvider) Proxy.newProxyInstance(targetClass.getClassLoader(),
                new Class[] { targetClass },
                new CachedProviderHandler(new FontProviderFromDisk()));
        }
    }
    ③ 这也是为什么Spring这么受欢迎的一个原因Spring容器代替工厂,Spring AOP代替JDK动态代理,让面向切面编程更容易实现。在Spring的帮助下轻松添加,移除动态代理,且对源代码无任何影响。编辑于 2016-03-0515713 条评论分享收藏感谢收起雨夜偷牛的人WEB Developer100 人赞同了该回答最近正好在看,特来挖坟。关于动态代理设计模式很可能题主就在不知不觉中使用了,例如Spring中的AOP,Struts2中的拦截器等。先来看静态代理模式代码:package test;

    public interface Subject  
    {  
      public void doSomething();  
    }
    package test;

    public class RealSubject implements Subject  
    {  
      public void doSomething()  
      {  
        System.out.println( "call doSomething()" );  
      }  

    package test;

    public class SubjectProxy implements Subject
    {
      Subject subimpl = new RealSubject();
      public void doSomething()
      {
         subimpl.doSomething();
      }
    }
    package test;

    public class TestProxy
    {
       public static void main(String args[])
       {
           Subject sub = new SubjectProxy();
           sub.doSomething();
       }
    }
    刚开始我会觉得SubjectProxy定义出来纯属多余,直接实例化实现类完成操作不就结了吗?后来随着业务庞大,你就会知道,实现proxy类对真实类的封装对于粒度的控制有着重要的意义。但是静态代理这个模式本身有个大问题,如果类方法数量越来越多的时候,代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题。先看代码:package test;

    public interface Subject  
    {  
      public void doSomething();  
    }
    package test;

    public class RealSubject implements Subject  
    {  
      public void doSomething()  
      {  
        System.out.println( "call doSomething()" );  
      }  

    package test;

    import java.lang.reflect.InvocationHandler; 
    import java.lang.reflect.Method; 
    import java.lang.reflect.Proxy; 

    public class ProxyHandler implements InvocationHandler
    {
        private Object tar;

        //绑定委托对象,并返回代理类
        public Object bind(Object tar)
        {
            this.tar = tar;
            //绑定该类实现的所有接口,取得代理类
            return Proxy.newProxyInstance(tar.getClass().getClassLoader(),
                                          tar.getClass().getInterfaces(),
                                          this);
        }   

        public Object invoke(Object proxy , Method method , Object[] args)throws Throwable
        {
            Object result = null;
            //这里就可以进行所谓的AOP编程了
            //在调用具体函数方法前,执行功能处理
            result = method.invoke(tar,args);
            //在调用具体函数方法后,执行功能处理
            return result;
        }
    }
    public class TestProxy
    {
        public static void main(String args[])
        {
               ProxyHandler proxy = new ProxyHandler();
               //绑定该类实现的所有接口
               Subject sub = (Subject) proxy.bind(new RealSubject());
               sub.doSomething();
        }
    }
    看完代码,现在我来回答,动态代理的作用是什么:Proxy类的代码量被固定下来,不会因为业务的逐渐庞大而庞大;可以实现AOP编程,实际上静态代理也可以实现,总的来说,AOP可以算作是代理模式的一个典型应用;解耦,通过参数就可以判断真实类,不需要事先实例化,更加灵活多变。

    展开全文
  • Java 动态代理作用是什么 ==AOP?

    千次阅读 2016-12-08 14:38:36
    关于动态代理设计模式很可能题主就在不知不觉中使用了,例如Spring中AOP,Struts2中拦截器等。 先来看静态代理模式代码: package test; public interface Subject { public void doSomething(); } ...
    
    
    展开全文
  • 基于接口的动态代理 基于子类的动态代理 基于接口的动态代理 类: Proxy 提供者JDK 如何创建代理对象? 使用Proxy类中++newProxyInstance++方法 创建被代理对象要求: 被代理类至少要实现一个接口,如果...
  • 反射 反射最大的作用之一就在于我们可以不在编译时知道某个对象的类型,而在运行时通过提供完整的”包名+类名.class”得到。注意:不是在编译时,而是在运行时。代理模式(通过代理静默地解决一些业务...动态代理...
  • 代理是一种常用的设计模式,主要作用是为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以再客户端和目标对象之间起到中介的作用. (2)什么时候使用...
  • 看完代码,现在我来回答,动态代理的作用是什么:Proxy类的代码量被固定下来,不会因为业务的逐渐庞大而庞大;可以实现AOP编程,实际上静态代理也可以实现,总的来说,AOP可以算作是代理模式的一个典型应用;解耦,...
  • jdk的动态代理及为什么需要接口

    万次阅读 多人点赞 2017-12-01 14:38:30
    动态代理有关,无非是使用JDK动态代理,和cglib动态代理。...首先,动态代理是个挺有用的东西,常见的就是javaAOP的有关,主要的作用是是在一个方法使用前后,能进行别的处理。比如吧,aop所说的,面向切面...
  • 动态代理利用了反射机制,使用更简单,但背后逻辑…先说明一点,自动代理代理的是接口,而不是类,Proxy类产生的代理对象Proxy的一个实例,这个实例实现了我们定义的接口 静态代理: 举一个黄牛代理买票的例子...
  • Java 动态代理实例

    2017-07-12 13:49:14
    动态代理的作用是什么: Proxy类的代码量被固定下来,不会因为业务的逐渐庞大而庞大;可以实现AOP编程,实际上静态代理也可以实现,总的来说,AOP可以算作是代理模式的一个典型应用;解耦,通过参数就可以判断...
  • 目录概述特点作用分类基于接口的动态代理基于子类的动态代理 概述   什么是代理。个人认为就是我们常说:中间商赚差价。   在以往消费模式中,往往都消费者直接联系生产者,但这个时候生产者又要生产产品...
  • 在使用IP海换IP软件(iphai.cn)时候,会看到设置地方有这个专用词出现,但是不知道它代表的是什么?这样会对我们使用代理IP有影响吗?理论上,按照指引去设置就可以正常使用,但是多了解一些网络知识也没有坏处。...
  • jdk动态代理的使用

    2020-06-27 11:23:42
    使用代理模式的作用3、实现代理的方式二、动态代理 一、什么是动态代理 使用jdk的反射机制,创建对象的能力,创建的代理类的对象,而不用创建类文件,不用写Java文件。 动态:在执行程序时,调用jdk提供的方法才能...
  • 动态代理

    2019-11-14 14:27:50
    文章目录代理模式什么是代理代理模式的作用代理模式的分类(实现方式)静态代理代码解释静态代理的缺点动态代理jdk动态代理初识动态代理(有接口,有目标类,理解就好)全流程分析与图解进一步理解(只有接口,无...
  • SEO:汉译为搜索引擎优化。 一种方式:利用搜索引擎规则提高网站在有关搜索...我们在尝试运用动态代理IP之前,先说说咱们有什么常规化seo优化方式去优化网站,其中要做好2点。第一点做好基本优化。第二点,深入
  • 1.什么是代理 根据原有对象生成一个占位(代理对象),然后由代理对象来代理真实对象,控制对真实对象...3.代理的作用(好处) 4.常用的代理技术有哪些 1)JDK动态代理 2)CGLIB动态代理 5.JDK动态代理 1)代...
  • 网络时代下,互联网工作者对代理IP的需求也越来越...代理服务器(Proxy Server)一种重要的服务器安全功能,它的工作主要在开放系统互联(OSI)模型的会话层,从而起到防火墙的作用。 代理ip通俗讲就是改变你本机i...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 694
精华内容 277
关键字:

动态代理的作用是什么