参考: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的方法!!!!