Java的三种代理模式简述
本文着重讲述三种代理模式在java代码中如何写出,为保证文章的针对性,暂且不讨论底层实现原理,具体的原理将在下一篇博文中讲述。
代理模式是什么
代理模式是一种设计模式,简单说即是在不改变源码的情况下,实现对目标对象的功能扩展。
比如有个歌手对象叫Singer,这个对象有一个唱歌方法叫sing()。
public class Singer{
public void sing(){
System.out.println("唱一首歌");
}
}
假如你希望,通过你的某种方式生产出来的歌手对象,在唱歌前后还要想观众问好和答谢,也即对目标对象Singer的sing方法进行功能扩展。
public void sing(){
System.out.println("向观众问好");
System.out.println("唱一首歌");
System.out.println("谢谢大家");
}
但是往往你又不能直接对源代码进行修改,可能是你希望原来的对象还保持原来的样子,又或许你提供的只是一个可插拔的插件,甚至你有可能都不知道你要对哪个目标对象进行扩展。这时就需要用到java的代理模式了。网上好多用生活中的经理人的例子来解释“代理”,看似通俗易懂,但我觉得不适合程序员去理解。程序员应该从代码的本质入手。
Java的三种代理模式
想要实现以上的需求有三种方式,这一部分我们只看三种模式的代码怎么写,先不涉及实现原理的部分。
1.静态代理
public interface ISinger {
void sing();
}
/**
* 目标对象实现了某一接口
*/
public class Singer implements ISinger{
public void sing(){
System.out.println("唱一首歌");
}
}
/**
* 代理对象和目标对象实现相同的接口
*/
public class SingerProxy implements ISinger{
// 接收目标对象,以便调用sing方法
private ISinger target;
public UserDaoProxy(ISinger target){
this.target=target;
}
// 对目标对象的sing方法进行功能扩展
public void sing() {
System.out.println("向观众问好");
target.sing();
System.out.println("谢谢大家");
}
}
测试
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
//目标对象
ISinger target = new Singer();
//代理对象
ISinger proxy = new SingerProxy(target);
//执行的是代理的方法
proxy.sing();
}
}
总结:其实这里做的事情无非就是,创建一个代理类SingerProxy,继承了ISinger接口并实现了其中的方法。只不过这种实现特意包含了目标对象的方法,正是这种特征使得看起来像是“扩展”了目标对象的方法。假使代理对象中只是简单地对sing方法做了另一种实现而没有包含目标对象的方法,也就不能算作代理模式了。所以这里的包含是关键。
缺点:这种实现方式很直观也很简单,但其缺点是代理对象必须提前写出,如果接口层发生了变化,代理对象的代码也要进行维护。如果能在运行时动态地写出代理对象,不但减少了一大批代理类的代码,也少了不断维护的烦恼,不过运行时的效率必定受到影响。这种方式就是接下来的动态代理。
2.动态代理(也叫JDK代理)
跟静态代理的前提一样,依然是对Singer对象进行扩展
public interface ISinger {
void sing();
}
/**
* 目标对象实现了某一接口
*/
public class Singer implements ISinger{
public void sing(){
System.out.println("唱一首歌");
}
}
这回直接上测试,由于java底层封装了实现细节(之后会详细讲),所以代码非常简单,格式也基本上固定。
调用Proxy类的静态方法newProxyInstance即可,该方法会返回代理类对象
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
接收的三个参数依次为:
ClassLoader loader:指定当前目标对象使用类加载器,写法固定Class<?>[] interfaces:目标对象实现的接口的类型,写法固定InvocationHandler h:事件处理接口,需传入一个实现类,一般直接使用匿名内部类
测试代码
public class Test{
public static void main(String[] args) {
3 Singer target = new Singer();
ISinger proxy = (ISinger) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("向观众问好");
//执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("谢谢大家");
return returnValue;
}
});
proxy.sing();
}
}
总结:以上代码只有标黄的部分是需要自己写出,其余部分全都是固定代码。由于java封装了newProxyInstance这个方法的实现细节,所以使用起来才能这么方便,具体的底层原理将会在下一小节说明。
缺点:可以看出静态代理和JDK代理有一个共同的缺点,就是目标对象必须实现一个或多个接口,加入没有,则可以使用Cglib代理。
3.Cglib代理
前提条件:
- 需要引入cglib的jar文件,由于Spring的核心包中已经包括了Cglib功能,所以也可以直接引入spring-core-3.2.5.jar
- 目标类不能为final
- 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
/**
* 目标对象,没有实现任何接口
*/
public class Singer{ public void sing() {
System.out.println("唱一首歌");
}
}
/**
* Cglib子类代理工厂
*/
public class ProxyFactory implements MethodInterceptor{
// 维护目标对象
private Object target; public ProxyFactory(Object target) {
this.target = target;
} // 给目标对象创建一个代理对象
public Object getProxyInstance(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
} @Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("向观众问好");
//执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("谢谢大家");
return returnValue;
}
}
这里的代码也非常固定,只有标黄部分是需要自己写出
测试
/**
* 测试类
*/
public class Test{
public static void main(String[] args){
//目标对象
Singer target = new Singer();
//代理对象
Singer proxy = (Singer)new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法
proxy.sing();
}
}
总结:三种代理模式各有优缺点和相应的适用范围,主要看目标对象是否实现了接口。以Spring框架所选择的代理模式举例
在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理
下一篇博文将详细讲述动态代理和Cglib代理的底层原理,敬请期待。
Java的三种代理模式简述的更多相关文章
- Java的三种代理模式
Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩 ...
- 理解java的三种代理模式
代理模式是什么 代理模式是一种设计模式,简单说即是在不改变源码的情况下,实现对目标对象的功能扩展. 比如有个歌手对象叫Singer,这个对象有一个唱歌方法叫sing(). 1 public class ...
- Java的三种代理模式(Spring动态代理对象)
Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩 ...
- Java的三种代理模式&完整源码分析
Java的三种代理模式&完整源码分析 参考资料: 博客园-Java的三种代理模式 简书-JDK动态代理-超详细源码分析 [博客园-WeakCache缓存的实现机制](https://www.c ...
- 转!!Java的三种代理模式
转自 http://www.cnblogs.com/cenyu/p/6289209.html 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象 ...
- 秒懂 Java 的三种代理模式
前言 代理(Proxy)模式是一种结构型设计模式,提供了对目标对象另外的访问方式:即通过代理对象访问目标对象. 这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. ...
- Java中三种代理模式
代理模式 代理(Proxy)是一种设计模式,提供了间接对目标对象进行访问的方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的功能上,增加额外的功能补充,即扩展目标对象的功能. 这就 ...
- Java的三种代理模式(Proxy,CGLib)
1.静态代理,这种不用说最不靠谱.每个类一个代理,代码量很大. 2.JDK代理.使用java.lang.reflect.Proxy进行代理,但是被代理的类必须要实现接口. 3.Cglib代理.不用实现 ...
- Java的三种代理模式:静态代理/JDK动态代理/Cglib动态代理
1.静态代理:需要定义接口或者父类,目标对象与代理对象均实现同一接口或继承同一父类. 2.JDK动态代理:需要目标对象实现一个接口,通过动态反射的机制,生成代理对象,实现同一个接口 3.Cglib动态 ...
随机推荐
- ⑨的完美冻青蛙(frog)
⑨的完美冻青蛙(frog) 时间限制: 1 Sec 内存限制: 128 MB 题目描述 输入 第一行是一个正整数n,表示上式中的p的个数. 接下来有n行,每一行两个正整数pi 和ei . 输出 ...
- 逃跑(escape)
逃跑(escape) 时间限制: 3 Sec 内存限制: 128 MB 题目描述 输入 第一行是5个正整数,n,m,k,S,T,分别代表无向图点数,边数,蝙蝠的数量,二小姐所在起点的编号,目标点的编 ...
- 流畅的python学习笔记:第五章
在python中一切都可以视作为对象,包括函数.我们来看个例子: def function_try(): '''it is funciton try doc''' print 'fun ...
- redis可视化客户端工具
TreeNMS是一款redis,Memcache可视化客户端工具,采用JAVA开发,实现基于WEB方式对Redis, Memcached数据库进行管理.维护. 功能包括:NoSQL数据库的展示,库表的 ...
- 如何编写一个带命令行参数的Python文件
看到别人执行一个带命令行参数的python文件,瞬间觉得高大上起来.牛逼起来,那么如何编写一个带命令行参数的python脚本呢?不用紧张,下面将简单易懂地让你学会如何让自己的python脚本,支持带命 ...
- TCP札记
协议对于通信就像算法对于计算一样.算法允许人们在不必知道特定的CPU指令集的情况下指定或理解具体的计算形式.同样地,通信协议允许人们不依赖特定厂家的网络硬件来指定或理解数据通信. 网络协议通常分不同层 ...
- java数组中取出最大值
class Demo{ public static void main(String []args){ int[] arr={3,54,456,342,2798}; int max=getMax(ar ...
- ajax异步加载遮罩层特效
<!doctype html> <html> <head> <title>遮罩层(正在加载中)</title> <meta chars ...
- JAVA基础——Arrays工具类十大常用方法
Arrays工具类十大常用方法 原文链接:http://blog.csdn.net/renfufei/article/details/16829457 0. 声明数组 String[] aArray ...
- 10.解决VUEX刷新的时候出现数据消失
通常,我们在使用vue编写页面时,会需要使用vuex在组件间传递(或者说共同响应)同一个数据的变化.例如:用户的登录信息. 下面,我们使用传递用户登录信息的例子来一步步解决这个问题. 首先,我们的第一 ...