动态代理之JDK代理和CGLIB代理

代理模式
为其他对象提供一个代理以控制对某个对象的访问。

动态代理
利用反射机制在运行时创建代理类。

举个例子,首先产生一个接口:

public interface ISomeInterface {
	String doSomething(String arg);
}

被代理的实际对象(接口的实现类):

public class TargetClass implements ISomeInterface {
	
	public TargetClass() {
	}

	@Override
	public String doSomething(String arg) {
		System.out.println(arg);
		return arg;
	}
	
	public String dealOne(String arg) {
		System.out.println("[" + arg + "]");
		return null;
	}

}

上述的被代理类除了实现接口的方法doSomething(),还另外写了一个方法dealOne()。

JDK代理模式

public class JDKProxy {

	public JDKProxy() {
	}
	
	@SuppressWarnings("unchecked")
	public static <T> T getProxy(T target) {
		Class<?> klass = target.getClass();
		ClassLoader classLoader = klass.getClassLoader();
		Class<?>[] interfaces = klass.getInterfaces();
		
		return (T) Proxy.newProxyInstance(classLoader, interfaces, 
					new InvocationHandler() {	
						@Override
						public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
							System.out.println("前置拦截!");
							Object result = method.invoke(target, args);
							System.out.println("后置拦截!");
							return result;
						}
					});
	}
	
}

InvocationHandler接口中的抽象方法:

public Object invoke(Object proxy, Method method, Object[] args)

第一个参数proxy一般是指代理类,method是被代理的方法,如上例中的doSomething(),args为该方法的参数数组。

InvocationHandler是一个接口,里面只有一个抽象方法invoke,这里通过匿名内部类的方式产生InvocationHandler接口的实现类,利用invoke方法提供的参数method,args以及被代理对象target,通过反射机制执行被代理类中的方法。在方法执行之前,我们分别输出了“前置拦截”和“后置拦截”,这里可以看到AOP的影子,AOP 功能模块就是采用动态代理的机制来实现切面编程。

测试一下:

public class JDKTest {
	
	public static void main(String[] args) {
		TargetClass target = new TargetClass();
		
		ISomeInterface targetClass = JDKProxy.getProxy(target);
		String str = targetClass.doSomething("abcd");
		System.out.println(str);
	}
	
}

执行结果:

前置拦截!
abcd
后置拦截!
abcd

在这里插入图片描述
从上图可以看出,代理对象只能调用接口方法,我们之前写的dealOne()方法不能被调用。

通过一个JDKProxy类将核心过程进行封装,使用时只需调用getProxy()方法,就可产生代理对象。

JDK代理模式:
1.被代理的类,必须有接口;
2.产生的代理对象,其类型是接口类型的派生类类型;
3.代理对象只能调用接口方法;

CGLIB代理模式

public class CGLIBProxy {

	public CGLIBProxy() {
	}
	
	@SuppressWarnings("unchecked")
	public static <T> T getProxy(T target) {
		Class<?> klass = target.getClass();
		
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(klass);
		enhancer.setCallback(new MethodInterceptor() {
			
			@Override
			public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
				System.out.println("前置拦截!");
				Object result = method.invoke(target, args);
				System.out.println("后置拦截!");
				return result;
			}
		});
		return (T) enhancer.create();
	}
	
}

测试一下:

public class CGLIBTest {

	public static void main(String[] args) {
		TargetClass targetClass = new TargetClass();
		
		TargetClass target = CGLIBProxy.getProxy(targetClass);
		target.doSomething("abcd");
		target.dealOne("1234");
	}

}

运行结果:

前置拦截!
abcd
后置拦截!
前置拦截!
[1234]
后置拦截!

从上图可以看出,cglib代理除了可以调用接口方法外,还可以调用dealOne方法。因为CGLIB代理的原理是会创建一个被代理类的子类对象,所以,被代理类中的可继承的方法都可以被调用。如果被代理类存在不能继承的方法,即final修饰的方法,那就无法调用,当然如果被代理类本身是final修饰的类,那就不能被代理。

CGLIB代理模式:
1.被代理的类,不必须实现接口;
2.CGLib代理的原理是,会创建一个被代理类的子类对象;
3.代理对象可以调用除了final修饰的其它所有方法。

最后,我们可以做一个工具类,将JDK代理和CGLIB代理整合起来。

public class MecProxy {
	private static final int JDK_PROXY = 0;
	private static final int CGLIB_PROXY = 1;
	
	private int proxyType = JDK_PROXY;
	private Intercepter intercepter;
	private INewInstanceMaker maker;
	
	public MecProxy() {
	}

	public void setProxyType(int proxyType) {
		if (proxyType != JDK_PROXY && proxyType != CGLIB_PROXY) {
			this.proxyType = JDK_PROXY;
		}
		this.proxyType = proxyType;
	}

	public void setIntercepter(Intercepter intercepter) {
		this.intercepter = intercepter;
	}

	public void setMaker(INewInstanceMaker maker) {
		this.maker = maker;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getProxy(Class<?> klass) {
		Object target = null;
		if (maker == null) {
			try {
				target = klass.newInstance();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		} else {
			target = maker.getNewInstance(klass);
		}
		
		return (T) getProxy(target);
	}
	
	public <T> T getProxy(T target) {
		if (proxyType == JDK_PROXY) {
			JDKProxy jdkProxy = new JDKProxy();
			jdkProxy.setIntercepter(intercepter);
			jdkProxy.setMaker(maker);
			
			return jdkProxy.getProxy(target);
		}
		CGLIBProxy cglibProxy = new CGLIBProxy();
		cglibProxy.setIntercepter(intercepter);
		cglibProxy.setMaker(maker);
		
		return cglibProxy.getProxy(target);
	}
	
}

Intercepter是一个接口,拦截器,可以在调用被代理对象的方法中设置进去,也就是在上述JDKProxy或者CGLIBProxy中增加Intercepte类型成员,在method.invoke(…)之前完成设置即可.

public interface Intercepter {
	boolean before();
	Object after(Object result);
}

INewInstanceMaker也是一个接口,创建被代理类对象,因为有的类对象不是直接newInstance获得的.

public interface INewInstanceMaker {
	Object getNewInstance(Class<?> klass);
}

相应地,对JDKProxy和CGLIBProxy两个类可以做些修改补充.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值