美文网首页技术干货Java学习笔记
Java静态代理&动态代理笔记

Java静态代理&动态代理笔记

作者: BrightLoong | 来源:发表于2017-03-17 15:40 被阅读0次
    java代理
    阅读原文请访问我的博客BrightLoong's Blog

    最近在学习Java反射的一些知识,看到了一些有关代理的例子,好记性不如烂笔头,所以这里将它记录下来。接下来话不多说,直接进入主题。

    代理:为其他对象提供一个代理以控制对某个对象的访问。

    静态代理

    • 接口
    public interface IDoSomething {
        public int doSometing(int num);
    }
    
    • 被代理类的实现
    public class Sing implements IDoSomething {
    
        @Override
        public int doSometing(int num) {
            System.out.println("Sing a song");
            return num;
        }
    }
    
    • 代理类的实现
     public class SingProxy implements IDoSomething{
        
        private IDoSomething sing = new Sing();
    
        @Override
        public int doSometing(int num) {
            System.out.println("Befor singing ");
            int result = sing.doSometing(num);
            System.out.println("After singing");
            return result;
        }
    
    }
    
    • 测试类
    public class ProxyDemo {
         public static int sing(IDoSomething sing, int num) {
             return sing.doSometing(num);
         }
         public static void main(String[] args) {
             System.out.println(ProxyDemo.sing(new SingProxy(), 5));
         }
     }
    
    • 输出结果
    Befor singing 
    Sing a song
    After singing
    5
    

    以上就是简单的静态代理,不在过多的介绍,下面是动态代理。

    动态代理

    Java的动态代理可以动态的创建代理并动态的处理对所代理方法的调用。动态代理有两种实现方法,一种是使用JDK自带的,一种是使用Cglib实现。

    实现JDK自带的动态代理

    实现JDK自带的动态代理,关键是实现InvocationHandler,同时它要求被代理对象必须有接口。下面是实现的代码,我加上了必要的注释。

    • 接口
    public interface IProxyClass {
        public int doSomething(int i);
    }
    
    • 被代理类的实现
    public class ProxyClassImpl implements IProxyClass {
        @Override
        public int doSomething(int num) {
            System.out.println("方法执行中.....");
            return num;
        }
    }
    
    • 实现InvocationHandler接口
        这里我实现了InvocationHandler接口,并手动生成了代理类,保存到了电脑F盘上
    public class DynamicProxyHandler implements InvocationHandler {
        
        private Object proxied;
        
        /**
         * @param proxied 被代理对象
         */
        public DynamicProxyHandler(Object proxied) {
            this.proxied = proxied;
        }
        
        /**
         * 返回代理对象
         * @return
         */
        public Object newProxyInstance() {
            return Proxy.newProxyInstance(proxied.getClass().getClassLoader(), proxied.getClass().getInterfaces(), this);
        }
    
        /**
         * 
         * @param proxy 代理对象
         * @param method 代理方法
         * @param args 方法参数
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
            //将代理对象生成字节码到F盘上,方便反编译出java文件查看,实际动态代理是不需要自己生成的
            addClassToDisk(proxy.getClass().getName(), ProxyClassImpl.class,"F:/$Proxy0.class");
            System.out.println("method:"+method.getName());  
            System.out.println("args:"+args[0].getClass().getName());  
            System.out.println("Before invoke method...");  
            Object object=method.invoke(proxied, args);
            System.out.println("After invoke method...");  
            return object;  
        }
        
        /**
         * 用于生产代理对象的字节码,并将其保存到硬盘上
         * @param className
         * @param cl
         * @param path
         */
        private void addClassToDisk(String className, Class<?> cl, String path) {
            //用于生产代理对象的字节码
            byte[] classFile = ProxyGenerator.generateProxyClass(className, cl.getInterfaces());
            FileOutputStream out = null;  
            try {  
                out = new FileOutputStream(path);  
                //将代理对象的class字节码写到硬盘上
                out.write(classFile);  
                out.flush();  
            } catch (Exception e) {  
                e.printStackTrace();  
            } finally {  
                try {  
                    out.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }
        
    }
    
    • 测试类
    public class SimpleProxyDemo {
          public static void main(String[] args) throws SecurityException, NoSuchMethodException {
              ProxyClassImpl c = new ProxyClassImpl();
              DynamicProxyHandler proxyHandler = new DynamicProxyHandler(c);
              IProxyClass proxyClass = (IProxyClass)proxyHandler.newProxyInstance();
              System.out.println(proxyClass.getClass().getName());
              System.out.println(proxyClass.doSomething(5));
          }
    }
    
    • 输出结果
    com.sun.proxy.$Proxy0
    method:doSomething
    args:java.lang.Integer
    Before invoke method...
    方法执行中.....
    After invoke method...
    5
    

    从结果我们可以看到(IProxyClass)proxyHandler.newProxyInstance();实际返回的是com.sun.proxy.$Proxy0,我们把生成的$Proxy0.class文件,使用jad.exe进行反编译,使用命令(要求文件和jad.exe在同一个目录下,或者你可以吧jad加到环境变量中去):

    jad -p java $Proxy0.class

    得到的$Proxy0.java如下:

    public final class $Proxy0 extends Proxy
        implements IProxyClass
    {
    
        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 void doSomething(int i)
        {
            try
            {
                super.h.invoke(this, m3, new Object[] {
                    Integer.valueOf(i)
                });
                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);
            }
        }
    
        public final String toString()
        {
            try
            {
                return (String)super.h.invoke(this, m2, null);
            }
            catch(Error _ex) { }
            catch(Throwable throwable)
            {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        private static Method m1;
        private static Method m3;
        private static Method m0;
        private static Method m2;
    
        static 
        {
            try
            {
                m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
                    Class.forName("java.lang.Object")
                });
                m3 = Class.forName("io.github.brightloong.proxy.IProxyClass").getMethod("doSomething", new Class[] {
                    Integer.TYPE
                });
                m0 = Class.forName("java.lang.Object").getMethod("hashCode", 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());
            }
        }
    }
    

    可以看到实际调用的是25行的doSometing()方法。如果你想了解更加具体的JDK动态代理的实现原理可以访问Rejoy的博文JDK动态代理实现原理

    使用Cglib实现动态代理

    Cglib不是jdk自带的jar包,需要下载并加入到项目中。个人觉得Cglib比使用jdk自带的实现动态代理更为先进,毕竟它不再需要接口,而且它还有其他强大的功能,大家可以自行研究。

    • 实现MethodInterceptor接口
    public class CglibProxy implements MethodInterceptor{
        private Enhancer enhancer = new Enhancer();
        @Override
        /**
         * 
         * @param o 是被代理对象
         * @param method 调用方法的Method对象
         * @param args 方法参数
         * @param methodProxy
         * @return cglib生成用来代替Method对象的一个对象,使用MethodProxy比调用JDK自身的Method直接执行方法效率会有提升
         * @throws Throwable
         */
        public Object intercept(Object o, Method method, Object[] args,
                MethodProxy methodProxy) throws Throwable {
            System.out.println("before " + methodProxy.getSuperName());  
            System.out.println(method.getName());  
            Object o1 = methodProxy.invokeSuper(o, args);  
            //Object o2 = method.invoke(o, args); 使用这种方式会发生死循环,因为方法会被拦截
            System.out.println("after " + methodProxy.getSuperName());  
            return o1;  
        }
        
        public  Object newProxyInstance(Class<?> c) {
            //设置产生的代理对象的父类。
            enhancer.setSuperclass(c); 
            //设置CallBack接口的实例
            enhancer.setCallback(this);  
            //使用默认无参数的构造函数创建目标对象 
            return enhancer.create();  
        }
    }
    
    • 被代理对象和测试类
    public class CglibDemo {
        public static void main(String[] args) {
            CglibProxy cglibProxy = new CglibProxy();  
            Do o = (Do)cglibProxy.newProxyInstance(Do.class);  
            System.out.println(o.doSomething(5));
        }
    }
    class Do{
        public int doSomething(int num){
            System.out.println("方法执行中。。。。。。");
            return num;
        }
    }
    
    • 输出结果
    before CGLIB$doSomething$0
    doSomething
    方法执行中。。。。。。
    after CGLIB$doSomething$0
    5
    

    本篇笔记参考于:

    http://www.cnblogs.com/shijiaqi1066/p/3429691.html

    http://rejoy.iteye.com/blog/1627405

    相关文章

      网友评论

        本文标题:Java静态代理&动态代理笔记

        本文链接:https://www.haomeiwen.com/subject/ssbenttx.html