java代理模式的学习(动态代理+静态代理)

本文深入探讨了软件设计中的代理模式,包括静态代理与动态代理的区别及其应用场景。通过具体实例,介绍了如何实现这两种代理,并分析了各自的优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、为什么要使用代理模式

       为了为某些类的方法附加上额外且通用的功能,同时不改变原来的代码结构。eg:为操作数据库的代码加上事务(使用spring的aop)


二、代理模式的角色

       1、抽象角色:是下面的真实角色和代理角色的公有方法的集合。一般为接口,也可以是抽象方法

       2、真实角色:是拥有核心功能的类

       3、代理角色:拥有真实角色的引用,同时附加了额外的功能。

      eg:租房子这件事里面,有中介,房主,和租房者,这里的租房者就是客户端,中介是代理角色,房主就是真实角色。租房者要租房,但是他是通过中介这个代理去完成租房

           (核心功能)这件事的,但是同时他能够帮客户与房主联系,讨价还价等等(附加功能)。但是最终还是房主把房子租给了客户(完成核心功能的最终还是真实角色)。

    

三、三者之间的关系

      1、真实角色和代理角色要实现抽象角色定义的公有方法

      2、代理角色拥有真实角色的引用   


四、静态代理

首先看实现吧

1、抽象角色

package com.chenrui.proxy;

/**
 * 代理模式中的公有接口(起到统一的作用)
 * */
public interface Subject {

	public void request();
	
}

2、真实角色

</pre><pre name="code" class="java">package com.chenrui.proxy;

/**
 * 代理模式里面的真实角色
 * */
public class RealSubject implements Subject{

	@Override
	public void request() {
		System.out.println("this is the realSubject");
	}
	
	
}

3、代理角色

package com.chenrui.proxy;

/**
 * 代理模式里面的代理角色
 * */
public class ProxySubject implements Subject {

	private RealSubject rs;//代理角色所拥有的真实角色的引用,用于访问真实角色

	@Override
	public void request() {
		if(rs == null) {
			rs = new RealSubject();
		}
		rs.request();
		add();
	}
	
	public void add() {
		System.out.println("the extra function");
	}
	
}

4、测试程序
package com.chenrui.proxy;

public class Main {

	public static void main(String[] args) {
		
		Subject s = new ProxySubject();  
		
		s.request();
	}
}

最终的输出结果:

this is the realSubject
the extra function
静态代理的流程:

1、使用抽象角色的引用 获得 代理角色的对象(在里面生成了真实角色的对象)

2、调用方法   (这里调用的是代理角色的同名方法,在实现了真实角色的方法的同时,添加了额外功能,上面的是add)


静态代理的优点:编写简单,理解也简单吧。

静态代理的缺点:每一个类如果要添加额外功能的时候,都要对应一个代理类,在多个类的方法的额外功能相同的情况下,将会产生大量重复的代码。

为了解决这个问题,所以有了动态代理。


五、动态代理

  首先也先上代码

1、抽象角色(这个跟静态代理的一致)

package com.chenrui.DynamicProxy;

/**
 * 动态代理的接口类(抽象角色)
 * */
public interface Hello {

    void sayHello(String to);
  
    void print(String p); 
 
}

2、真实角色(这个也一致)

package com.chenrui.DynamicProxy;

/**
 * 真实角色
 * */
public class HelloImpl implements Hello {  
    
    public void sayHello(String to) {  
        System.out.println("Say hello to " + to);  
    }  
      
    public void print(String s) {  
        System.out.println("print : " + s);  
    }  
      
}  

3、处理类(这个就跟静态代理的不一致了,先把代码看完,下面有详细讲解)

package com.chenrui.DynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 相比静态代理的优点,这个是可以作为多个类的处理类的,比如添加事务的处理类。
 * */
public class LogHandler implements InvocationHandler {
    
    private Object dele;
    
