好处
安全原因,屏蔽客户端直接访问对象
程调用中,需要使用代理类处理远程方法调用的技术细节【
rmi】提供系统性能,对真实对象进行封装,达到
延时加载的目的。
注意点
屏蔽外部对真实对象的访问
代理类可以通过字节码自动加载的方式,运行时动态生成代理类(反射)
真实对象的延迟加载
技术实现
静态代理
动态代理 【使用字节码动态生成加载技术,在运行时生成并加载类】
jdk
cglib [高级字节码生成工具库]
javassist [高级字节码生成工具库]
asm [低级字节码生成工具库] 性能最好,但不常用
实践
实践都是采用接口的方式。
先定义一个接口:
package xin.rtime.design.proxy.service;//查询接口public interface IQuery {String select();}
接口具体的实现:
package xin.rtime.design.proxy.service;//实际发生者 查询的实现类public class IQueryImpl implements IQuery {@Overridepublic String select() {return "select data.";}}
静态代理
创建一个代理对象
IQueryProxy, 实现上面定义的接口。在类中定义实际代理的真实对象。
在外部调用的方法中,实现
懒加载,并调用实际真实的方法返回结果。
package xin.rtime.design.proxy;import xin.rtime.design.proxy.service.IQuery;import xin.rtime.design.proxy.service.IQueryImpl;// 简单的代理 [静态代理]public class Proxy {public static void main(String[] args) {IQuery queryProxy = new IQueryProxy();System.out.println(queryProxy.select());}}// 代理class IQueryProxy implements IQuery {private IQuery query = null; // 实际发生者的实例@Overridepublic String select() {if (query == null) // 懒加载query = new IQueryImpl();return query.select();}}
动态代理
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));
}
}
实验结果:
300万次 调用代理对象具体方法,可以统计性能排序【从高到低】如下:
对象的加载: cglib < javassist < jdk < javassistByteCode
调用: javassistByteCode < cglib < jdk < javassist
建议采用 cglib 的方式。
使用场景
Hibernate框架的 懒加载 使用的是 cglib框架实现。
