美文网首页
Java基础->动态代理是什么东东

Java基础->动态代理是什么东东

作者: 杨0612 | 来源:发表于2020-11-02 16:09 被阅读0次
    什么是动态代呢

    在运行期动态生成代理对象,控制被代理对象的访问,这有别于静态代理在编译期生成代理对象。

    作用是什么

    代理对象可以丰富被代理对象的操作,大白话,就是可以在访问前后增加若干代码处理,例如在方法前后打印日志判断方法执行时间。

    动态代理例子1 - 生成代理对象

    Proxy.newProxyInstance 是JDK提供生成动态代理对象的api。通过newProxyInstance动态生成Api的实现类,调用setName以及getName方法,都会回调到InvocationHandler的invoke方法,invoke就是对接口方法的具体实现。

    //Api接口定义两个方法
    public interface Api {
        void setName(String name);
        String getName();
    }
      //调用
        public void test() {
            Api api = (Api) Proxy.newProxyInstance(Api.class.getClassLoader(), new Class[]{Api.class}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Log.d("test", "method name:" + method.getName());//打印调用的方法名
                    if ("getName".equals(method.getName())) {//如果是调用getName则返回"yang"
                        return "yang";
                    } else if ("setName".equals(method.getName())) {//如果是调用setName方法则打印请求参数
                        for (Object a : args) {//打印参数
                            Log.d("test", "args:" + a);
                        }
                    }
                    return null;//对应无返回值的方法,直接返回null即可。
                }
            });
            
            //调用处
            api.setName("yang");
            String name=api.getName();
            Log.d("test", "getName=" +name );
        }
    

    打印结果如下:

    2020-09-23 10:40:47.150 20218-20218/com.yang.myapplication6 D/test: method name:setName
    2020-09-23 10:40:47.150 20218-20218/com.yang.myapplication6 D/test: args:yang
    2020-09-23 10:40:47.150 20218-20218/com.yang.myapplication6 D/test: method name:getName
    2020-09-23 10:40:47.150 20218-20218/com.yang.myapplication6 D/test: getName=yang
    
    动态代理例子2 - 增强被代理对象的能力

    MyApi 是Api接口的具体实现,同样通过Proxy.newProxyInstance生成代理对象,不同的是,在invoke方法中,增加代码处理以后需要反射调用MyApi 实现的方法。

    public class MyApi implements Api {
        @Override
        public void setName(String name) {
            Log.d("test", "setName name=" + name);
        }
        @Override
        public String getName() {
            return "getName";
        }
    }
    
         final Api api = new MyApi();//被代理对象
            //返回代理对象
            Api proxyApi = (Api) Proxy.newProxyInstance(api.getClass().getClassLoader(), api.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Log.d("test", "invoke");
                    return method.invoke(api, args);//调用被代理对象方法
                }
            });
    
            //调用代理对象
            proxyApi.setName("yang");
            String name=proxyApi.getName();
            Log.d("test", "getName=" + name);
    
    Proxy.newProxyInstance 接口

    是JDK提供生成动态代理对象的api

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

    参数解析:

    • ClassLoader :类加载器,传入被代理对象的类加载器即可,例子2中 被代理对象为Api,所以传入api.getClass().getClassLoader();
    • interfaces,代理对象需要实现接口,例子2中,代理对象需要实现Api接口,所以传入api.getClass().getInterfaces(),也可以传入new Class[]{Api.class};
    • InvocationHandler,拦截器,当调用方法时,会回调到InvocationHandler的invoke方法,这里可以丰富访问;
    InvocationHandler.invoke 回调
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{}
    

    参数解析:

    • proxy:动态代理对象,例子2中代理对象就是proxyApi ,如果在invoke内部调用了method.invoke(proxy,args),将会出现死循环,最后抛出异常;
    • method:被调用方法的Method对象,调用setName,则Method就是setName方法的封装;
    • args:被调用方法的参数,调用setName,则args就是new Object[]{name};
    代理对象生成在哪呢

    代理对象是在运行时生成的,我也没找到他具体生成的地方,以下代码是查看网上的博客得到的

    public final class $Proxy0 extends Proxy implements Api { //1
    
        static {//2
            try {
                m1 = Class.forName("com.yang.myapplication6.Api").getMethod("setName");
                m2 = Class.forName("com.yang.myapplication6.Api").getMethod("getName");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    
        private static Method m1;
        private static Method m2;
    
        public $Proxy0(InvocationHandler var1) throws  {//3
            super(var1);
        }
    
        public final void setName(String name)  {//4
            try {
                super.h.invoke(this, m1, new Object[]{name});
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        @Override
        public String getName() {
            try {
                return (String) super.h.invoke(this, m2, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        } 
    }
    
    • 注释1:$Proxy0是代理对象的类型,它继承Proxy以及实现Api接口;
    • 注释2:静态代码块中,反射获取Api接口定义的方法Method;
    • 注释3:构建对象,需要传入InvocationHandler对象;
    • 注释4:setName方法的具体实现,会调用super.h.invoke(this, m1, new Object[]{name}),这个h就是在生成动态代理对象传入的InvocationHandler,所以当在外部通过代理对象调用相应方法时候,会回调到InvocationHandler.invoke;
    类图
    1.png
    动态代理使用场景

    动态代理,适用于对所有代理方法做统一处理的场景;
    动态代理可以没有实现,实现类由虚拟机生成;

    总结
    • 动态代理可以丰富被代理对象的行为;
    • 动态代理对象是动态生成的;

    以上分析有不对的地方,请指出,互相学习,谢谢哦!

    相关文章

      网友评论

          本文标题:Java基础->动态代理是什么东东

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