文章关键词:java代理,jdk动态代理,cglib代理,AOP,切面编程
今天,逛技术博客时,无意间发现了一篇有关动态代理模式的文章,感觉写的不错,自己正好也在巩固下基础知识,虽然实际工作中用代理的模式的不是特别多,那是因为你使用的框架,已经帮你封装好了,所以,你可能感觉不到,但是感觉不到不但表不存在,了解下它的原理和使用场景还是能提高下逼格的。于是散仙总结了下文,还有一个实战小例子,用来帮助理解。下面开始:
一:什么是代理?
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式一般涉及到的角色有:
抽象角色:声明真实对象和代理对象的共同接口;
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
说白了,代理模式其实就相当于找房子时的中介,你无须找到真实的房东,就可以把租房子这件事给办了,而且因为中介,所以便有了一些额外增强的功能,所以少数中介会提高房价或黑你一下,当然咱们的程序肯定不是跟中介一个德行的。
二:java中代理的种类
(1)静态代理: 只能提供固定的类的代理,局限性比较大
(2)JDK动态代理:只能代理接口,通过反射,提高了灵活性,但性能稍慢
(3)CGLIB代理:Spring AOP的默认实现,可以代理类,无须提供对应接口,通过ASM(asm是Java字节码操控框架,ASM是一套java字节码生成架构,它可以动态生成二进制格式的stub类或其它代理类,或者在类被java虚拟机装入内存之前,动态修改类)字节码增强,速度要快于JDK的默认的动态代理。
三:使用代理的应用场景
这个问题,其实就跟问Spring AOP切面编程的好处,就非常类似了,通过代理模式,我们可以无侵入式的,很方便的实现日志记录功能,方法权限拦截功能,事务控制等功能。
四:实战例子
项目结构:
百度云盘下载: http://pan.baidu.com/s/1eQ0h5YE
1,JDK动态代理,模拟事务控制功能,已经记录方法的耗时功能
package com.easy.model;
/***
*
* @author qindongliang
*
*/
public class Person {
//用户名
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name) {
super();
this.name = name;
}
public Person() {
// TODO Auto-generated constructor stub
}
}
Dao接口已经实现类,里面含有测试的方法:
package com.easy.dao;
import com.easy.model.Person;
/***
* 动态代理测试dao
* @author qindongliang
*
*/
public interface PersonDao {
//add user
public void addUser(Person user);
//delete user
public void deleteUser(String name);
//update user
public void updateUser(String name);
}
实现类:
package com.easy.dao.impl;
import com.easy.dao.PersonDao;
import com.easy.model.Person;
/***
* Dao的实现类
* @author qindongliang
*
*/
public class PersonDaoImpl implements PersonDao {
@Override
public void addUser(Person user) {
// TODO Auto-generated method stub
// System.out.println("add user name: "+user.getName());
try{
Thread.sleep(1000);//sleep 1 second
}catch(Exception e){
e.printStackTrace();
}
}
@Override
public void deleteUser(String name) {
// System.out.println("delete user name: "+name);
try{
Thread.sleep(1500);//sleep 1.5 second
}catch(Exception e){
e.printStackTrace();
}
}
@Override
public void updateUser(String name) {
// System.out.println("update user name: "+name);
try{
Thread.sleep(2000);//sleep 2 second
}catch(Exception e){
e.printStackTrace();
}
}
}
JDK动态代理实现:
package com.easy.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
/***
* JDK动态代理例子
* @author qindongliang
*
*/
public class JDK_Proxy implements InvocationHandler {
//代理实例
private Object proxy;
//通过构造参数赋值
public JDK_Proxy(Object proxy) {
this.proxy = proxy;
}
//格式化时间
private static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object obj=null;
//开始时间
long start=System.currentTimeMillis();
//以add开头的方法,加入事务控制
if(method.getName().startsWith("add")){
//开启事务
startTransaction(method);
obj=method.invoke(this.proxy, args);
//关闭事务
closeTransaction(method);
}else{
obj=method.invoke(this.proxy, args);
}
//调用结束时间
long end=System.currentTimeMillis();
System.out.println(sdf.format(new Date())+" "+method.getName()+"调用方法执行时间为:"+(end-start)/1000+"秒!");
System.out.println();
return obj;
}
//模拟开启事务
public void startTransaction(Method method){
System.out.println("请注意:"+method.getName()+"开启了 commit 事务操作 !");
}
//模拟关闭事务
public void closeTransaction(Method method){
System.out.println("请注意:"+method.getName()+"关闭了 commit 事务操作 !");
}
}
无接口的cglib代理测试类:
package com.easy.dao.impl;
/***
* 此类没有实现任何接口,只能使用Cglib代理来调用方法
* 必须有无参的构造方法
* @author qindongliang
*
*/
public class CarManager {
private String name;
public CarManager() {
// TODO Auto-generated constructor stub
}
public CarManager(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void run()throws Exception{
System.out.println(this.getName()+"汽车启动了!");
Thread.sleep(3000);
}
public void stop(){
System.out.println(this.getName()+"汽车停止了!");
}
}
Cglib代理的实现:
package com.easy.proxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/***
* Cglib代理测试
* @author qindongliang
*/
public class CGLIB_Proxy implements MethodInterceptor{
//代理对象
private Object target;
@SuppressWarnings("unchecked")
public static <T> T proxyTarget(T t) {
Enhancer en = new Enhancer();
en.setSuperclass(t.getClass());
en.setCallback(new CGLIB_Proxy(t));
T tt = (T) en.create();
return tt;
}
public CGLIB_Proxy(Object target) {
this.target = target;
}
@Override
public Object intercept(Object arg0, Method method, Object[] args,MethodProxy arg3) throws Throwable {
System.out.println("调用 "+method.getName()+" 开始...... ");
//调用代理
Object obj=method.invoke(this.target, args);
System.out.println("调用 "+method.getName()+" 结束...... ");
return obj;
}
}
测试的代码:
package com.easy.main;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import com.easy.dao.PersonDao;
import com.easy.dao.impl.CarManager;
import com.easy.dao.impl.PersonDaoImpl;
import com.easy.model.Person;
import com.easy.proxy.CGLIB_Proxy;
import com.easy.proxy.JDK_Proxy;
/**
* 包含jdk动态代理和cglib代理的测试
* @author qindongliang
*
*/
public class MethodTest {
public static void main(String[] args) throws Exception {
//cglibProxyTest();
jdkProxyTest();
}
/****
* cglib代理测试
*/
public static void cglibProxyTest()throws Exception{
CarManager car=new CarManager("大众");
//
// car.run();
// car.stop();
//得到代理类对象
CarManager carManager=CGLIB_Proxy.proxyTarget(car);
carManager.run();
}
/***
* jdk动态代理
*/
public static void jdkProxyTest() {
PersonDao user=new PersonDaoImpl();
Class<?> cls=user.getClass();
InvocationHandler handler=new JDK_Proxy(user);
//转换得来的代理对象
PersonDao proxy=(PersonDao)Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), handler);
proxy.addUser(new Person("hadoop"));
proxy.deleteUser("lucene");
proxy.updateUser("solr and elasticsearch");
}
}
测试JDK动态代理结果:
测试cglib代理结果:
总结:使用代理模式,在一些场景,非常灵活,而且无侵入式的发挥巨大的作用,这就是面向切面编程的核心思想。
最后欢迎大家扫码关注微信公众号:我是攻城师(woshigcs),我们一起学习,进步和交流!(woshigcs)
本公众号的内容是有关搜索和大数据技术和互联网等方面内容的分享,也是一个温馨的技术互动交流的小家园,有什么问题随时都可以留言,欢迎大家来访!
参考文章:
http://andy136566.iteye.com/blog/967952
http://www.cnblogs.com/coderworld/p/java-reflect.html
http://blog.csdn.net/leon709/article/details/9529307
http://www.cnblogs.com/coderworld/p/java-dynamic-proxy.htm