您的位置:首页 > 编程语言 > Java开发

java动态代理的实现

2015-11-05 16:08 447 查看
本文探讨两种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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 反射 动态代理 jdk