代理的作用
在实际的项目中,往往有一些通用的功能需要穿插在项目功能代码的各个角落,比如很常见的log的记录,还有接口访问的权限检查。总结下来,这类的代码耦合,一种是预处理(信息过滤、权限校验),用来控制访问;另一种是其他通用功能的织入用于功能扩展。
使用代理的目的,就是解耦。把琐碎的事情交给代理类去做,让核心业务代码专注于业务,而无须关注那些通用功能的实现,且那些通用功能的代码也不会污染了核心的业务代码。经典的Spring Aop实际上就是代理的巧妙运用。
另外EasyMock这类方便unit test的工具,也是基于代理。也正是对EasyMock的好奇,才来了解一下java的代理。
java代理的实现方法
静态代理
-
动态代理
-
静态代理的实现思路
一个目标类(被代理类),一个代理类,这两个是核心,还有一个抽象接口,其实如果只是代理这个功能来说,抽象接口的意义是不明显的。使用了抽象接口,主要在于在面向接口编程时,代理类可以很优雅地取代目标类。
首先说不使用抽象接口的。实现机制就是代理类持有目标类的对象,因为现在代理类持有了这个对象,就可以不直接使用目标类对象来invoke方法,而是调用代理类的某个方法,然后代理类方法间接地去invoke目标方法,这样的间接过程,就可以在目标方法的执行前后追加其他业务逻辑。 原本的
Target.invoke()
也变成了Proxy.invoke()
;代码:
-
//目标类
public class Real implements IService{
@Override
public void execute(){
//do something
}
}
//代理类
public class Proxy implements IService{
private Real realObj;
public Proxy(Real realObj){
this.realObj = realObj;
}
@Override
public void execute(){
//do pre
realObj.execute();
//do after
}
}
//抽象类
public Interface IService{
public void execute();
}
实际使用是将实体类传给代理的构造器,调用代理类的方法,就起到代理的作用。
public static void main(String []agrs){
IService proxy = new Proxy(new Real());
proxy.execute();
}
- 动态代理
JDK提供了动态代理的方法。动态代理的所谓动态,动的是代理类。动态代理和静态代理的区别,就是动态生成了代理类,免去了手工编写代理类的无趣。与静态相比,可以少写代码偷点懒。
动态代理的实现思路和静态是一致的。简要记录一下api的使用。
public static void main(String[] args) {
IService real = new RealService();
InvocationHandler handler = new MyInvocationHandler(real);
IService proxy = (IService)Proxy.newProxyInstance(real.getClass().getClassLoader(),real.getClass().getInterfaces(),handler);
proxy.execute();
}
InvocationHandler是JDK定义的接口,是一种Callback接口,通过它传人的回调函数,代理类对象上的的所有方法调用都回到这个Callback中的可执行代码。使用InvocationHandler方法既可以创建类实现InvocationHandler接口,也可以使用匿名内部类。
动态代理的主要优点是,减少代码量,并且实现的Callback类可重用,但是此方法的局限是只能代理实现了接口的类,因为在Proxy.newProxyInstance()方法中,使用类加载器和目标类的接口在运行时生成了代理类。在编译后实际上可以发现一个包含*$*的class文件。
- cglib——JDK动态代理的补全方案
cglib可以代理未实现接口的类。
CGLIB原理是动态生成一个目标类的子类,子类重写目标类的所有不是final的方法(因为final方法无法重写),在方法体中织入了回调函数的调用。
其使用方法是基于Enhancer类,Enhancer设置继承的父类(即为要代理的类),设置回调函数。现有的回调函数有:
- net.sf.cglib.proxy.FixedValue, all method calls return a fixed value which is generated by the anonymous
FixedValue
implementation.
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyClass.class);
// FixedValue is a intercepter
enhancer.setCallback(new FixedValue() {
@Override
public Object loadObject() throws Exception {
System.out.println("FixedValue");
return "hello,cglib";
}
});
MyClass proxy = (MyClass) enhancer.create();
proxy.dosome();
proxy.execute();
System.out.println(proxy.getClass()); //final method
//author said:
/*The anonymous subclass of FixedValue would become hardly referenced from the enhanced SampleClass such that neither the anonymous FixedValue instance or the class holding the @Test method would ever be garbage collected. This can introduce nasty memory leaks in your applications. Therefore, do not use non-static inner classes with cglib. (I only use them in this overview for keeping the examples short.)
*/
- net.sf.cglib.proxy.InvocationHandler,和jdk中同款,它拦截所有方法到
invoke()
,和FixedValue
类似,但是比前者要更灵活一点。在其内部处理要注意无限递归调用。
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyClass.class);
// net.sf.cglib.proxy.InvocationHandler is a intercepter
enhancer.setCallback(new net.sf.cglib.proxy.InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("InvocationHandler");
return null;
}
});
MyClass proxy = (MyClass) enhancer.create();
proxy.dosome();
proxy.execute();
- net.sf.cglib.proxy.MethodInterceptor,The
MethodInterceptor
allows full control over the intercepted method and offers some utilities for calling the method of the enhanced class in their original state.它的回调方法里proxy.invokeSuper(obj, args);
可以通过父类调用其它方法,而不必全部都指向回调函数。
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyClass.class);
// MethodInterceptor
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
return "Hello cglib!";
} else {
return proxy.invokeSuper(method, args);
}
}
});
MyClass proxy = (MyClass) enhancer.create();
proxy.dosome();
proxy.execute();
proxy.hashCode();
System.out.println(proxy.getClass());
这些都是既有的回调函数,需要更加的自定义,可以使用Enhancer#setCallbackFilter(CallbackHelper callbackHelper)
方法自定义回调函数,callbackHelper返回回调函数对象,至于返回那种,就是可以在callbackHelper中自定义了,如对不同的方法,指定不同的回调函数。
Enhancer enhancer = new Enhancer();
CallbackHelper callbackHelper = new CallbackHelper(SampleClass.class, new Class[0]) {
@Override
protected Object getCallback(Method method) {
if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
return new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "Hello cglib!";
};
}
} else {
return NoOp.INSTANCE; // A singleton provided by NoOp.
}
}
};
enhancer.setSuperclass(MyClass.class);
enhancer.setCallbackFilter(callbackHelper);
enhancer.setCallbacks(callbackHelper.getCallbacks());
//code snippet via https://github.com/cglib/cglib/wiki/Tutorial
summary
java代理的实现机制,大概就是这两步:(1)创建目标类的可替代类;(2)织入回调方法。
回头去看静态代理,实际上就是代理实现的基础与核心。后面的动态代理,无论是JDK自带的还是CGLIB扩展的,无非就是在寻找方法取代手工编写可替代类的方法,并将整个过程“接口化”。