美文网首页
JDK动态代理

JDK动态代理

作者: MakeItSimple | 来源:发表于2019-03-07 21:19 被阅读0次

    在Java中,动态代理使用的频率是很高的,比如Spring的AOP实现中就使用到了动态代理。作为一名有追求的码农,把这些基础搞清楚也是应该的。动态代理的实现方式有多种,各有特点,从JDK自带的动态代理,到CGLib、Javassist等。通常,动态代理用来给已有的接口实现增加通用的强化逻辑。

    概要

    本文主要涉及JDK动态代理的相关话题,包含以下内容:

    1. 什么是JDK动态代理?
    2. 如何使用JDK动态代理?
    3. 动态代理是如何实现的?
    4. JDK动态代理的使用场景和优缺点是什么?

    什么是动态代理

    所谓动态代理是相对于静态代理而言的。静态代理需要从代码层面为每种需要代理的类编写静态代理类,使用起来很不灵活。如果代理的类方法很多,会让你崩溃。而动态代理则更加灵活,它会在代码运行时或者编译时自动封装被代理类,大大降低编码量。

    如何使用JDK动态代理

    JDK为我们提供了一套自动生成代理Class的机制,以此来实现动态代理。JDK首先定义了接口InvocationHandler,这个接口有两个重要的作用。

    • 该接口唯一的方法提供了强化接口功能的入口。方法定义如下:
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
      在此方法中,强化功能的逻辑被定义。比如:在某个方法调用前统一打印日志等。一旦调用该方法,被代理对象的功能和强化功能都会被实现。实现该接口的实现类中,在创建对象时,是需要传入原始的被代理对象的,以便在invoke方法中触发被代理对象的实现逻辑。
    • 为JDK自动生成的代理类提供统一的调用。也就是说,自动生成的代理类中,是调用这个接口的方法真正触发了接口本身功能及其强化功能。实际该接口隔离了动态代理类对被代理对象的耦合,这样和动态代理类耦合的仅仅是被代理的接口(可能是多个接口)和功能强化接口InvocationHandler。

    实例

    1. 定义两个服务接口。
    public interface UserService {
        UserInfo getUserById(int id);
        String getUserNameById(int id);
    }
    public interface SchoolService {
        String getSchoolName();
    }
    
    1. 服务接口的实现类,注意这里实现了上面两个接口。
    public class UserServiceImpl implements UserService, SchoolService {
        // 用户信息列表
        static final List<UserInfo> USERS = new ArrayList<UserInfo>();
    
        static {
            // 模拟三个用户
            USERS.add(createUser(1, 30, "user1", "male"));
            USERS.add(createUser(2, 28, "user2", "female"));
            USERS.add(createUser(3, 20, "user3", "male"));
        }
    
        static private UserInfo createUser(int id, int age, String name, String sex) {
            UserInfo info = new UserInfo();
            info.setId(id);
            info.setAge(age);
            info.setName(name);
            info.setSex(sex);
            return info;
        }
    
        public UserInfo getUserById(int id) {
            for (UserInfo info : USERS) {
                if (info.getId() == id) {
                    System.out.println(info.toString());
                    return info;
                }
            }
    
            return null;
        }
    
        public String getUserNameById(int id) {
            UserInfo userInfo = getUserById(id);
            if (userInfo != null) {
                return userInfo.getName();
            }
            return "User id is invalid!";
        }
    
        public String getSchoolName() {
            System.out.println("QingHua University");
            return "QingHua University";
        }
    }
    
    1. 功能强化类
    /**
     * 使用JDK的动态代理
     */
    public class UserServiceJdkProxy implements InvocationHandler {
    
        // 被代理对象,这里就是UserServiceImpl对象。
        private Object target;
    
        // 通过构造函数传入被代理对象
        public UserServiceJdkProxy(Object target) {
            this.target = target;
        }
    
        // proxy是自动生成的代理类对象;
        // method是代理的接口方法,调用哪个就传入哪个;由于这里每个接口方法都会被传入,所以如果只针对某个方法做强化,就需要根据方法名区分。比如,本例中只对方法getUserById做强化,在其调用前做before(),调用后做after()。
        // args是方法参数
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = null;
            if ("getUserById".equals(method.getName())) {
                before();//功能强化
                result = method.invoke(target, args);
                after();//功能强化
            } else {
                result = method.invoke(target, args);
            }
            return result;
        }
    
        // 方法调用前的强化逻辑
        private void before() {
            System.out.println("before");
        }
    
        // 方法调用后的强化逻辑
        private void after() {
            System.out.println("after");
        }
    }
    
    1. 客户端代码
    public class MainApp {
        public static void main(String[] args) {
            UserService userService = new UserServiceImpl();
            UserService userServiceProxy = (UserService) Proxy.newProxyInstance(
                    UserService.class.getClassLoader(),
                    userService.getClass().getInterfaces(),
                    new UserServiceJdkProxy(userService));
            userServiceProxy.getUserById(1);
    
            SchoolService schoolService = (SchoolService) userServiceProxy;
            schoolService.getSchoolName();
        }
    }
    

    这里,Proxy.newProxyInstance方法是关键,该方法中会动态生成代理类$Proxy0,并实例化。userServiceProxy实际就是这个动态代理类对象。代理类同时实现了被代理对象实现的两个接口,所以可以强制转换成UserService和SchoolService。类似如下的定义:

    public class $Proxy0 implements UserService, SchoolService {
        ...略...
    }
    

    看来,如果要想知道动态代理的实现机制,研究Proxy类是关键。

    动态代理是如何实现的

    JDK实际是定义了一套动态代理的套路给我们,有了这个套路,我们就只需要实现InvocationHandler接口即可。下面来说说这个套路。

    • 首先我们实现接口InvocationHandler,其中定义了我们的强化逻辑;
    • 然后,根据ClassLoader、被代理类实现的接口数组(可能是多个)以及实现了接口InvocationHandler的对象来生成统一的代理类$Proxy0。这正是Proxy.newProxyInstance方法的三个参数。
    • 最终返回给客户端的就是JDK自动生成的动态类对象,它代理了原始实现类,加上了强化逻辑。

    自动生成的动态代理类$Proxy0源码如下:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package com.sun.proxy;
    
    import com.ws.springframework.aop.model.UserInfo;
    import com.ws.springframework.aop.service.SchoolService;
    import com.ws.springframework.aop.service.UserService;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class $Proxy0 extends Proxy implements UserService, SchoolService {
        private static Method m1;
        private static Method m2;
        private static Method m4;
        private static Method m5;
        private static Method m3;
        private static Method m0;
    
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
        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);
            }
        }
    
        public final String toString() throws  {
            try {
                return (String)super.h.invoke(this, m2, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final String getUserNameById(int var1) throws  {
            try {
                return (String)super.h.invoke(this, m4, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final String getSchoolName() throws  {
            try {
                return (String)super.h.invoke(this, m5, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final UserInfo getUserById(int var1) throws  {
            try {
                return (UserInfo)super.h.invoke(this, m3, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final int hashCode() throws  {
            try {
                return (Integer)super.h.invoke(this, m0, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m4 = Class.forName("com.ws.springframework.aop.service.UserService").getMethod("getUserNameById", Integer.TYPE);
                m5 = Class.forName("com.ws.springframework.aop.service.SchoolService").getMethod("getSchoolName");
                m3 = Class.forName("com.ws.springframework.aop.service.UserService").getMethod("getUserById", Integer.TYPE);
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    

    这里有几点需要说明下:

    1. 除了被代理对象的接口方法之外,还有Object类下的三个函数也可能同时被强化。(这里如果没有“getUserById”的限制,那么所有的方法都会被强化)
    2. 代理类的基本逻辑就是遍历被代理对象所实现接口的所有method(外加Object的三个方法),自动生成代理类的Method。这些Method中统一调用InvocationHandler接口的方法,以触发强化后的接口逻辑。
    3. 从代理类的定义可以看出来,因为Java不支持多重继承,所以只能代理实现接口的实现类。
    4. Proxy中封装了生成代理class的逻辑。具体在ProxyClassFactory.apply方法中,最终的class文件是在ProxyGenerator.generateProxyClass中生成,有兴趣的可以去研究下。提醒下,ProxyClassFactory实现了BiFunction,其基本语意是输入ClassLoader、Class数组(接口数组),生成Class文件。

    JDK动态代理的使用场景和优缺点

    如果被代理类实现了接口,我们就使用JDK自带的代理机制。

    • 优点:不再依赖其他的类库,比如:CGlib等。据说,随着JDK版本的升级,JDK动态代理的效率越来越好,甚至高于CGlib的方式。
    • 缺点:只能代理实现了接口的类。

    相关文章

      网友评论

          本文标题:JDK动态代理

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