设计模式--代理模式Proxy

参考:https://blog.csdn.net/weixin_48052161/article/details/118684585

外观模式、代理模式和中介者模式的区别
外观模式(Facade Pattern)
定义一个外观类,外观类隐藏系统的复杂性,为客户端提供简化的方法和对现有系统类方法的委托调用。
例如:二手房交易的中介,属于外观模式。买房者通过中介可以简单地买到二手房,中介自己把联系房东看房砍价、过户、交税这些复杂的事情都搞定了。

代理模式(Proxy Pattern)
用一个代理类代表另一个类的功能,但是不改变被代理类的功能。目的是控制对被代理类的访问。

中介者模式(Mediator Pattern)
用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。将各对象之间的网状结构分离为星型结构。
例如:MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。微信群是各群员之间的中介者。

代理模式的分类:静态代理和动态代理

静态代理:在编译期间就被确定下来了:
代码演示:鸿星尔克加工厂
ClothFactory抽象:

package com.example.dtest.design23.proxy.state;

public interface ClothFactory {
    void produceCloth();
}

HXRKClothFactory工厂实现类:

package com.example.dtest.design23.proxy.state;

public class HXRKClothFactory implements ClothFactory{
    @Override
    public void produceCloth() {
        System.out.println("HXRK工厂生产一批运动服");
    }
}

ProxyClothFactory工厂代理类:

package com.example.dtest.design23.proxy.state;

public class ProxyClothFactory implements ClothFactory{

    private ClothFactory factory;

    public ProxyClothFactory(ClothFactory factory){
        this.factory = factory;
    }

    @Override
    public void produceCloth() {

        System.out.println("代理工厂做一些准备工作");

        factory.produceCloth();

        System.out.println("代理工厂做一些后续的收尾工作");

    }
}

测试类:

package com.example.dtest.design23.proxy.state;

public class StaticProxyTest {

    public static void main(String[] args) {
        //创建被代理类的对象
        ClothFactory hxrk = new HXRKClothFactory();
        //创建代理类的对象
        ClothFactory proxyClothFactory = new ProxyClothFactory(hxrk);

        proxyClothFactory.produceCloth();
    }

}

在这里插入图片描述

动态代理
参考:https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html
要了解动态代理首先的又反射的基本知识!
反射
我们得到类的实例,可以通过new,也可以通过工厂,也可以通过反射;
但是反射满足动态获取!
反射是适用于:一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
获取反射中的Class对象
在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的 Class 对象。

在 Java API 中,获取 Class 类对象有三种方法:

第一种,使用 Class.forName 静态方法。当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。

Class clz = Class.forName(“java.lang.String”);
第二种,使用 .class 方法。

这种方法只适合在编译前就知道操作的 Class。

Class clz = String.class;
第三种,使用类对象的 getClass() 方法。

String str = new String(“Hello”);
Class clz = str.getClass();

通过反射创建类对象
通过反射创建类对象主要有两种方式:通过 Class 对象的 newInstance() 方法通过 Constructor 对象的 newInstance() 方法

**第一种:通过 Class 对象的 newInstance() 方法。

Class clz = Apple.class;
Apple apple = (Apple)clz.newInstance();**

**第二种:通过 Constructor 对象的 newInstance() 方法

Class clz = Apple.class;
Constructor constructor = clz.getConstructor();
Apple apple = (Apple)constructor.newInstance();**

通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。

Class clz = Apple.class;
Constructor constructor = clz.getConstructor(String.class, int.class);
Apple apple = (Apple)constructor.newInstance(“红富士”, 15);

通过反射获取类属性、方法、构造器
我们通过 Class 对象的 getFields() 方法可以获取 Class 类的属性,但无法获取私有属性

Class clz = Apple.class;
Field[] fields = clz.getFields();
for (Field field : fields) {
    System.out.println(field.getName());
}

输出结果是:

price

而如果使用 Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性:

Class clz = Apple.class;
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
    System.out.println(field.getName());
}

输出结果是:

name
price

与获取类属性一样,当我们去获取类方法、类构造器时,如果要获取私有方法或私有构造器,则必须使用有 declared 关键字的方法。

