因系统架构变动,需要使用到代理技术。研究了一下java中常用的3种代理方式,这里做一下使用上的总结。下面分别对jdk、cglib、javaassit做一个简单介绍。这里漏了asm,是因为asm使用起来较复杂,必须使用的场景极少。
使用jdk proxy的好处是,它是java自带的,不需要再依赖任何第三方jar包。坏处是,它只能对接口做代理。
class InjectProxy implements InvocationHandler {
private Object target;
private InjectProxy(Object target){
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
return method.invoke(target, args);
}
}
InvocationHandler ih = new InjectProxy( target );
ClassLoader classLoader = InjectProxy.class.getClassLoader();
return Proxy.newProxyInstance( classLoader, target.getClass().getInterfaces(), ih );
cglib大家都很熟,它封装了asm的复杂接口,使用起来比较简单。
cglib主要包含4个概念,BeanGenerator、Enhancer、MethodInterceptor、LazyLoader、Dispatcher。
beangenerator主要用于动态生成一个类的子类,可以给子类动态添加一些成员变量,自动生成Getter、Setter方法。缺点是它只能生成含默认构造函数的子类。
BeanGenerator gen = BeanGenerator();
gen.setSuperclass(SuperClass.class);
gen.addProperty("name", String.class);
Class subClazz = (Class)gen.createClass();
SuperClass obj = (SuperClass)gen.create();
enhancer用于实现某个方法调用的aop功能。enhancer生成对象会包含很多cglib自动添加进去的属性,所以最后生成的对象会比较大。
public class MethodInterceptorImpl implements MethodInterceptor{
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
}
//代理invoke方法
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallbacks(new Callback[]{NoOp.INSTANCE, new MethodInterceptorImpl()});
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
//只拦截Algorithm接口定义的invoke方法
if(method.getName().equals("invoke"))
return 1;
return 0;
}
});
enhancer.setUseFactory(false);
//new 出对象
Object proxy = enhancer.create();
lazyLoader是cglib的代理延迟初始化接口。当接口方法被第一次调用时,才确定实际要访问的对象。什么意思?看代码
public class ConcreteClassLazyLoader implements LazyLoader{
public class PropertyBean {
private String propertyName;
public String getPropertyName() {
return propertyName;
}
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
}
@Override
public Object loadObject() throws Exception {
System.out.println("LazyLoader loadObject() ...");
PropertyBean bean=new PropertyBean();
bean.setPropertyName("lazy-load object propertyName!");
return bean;
}
public static void main(String[] args){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(PropertyBean.class);
PropertyBean propertyBean = (PropertyBean)enhancer.create(PropertyBean.class,new ConcreteClassLazyLoader());
//此处会回调loadObject
System.out.println(propertyBean.getPropertyName());
System.out.println("after...");
//之后不再回调loadObejct,直接访问第一次返回的对象
System.out.println(propertyBean.getPropertyName());
}
}
Dispatcher功能与LazyLoader相同,只是dispatcher每次都会被回调。
public class ConcreteDispatcher implements Dispatcher{
public class PropertyBean {
private String propertyName;
public String getPropertyName() {
return propertyName;
}
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
}
@Override
public Object loadObject() throws Exception {
System.out.println("Dispatcher loadObject() ...");
PropertyBean bean=new PropertyBean();
bean.setPropertyName("Dispatcher object propertyName!");
return bean;
}
public static void main(String[] args){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(PropertyBean.class);
PropertyBean propertyBean = (PropertyBean)enhancer.create(PropertyBean.class,new ConcreteDispatcher());
//此处会回调loadObject
System.out.println(propertyBean.getPropertyName());
System.out.println("after...");
//每次都回调loadObejct
System.out.println(propertyBean.getPropertyName());
}
}
BeanGenerator适合给子类加成员变量
MethodInterceptor 适合做方法拦截
LazyLoader、Dispatcher适合做对象路由
javaassist的使用频率少于cglib,因为它使用起来比cglib要稍微麻烦些,可以通过动态拼接源码来生成对象是它的强项。
protected CtClass createSubClass(Class> clazz) throws CannotCompileException, NotFoundException, ClassNotFoundException{
/*javassist生成具有needLog属性的子类*/
ClassPool pool = ClassPool.getDefault();
String proxyClassName = clazz.getName() + "$LogAble";
CtClass cc = pool.getOrNull(proxyClassName);
if(cc == null){
cc = pool.makeClass(proxyClassName);
cc.setModifiers(Modifier.PUBLIC);
//父类
CtClass superClass = pool.getOrNull(clazz.getName());
if(superClass == null)
superClass = pool.makeClass(clazz.getName());
cc.setSuperclass(superClass);
//构造函数
CtClass stringClass = pool.get("java.lang.String");
CtClass[] constructorParamClassArr = new CtClass[]{stringClass};
//生成一个同父类的构造函数
CtConstructor ctc = CtNewConstructor.make(constructorParamClassArr,null,CtNewConstructor.PASS_PARAMS,null,null, cc);
cc.addConstructor(ctc);
//接口
addInterface(cc, LogAopFilter.class);
//添加needLog属性
addClassProperty(cc, "needLog", Boolean.class);
}
/*javassist方法结束*/
return cc;
}
/*
* 子类添加接口
*/
protected void addInterface(CtClass cc, Class> interfaceClass){
ClassPool pool = ClassPool.getDefault();
CtClass ccInterface = pool.getOrNull(interfaceClass.getName());
if(ccInterface == null)
ccInterface = pool.makeInterface(interfaceClass.getName());
cc.addInterface(ccInterface);
}
/*
* 子类添加属性
*/
protected void addClassProperty(CtClass cc, String name, Class> type) throws CannotCompileException{
addClassProperty(cc, name, type, "");
}
protected void addClassProperty(CtClass cc, String name, Class> type, String decorate) throws CannotCompileException{
CtField field = CtField.make(String.format("private %s %s %s;", decorate, type.getName(), name), cc);
cc.addField(field);
CtMethod getMethod = CtMethod.make(String.format("public %s get%s(){return %s;}", type.getName(), name.substring(0, 1).toUpperCase() + name.substring(1), name), cc);
cc.addMethod(getMethod);
CtMethod setMethod = CtMethod.make(String.format("public void set%s(%s value) {this.%s = $1;return;}",name.substring(0, 1).toUpperCase() + name.substring(1), type.getName(), name), cc);
cc.addMethod(setMethod);
}
javaassit生成代理对象的过程和cglib差不多。
private class MethodHandlerImpl implements MethodHandler {
final Object delegate;
MethodHandlerImpl(Object delegate) {
this.delegate = delegate;
}
public Object invoke(Object self, Method m, Method proceed, Object[] args) throws Throwable {
return m.invoke(delegate, args);
}
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setSuperclass(SuperClass.class);
proxyFactory.setInterfaces(new Class[] { SuperInterface.class });
Class> proxyClass = proxyFactory.createClass();
SuperInterface proxy = (SuperInterface) proxyClass.newInstance();
((ProxyObject) proxy).setHandler(new MethodHandlerImpl(delegate));
对三种框架生成的代理对象的性能测试网上有很多,可以参考这篇。
基本上的顺序是
javaassit拼接源码生成的方法 (5倍于) > cglib MethodInterceptor生成的方法 (2倍于) > jdk proxy生成的方法 > javaassist MethodInterceptor生成的方法。