CGLIb代理介绍与两种代理的工具化

前言:

    上一篇博客中详细剖析了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代理。

第三四部分我们的目标类既实现了接口,也不是接口类型,此时两种代理均可使用,只需设置即可。

    我会在后序的一些项目中使用到这个代理工具。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值