    public LogHandler(Object obj) {
        this.dele = obj;
    }
    
    
    /**
     * 代理是在这里为原本的类添加新的功能
     * 这些能够用来进行用来为某些操作之前进行检查,特别是使用面向切面编程的时候。
     * */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        doBefore();
        Object result = method.invoke(dele, args);//这个是真正执行方法的地方
        after();
        return result;
    }
    
    private void doBefore() {
        System.out.println("before....");
    }
    
    private void after() {
        System.out.println("after....");
    }
}

4、测试程序

package com.chenrui.DynamicProxy;

import java.lang.reflect.Proxy;

/**
 * 测试程序
 * 
 * */


public class ProxyTest {  
	
    public static void main(String[] args) {  
        HelloImpl impl = new HelloImpl();   
        LogHandler handler = new LogHandler(impl); //这里指定了代理类所拥有的真实角色
        //这里把handler与新生成的代理类相关联  ,这里的动态代理的类是由jvM生成的,他所拥有的东西
        //(1)一个handler(拥有一个真实角色),handler同时还有一个invoke方法并且我们自己能够给他添加相应的功能
        //(2)生成的对象实现了了impl所实现的接口,也就是公共的接口
        Hello hello = (Hello) Proxy.newProxyInstance(impl.getClass().getClassLoader(), impl.getClass().getInterfaces(), handler);  
        //这里无论访问哪个方法,都是会把请求转发到handler.invoke  
        hello.print("All the test");  //在这一步,java将会调用proxy内部的LogHandler的invoke方法,当调用方法的时候,调用方法的对象就是上面的impl,方法的参数就是这里的参数,java内部提供了将这里的参数转换成object的方法
        hello.sayHello("Denny");  
    }  
  
}  

5、动态代理所用到的类和接口

     (1)Proxy类:使用其中的newProxyInstance方法生成一个代理对象,这个对象是jvm内部帮助我们生成的,这个生成的就是代理角色.

   方法的具体定义如下:

<pre name="code" class="java">public static Object newProxyInstance(ClassLoader loader,
                      Class<?>[] interfaces,
                      InvocationHandler h)
                               throws IllegalArgumentException


 loader:是真实角色的类加载器; 

interfaces:是真实角色所实现的所有公有接口的数组,这个相当于是让代理角色实现了公有接口

InvocationHandler:是一个处理器类,里面拥有真实角色的实现,额外功能的附加以及核心方法的调用都是在里面实现的

    (2)Interface InvocationHandler: 上面的处理器都要求要实现这个接口。里面有一个invoke方法


6、处理器(实现了InvocationHandler的类)的作用

      (1)实现额外功能

       (2)决定额外功能与和核心功能的调用顺序。

       (3)当代理类调用某个方法的时候,就是调用这里的invoke();然后里面的method.invoke(obj,args)方法实际上就是利用反射调用真实角色的对应方法。

7、抽象角色

       代理类:是动态生成的类(测试程序中的hello)通过newProxyInstance方法,在生成它的时候必须先提供接口给他,然后该类就宣布他实现了这些接口,但是实际上,他                          不会完成实际的工作,他对接口里面的方法的调用,实际上是对真实角色方法的调用。

8、动态体现在哪里?

      体现在代理类是在程序运行阶段才确定的,而不是在程序的编译阶段。


六、总结

静态代理和动态代理的比较
    (1) 动态代理使得附加操作能够被多个类共同使用(或者说是附加操作能够与不同类的不同方法组装到一起)
    (2) 组件之间的比较
              真实角色:一样
              接口:一样
              代理角色:不一样;代理角色里面其实还是可以分类的:(1)附加的功能 (2)真实角色的引用
              动态代理和静态代理最大的区别就是:静态代理将真实角色和附加功能绑定到了一起,而动态代理是将这两部分分成了两               个模块,使得他们能够任意组装成新的代理类。使得附加功能部分能够复用(handler)。
eg:还是数据库的事务,这些是所有数据库操作都共用的功能,这个时候他们就能够共用一个处理器了

      







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值