概念
代理:为控制A对象,而创建出新B对象,由B对象代替执行A对象所有操作,称之为代理。一个代理体系建立涉及到3个参与角色:真实对象(A),代理对象(B),客户端。
其中的代理对象(B)起到中介作用,连通真实对象(A)与客户端,如果进一步拓展,代理对象可以实现更加复杂逻辑,比如对真实对象进行访问控制。
案例
需求:员工业务层接口调用save需要admin权限,调用list不需要权限,没权限调用时抛出异常提示。
静态代理
/**
 * 代理接口
 */
public interface IEmployeeService {
    void save();
 
    void list();
}/**
 * 真实对象
 */
public class EmployeeServiceImpl implements IEmployeeService {
    @Override
    public void save() {
        System.out.println("EmployeeServiceImpl-正常的save....");
    }
    @Override
    public void list() {
        System.out.println("EmployeeServiceImpl-正常的list....");
    }
}/**
 * 模拟当前登录用户对象
 */
public class SessionHolder {
    private static String currentUser;
    public static String  getCurrentUser(){
        return currentUser;
    }
    public static void   setCurrentUser(String currentUser){
        SessionHolder.currentUser = currentUser;
    }
}/**
 * 代理对象
 */
public class EmployeeProxy implements IEmployeeService {
    //真实对象
    private EmployeeServiceImpl employeeService;
    public EmployeeProxy(EmployeeServiceImpl employeeService){
        this.employeeService = employeeService;
    }
    @Override
    public void save() {
        //权限判断
        if("admin".equals(SessionHolder.getCurrentUser())){
            employeeService.save();
        }else{
            throw new RuntimeException("当前非admin用户,不能执行save操作");
        }
    }
    @Override
    public void list() {
        employeeService.list();
    }
}public class App {
    public static void main(String[] args) {
        System.out.println("----------------真实对象--------------------");
        EmployeeServiceImpl employeeService = new EmployeeServiceImpl();
        employeeService.list();
        employeeService.save();
        System.out.println("----------------代理对象--------------------");
        SessionHolder.setCurrentUser("dafei");  //设置权限(当前登录用户)
        EmployeeProxy employeeProxy = new EmployeeProxy(employeeService);
        employeeProxy.list();
        employeeProxy.save();
    }
}----------------真实对象-------------------- EmployeeServiceImpl-正常的list.... EmployeeServiceImpl-正常的save.... ----------------代理对象-------------------- EmployeeServiceImpl-正常的list.... Exception in thread "main" java.lang.RuntimeException: 当前非admin用户,不能执行save操作 at com.langfeiyes.pattern.proxy.demo.EmployeeProxy.save(EmployeeProxy.java:20) at com.langfeiyes.pattern.proxy.demo.App.main(App.java:16)
使用真实对象EmployeeServiceImpl 直接调用时,不管是list 还是save都能直接访问,但不符合需求上的admin权限限制。如果使用代理对象EmployeeProxy,可以完成需求实现。
通过直接创建新类新类代理对象方式完成代理逻辑,这种方式称之为静态代理模式。
JDK动态代理模式
Java常用的动态代理模式有JDK动态代理,也有cglib动态代理,此处重点讲解JDK的动态代理
还是原来的需求,前面的IEmployeeService EmployeeServiceImpl SessionHolder 都没变,新加一个JDK代理控制器-EmployeeInvocationHandler
/**
 * jdk动态代理控制类,由它牵头代理类获取,代理方法的执行
 */
public class EmployeeInvocationHandler  implements InvocationHandler {
    //真实对象-EmployeeServiceImpl
    private Object target;
    public EmployeeInvocationHandler(Object target){
        this.target = target;
    }
    //获取jvm在内存中生成代理对象
    public Object getProxy(){
        return  Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }
    //代理对象控制执行方法
    //参数1:代理对象
    //参数2:真实对象的方法(使用方式得到方法对象)
    //参数3:真实对象方法参数列表
    //此处是代理对象对外暴露的可编辑的方法处理场所,代理对象每调用一个次方法,就会执行一次invoke
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String name = method.getName();
        if("save".equals(name) && !"admin".equals(SessionHolder.getCurrentUser())){
            throw new RuntimeException("当前非admin用户,不能执行save操作");
        }
        return method.invoke(target, args);
    }
}测试App类稍微改动下:
public class App {
    public static void main(String[] args) {
        System.out.println("----------------真实对象--------------------");
        EmployeeServiceImpl employeeService = new EmployeeServiceImpl();
        employeeService.list();
        employeeService.save();
 
        System.out.println("----------------代理对象--------------------");
        SessionHolder.setCurrentUser("dafei");
        EmployeeInvocationHandler handler = 
            new EmployeeInvocationHandler(employeeService);
        IEmployeeService proxy = (IEmployeeService) handler.getProxy();
        proxy.list();
        proxy.save();
 
    }
}上面代码一样可以实现需求,跟静态代理区别就在于少创建了代理对象。此时存在疑问点,没有创建代理对象,为啥可以实现代理类调用呢??
原理分析
先抛出结论JDK动态代理底层实现原理:使用接口实现方式,运行时,在内存中动态构建出一个类,然后编译,执行。这个类是一次性的,JVM停止,代理类就消失。
参与角色 要理解JDK动态代理原理,首先得了解JDK动态代理涉及到的类

