动态代理是Java设计模式中使用频率较高的模式了,比如AOP将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码---解耦。刚开始接触动态代理是在设计模式和别人的博客中看到的,稍有理解就在博客中记录了下来,现在在来看,感觉就跟没学一样哈哈哈,最近看Java核心技术卷1,其在6.5小节提了一下动态代理,开头就说这是一种高级技术,是针对系统设计人员来说的(架构师),一般程序员遇到的情况少,而我感觉,不对啊,我们公司项目就用到了呀,赶紧再学习下!
首先说说概念:
什么是代理?从现实上来说,就好像我要做某件事,但是表面我上不想让别人知道实际上是我干的,我要找一个中间人来替我进行对外交互,比如我想写了情书要送给喜欢的女孩子,自己不好意思送就找一傻点儿的哥们替我送,他就是那种傻子,这种就是我们小时候认识的笨笨的,只会跑跑腿的哥们哈哈哈,吧这哥们就是我的代理,实际上写情书的是我,人家女孩子一看也喜欢我要给我回信(啧啧,一般都是这样),哥们跟小姐姐说:我拿回去读一下啊,这实际上也是拿回去给我读(他没法读,他是文盲)。所以说代理对外执行了操作,实际上是大哥我在背地里干的,有人要说了,自己送情书不是更有诚意?更能打动女神吗?让别人帮你做不是浪费人力物力还事倍功半?但是我作为一个大哥我写情书多累啊,绞尽脑汁地写一天写了500个字,自己害羞不是重点,重点是我每次写完就很累,然后想躺在床上吃零食,又懒得去买,另外每次写情书就没空写作业啦,让小弟过来帮着抄一下,我专心写情书,写完情书多累啊,可以让他在送完回来的路上再给买点儿零食啊,跑个腿啥的,而让小弟每次送情书干的这些是我是可以自己定义的,我让他干嘛就干嘛,哈哈哈!
下面先说说静态代理概念,
静态代理:代理类是在编译时就实现好的。也就是说 Java 编译完成后代理类是一个实际的 class 文件。拿泡妞来举例,首先我们定义一个借口:
/**
* 泡妞(引文垃圾,见谅)行为接口
*/
public interface FindLove {
//写情书
void writeLovePaper();
//送花儿
void sendFlowers();
//送礼物
void sendGifts();
}
接下来定义我和我的兄弟代理,均实现该泡妞接口
/**
* 委托类(幕后黑手我)
*/
public class Boy implements FindLove {
@Override
public void writeLovePaper() {
System.out.println("小美,我老喜欢你了,我每天晚上想你,碎不着觉!");
}
@Override
public void sendFlowers() {
System.out.println("花儿好像你啊,小美");
}
@Override
public void sendGifts() {
System.out.println("送你两包辣条,小美,我爱你爱得吃辣条都想你");
}
}
/**
* 代理类(傻兄弟苦力)
*/
public class BrotherProxy implements FindLove {
private Boy me;//实际背后执行者
public BrotherProxy(Boy boy) {
this.boy = boy;
}
@Override
public void writeLovePaper() {
System.out.println("替我抄作业(我要写情书,没空写作业了!)");
me.writeLovePaper();
System.out.println("买饮料、烧烤、薯片!");
}
@Override
public void sendFlowers() {
me.sendFlowers();
System.out.println("回来路上,买饮料、烧烤、薯片!");
}
@Override
public void sendGifts() {
me.sendGifts();
System.out.println("回来路上,买饮料、烧烤、薯片!");
}
}
这样我们就可以实现代理做苦力的事情了,是不是很舒服!那为什么又要有动态代理呢,想都不用想,肯定是静态代理弱鸡有缺陷啦,比如:
1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为Me类的访问提供了代理,但是如果还要为其他同学OtherClassMate类提供代理的话,就需要我们再次添加代理OtherClassMate的代理类。
现在问题来了如果每个同学泡妞就要找一个自己的傻子同学做代理,哪里培养那么多听话的傻子呢?如果青少年不能泡到女神,国家光棍将越来越多,老龄化加重,生厂力急剧下滑,国力日渐萎靡。国家一看这样不行啊,现在国内单身狗这么多,得促进男女配对啊,就招募一帮爱国的傻子做公务员,让他们做爱情代理、道歉代理等,啧啧!这样我们每个人都能快乐得追求幸福了不是,哈哈哈!那这就是我们智能化后的代理---动态代理,在我们需要送情书之前,打个110:喂,我要泡妞,给来个跑腿!
下面说说动态
动态代理:代理类是在运行时生成的。也就是说 Java 编译完之后并没有实际的 class 文件,而是在运行时动态生成的类字节码,并加载到JVM中。
动态代理实质实际上是两组静态代理组合!其优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数
Java实现动态代理的大致步骤如下:
1.定义一个委托类和公共接口(Boy和FindLove)。
2.自己定义一个类(调用处理器类,即实现InvocationHandler接口),这个类的目的是指定运行时将生成的代理类需要完成的具体任务(包括Preprocess和Postprocess),即代理类调用任何方法都会经过这个调用处理器类。
/*** 调用处理器
*/
public class FindLoveProxyimplements InvocationHandler {
private Objecttarget;//委托类对象
public FindLoveProxy(Object target) {
this.target = target;
}
@Override
public Objectinvoke(Object proxy, Method method, Object[] args)throws Throwable {
System.out.println("抄作业");
Object result = method.invoke(target, args);
System.out.println("买零食、跑腿等!");
return result;
}
}
3.生成代理对象(当然也会生成代理类),需要为他指定(1)委托对象(2)实现的一系列接口(3)调用处理器类的实例。因此可以看出一个代理对象对应一个委托对象,对应一个调用处理器实例。
InvocationHandler handler =new FindLoveHandler(new Boy());
Class[] interfaces = {FindLove.class,Apology.class};//需要实现的接口
FindLove proxy = (FindLove) Proxy.newProxyInstance(null, interfaces, handler);//null表示采用默认的类加载器
proxy.writeLovePaper();
proxy.sendFlowers();
proxy.sendGifts();
实际项目中,如果我们想实现一个数据库的短连接,及在使用过后就断开,就使用代理(动静都行)来实现,动态代理方便一些,这里使用了lamda简化,代码如下。
public DataBaseCommands getShortCommand(int database) {
DataBaseConnection connection = getDatabaseConnection();
RedisCommands target = connection.sync();
return (RedisCommands) Proxy.newProxyInstance(DataBaseCommands.class.getClassLoader(), new Class[]{DataBaseCommands.class}
, (proxy, method, args) -> {
Object result = method.invoke(target, args);
connection .close();
return result;
});
}