代理模式是什么

代理模式是一种设计模式,简单说即是在不改变源码的情况下,实现对目标对象功能扩展

比如有个歌手对象叫Singer,这个对象有一个唱歌方法叫sing()。

1 public class Singer{
2 public void sing(){
3 System.out.println("唱一首歌");
4 }
5 }

假如你希望,通过你的某种方式生产出来的歌手对象,在唱歌前后还要想观众问好和答谢,也即对目标对象Singer的sing方法进行功能扩展。

1 public void sing(){
2 System.out.println("向观众问好");
3 System.out.println("唱一首歌");
4 System.out.println("谢谢大家");
5 }

但是往往你又不能直接对源代码进行修改,可能是你希望原来的对象还保持原来的样子,又或许你提供的只是一个可插拔的插件,甚至你有可能都不知道你要对哪个目标对象进行扩展。这时就需要用到java的代理模式了。网上好多用生活中的经理人的例子来解释“代理”,看似通俗易懂,但我觉得不适合程序员去理解。程序员应该从代码的本质入手。

Java的三种代理模式

想要实现以上的需求有三种方式,这一部分我们只看三种模式的代码怎么写,先不涉及实现原理的部分。

1.静态代理

 1 public interface ISinger {
2 void sing();
3 }
4
5 /**
6 * 目标对象实现了某一接口
7 */
8 public class Singer implements ISinger{
9 public void sing(){
10 System.out.println("唱一首歌");
11 }
12 }
13
14 /**
15 * 代理对象和目标对象实现相同的接口
16 */
17 public class SingerProxy implements ISinger{
18 // 接收目标对象,以便调用sing方法
19 private ISinger target;
20 public UserDaoProxy(ISinger target){
21 this.target=target;
22 }
23 // 对目标对象的sing方法进行功能扩展
24 public void sing() {
25 System.out.println("向观众问好");
26 target.sing();
27 System.out.println("谢谢大家");
28 }
29 }

测试

 1 /**
2 * 测试类
3 */
4 public class Test {
5 public static void main(String[] args) {
6 //目标对象
7 ISinger target = new Singer();
8 //代理对象
9 ISinger proxy = new SingerProxy(target);
10 //执行的是代理的方法
11 proxy.sing();
12 }
13 }

  总结:其实这里做的事情无非就是,创建一个代理类SingerProxy,继承了ISinger接口并实现了其中的方法。只不过这种实现特意包含了目标对象的方法,正是这种特征使得看起来像是“扩展”了目标对象的方法。假使代理对象中只是简单地对sing方法做了另一种实现而没有包含目标对象的方法,也就不能算作代理模式了。所以这里的包含是关键。

  缺点:这种实现方式很直观也很简单,但其缺点是代理对象必须提前写出,如果接口层发生了变化,代理对象的代码也要进行维护。如果能在运行时动态地写出代理对象,不但减少了一大批代理类的代码,也少了不断维护的烦恼,不过运行时的效率必定受到影响。这种方式就是接下来的动态代理。

2.动态代理(也叫JDK代理)

跟静态代理的前提一样,依然是对Singer对象进行扩展

 1 public interface ISinger {
2 void sing();
3 }
4
5 /**
6 * 目标对象实现了某一接口
7 */
8 public class Singer implements ISinger{
9 public void sing(){
10 System.out.println("唱一首歌");
11 }
12 }

这回直接上测试,由于java底层封装了实现细节(之后会详细讲),所以代码非常简单,格式也基本上固定。

调用Proxy类的静态方法newProxyInstance即可,该方法会返回代理类对象

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

接收的三个参数依次为:

  • ClassLoader loader:指定当前目标对象使用类加载器,写法固定
  • Class<?>[] interfaces:目标对象实现的接口的类型,写法固定
  • InvocationHandler h:事件处理接口,需传入一个实现类,一般直接使用匿名内部类

测试代码

 1 public class Test{
2 public static void main(String[] args) {
3 Singer target = new Singer();
4 ISinger proxy = (ISinger) Proxy.newProxyInstance(
5 target.getClass().getClassLoader(),
6 target.getClass().getInterfaces(),
7 new InvocationHandler() {
8 @Override
9 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
10 System.out.println("向观众问好");
11 //执行目标对象方法
12 Object returnValue = method.invoke(target, args);
13 System.out.println("谢谢大家");
14 return returnValue;
15 }
16 });
17 proxy.sing();
18 }
19 }

总结:以上代码只有标黄的部分是需要自己写出,其余部分全都是固定代码。由于java封装了newProxyInstance这个方法的实现细节,所以使用起来才能这么方便,具体的底层原理将会在下一小节说明。

缺点:可以看出静态代理和JDK代理有一个共同的缺点,就是目标对象必须实现一个或多个接口,加入没有,则可以使用Cglib代理。

3.Cglib代理

前提条件:

  • 需要引入cglib的jar文件,由于Spring的核心包中已经包括了Cglib功能,所以也可以直接引入spring-core-3.2.5.jar
  • 目标类不能为final
  • 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
