JDK动态代理

2019-03-07  本文已影响0人  MakeItSimple

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

概要

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

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

什么是动态代理

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

如何使用JDK动态代理

JDK为我们提供了一套自动生成代理Class的机制,以此来实现动态代理。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接口即可。下面来说说这个套路。

自动生成的动态代理类$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自带的代理机制。

上一篇下一篇

猜你喜欢

热点阅读