JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例。
一、首先我们进行JDK动态代理的演示。
现在我们有一个简单的业务接口Saying,如下:
package testAOP;
public interface Saying {
public void sayHello(String name);
public void talking(String name);
}
一个简单的实现类SayingImpl,如下:
package testAOP; public class SayingImpl implements Saying { @Override public void sayHello(String name) { // TODO Auto-generated method stub System.out.println(name + ":大家好啊!"); } @Override public void talking(String name) { // TODO Auto-generated method stub System.out.println(name + ":我的意思是,我们要努力建设和谐社会!"); } }
我们要实现的是,在sayHello和talking之前和之后分别动态植入处理。
JDK动态代理主要用到java.lang.reflect包中的两个类:Proxy和InvocationHandler.
InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态的将横切逻辑和业务逻辑编织在一起。
Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。
如下,我们创建一个InvocationHandler实例:
package testAOP;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
MyInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//目标方法前执行
System.out.println("——————————————————————————");
System.out.println("下一位请登台发言!");
//目标方法调用
Object obj = method.invoke(target, args);
//目标方法后执行
System.out.println("大家掌声鼓励!");
return obj;
}
}
下面是测试:
package testAOP;
import java.lang.reflect.Proxy;
public class JDKProxyTest {
public static void main(String[] args) {
// 希望被代理的目标业务类
Saying target = new SayingImpl();
// 将目标类和横切类编织在一起
MyInvocationHandler handler = new MyInvocationHandler(target);
// 创建代理实例
Saying proxy = (Saying) Proxy.newProxyInstance(
target.getClass().getClassLoader(),//目标类的类加载器
target.getClass().getInterfaces(),//目标类的接口
handler);//横切类
proxy.sayHello("小明");
proxy.talking("小丽");
}
}
运行情况如下:
—————————————————————————— 下一位请登台发言! 小明:大家好啊! 大家掌声鼓励! —————————————————————————— 下一位请登台发言! 小丽:我的意思是,我们要努力建设和谐社会! 大家掌声鼓励!
使用JDK动态代理有一个很大的限制,就是它要求目标类必须实现了对应方法的接口,它只能为接口创建代理实例。我们在上文测试类中的Proxy的newProxyInstance方法中可以看到,该方法第二个参数便是目标类的接口。如果该类没有实现接口,这就要靠cglib动态代理了。
CGLib采用非常底层的字节码技术,可以为一个类创建一个子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势植入横切逻辑。
二、接下来我们进行cglib动态代理的演示。
首先我们需要导包,我用的包是cglib-nodep-2.1_3.jar。
我们首先创建一个代理创建器CglibProxy:
package testAOP.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor{
Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
//设置需要创建的子类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通过字节码技术动态创建子类实例
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
// TODO Auto-generated method stub
System.out.println("——————————————————————————");
System.out.println("下一位请登台发言!");
//目标方法调用
Object result = proxy.invokeSuper(obj, args);
//目标方法后执行
System.out.println("大家掌声鼓励!");
return result;
}
}
然后进行测试:
package testAOP.cglib;
import testAOP.Saying;
import testAOP.SayingImpl;
public class CglibProxyTest {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
//通过动态生成子类的方式创建代理类
Saying target = (Saying) proxy.getProxy(SayingImpl.class);
target.sayHello("小明");
target.talking("小丽");
}
}
结果与JDK动态代理没有任何区别。
CGLib动态代理能代理类和接口,但是不能代理final类,也是有一定局限性。
JDK动态代理和CGLib动态代理都是运行时增强,通过将横切代码植入代理类的方式增强。与此不同的是AspectJ,它能够在通过特殊的编译器在编译时期将横切代码植入增强,这样的增强处理在运行时候更有优势,因为JDK动态代理和CGLib动态代理每次运行都需要增强。
比较:
静态代理是通过在代码中显式定义一个业务实现类一个代理类,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;