java动态代理的实现
2015-11-05 16:08
447 查看
本文探讨两种java动态代理技术,JDK和cglib
使用JDK的代理只能对有接口的类做代理,且不能代理接口之外的方法(实现类中扩展的),cglib可以对无接口的类做代理,不受接口的限制
以下对两种代理方式进行剖析,通过模拟Spring的注入依赖(IOC)和面向切面(AOP)
首先看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的代理对象,则会报错:
当然了,这个错误是可控的,在类型检查的过程中直接把无接口的类过滤掉,不使用代理直接返回对象不就可以了,把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
代理有什么用?
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对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树
- [原创]java局域网聊天系统