好处

  1. 安全原因,屏蔽客户端直接访问对象

  2. 程调用中,需要使用代理类处理远程方法调用的技术细节【 rmi

  3. 提供系统性能,对真实对象进行封装,达到 延时加载 的目的。

注意点

  1. 屏蔽外部对真实对象的访问

  2. 代理类可以通过字节码自动加载的方式,运行时动态生成代理类(反射)

  3. 真实对象的延迟加载

技术实现

  • 静态代理

  • 动态代理 【使用字节码动态生成加载技术,在运行时生成并加载类】

    • jdk

    • cglib [高级字节码生成工具库]

    • javassist [高级字节码生成工具库]

    • asm [低级字节码生成工具库] 性能最好,但不常用

实践

实践都是采用接口的方式。
先定义一个接口:

  1. package xin.rtime.design.proxy.service;
  2. //查询接口
  3. public interface IQuery {
  4. String select();
  5. }

接口具体的实现:

  1. package xin.rtime.design.proxy.service;
  2. //实际发生者 查询的实现类
  3. public class IQueryImpl implements IQuery {
  4. @Override
  5. public String select() {
  6. return "select data.";
  7. }
  8. }

静态代理

  1. 创建一个代理对象 IQueryProxy , 实现上面定义的接口。

  2. 在类中定义实际代理的真实对象。

  3. 在外部调用的方法中,实现 懒加载 ,并调用实际真实的方法返回结果。

  1. package xin.rtime.design.proxy;
  2. import xin.rtime.design.proxy.service.IQuery;
  3. import xin.rtime.design.proxy.service.IQueryImpl;
  4. // 简单的代理 [静态代理]
  5. public class Proxy {
  6. public static void main(String[] args) {
  7. IQuery queryProxy = new IQueryProxy();
  8. System.out.println(queryProxy.select());
  9. }
  10. }
  11. // 代理
  12. class IQueryProxy implements IQuery {
  13. private IQuery query = null; // 实际发生者的实例
  14. @Override
  15. public String select() {
  16. if (query == null) // 懒加载
  17. query = new IQueryImpl();
  18. return query.select();
  19. }
  20. }

动态代理

Jdk

package xin.rtime.design.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import xin.rtime.design.proxy.service.IQuery;
import xin.rtime.design.proxy.service.IQueryImpl;

// Jdk原生代理
public class JdkProxy {

    public static IQuery createJdkProxy() {

        IQuery jdkProxy = (IQuery) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
                new Class[] { IQuery.class }, new JdkQueryHandler());

        return jdkProxy;
    }

    public static void main(String[] args) {

        IQuery queryProxy = createJdkProxy();
        System.out.println(queryProxy.select());

    }
}

// 调用方法的handler
class JdkQueryHandler implements InvocationHandler {
    private IQuery query = null; // 实际发生者的实例

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (query == null) // 懒加载
            query = new IQueryImpl();

        return query.select();
    }

}

Cglib

引入依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.5</version>
</dependency>

Code

package xin.rtime.design.proxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import xin.rtime.design.proxy.service.IQuery;
import xin.rtime.design.proxy.service.IQueryImpl;

// cglib实现动态代理
public class CglibProxy {

    public static IQuery createCglibProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(new CglibQueryInterceptor());

        enhancer.setInterfaces(new Class[] {IQuery.class});
        IQuery cglibProxy = (IQuery) enhancer.create();

        return cglibProxy;
    }

    public static void main(String[] args) {

        IQuery queryProxy = createCglibProxy();
        System.out.println(queryProxy.select());
    }
}

class CglibQueryInterceptor implements MethodInterceptor {
    private IQuery query = null; // 实际发生者的实例

    @Override
    public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
        if (query == null) // 懒加载
            query = new IQueryImpl();

        return query.select();
    }
}

Javassist

引入依赖

<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.22.0-GA</version>
</dependency>

Code

package xin.rtime.design.proxy;

import java.lang.reflect.Method;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtNewConstructor;
import javassist.CtNewMethod;
import javassist.NotFoundException;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import javassist.util.proxy.ProxyObject;
import xin.rtime.design.proxy.service.IQuery;
import xin.rtime.design.proxy.service.IQueryImpl;

// jboss - javassist 动态代理实现
public class JavassistProxy {

    // 接口的方式
    public static IQuery createJavassistDynProxy() throws InstantiationException, IllegalAccessException {

        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setInterfaces(new Class[] { IQuery.class });
        Class<?> proxyClass = proxyFactory.createClass();
        IQuery javassistProxy = (IQuery) proxyClass.newInstance();

        ((ProxyObject) javassistProxy).setHandler(new JavassistQueryHandler());

        return javassistProxy;

    }

