精华内容
下载资源
问答
  • 静态代理 代理:顾名思义就是类似于经纪人的角色,当某个导演要找某个演员拍戏的时候,往往会找到这个演员的经纪人(也许不很恰当,不了解饭圈的运作,例子也不是很恰当,当个故事听一听,便于理解),这个经纪人...

    静态代理

    代理:顾名思义就是类似于经纪人的角色,当某个导演要找某个演员拍戏的时候,往往会找到这个演员的经纪人(也许不很恰当,不了解饭圈的运作,例子也不是很恰当,当个故事听一听,便于理解),这个经纪人就是代理对象。大明星和经纪人都要实现person接口(共同的接口),经纪人(代理对象)要内部要包含大明星(被代理对象),就是一个简单的代理模式。
    在这里插入图片描述

    person接口

    public interface Person {
    	// 表演方法
        void perform(String str);
    }
    
    

    带明星类

    public class WomenStar implements Person{
    
        @Override
        public void perform(String str) {
            System.out.println("参演"+ str + "主角");
        }
    }
    

    经纪人类

    public class jingjiren implements Person {
    
    	// 要将大明星包进来
        private Person person;
        public jingjiren(Person person){
            super();
            this.person = person;
        }
    
        @Override
        public void perform(String str) {
            doSomeThingBefore();
            person.perform(str);
            doSomeThingAfter();
        }
    
        private void doSomeThingBefore(){
            System.out.println("阅读剧本");
        }
    
        private void doSomeThingAfter(){
            System.out.println("拿到报酬");
        }
    }
    

    Main方法

    public class Main {
        public static void main(String[] args) {
            Person person = new WomenStar();
            jingjiren fp = new jingjiren(person);
            fp.perform("女");
    
        }
    }
    
    

    运行结果

    在这里插入图片描述

    动态代理

    静态代理有个问题,如果某大导演是要找另一个明星拍戏,那么就要再找另一位经纪人,这违背了开闭原则(对扩展打开,对修改关闭)。所以我们要使用动态代理。

    经纪人类

    public class ProxyMai implements InvocationHandler {
        private Object person;
        public void setFactory(Person person){
            this.person = person;
    
        }
        // 通过proxy获取动态代理的对象
        public Object getProxyInstance(){
            return Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), this);
        }
        // 通过动态代理对象对方法进行增强
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            doSomeThingBefore();
            Object ret = method.invoke(person, args);
            doSomeThingAfter();
            return null;
        }
        private void doSomeThingBefore(){
            System.out.println("阅读剧本");
        }
        private void doSomeThingAfter(){
            System.out.println("拿到报酬");
        }
    }
    

    改成动态代理类之后,需要哪个明星直接创建该明星类,然后添加进来即可。

    展开全文
  • 使用java完成动态代理的演示代码,适合初学者理解动态代理的功能,进行参考,本代码是简单的demo代码,仅用于学习参考。
  • 四、动态代理的JDK实现原理 4.1核心类/接口 4.2 代理类$Proxy0解析 4.3 动态代理的经典使用五、手写代码模拟JDK动态代理六、参考资料项目源码已经上传,欢迎点击下载~先将自己总结的Java动态代理UML图放在前面,...

    前言

    文章目录如下,便于快速索引

    一、什么叫代理?

    二、什么叫动态代理?

    三、动态代理有什么优势?

    四、动态代理的JDK实现原理

        4.1核心类/接口

        4.2 代理类$Proxy0解析

        4.3 动态代理的经典使用

    五、手写代码模拟JDK动态代理

    六、参考资料

    项目源码已经上传,欢迎点击下载~

    先将自己总结的Java动态代理UML图放在前面,用相同的颜色代表同一个或本质上相同的类与方法,便于大家理解。接口UserService是我们自己定义的接口——接口中有方法execute();被代理类是实现了该接口的具体类;代理类则是存在于内存中,也实现了UserService接口的类,在内存中,该类名为$Proxy0。


        下面进入正文,今天想跟大家分享一下我自己在学习Java动态代理过程中的理解与收获,最后用自己的代码模拟实现JDK的动态代理。

    一、什么叫代理?

        这个概念不是我想表达的重点,所以这里我引用别人一篇博文的例子

        “动态代理技术就是用来产生一个对象的代理对象的。在开发中为什么需要为一个对象产生代理对象呢?
        举一个现实生活中的例子:歌星或者明星都有一个自己的经纪人,这个经纪人就是他们的代理人,当我们需要找明星表演时,不能直接找到该明星,只能是找明星的代理人。比如刘德华在现实生活中非常有名,会唱歌,会跳舞,会拍戏,刘德华在没有出名之前,我们可以直接找他唱歌,跳舞,拍戏,刘德华出名之后,他干的第一件事就是找一个经纪人,这个经纪人就是刘德华的代理人(代理),当我们需要找刘德华表演时,不能直接找到刘德华了(刘德华说,你找我代理人商谈具体事宜吧!),只能是找刘德华的代理人,因此刘德华这个代理人存在的价值就是拦截我们对刘德华的直接访问!
        这个现实中的例子和我们在开发中是一样的,我们在开发中之所以要产生一个对象的代理对象,主要用于拦截对真实业务对象的访问。那么代理对象应该具有什么方法呢?代理对象应该具有和目标对象相同的方法

        所以在这里明确代理对象的两个概念:

        1、代理对象存在的价值主要用于拦截对真实业务对象的访问。(事务的开启与关闭)

        2、代理对象应该具有和目标对象(真实业务对象)相同的方法。(要求实现同一接口)

        刘德华(真实业务对象)会唱歌,会跳舞,会拍戏,我们现在不能直接找他唱歌,跳舞,拍戏了,只能找他的代理人(代理对象)唱歌,跳舞,拍戏,一个人要想成为刘德华的代理人,那么他必须具有和刘德华一样的行为(会唱歌,会跳舞,会拍戏),刘德华有什么方法,他(代理人)就要有什么方法,我们找刘德华的代理人唱歌,跳舞,拍戏,但是代理人不是真的懂得唱歌,跳舞,拍戏的,真正懂得唱歌,跳舞,拍戏的是刘德华,在现实中的例子就是我们要找刘德华唱歌,跳舞,拍戏,那么只能先找他的经纪人,交钱给他的经纪人,然后经纪人再让刘德华去唱歌,跳舞,拍戏。”

    二、什么叫动态代理?

        代理类在程序运行时创建的代理方式被成为动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。

    三、动态代理有什么优势?

        通过使用代理,通常有两个优点:

        优点一:可以隐藏被代理类的实现;

        优点二:可以实现客户与被代理类间的解耦,在不修改被代理类代码的情况下能够做一些额外的处理。

    四、动态代理的JDK实现原理

        在java的动态代理机制中,有两个重要的类和接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。当然,我还想带各位深入了解一下存在于JVM中神秘的动态代理类——$Proxy0。最后再给出java动态代理的经典使用流程。

    4.1 核心类/接口

    4.1.1 java.lang.reflect.Proxy类

        Proxy类提供了用于创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。

    public class Proxy
    extends Object
    implements Serializable

        (1)Proxy的主要静态变量

    // 映射表:用于维护类装载器对象到其对应的代理类缓存
    private static Map loaderToCache = new WeakHashMap(); 
    
    // 标记:用于标记一个动态代理类正在被创建中
    private static Object pendingGenerationMarker = new Object(); 
    
    // 同步表:记录已经被创建的动态代理类类型,主要被方法 isProxyClass 进行相关的判断
    private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap()); 
    
    // 关联的调用处理器引用
    protected InvocationHandler h;

        (2)Proxy的构造方法

    // 由于 Proxy 内部从不直接调用构造函数,所以 private 类型意味着禁止任何调用
    private Proxy() {} 
    
    // 由于 Proxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用
    protected Proxy(InvocationHandler h) {this.h = h;} 

        (3)Proxy静态方法newProxyInstance

    public static Object newProxyInstance(ClassLoader loader, 
                Class<?>[] interfaces, 
                InvocationHandler h) 
                throws IllegalArgumentException { 
    
        // 检查 h 不为空,否则抛异常
        if (h == null) { 
            throw new NullPointerException(); 
        } 
    
        // 获得与制定类装载器和一组接口相关的代理类类型对象
        /*
         * Look up or generate the designated proxy class.
         */
            Class<?> cl = getProxyClass0(loader, interfaces); 
    
        // 通过反射获取构造函数对象并生成代理类实例
        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                SecurityManager sm = System.getSecurityManager();
                if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                    // create proxy instance with doPrivilege as the proxy class may
                    // implement non-public interfaces that requires a special permission
                    return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                        public Object run() {
                            return newInstance(cons, ih);
                        }
                    });
                } else {
                    return newInstance(cons, ih);
                }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        } 
        }
    
    private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
            try {
                return cons.newInstance(new Object[] {h} );
            } catch (IllegalAccessException e) {
                throw new InternalError(e.toString());
            } catch (InstantiationException e) {
                throw new InternalError(e.toString());
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString());
                }
            }
        }

        这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义。

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
    
    //loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
    //interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
    //h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
    

        官方JDK文档给出了使用该方法创建一个动态代理类的模板:

    Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                              new Class[] { Foo.class },
                                              handler);
        动态代理真正的关键是在 getProxyClass0 方法。这个我们在后面手动实现模拟JDK动态代码的时候可以看到。

    4.1.2  java.lang.reflect.InvocationHandler接口

        每一个动态代理类中都有一个实现了InvocationHandler这个接口(代码中的中介)的实例handler类(即前言中的MyInvocationHandler类),当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke(对方法的增强就写在这里面) 方法来进行调用。

    import java.lang.reflect.Method;
    
    public interface MyInvocationHandler {
    
        Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    
    }
        我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    //proxy:  内存中的代理实例 $proxy0
    //method:  内存代理实例中class.forName("被代理类").getMethod("目标方法") 即被代理的类的方法对象
    //args:  指代的是调用真实对象某个方法时接受的参数

        我们怎么确认上面几个参数就是这个意思呢?那就看看下面这节吧~

    4.2 代理类$Proxy0解析

    (1)为什么内存中的动态代理类叫做$Proxy0?


        这个可以通过断点查看到~

    (2)怎么拿到动态代理类的字节码文件?

      public static void createProxyFile() throws IOException {
            byte[] generateProxyClass = ProxyGenerator.generateProxyClass("$Proxy0", new Class<?>[]{UserService.class});
    
            FileOutputStream outputStream = new FileOutputStream("$Proxy0.class");
            outputStream.write(generateProxyClass);
            outputStream.close();
        }
        在4.3 (5)中最终输出动态代理类执行结果后可以调用上面的方法,即可得到字节码文件

    (3)动态代理类$Proxy0字节码文件解析


        很清楚,动态代理类实现了UserService接口,继承了Proxy类

        首先我们看左边为动态代理类的代码结构。

    • 构造方法
    //$Proxy类
    public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
    }
    //父类Proxy
    protected InvocationHandler h;
    protected Proxy(InvocationHandler h) {
            Objects.requireNonNull(h);
            this.h = h;
    }

        可以看到动态代理类包含一个有参构造方法,内部调用了父类方法,其实也就是完成了调用处理器 InvocationHandler的实例化。该构造方法大家可得注意,在4.3(6)中提到的动态代理创建流程第3步,使用JAVA反射机制获取动态代理对象时:

    Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
        就是为了调用本构造方法。

    • Object方法

        大家可以看到动态代理类有m0~m3四个方法,这四个方法分别是什么呢?

    static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m3 = Class.forName("proxy.test.UserService").getMethod("execute");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }

        可以看到,通过java反射机制,除了m3是UserService接口方法的实现以外,其他方法都是Object的方法。那这些方法有什么特点呢?

    public final boolean equals(Object var1) throws  {
            try {
                return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
        其实核心就一句话 h.invoke() 也就是说动态代理类中的方法都使用了java反射机制去调用调用处理器 InvocationHandler中的方法。

    • 接口方法

        接口方法与Object方法一样,内部就一句话。我们注意到,invoke方法传入3个参数,这个invoke方法也就是4.1.2中我们提到的InvocationHandler接口的 invoke方法,那理解3个参数的意义也就很简单了。

        参数1传入的为this——即$Proxy0本身,所以是内存中的动态代理对象

       参数2传入的为m3——也就是proxy.test.UserService中名为execute的方法,即接口中的方法。而这也完全证实了之前在“一、什么是代理?”部分提到的第二个特点——代理对象应该具有和目标对象(真实业务对象)相同的方法。(要求实现同一接口)

        参数3传入的为null——因为execute方法没有参数,所以为空。

    public final String execute() throws  {
            try {
                return (String)super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }

    4.3 动态代理的经典使用

    (1)理解UML图


        再把本图放上来暖场~JAVA中动态代理的使用基本就是如上图所示。下图是我们程序的目录结构,下面我们用代码来实现。


    (2)定义对象的行为接口UserService

    package proxy.test;
    
    public interface UserService {
    
        public String execute() throws Throwable ;
    }
    (3)定义目标业务对象类UserServiceImpl
    package proxy.test;
    
    public class UserServiceImpl implements UserService{
        @Override
        public String execute() throws Throwable {
            System.out.println("step 2 执行方法啦!!");
            return "step 2 执行方法啦!!";
        }
    }

    (4)自定义“调用处理程序”——MyInvocationHandler

    package proxy.test;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class MyInvocationHandler implements InvocationHandler {
    
        private UserService userService;
    
        public MyInvocationHandler(UserService userService) {
            this.userService = userService;
        }
        
        /*
        * @proxy 内存中的代理实例 $proxy0
        * @method 内存代理实例中class.forName("被代理类").getMethod("目标方法") 即被代理的类的方法对象
        * @args 方法参数
        * */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            before();   //事务开启
    
            method.invoke(userService, args);
    
            after();    //事务关闭
    
            return "成功了";
        }
    
        private void before() {
            System.out.println("事务开启!");
        }
    
        private void after() {
            System.out.println("事务关闭");
        }
    }
        动态代理最具魅力所在——也就是上面代码中事务开启和关闭部分,总结起来就是:实现了方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法),因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象,具体应用的话,比如可以添加调用日志,做事务控制等。

    (5)代理类生成并测试代码

    package proxy.test;
    import java.lang.reflect.Proxy;
    
    public class MyTest {
    
        public static void main(String[] args) throws Throwable {
            System.out.println("---------------JDK动态代理----------------");
            UserService userService = (UserService) Proxy.newProxyInstance(MyTest.class.getClassLoader(),
                    new Class<?>[]{UserService.class},
                    new MyInvocationHandler(new UserServiceImpl()));
    
            userService.execute();
        }
    }

        上面的代码也就是4.1.1(3)中提到的JDK文档给出的创建代理类方式。通过强制类型转换并执行相应方法,得到输出如下:


    (6)动态代理创建流程总结

        动态代理的创建是基于java反射的,一个典型的动态代理创建对象过程可分为以下四个步骤:

    • 1.通过实现InvocationHandler接口创建自己的调用处理器 
            InvocationHandler handler = new InvocationHandlerImpl(...);
    • 2.通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
            Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
    • 3.通过反射机制获取动态代理类$Proxy0的构造函数,其参数类型是调用处理器接口类型
            Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
    • 4.通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
            Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

        为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。生成的动态代理类$Proxy0继承Proxy类实现UserService接口,实现的UserService的方法实际调用调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))。

    (7)JDK动态代理的不足

        诚然,Proxy已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理的桎梏,因为它的设计注定了这个遗憾。摆脱这个遗憾就得依靠CGLIB(此处等待下文)。

    五、手写代码模拟JDK动态代理

    5.1 原理解析

        手撸代码模拟JDK动态代理,其实也就是对java.lang.reflect.Proxy类的功能进行模拟。其步骤主要有以下四步:

    (1)创建代理类的源码; 

        拿到被代理类(如UserServiceImpl)实现的接口类对象(如UserService.class),遍历里面的方法(如execute()方法),以字符串的形式拼凑出代理类源码(动态代理类与被代理类实现同一接口在此体现),将代理类的源码写到本地java文件

    (2)将源码进行编译成字节码; 

        读取源码,编译java文件,得到.class字节码文件(的路径)

    (3)将字节码加载到内存; 

    (4)实例化代理类对象并返回给调用者。

        其中步骤(1)、(2)、(3)就是我们自定义的Proxy类所要完成的功能,类的结构如下图;步骤(4)是我们功能代码/测试代码要实现的。下面我们对每一步进行解析。


    5.2 创建代理类的源码

    项目实现源码已经上传,欢迎点击下载~

    (1)使用字符串拼凑动态代理对象的java源码

     //用字符串的形式拼凑出内存里的代理类
        static String rt = "\r\n";
        private static String get$Proxy0(Class<?> interfaces) {
    
            Method[] methods = interfaces.getMethods();
    
            String proxyClass = "package proxy;" + rt
                    + "import java.lang.reflect.Method;" + rt
                    + "public class $Proxy0 implements " + interfaces.getName() + "{"
                    + rt + "MyInvocationHandler h;" + rt
                    + "public $Proxy0(MyInvocationHandler h) {" + rt
                    + "this.h = h;" + rt + "}" + getMethodString(methods, interfaces)
                    + rt + "}";
            return proxyClass;
        }
    
        private static String getMethodString(Method[] methods, Class<?> interfaces) {
            String proxyMethod = "";
    
            for (Method method : methods) {
                proxyMethod += "public String " + method.getName()
                        + "() throws Throwable {" + rt + "Method md = "
                        + interfaces.getName() + ".class.getMethod(\"" + method.getName()
                        + "\",new Class[]{});" + rt
                        + "return (String)this.h.invoke(this, md, null);" + rt + "}" + rt;
            }
    
            return proxyMethod;
        }
        上面这段代码所模拟的具体代码实现在JDK中是在jar包中的,其得到的结果就是生成了$Proxy0.java
    package proxy;
    import java.lang.reflect.Method;
    
    public class $Proxy0 implements proxy.test.UserService{
        MyInvocationHandler h;
        public $Proxy0(MyInvocationHandler h) {
            this.h = h;
        }
        
        public String execute() throws Throwable {
            Method md = proxy.test.UserService.class.getMethod("execute",new Class[]{});
            return (String)this.h.invoke(this, md, null);
        }
    }

    (2)将源码写入本地文件

     private static void outputFile(String proxyClass, String path) {
            File f = new File(path);
            try {
                FileWriter fw = new FileWriter(f);
                fw.write(proxyClass);
                fw.flush();
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    5.3 将源码进行编译成字节码

    private static void compileJavaFile(String fileName) {
            try {
                //获得当前系统中的编译器
                JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
                //获得文件管理者
                StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
                Iterable<? extends JavaFileObject> fileObjects = manager.getJavaFileObjects(fileName);
                //编译任务
                JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, fileObjects);
                //开始编译,执行完可在当前目录下看到.class文件
                task.call();
                //关闭文件管理者
                manager.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        这段代码用的都是JAVA自带的编译工具,不做过多解释。

    5.4 将字节码加载到内存

    private static Object loadClassToJvm(MyInvocationHandler h) {
            try {
                //使用自定义类加载器
                MyClassLoader loader = new MyClassLoader("C:\\HailinLuo\\CODING\\JAVA\\JavaProgramming\\src\\proxy");
                //得到动态代理类的反射对象
                Class<?> $Proxy0 = loader.findClass("$Proxy0");
                //通过反射机制获取动态代理类$Proxy0的构造函数,其参数类型是调用处理器接口类型
                Constructor<?> constructors = $Proxy0.getConstructor(MyInvocationHandler.class);
                //通过构造函数创建动态代理类实例
                return constructors.newInstance(h);
            } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
                e.printStackTrace();
            }
            return null;
        }

        这里使用了自定义的类加载器MyClassLoader 

    package proxy;
    
    import java.io.*;
    
    public class MyClassLoader extends ClassLoader {
        File dir;
        //把文件路径用构造函数传进来
        public MyClassLoader(String path) {
            dir = new File(path);
        }
        /*
         * 本方法就是去加载对应的字节码文件
         * */
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            //如果文件路径可用
            if (dir != null) {
                File clazzFile = new File(dir, name + ".class");
                //如果字节码文件存在
                if (clazzFile.exists()) {
                    //把字节码文件加载到VM
                    try {
                        //文件流对接class文件
                        FileInputStream inputStream = new FileInputStream(clazzFile);
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        byte[] buffer = new byte[1024];
                        int len;
                        //将class文件读取到buffer中
                        while ((len = inputStream.read(buffer)) != -1) {
                            //将buffer中的内容读取到baos中的buffer
                            baos.write(buffer, 0, len);
                        }
                        //将buffer中的字节读到内存加载为class
                        return defineClass("proxy." + name, baos.toByteArray(), 0, baos.size());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return super.findClass(name);
        }
    }

        也可以使用URLClassLoader 加载,如下所示

    //load到内存
            URL[] urls = new URL[]{new URL("C:\\HailinLuo\\CODING\\JAVA\\JavaProgramming\\src\\proxy")};
            URLClassLoader urlClassLoader = new URLClassLoader(urls);
            Class cls = urlClassLoader.loadClass("proxy.$Proxy0");

    5.5 实例化代理类对象并返回给调用者

    UserService service = (UserService) MyProxy.newProxyInstance(MyTest.class.getClassLoader(),
                    UserService.class,
                    new MyInvocationHandlerImpl(new UserServiceImpl()));
    
            service.execute();

        没什么好说的。。。

    5.6 输出结果


        如上图所示,下方红框所示输出结果与JDK动态代理效果一致。说明我们的模拟是成功的!而上面那个红框中的$Proxy0.java和$Proxy0.class则是5.2(生成源码)与5.3(编译为字节码)执行的结果。

        OK,分享就到这里~

    六、参考资料

    项目源码已经上传,欢迎点击下载~

    http://www.jb51.net/article/86531.htm

    https://blog.csdn.net/pangqiandou/article/details/52964066

    https://blog.csdn.net/scplove/article/details/52451899

    https://www.jianshu.com/p/dbce090d5c3e?1487292535486

    https://blog.csdn.net/ljt2724960661/article/details/52507314


    展开全文
  • Java动态代理动态代理好处优点简介代码演示总结 动态代理好处 优点简介 实现无侵入式的代码扩展;在不用修改业务代码的情况下,增加一些公共扩展;增加代码的可扩展性和灵活性; 废话不多 直接上代码代码...

    动态代理好处

    优点简介

    实现无侵入式的代码扩展;在不用修改业务代码的情况下,增加一些公共扩展;增加代码的可扩展性和灵活性;
    废话不多 直接上代码。

    代码演示

    接口与其实现

    package proxy.service;
    
    /**
     * 被代理对象接口
     * @author admin
     *
     */
    public interface UserService {
    	void eat();
    	void speak();
    }
    
    
    package proxy.service.impl;
    
    import proxy.service.UserService;
    
    public class UserServiceImpl implements UserService {
    
    	@Override
    	public void eat() {
    		System.out.println("吃饭中。。。");
    	}
    
    	@Override
    	public void speak() {
    		System.out.println("说话中。。。");
    	}
    
    }
    

    切面类(扩展类)

    package proxy.service;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * 切面类 合并代理类
     * @author admin
     *
     */
    public class MyRepect implements InvocationHandler{
    	
    	private UserService us ;
    
    	public MyRepect(UserService us) {
    		super();
    		this.us = us;
    	}
    
    	public void before() {
    		System.out.println("前置方法");
    	}
    	
    	public void after() {
    		System.out.println("后置方法");
    	}
    
    	//获取代理类 之所以要类类加载器参数  是 代理类 动态生成的  并没有class文件
    	public Object getUserService() {
    		return Proxy.newProxyInstance(us.getClass().getClassLoader(), us.getClass().getInterfaces(), this);
    	}
    
    	//交织 业务类 和 切面类 
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		before();
    		Object ret = method.invoke(us, args);
    		after();
    		return ret;
    	}
    }
    

    测试类

    package proxy.test;
    
    import proxy.service.MyRepect;
    import proxy.service.UserService;
    import proxy.service.impl.UserServiceImpl;
    
    public class MainTest {
    
    	public static void main(String[] args) {
    		
    		//获取被代理对象
    		UserService us = new UserServiceImpl();
    		MyRepect myRepect = new MyRepect(us);
    		UserService userService = (UserService) myRepect.getUserService();
    		userService.eat();
    	}
    }
    
    

    运行结果:
    在这里插入图片描述

    总结

    1. 使用 java.lang.reflect.Proxy 需要代理对象有接口 不能直接代理实现类;
    2. 动态代理 代理类是在代码运行是生成的 没有对应的class文件;
    3. 它实现了 设计模式 的 装饰者模式

    最后如果大家有什么疑问需要讨论 可以给我留言 (`・ω・´)

    展开全文
  • java 动态代理代码

    2015-05-19 12:02:48
    java 动态代理代码 最近学了java 代理,感觉很神奇,尤其是动态代理,功能真是强大,如果有了动态代理,那么黑客是不是更加发展一步呢。。。 实现java动态代理,主要实现几个步骤 1.实现 InvocationHandler ...

    java 动态代理代码

    最近学了java 代理,感觉很神奇,尤其是动态代理,功能真是强大,如果有了动态代理,那么黑客是不是更加发展一步呢。。。
    实现java动态代理,主要实现几个步骤

    1.实现 InvocationHandler 接口,然后实现 public Object invoke(Object proxy, Method m, Object[] args) 方法,代码如下
    package com.chapter1.dynamicproxy;
    
    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 m, Object[] args)
    			throws Throwable {
    		// TODO Auto-generated method stub
    		System.out.println("before");
    		m.invoke(this.proxied, args);
    		return null;
    	}
    
    }
    

    2. 应用步骤
    package com.chapter1.dynamicproxy;
    
    import java.lang.reflect.Proxy;
    
    public class Test {
    
    	public static void main(String[] args) {
    
    		RealCoder rc = new RealCoder();
    		ProxyHandler ph = new ProxyHandler(rc);
    		Coder proxyc = (Coder)Proxy.newProxyInstance(rc.getClass().getClassLoader(),rc.getClass().getInterfaces(), ph);
    		proxyc.coding();
    	}
    
    }
    

    rc 是被代理的对象,ph是自己创建的对象,然后用 Proxy 创建代理实例再调用方法。

    展开全文
  • java动态代理演示代码

    2020-07-24 01:23:45
    * JDK动态代理 */ public class Proxy1{ public static void main(String[] args) { Rent host = new Host(); WatchHostProxy watchHost = new WatchHostProxy(host); ContractProxy contract = new ...
  • java静态代理和动态代理   java设计模式中,常说的代理模式使用的是静态代理模式。 代理模式:为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被...
  • Java动态代理代码编写

    2019-08-26 00:04:38
    Java动态代理代码编写 代理的概念  动态代理技术是整个java技术中最重要的一个技术,它是学习java框架的基础,不会动态代理技术,那么在学习Spring这些框架时是学不明白的。  动态代理技术就是用来产生一个对象...
  • 1. 静态代理模式: package proxy; /** * room工厂 * roomSize:房子的大小 * roomPrice:房子的价格 */ interface RoomFactory{ void roomSize(); void roomPrice(); } /** * 被代理类,人要买房子 */ class...
  • Java动态代理代码实现

    2020-08-07 17:38:18
    Java动态代理1、不使用代理的写法2、jdk动态代理3、CGLib动态代理 1、不使用代理的写法 package com.springbasic.proxy; ​ public class ServiceDemo { ​ public void service() { // 性能监控 System.out....
  • JAVA动态代理 - JDK -- 纯JAVA代码

    千次阅读 2016-10-04 22:40:11
    JAVA动态代理 - JDK -- 纯JAVA代码
  • JAVA动态代理代码示例

    2019-04-11 13:49:28
    概念理解 ...我们可以不通过直接与明星对话的情况下,而通过明星经纪人(代理)与其产生间接对话,JAVA中的代理分为两种。 静态代理类:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行...
  • java动态代理代码学习

    2013-05-09 10:10:57
    xy1/   引言 Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理...本文首先从 Java 动态代理的运行机制和特点出发,对其代码进行了分析,推演了动态生成类的内部实现。   回页首 代理:设计模式
  • 话不多说,看代码 静态代理 package Test; /* 静态代理 代理模式: 1.主题接口 2.被代理类 3.代理类 */ public class StaticProxy { public static void main(String[] args) { UserDao proxy...
  • java动态代理

    万次阅读 2020-12-14 17:59:02
    最近在看一些技术源码的时候,发现很多地方都是动态代理, 真可谓是一切皆代理啊,所以我们需要弄明白代理模式这样在看源码的时候会好受很多。 2、基本概念 代理(Proxy)模式提供了间接访问目标对象的方式,即通过...
  • 生成动态代理的通用java代码。这段代码是一个类似Spring的可配置AOP框架中的一个类,其中Advice1是接口,包含before和after两个方法。 package pack.aop; import java.lang.reflect.InvocationHandler; import ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,203
精华内容 1,681
关键字:

动态代理java代码

java 订阅