设计模式(四)--代理模式

设计模式(四)–代理模式

其他链接

JVM学习笔记(一)
JVM学习笔记(二)
JVM学习笔记(三)
JVM学习笔记(四)
(待更新…)

Java NIO
(待更新…)

设计模式(一)
设计模式(二)
设计模式(三)
设计模式(四)
(待更新…)

代理模式

1.简介

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

代理模式又分以下两种:

  • 静态代理

  • 动态代理

2.静态代理

角色

  • 抽象角色:接口或抽象类,统一操作标准
  • 真实角色:被代理的角色(如房东)
  • 代理角色:负责代理真实角色,代理真实角色去做其附属操作(如中间商)
  • 客户:访问代理的人。(如租客)

代码实现

租房接口—抽象角色

public interface Rent{
    
    
	//出租房子
	public void rent();
}

房东类----真实角色

public class Host implements Rent{
    
    
	//租房子
	public void rent(){
    
    
		System.out.println("出租房子");
	}
}

代理类—代理角色

public class Proxy implements Rent{
    
    
	private Host host;
	
	public Proxy(){
    
    }
	public Proxy(Host host){
    
    
		this.host = host;
	}
	//代理方法
	public void rent(){
    
    
        //代理角色给真实角色扩展了功能
		this.money();
		host.rent();
        this.seeHouse();
	}
	
	//一些代理的附属操作
	public void money(){
    
    
		System.out.println("代理收钱");
	}
	public void seeHouse(){
    
    
		System.out.println("代理带你看房子");
	}
}

客户类—客户

public class Client{
    
    
	public static void main(String[] args){
    
    
        Host host = new Host();
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}

好处

1、代理模式能将代理对象与真实被调用的目标对象分离。

2、一定程度上降低了系统的耦合度,扩展性好。

3、可以起到保护目标对象的作用。

4、可以对目标对象的功能增强。

3.动态代理

学动态代理最重要是get到那个动的概念,过于理论是很难理解动态代理的,结合代码实践反而易于理解。动态代理的核心就是通过反射机制实现的,通过反射获得被代理类的方法和调用执行器的方法。

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的
  • 动态代理分两大类:
    • 基于接口的动态代理
    • 基于类的动态代理

代码实现

租房接口—抽象角色

public interface Rent{
    
    
	//出租房子
	public void rent();
}

房东类----真实角色

public class Host implements Rent{
    
    
	//租房子
	public void rent(){
    
    
		System.out.println("出租房子");
	}
}

代理调用执行器

如果按静态代理那套的话 ,这里要写代理角色的,但是动态代理不同,动态代理的代理角色是动态生成的,这也是体现其动的表现。我们通过这个代理执行其去动态生成代理类。

  • InvocationHandler是java reflect包下的接口,其提供了invoke方法接口,用于代理角色的方法执行接口。
  • Proxy 也是java reflect包下的,其提供了一个静态方法放回一个动态代理类。

如果上面两个没听懂的话,无所谓,只要记住Proxy提供放回一个动态代理角色的方法,而实现InvocationHandler接口的类可以通过invoke()执行代理方法即可。

public class ProxyInvocationHandler implements InvocationHandler{
    
    
    //被代理的接口
    private Object target;
    
    public void setTarget(Object target){
    
    
        this.target = target;
    }
    
    public Object getProxy(){
    
    
        //返回动态代理类
        /*
        	Proxy的静态方法newProxyInstance返回的是代理对象,参数如下:
    (ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
        	loader:实现InvocationHandler接口的类的加载器
        	interfaces:被代理类的接口
        	h:实现InvocationHandler的类
        */
        return Proxy.newProxyInstance(this.getClass().getClassLoad(),
                                     target.getClass().getInterfaces(),
                                     this)
    }
    
    @Override
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
    
    
        /*
        	动态代理类一旦调用接口方法,就会执行invoke方法,
        	在这里可以对method.getName()做判断,看看是调用了那个接口方法。
        */
        System.out.println("这里可以添加代理操作!!!");
        Object result = method.invoke(this.target,args)
        return result;
    }
    
}

客户类—客户

public class Client{
    
    
	public static void main(String[] args){
    
    
        //创建被代理的类
        Host host = new Host();
        //创建代理调用执行器
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //设置代理对象
        pih.setTarget(host);
        //放回动态代理
        Rent hostProxy = (Rent)pih.getProxy();
        //动态代理调用接口方法
        hostProxy.rent();
    }
}

解析

上面代码执行流程开始解析:

  1. 创建被代理的类,这没什么好说的。
  2. 创建好代理调用执行器,该执行器实现了InvocationHandler接口,并且我还创建了getProxy()方法,使其能放回动态代理类。
  3. 调用setTarget(host)给该代理调用执行器绑定要被代理的对象
  4. 调用getProxy()方法,这步就好好好解析了,看看Proxy.newProxyInstance()的参数,这些参数告知了Proxy,被代理类有哪些接口方法(就是实现了那个接口方法interfaces),也告知了Proxy哪些类实现了InvocationHandler接口,并及其类的加载器。这些参数告知了放回了代理类应该有哪些方法,当这些方法被调用时,应该去找那个InvocationHandler接口的实现类去执行。
  5. 代理类执行rent()接口方法,实践上是去调用了ProxyInvocationHandler中的invoke()方法。

机制解析:

核心:就是通过反射机制获得被代理类的方法和调用执行器的方法。

有人就会问,为什么Proxy放回的动态代理会有被代理类的接口方法?

  • 那是因为啊,在newProxyInstance()时,你告知Proxy 被代理类的接口是那个(interfaces参数),所以放回的动态代理是实现了被代理类的接口方法的。

那为什么调用被代理类的接口的方法是去调用了ProxyInvocationHandler的invoke()方法呢?

  • 这就是动态代理的精髓了,当你给Proxy.newProxyInstance()方法传递InvocationHandler接口的实现类及其实现类的加载器的时候,Proxy其实就是完全拿到了那个类了,完全可以通过反射和这个类的加载器来创建该InvocationHandler接口的实现类,当你调用被代理类的接口方法rent()时,其实内部就是调用invoke()方法,并且给invoke方法传递你调用的是那个接口方法,还有其传递的参数。

猜你喜欢

转载自blog.csdn.net/qq_43203949/article/details/115103351