    // 通过字节码的方式
    public static IQuery createJavassistBytecodeDynProxy() throws NotFoundException, CannotCompileException, InstantiationException, IllegalAccessException {

        ClassPool pool = new ClassPool(true);

        CtClass makeClass = pool.makeClass(IQuery.class.getName() + "Javassist-BytecodeProxy");
        // 实现接口
        makeClass.addInterface(pool.get(IQuery.class.getName()));
        makeClass.addConstructor(CtNewConstructor.defaultConstructor(makeClass));
        makeClass.addField(CtField.make("public " + IQuery.class.getName() + " real;", makeClass));
        String queryName = IQueryImpl.class.getName();
        // 添加方法
        makeClass.addMethod(CtNewMethod.make(
                "public String select(){if(real ==null)real=new " + queryName + "();return real.select();}",
                makeClass));
        //基于以上信息生成动态代理类
        Class<?> pc = makeClass.toClass();
        IQuery javassistBytecodeProxy = (IQuery) pc.newInstance();

        return javassistBytecodeProxy;

    }

    public static void main(String[] args) throws InstantiationException, IllegalAccessException, NotFoundException, CannotCompileException {

        // IQuery queryProxy = createJavassistDynProxy();
        IQuery queryProxy = createJavassistBytecodeDynProxy();
        System.out.println(queryProxy.select());
    }

}

class JavassistQueryHandler implements MethodHandler {

    private IQuery query = null; // 实际发生者的实例

    @Override
    public Object invoke(Object arg0, Method arg1, Method arg2, Object[] arg3) throws Throwable {
        if (query == null) // 懒加载
            query = new IQueryImpl();

        return query.select();
    }

}

验证

package xin.rtime.design;

import javassist.CannotCompileException;
import javassist.NotFoundException;
import xin.rtime.design.proxy.CglibProxy;
import xin.rtime.design.proxy.JavassistProxy;
import xin.rtime.design.proxy.JdkProxy;
import xin.rtime.design.proxy.service.IQuery;

//代理测试类
public class ProxyTest {

    public static final int CIRCLE = 3000000;

    public static void main(String[] args)
            throws InstantiationException, IllegalAccessException, NotFoundException, CannotCompileException {

        IQuery query = null;
        long begin = System.currentTimeMillis();
        query = JdkProxy.createJdkProxy();
        System.out.println("createJdkProxy:" + (System.currentTimeMillis() - begin));
        System.out.println("JdkProxy class:" + query.getClass().getName());
        begin = System.currentTimeMillis();
        for (int i = 0; i < CIRCLE; i++)
            query.select();
        System.out.println("callJdkProxy:" + (System.currentTimeMillis() - begin));

        begin = System.currentTimeMillis();
        query = CglibProxy.createCglibProxy();
        System.out.println("createCglibProxy:" + (System.currentTimeMillis() - begin));
        System.out.println("CglibProxy class:" + query.getClass().getName());
        begin = System.currentTimeMillis();
        for (int i = 0; i < CIRCLE; i++)
            query.select();
        System.out.println("callCglibProxy:" + (System.currentTimeMillis() - begin));

        begin = System.currentTimeMillis();
        query = JavassistProxy.createJavassistDynProxy();
        System.out.println("createJavassistDynProxy:" + (System.currentTimeMillis() - begin));
        System.out.println("JavassistDynProxy class:" + query.getClass().getName());
        begin = System.currentTimeMillis();
        for (int i = 0; i < CIRCLE; i++)
            query.select();
        System.out.println("callJavassistDynProxy:" + (System.currentTimeMillis() - begin));

        begin = System.currentTimeMillis();
        query = JavassistProxy.createJavassistBytecodeDynProxy();
        System.out.println("createJavassistBytecodeDynProxy:" + (System.currentTimeMillis() - begin));
        System.out.println("JavassistBytecodeDynProxy class:" + query.getClass().getName());
        begin = System.currentTimeMillis();
        for (int i = 0; i < CIRCLE; i++)
            query.select();
        System.out.println("callJavassistBytecodeDynProxy:" + (System.currentTimeMillis() - begin));

    }

}

实验结果:
代理模式 - 图1

300万次 调用代理对象具体方法,可以统计性能排序【从高到低】如下:

对象的加载: cglib < javassist < jdk < javassistByteCode
调用: javassistByteCode < cglib < jdk < javassist

建议采用 cglib 的方式。

使用场景

Hibernate框架的 懒加载 使用的是 cglib框架实现。