1 /**
2 * 目标对象,没有实现任何接口
3 */
4 public class Singer{
5
6 public void sing() {
7 System.out.println("唱一首歌");
8 }
9 }
 1 /**
2 * Cglib子类代理工厂
3 */
4 public class ProxyFactory implements MethodInterceptor{
5 // 维护目标对象
6 private Object target;
7
8 public ProxyFactory(Object target) {
9 this.target = target;
10 }
11
12 // 给目标对象创建一个代理对象
13 public Object getProxyInstance(){
14 //1.工具类
15 Enhancer en = new Enhancer();
16 //2.设置父类
17 en.setSuperclass(target.getClass());
18 //3.设置回调函数
19 en.setCallback(this);
20 //4.创建子类(代理对象)
21 return en.create();
22 }
23
24 @Override
25 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
26 System.out.println("向观众问好");
27 //执行目标对象的方法
28 Object returnValue = method.invoke(target, args);
29 System.out.println("谢谢大家");
30 return returnValue;
31 }
32 }

这里的代码也非常固定,只有标黄部分是需要自己写出

测试

 1 /**
2 * 测试类
3 */
4 public class Test{
5 public static void main(String[] args){
6 //目标对象
7 Singer target = new Singer();
8 //代理对象
9 Singer proxy = (Singer)new ProxyFactory(target).getProxyInstance();
10 //执行代理对象的方法
11 proxy.sing();
12 }
13 }

总结:三种代理模式各有优缺点和相应的适用范围,主要看目标对象是否实现了接口。以Spring框架所选择的代理模式举例

在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理

java的3中代理模式的更多相关文章

  1. 【原】谈谈对Objective-C中代理模式的误解

    [原]谈谈对Objective-C中代理模式的误解 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这篇文章主要是对代理模式和委托模式进行了对比,个人认为Objective ...

  2. java设计模式之Proxy(代理模式)

    java设计模式之Proxy(代理模式) 2008-03-25 20:30 227人阅读 评论(0) 收藏 举报 设计模式javaauthorizationpermissionsstringclass ...

  3. Java多线程编程中Future模式的详解

    Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...

  4. 黑马程序员:Java基础总结----静态代理模式&动态代理

    黑马程序员:Java基础总结 静态代理模式&动态代理   ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! 静态代理模式 public  class  Ts {   ...

  5. Java的三种代理模式

    Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩 ...

  6. Java设计模式之《代理模式》及应用场景

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6525527.html 代理模式算是我接触较早的模式,代理就是中介,中间人.法律上也有代理, ...

  7. Java的三种代理模式简述

    本文着重讲述三种代理模式在java代码中如何写出,为保证文章的针对性,暂且不讨论底层实现原理,具体的原理将在下一篇博文中讲述. 代理模式是什么 代理模式是一种设计模式,简单说即是在不改变源码的情况下, ...

  8. 理解java的三种代理模式

    代理模式是什么 代理模式是一种设计模式,简单说即是在不改变源码的情况下,实现对目标对象的功能扩展. 比如有个歌手对象叫Singer,这个对象有一个唱歌方法叫sing(). 1 public class ...

  9. Java的三种代理模式(Spring动态代理对象)

    Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩 ...

  10. Java设计模式学习记录-代理模式

    代理模式 代理模式是常见设计模式的一种,代理模式的定义是:为其他对象提供一种代理以控制对这个对象的访问. 在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起 ...

随机推荐

  1. 为什么手游选择lua热更新方案。

    https://www.zhihu.com/question/29603593 主要因为iOS系统的原因. 在这个知乎问题里,有两条回答,能解释我的疑惑.如下两个图. 我个人总结为,编译型语言(C#) ...

  2. PaddlePaddleOCR安装教程

    1.首先下载并安装python3.7.8 链接地址: 注:安装时Add path勾选 2.进入命令提示符界面 3.升级pip版本 python -m pip install --upgrade pip ...

  3. vvv,具名插槽

    <!DOCTYPE html> <html> <head> <script src="a.js"></script> & ...

  4. C语言学习--指针数组

    #include<stdio.h> //指针数组, 数组里面的每一个元素都是指针 int main() { int a = 10; int b = 20; int c = 30; // i ...

  5. 【20】python之操作MySQL数据库

    一.连接库安装 Python2.x:MySQLdb Python3.x :pymysql 二.接口信息 #创建数据库连接 pymysql.Connect()参数说明 host(str): MySQL服 ...

  6. 解决Vue.js devtools插件成功装上,却在控制台中找不到的问题

    参考:https://blog.csdn.net/CCCCt1/article/details/97668483

  7. 01、kafka常用命令

    001.kafka版本 kafka_2.13-3.0.0 kafka_2.12-2.8.0 002.模拟给topic名称是 yikuang 的发一条数据(hello world) ./kafka-co ...

  8. rdlc 中文在win10中显示异常,在非win10中显示正常

    RDLC中的默认字体Arial. 在win10中,RDLC为Arial时,显示中文会异常,这个时候,只需要将Font修改为中文字体就可以了,例如修改为宋体.

  9. Java流程控制练习

    练习 打印三角形及Debug的使用 public class TestDemo { public static void main(String[] args) { //打印三角形 5行 for(in ...

  10. Qt中资源文件qrc中的路径访问

    首先先看一下我们的qrc文件目录结构:  在文件系统中的目录结构是这样的:   请务必注意这边的前缀(按照网友推荐,大部分项目前缀都是只写一个"/"):  接下来进入正题,我们来分 ...