java动态代理的实现

本文探讨两种java动态代理技术,JDK和cglib
代理有什么用?
Spring的面向切面编程(Aspect)就是利用代理技术实现,获取注入对象的时候获取的其实并不是注入类的对象(target),而是其代理类的对象(proxyTarget)只是通过动态代理实现,对于编码者而言是透明的,在代理方法中可以对目标方法做事务控制、日志记录、异常捕获、错误处理等等工作。
代理方式有哪两种?
JDK动态代理和cglib
在Spring中如何使用他们,有什么区别?
Spring默认使用JDK的动态代理,如果在spring配置文件中添加<aop:aspectj-autoproxy proxy-target-class="true" />,则使用cglib,false还是使用JDK
使用JDK的代理只能对有接口的类做代理,且不能代理接口之外的方法(实现类中扩展的),cglib可以对无接口的类做代理,不受接口的限制
以下对两种代理方式进行剖析,通过模拟Spring的注入依赖(IOC)和面向切面(AOP)
<?xml version="1.0" encoding="UTF-8"?>
<beans>
	<aspectj-autoproxy proxy-target-class="false" />

	<bean id="target1" class="com.java.advice151.TargetClass"></bean>
	
	<bean id="target2" class="com.java.advice151.TargetImpl"></bean>
</beans

首先看XML配置文件的内容,配置文件模拟Spring配置文件,第一个标签用来设置代理类型,后两个是注入的bean,然后看代码:

public class Advice106 {
	static Map<String, String> beans = new HashMap<String, String>();
	private static boolean proxy_target_class = false;
	public static void main(String[] args) throws Exception,
			Exception {
		parseXml2Beans(beans, "beans.xml");

		TargetInterface pi = getBean("target2");

		pi.save();
	}

	/**
	 * 解析XML文件,以获取文件中注入的类
	 * 
	 * @param beans
	 * @param string
	 */
	private static void parseXml2Beans(Map<String, String> beans, String string) {
		SAXReader reader = new SAXReader();
		try {
			InputStream xmlPath = Advice106.class.getClassLoader()
					.getResourceAsStream("beans.xml");
			Document document = reader.read(xmlPath);
			Element root = document.getRootElement();
			List<Element> elements = root.elements();

			for (Element element : elements) {
				if ("bean".equals(element.getName())) {
					beans.put(element.attributeValue("id"),
							element.attributeValue("class"));
				} else if ("aspectj-autoproxy".equals(element.getName())) {
					proxy_target_class = Boolean.parseBoolean(element.attributeValue("proxy-target-class"));
				}
			}
		} catch (DocumentException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 根据解析出来的参数判断代理方式
	 * 
	 * @param id
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private static <T> T getBean(String id)
			throws Exception {
		String className = beans.get(id);

		T target = (T) Class.forName(className).newInstance();
		if (proxy_target_class) {
			return cglibProxy(target);
		} else {
			return jdkProxy(target);
		}
	}

	/**
	 * JDK动态代理 直接创建代理对象
	 * 
	 * @param target
	 * @return
	 */
	private static <T> T jdkProxy(T target) {
		InvocationHandler handler = new JDKHandler<T>(target);
		return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target
				.getClass().getInterfaces(), handler);
	}

	/**
	 * JDK动态代理 先创建代理类,然后创建代理对象
	 * 
	 * @param target
	 * @return
	 */
	private static <T> T jdkProxy2(T target) {
		InvocationHandler handler = new JDKHandler<T>(target);
		Class<T> proxyClass = (Class<T>) Proxy.getProxyClass(target.getClass()
				.getClassLoader(), target.getClass().getInterfaces());
		T proxyObject = null;
		try {
			Constructor<T> con = proxyClass
					.getConstructor(new Class[] { InvocationHandler.class });
			proxyObject = con.newInstance(new Object[] { handler });
		} catch (Exception e) {
			e.printStackTrace();
		}
		return proxyObject;
	}



	/**
	 * 使用cglib方式代理
	 * 
	 * @param target
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T cglibProxy(T target){

		Enhancer enhancer = new Enhancer();

		enhancer.setSuperclass(target.getClass());

		enhancer.setCallback(new CglibHandler<T>(target));

		return (T) enhancer.create();
	}
}

/**
 * 目标接口
 * 
 * @author 201507160235
 *
 */
interface TargetInterface {
	public void save();

	public void add();

	public void query();
}

/**
 * 目标接口实现类
 * 
 * @author 201507160235
 *
 */
class TargetImpl implements TargetInterface {

	@Override
	public void save() {
		System.out.println("real business save method");

	}

	@Override
	public void add() {
		System.out.println("real business add method");

	}

	@Override
	public void query() {
		System.out.println("real business add method");

	}
}

/**
 * 目标类
 * 
 * @author 201507160235
 *
 */
class TargetClass {
	public void save() {
		System.out.println("no interface save method");

	}

	public void add() {
		System.out.println("no interface add method");

	}

	public void query() {
		System.out.println("no interface add method");

	}

}

代码都加着注释,运行结果:

代理句柄中预处理!
real business save method
代理句柄中最终处理


可以看到在配置文件proxy-target-class设置为false的时候可以使用JDK动态代理,当然了,如果改为true就会使用cglib代理。

cglib代理,事务开始!
real business save method
cglib代理,事务结束!
但是如果proxy-target-class=false的时候,强制获取TargetClass的代理对象,则会报错:

Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.java.advice151.TargetClass
	at com.java.advice151.Advice106.main(Advice106.java:25)

当然了,这个错误是可控的,在类型检查的过程中直接把无接口的类过滤掉,不使用代理直接返回对象不就可以了,把getBean方法修改为:

private static <T> T getBean(String id)
			throws Exception {
		String className = beans.get(id);

		T target = (T) Class.forName(className).newInstance();
		if (proxy_target_class) {
			return cglibProxy(target);
		} else {
			<span style="color:#ff6666;">if (target.getClass().getInterfaces().length <= 0) {
				return target;
			}</span>

			return jdkProxy(target);
		}
	}

红体字是添加的内容,若是不符合代理条件,直接返回,也就是本业务受理,自己玩去!运行后结果:

no interface save method


这里还有两个类的代码没贴出来,其实就是回调函数而已,你的业务所在的方法,请看,JDK代理的句柄类:

public class JDKHandler<T> implements InvocationHandler {
	T obj;

	public JDKHandler(T obj) {
		this.obj = obj;
	}
	@Override
	public T invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("代理句柄中预处理!");
		T result = (T) method.invoke(obj, args);
		System.out.println("代理句柄中最终处理");
		return result;
	}

}

必须继承Invocationhandler 实现其invoke方法,在其中书写业务员代码。

cglib的处理拦截器:

public class CglibHandler<T> implements MethodInterceptor {
	T target;

	public CglibHandler(T target) {
		this.target = target;
	}

	@Override
	public T intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		System.out.println("cglib代理,事务开始!");
		T proxyObj = (T) proxy.invoke(target, args);
		System.out.println("cglib代理,事务结束!");
		return proxyObj;
	}
}


多了一个参数而已,其他的结构上基本没区别。

他们之间最大的区别在哪儿呢?是代理的实现部分,JDK传入的是结构数组,而cglib传入的是superClass,回调函数类都是需要传入的,毕竟是你的业务所在。


另外再说下,jdkProxy2和jdkProxy是等价的,这个可以参考官方文档,只是比较麻烦一点而已,最常用的方式是jdkProxy

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值