java动态代理的实现

来源:互联网 发布:caxa工程师编程实例 编辑:程序博客网 时间:2024/06/05 05:55
本文探讨两种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 {@Overridepublic void save() {System.out.println("real business save method");}@Overridepublic void add() {System.out.println("real business add method");}@Overridepublic 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 methodcglib代理,事务结束!
但是如果proxy-target-class=false的时候,强制获取TargetClass的代理对象,则会报错:

Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.java.advice151.TargetClassat 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;}@Overridepublic 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;}@Overridepublic 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

0 0
原创粉丝点击