InvocationHandler:真实对象方法调用处理器,内置invoke方法,其功能:为真实对象定制代理逻辑
EmployeeInvocationHandler:员工服务真实对象方法调用处理器,此类有3个用途: 1>设置真实对象
     //真实对象-EmployeeServiceImpl
    private Object target;
    public EmployeeInvocationHandler(Object target){
        this.target = target;
    }2>定制代理方法实现逻辑
为真实对象save方法添加了权限校验逻辑
    //代理对象控制执行方法
    //参数1:代理对象
    //参数2:真实对象的方法(使用方式得到方法对象)
    //参数3:真实对象方法参数列表
    //此处是代理对象对外暴露的可编辑的方法处理场所,代理对象每调用一个次方法,就会执行一次invoke
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String name = method.getName();
        if("save".equals(name) && !"admin".equals(SessionHolder.getCurrentUser())){
            throw new RuntimeException("当前非admin用户,不能执行save操作");
        }
        return method.invoke(target, args);
    }3>返回代理对象
方法执行完之后,返回一个名为:$ProxyX的代理类(其中的X是序号,一般默认为0),这代理类由JDK动态构建出来。
    //获取jvm在内存中生成代理对象
    public Object getProxy(){
        return  Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }Proxy:动态代理控制类,是JDK动态生成的$ProxyX类的父类,它作用如下:
1>通过调用ProxyBuilder 类builder方法构建代理对象类
private static Constructor<?> getProxyConstructor(Class<?> caller,
                                                      ClassLoader loader,
                                                      Class<?>... interfaces){
            return proxyCache.sub(intf).computeIfAbsent(
                loader,
                (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
            );
}
2>通过newProxyInstance方法返回$ProxyX类的实例
   public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) {
    //...
   }$Proxy0:App类运行时,JDK动态构建出来的代理类,继承至Proxy类
public class App {
    public static void main(String[] args) {
        //System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
        System.out.println("----------------真实对象--------------------");
        EmployeeServiceImpl employeeService = new EmployeeServiceImpl();
        employeeService.list();
        employeeService.save();
        System.out.println("----------------代理对象--------------------");
        SessionHolder.setCurrentUser("dafei");
        EmployeeInvocationHandler handler = 
                     new EmployeeInvocationHandler(employeeService);
        IEmployeeService proxy = (IEmployeeService) handler.getProxy();
        proxy.list();
        proxy.save();
 
    }
}默认情况下JVM是不保存动态创建代理类字节码对象的,可以在main方法中配置代理参数让字节码保留
//JDK8之前
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//JDK8之后
System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");执行完之后,会在项目根目录生成代理类字节码对象。

为了方便解读,将一些不需要的方法剔除之后
$Proxy0类
public class $Proxy0 extends Proxy implements IEmployeeService {
    private static Method m4;
    private static Method m3;
    static {
        try {
            m4 = Class.forName("com.langfeiyes.proxy.demo.IEmployeeService")
                 .getMethod("save");
            m3 = Class.forName("com.langfeiyes.proxy.demo.IEmployeeService")
                 .getMethod("list");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public $Proxy0(InvocationHandler var1) throws Throwable {
        super(var1);
    }
    public final void save() throws Throwable {
        super.h.invoke(this, m4, (Object[])null);
    }
 
    public final void list() throws  Throwable{
        super.h.invoke(this, m3, (Object[])null);
    }
}从源码上看,$Proxy0的特点:
- 1>继承了Proxy类,实现了IEmployeeService 接口
- 2>通过静态块的方式反射IEmployeeService接口save与list方法,得到他们的方法对象Method
- 3>调用父类构造器,需要传入InvocationHandler 参数
- 4>重写IEmployeeService接口的save list方法靠的是父类Proxy的h属性.invoke方法
真相大白
下图所有参与动态代理的类:

下图是上图的操作时序图,跟着走就对了

到这,JDK动态代理就ok了。