动态代理:
的先看下静态代理和动态代理的优缺点;
参考:https://www.cnblogs.com/baizhanshi/p/6611164.html

静态代理类优缺点

优点:

代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合),对于如上的客户端代码,newUserManagerImpl()可以应用工厂将它隐藏,如上只是举个例子而已。

缺点:

1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度

2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为UserManager类的访问提供了代理,但是如果还要为其他类如Department类提供代理的话,就需要我们再次添加代理Department的代理类。

举例说明:代理可以对实现类进行统一的管理,如在调用具体实现类之前,需要打印日志等信息,这样我们只需要添加一个代理类,在代理类中添加打印日志的功能,然后调用实现类,这样就避免了修改具体实现类。满足我们所说的开闭原则。但是如果想让每个实现类都添加打印日志的功能的话,就需要添加多个代理类,以及代理类中各个方法都需要添加打印日志功能(如上的代理方法中删除,修改,以及查询都需要添加上打印日志的功能)

即静态代理类只能为特定的接口(Service)服务。如想要为多个接口服务则需要建立很多个代理类。

动态代理代码展示:

在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 类的支持

java.lang.reflect.Proxy类的定义如下:

//CLassLoader loader:类的加载器  
//Class<?> interfaces:得到全部的接口  
//InvocationHandler h:得到InvocationHandler接口的子类的实例  
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException  

通过这个方法,返回管理被代理的代理对象!

java.lang.reflect.InvocationHandler接口的定义如下:

//Object proxy:被代理的对象  
//Method method:要调用的方法  
//Object[] args:方法调用时所需要参数  
public interface InvocationHandler {  
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;  
}  

通过实现上面接口,从写invoke方法,达到通过反射动态产生的代理对象,调用方法时,是我们写的这个invoke的放射调用方法!!!!!

被代理类接口:

package com.example.dtest.design23.proxy.move;

public interface UserManager {

    public void addUser(String userId,String userName);

    public void delUser(String userId);

    public String findUser(String userId);

    public void modifyUser(String userId ,String userName);

}

被代理类实现类:

package com.example.dtest.design23.proxy.move;

public class UserManagerImpl implements UserManager{
    @Override
    public void addUser(String userId, String userName) {
        System.out.println("UserManagerImpl.addUser");
    }

    @Override
    public void delUser(String userId) {
        System.out.println("UserManagerImpl.delUser");
    }

    @Override
    public String findUser(String userId) {
        System.out.println("UserManagerImpl.findUser");
        return "张三";
    }

    @Override
    public void modifyUser(String userId, String userName) {
        System.out.println("UserManagerImpl.modifyUser");
    }
}

动态代理类:

package com.example.dtest.design23.proxy.move;

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

public class LogHandler implements InvocationHandler {

    // 目标对象
    private Object targetObject;
    //绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。
    public Object newProxyInstance(Object targetObject){
        this.targetObject = targetObject;
        //该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
        //第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
        //第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
        //第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
        //根据传入的目标返回一个代理对象
        return Proxy.newProxyInstance(
                targetObject.getClass().getClassLoader(),
                targetObject.getClass().getInterfaces(),
                this);

    }

    //关联的这个实现类的方法被调用时将被执行
    /*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("start-->>");
        for(int i=0;i<args.length;i++){
            System.out.println(args[i]);
        }
        Object ret = null;

        /*原对象方法调用前处理日志信息*/
        System.out.println("start-- >>");
        //调用目标方法
        ret = method.invoke(targetObject, args);

        /*原对象方法调用后处理日志信息*/
        System.out.println("success-->>");

        return ret;
    }
}

测试类:

package com.example.dtest.design23.proxy.move;

public class moveTest {

    public static void main(String[] args) {
        LogHandler logHandler = new LogHandler();
        UserManager userManager = (UserManager) logHandler.newProxyInstance(new UserManagerImpl());
        userManager.addUser("1234","张三");
    }

}

在这里插入图片描述

这里动态代理类是重点:
使用这个方法得到了被代理的对象的操作对象:
Proxy.newProxyInstance(
targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),
this);
而用这个对象调用方法时候,就会使用我们重写的invoke的方法!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值