前言:
上一篇博客中详细剖析了JDK动态代理。代理为控制要访问的目标对象提供了一种途径。当访问对象时,它引入了一个间接的层。JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。JDK的动态代理用起来非常简单,但它有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的继承的类,该怎么办?现在我们可以使用CGLIB包(这段资料来自百度百科。)
CGLIB代理特点:
1、被代理的类,不必须实现接口;
2、由于CGLib代理的原理是,创建一个被代理类的子类对象,因此,
如果被代理的类存在不能被继承的方法,则,这个代理类对象
当然就无法调用!即,被代理类中的final方法是不能被代理的;
当然,若被代理类本身是final类,则,不能被代理!
3、代理对象可以调用除了final修饰的其它所有方法。
使用方式:
CGLib代理的使用主要通过Enhancer对象的create()方法获得代理,要求通过setSuperclass注入目标类,否则默认代理Object类。要求通过setCallback方法注入拦截器,否则抛出java.lang.IllegalStateException异常。
下面为代码实现(必须拥有cglib包):
目标类(依旧沿用上一篇中的目标类,添加两个方法):
public class Service implements IService {
public Service() {
}
@Override
public void dealService() {
System.out.println("目标类功能!");
}
public void doSomething() {
System.out.println("[这不是接口方法 ]");
}
protected final void finalMethod() {
System.out.println("这是final方法");
}
}
增强类(没有变化):
public class OtherFunction {
public OtherFunction() {
}
public void before() {
System.out.println("前置增强!");
}
public void after() {
System.out.println("后置增强!");
}
}
拦截器:
public class ServiceInterceptor implements MethodInterceptor{
private Object target;
private OtherFunction otherFunction;
public ServiceInterceptor(Object target, OtherFunction otherFunction) {
this.target = target;
this.otherFunction = otherFunction;
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
otherFunction.before();
Object result = method.invoke(this.target, args);
otherFunction.after();
return result;
}
}
测试:
public class TestForCGLibProxy {
public static void main(String[] args) {
Service target = new Service();
OtherFunction otherFunction = new OtherFunction();
ServiceInterceptor interceptor =
new ServiceInterceptor(target, otherFunction);
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(interceptor);
Service service = (Service) enhancer.create();
service.dealService();
System.out.println("------分割线-----");
service.doSomething();
System.out.println("------分割线-----");
service.finalMethod();
System.out.println(service.getClass().getName());
System.out.println(service instanceof Service);
}
}
输出:
可以看到,通过cglib方式生成的代理对象是目标类的子类,无法对final方法进行增强,可以对目标类的非接口方法也进行增强,这是JDK代理所没有的特性。
代理工具化:
在不少的java框架中都使用到了代理,如Spring的Aop,RMI技术,Struts2中的拦截器等等。那么我们可否将做一个代理工具,将参数或者接口传入工具后就可以获得一个代理对象来使用。并模仿Aop中代理的原则,即:
如果目标对象实现了接口,默认情况下会采用JDK代理。
如果目标对象实现了接口,可以强制使用CGLIB代理。
如果目标对象没有实现了接口,必须采用CGLIB代理。
通过我们对代理的深入学习,我们用代理对象调用方法的时候,其内部真正执行方法都是在其拦截器里面实现的。(JDK代理为InvocationHandler的invoke方法,CGLib代理为MethodInterceptor里的intercept方法)。
那我们可以写一个接口去配置里面真正执行方法的过程,如果接口没有实现,则按默认的method.invoke方法执行。
接口
public interface IMethodInvoker {
<T> T methodInvoke(Object object, Method method, Object[] args);
}
JDKProxy
public class JDKProxy {
private IMethodInvoker methodInvoker;
public JDKProxy() {
}
//设置IMethodInvoker接口
public void setMethodInvoker(IMethodInvoker methodInvoker) {
this.methodInvoker = methodInvoker;
}
//传入接口的Class对象时的特殊处理
@SuppressWarnings("unchecked")
public <T> T getInterfaceJDKProxy(Class<?> klass) {
ClassLoader loader = klass.getClassLoader();
Class<?>[] interfaces = { klass };
return (T) Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
if (methodInvoker == null) {
result = method.invoke(null, args);
} else {
result = methodInvoker.methodInvoke(null, method, args);
}
return result;
}
});
}
//提供另一种方式获得代理对象,即传入Class对象,通过newInstance方法获得对象后调用下面的
//重载的方法即可。
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<?> klass) {
try {
Object target = klass.newInstance();
return (T) getProxy(target);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
//传入对象,可以通过该方法直接获得代理,invoke的实现由IMethodInvoker配置
@SuppressWarnings("unchecked")
public <T> T getProxy(T target) {
Class<?> klass = target.getClass();
ClassLoader loader = klass.getClassLoader();
Class<?>[] interfaces = klass.getInterfaces();
return (T) Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
if (methodInvoker == null) {
result = method.invoke(target, args);
} else {
result = methodInvoker.methodInvoke(target, method, args);
}
return result;
}
});
}
}
CGLibProxy
public class CGLibProxy {
private IMethodInvoker methodInvoker;
public CGLibProxy() {
}
public void setMethodInvoker(IMethodInvoker methodInvoker) {
this.methodInvoker = methodInvoker;
}
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<?> klass) {
try {
Object target = klass.newInstance();
return (T) getProxy(target);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
@SuppressWarnings("unchecked")
public <T> T getProxy(T target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
if (methodInvoker == null) {
result = method.invoke(target, args);
} else {
result = methodInvoker.methodInvoke(target, method, args);
}
return result;
}
});
return (T) enhancer.create();
}
}
工具类:
public class MyProxy {
public static final int JDK_PROXY = 0;
public static final int CGLIB_PROXY = 1;
//默认JDK代理
private int proxyType = JDK_PROXY;
private IMethodInvoker methodInvoker;
public MyProxy() {
}
//设置代理类型
public void setProxyType(int proxyType) {
if (this.proxyType != JDK_PROXY && this.proxyType != CGLIB_PROXY) {
this.proxyType = JDK_PROXY;
}
this.proxyType = proxyType;
}
public void setMethodInvoker(IMethodInvoker methodInvoker) {
this.methodInvoker = methodInvoker;
}
private <T> T getCGLIBProxy(Class<?> klass) {
CGLibProxy cgLibProxy = new CGLibProxy();
cgLibProxy.setMethodInvoker(methodInvoker);
return cgLibProxy.getProxy(klass);
}
@SuppressWarnings("unchecked")
private <T> T getCGLIBProxy(Object target) {
CGLibProxy cgLibProxy = new CGLibProxy();
cgLibProxy.setMethodInvoker(methodInvoker);
return (T) cgLibProxy.getProxy(target);
}
private <T> T getJDKProxy(Class<?> klass) {
JDKProxy jdkProxy = new JDKProxy();
jdkProxy.setMethodInvoker(methodInvoker);
//判断是否为接口,来选择不同的方法
if (klass.isInterface()) {
return jdkProxy.getInterfaceJDKProxy(klass);
}
return jdkProxy.getProxy(klass);
}
@SuppressWarnings("unchecked")
private <T> T getJDKProxy(Object target) {
JDKProxy jdkProxy = new JDKProxy();
jdkProxy.setMethodInvoker(methodInvoker);
return (T) jdkProxy.getProxy(target);
}
public <T> T getProxy(Class<?> klass) {
//该类本身就是接口,强制使用JDK代理
if (klass.isInterface()) {
return getJDKProxy(klass);
//该类没有实现任何接口,强制使用CGLib代理
} else if (klass.getInterfaces().length <= 0) {
return getCGLIBProxy(klass);
//不是上述情况下,由用户选择代理方式
} else {
return proxyType == JDK_PROXY ? getJDKProxy(klass)
: getCGLIBProxy(klass);
}
}
public <T> T getProxy(Object target) {
//该类没有实现任何接口,强制使用CGLib代理
if (target.getClass().getInterfaces().length <= 0) {
return getCGLIBProxy(target.getClass());
//接口不能直接生成对象,所以可以少一次判断
//不是上述情况下,由用户选择代理方式
} else {
return proxyType == JDK_PROXY ? getJDKProxy(target)
: getCGLIBProxy(target);
}
}
}
可以看到我编写的代理工具可以实现:
1.传入目标类对象或传入目标类的Class对象,通过getProxy方法就可以获取代理。
2.代理类所代理的方法的执行可以通过接口进行配置,体现了多态性。
3.可以选择代理类型,默认使用JDK代理,当传入接口的Class对象的时候,此时强制使用JDK代理。由传入的Class对象或者目标类对象没有实现任何接口,此时强制使用CGLib代理。若目标类本身不是接口,但实现了某些接口。那么此时可以自己选择代理方式。
测试代码:
public class TestForProxy {
public static void main(String[] args) {
//故意选择CGlib代理类型,但传入接口的Class对象
MyProxy mp = new MyProxy();
mp.setProxyType(MyProxy.CGLIB_PROXY);
IDoSomething d = mp.getProxy(IDoSomething.class);
System.out.println(d.getClass().getName());
//故意选择JDK代理类,但目标类没有实现任何接口
mp.setProxyType(MyProxy.JDK_PROXY);
StuInfo info = new StuInfo("x", "3", true);
StuInfo infoProxy = mp.getProxy(info);
System.out.println(infoProxy.getClass().getName());
System.out.println("-------分割线-------");
//目标类不是接口,也实现了接口,两种代理方式都试一下
mp.setProxyType(MyProxy.JDK_PROXY);
IDoSomething dsiProxy1 = mp.getProxy(DoSomethingImpl.class);
System.out.println(dsiProxy1.getClass().getName());
mp.setProxyType(MyProxy.CGLIB_PROXY);
DoSomethingImpl dsiProxy2 = mp.getProxy(DoSomethingImpl.class);
System.out.println(dsiProxy2.getClass().getName());
}
}
以下为结果:
结果分析:
说明:IDoSomething是我编写的一个测试所用的接口,DoSomethingImpl为这个接口的实现类,StuInfo是一个普通类,没有实现任何接口。
第一部分我们将代理类型设置为CGLib代理,通过接口的Class对象获得接口代理对象,此时工具会强制使用JDK代理。
第二部分我们将代理类型设置为JDK代理,通过目标类对象获得代理,由于目标类未实现任何接口,工具强制使用CGLib代理。
第三四部分我们的目标类既实现了接口,也不是接口类型,此时两种代理均可使用,只需设置即可。
我会在后序的一些项目中使用到这个代理工具。