设计模式.md
文章目录
第一章-设计模式概述
https://www.bilibili.com/video/BV1G4411c7N4?p=61
1什么是设计模式
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
序号 | 模式 & 描述 | 包括 |
---|---|---|
1 | 创建型模式 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。 | 工厂模式(Factory Pattern)抽象工厂模式(Abstract Factory Pattern)单例模式(Singleton Pattern)建造者模式(Builder Pattern)原型模式(Prototype Pattern) |
2 | 结构型模式 这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。 | 适配器模式(Adapter Pattern)桥接模式(Bridge Pattern)过滤器模式(Filter、Criteria Pattern)组合模式(Composite Pattern)装饰器模式(Decorator Pattern)外观模式(Facade Pattern)享元模式(Flyweight Pattern)代理模式(Proxy Pattern) |
3 | 行为型模式 这些设计模式特别关注对象之间的通信。 | 责任链模式(Chain of Responsibility Pattern)命令模式(Command Pattern)解释器模式(Interpreter Pattern)迭代器模式(Iterator Pattern)中介者模式(Mediator Pattern)备忘录模式(Memento Pattern)观察者模式(Observer Pattern)状态模式(State Pattern)空对象模式(Null Object Pattern)策略模式(Strategy Pattern)模板模式(Template Pattern)访问者模式(Visitor Pattern) |
4 | J2EE 模式 这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。 | MVC 模式(MVC Pattern)业务代表模式(Business Delegate Pattern)组合实体模式(Composite Entity Pattern)数据访问对象模式(Data Access Object Pattern)前端控制器模式(Front Controller Pattern)拦截过滤器模式(Intercepting Filter Pattern)服务定位器模式(Service Locator Pattern)传输对象模式(Transfer Object Pattern) |
2设计模式的原则
单开里依接合迪
设计原则名称 | 设计原则简介 | 重要性 |
---|---|---|
单一职责原则(Single Responsibility Principle, SRP) | 类的职责要单一,不能将太多的职责放在一个类中 | ★★★★☆ |
开闭原则(Open-Closed Principle, OCP) | 软件实体对扩展是开放的,但对修改是关闭的,即在不修改一个软件实体的基础上去扩展其功能 | ★★★★★ |
里氏代换原则(Liskov Substitution Principle, LSP) | 在软件系统中,一个可以接受基类对象的地方必然可以接受一个子类对象 | ★★★★☆ |
依赖倒转原则(Dependency Inversion Principle, DIP) | 要针对抽象层编程,而不要针对具体类编程 | ★★★★★ |
接口隔离原则(Interface Segregation Principle, ISP) | 使用多个专门的接口来取代一个统一的接口。(若一个类不需要某个接口的方法,就不去实现它) | ★★☆☆☆ |
合成复用原则(Composite Reuse Principle, CRP) | 在系统中应该尽量多使用组合和聚合关联关系,尽量少使用甚至不使用继承关系 | ★★★★☆ |
迪米特法则(Law of Demeter, LoD) | 一个软件实体对其他实体的引用越少越好,或者说如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,而是通过引入一个第三者发生间接交互 | ★★★☆☆ |
创建 设计模式的原则。
** 1单一原则**
目的:
- 降低类的复杂度,一个类只负责一项职责
- 提高类的可读性,可维护性。
- 降低变更引起的风险
- 通常情况下,应遵守单一职责原则,只有逻辑足够简单,才能在代码违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一原则。
2接口隔离原则
一个类不应该依赖它不需要的接口方法
把一个接口拆分为好几个接口,保证各个接口的隔离,实现类不需要就不去实现。
3依赖倒转原则
依赖接口,而不是依赖对象。
4里氏代换原则
在软件系统中,一个可以接受基类对象的地方必然可以接受一个子类对象。
如果某子类需要用到其它类的方法,可以在这个子类中创建对象,然后使用。
5开闭原则
软件实体对扩展是开放的(对提供方),但对修改是关闭的,即在不修改一个软件实体的基础上去扩展其功能。
当软件需求发生变化时,尽量通过扩展来实现变化,而不是通过修改已有代码来实现。
6迪米特法则
也叫最少知道原则
一个类对自己依赖的类知道的越少越好。
对于被依赖的类来说,不管其多么复杂,都应该尽量把逻辑封装在类的内部。只对外提供public方法.
尽量使用直接朋友(属性,参数),避免出现局部变量(非直接朋友)。
7合成复用原则
尽量使用合成/聚合的方式,而不是使用继承。
聚合(aggregation)关系是一种弱的整体和部分的关系,整体和部分可以相互独立。
组合(composition)关系是一种强的整体和部分的关系,整体和部分具有相同的生命周期,同生共死。
依赖关系:一个类中用到了其它类。
【泛化关系】: 是一种继承关系,表现在类与类的继承关系;接口与接口的继承关系。驾驶员继承人类。(UML中带三角箭头的实线,箭头指向接口)
【实现关系】: 是实现类对接口实现的体现。鸟实现飞行的接口。(带三角箭头的虚线线,箭头指向接口)
【关联关系】: 是类与类之间的链接,它使一个类知道另一个类的属性和方法。关联可以是双向的,也可以是单向的。鸟栖息在某片森林。比如:成员变量
聚合(aggregation)关系是一种弱的整体和部分的关系,整体和部分可以相互独立。
组合(composition)关系是一种强的整体和部分的关系,整体和部分具有相同的生命周期,同生共死。
第二章-创建型模式
单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式
单例模式
饿汉式单例
1 2 3 4 5 6
public SingleInstance{ private final static Singleton single=new Singleton(); public static Singleton getInstance(){ return single; } }
优点:没有线程安全问题。
缺点:随着类加载而加载,若没有使用,会造成内存浪费;
懒汉式单例
1 2 3 4 5 6 7 8 9 10 11 12 13
class Singleton { private static Singleton singleton; private Singleton() {} public static Singleton getInstancet) { if (singleton == nu1l) { synchronized (Singleton.class) { if (singleton == nu1l) { singleton = new Singleton(); } } return singleton; }
双重检查保证了线程安全
静态内部类的写法
1 2 3 4 5 6 7 8 9
class Singleton { private Singleton() {} private static class SingletonInstance { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonInstance.INSTANCE; } }
- 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
- 静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
- 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的
enum枚举
1 2 3 4 5 6
enum Singleton{ INSTANCE; public void method(){ // xxxx } }
不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
在JDK的应用
|
|
工厂模式
简单工厂模式
把实例化对象的代码抽取出来
工厂方法模式
对工厂类进行抽象。
抽象工厂模式
对工厂类和产品类进行抽象。形成一个工厂簇和产品簇。
在JDK-Calendar应用
使用了简单工厂模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
private static Calendar createCalendar(TimeZone zone, Locale aLocale) { if (aLocale.hasExtensions()) { String caltype = aLocale.getUnicodeLocaleType("ca"); if (caltype != null) { switch (caltype) { case "buddhist": cal = new BuddhistCalendar(zone, aLocale); break; case "japanese": cal = new JapaneseImperialCalendar(zone, aLocale); break; case "gregory": cal = new GregorianCalendar(zone, aLocale); break; } } } return cal; }
根据不同的参数,返回不同的对象。
原型模式
- 原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
- 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节
- 工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象.clone()
- 形象的理解:孙大圣拔出猴毛, 变出其它孙大圣
Spring中用到的原型模式prototype
1 2 3 4 5 6 7 8 9
<bean id="id01" class="com.atguigu.spring.bean.Monster" scope="prototype"/> //获取bean applicationContext.getBean("id01"); //看是否为原型模式 else if (mbd.isPrototype()) { prototypeInstance = this.createBean(beanName, mbd, args);
浅拷贝:拷贝对象,对于基本类型成员变量,没影响。对于引用类型的数据,则会拷贝引用,若改变此对象,则影响拷贝的其它对象。
深拷贝:拷贝对象,对于基本类型的成员属性,没影响。对于引用类型的数据,也会拷贝对象,改变此对象,不影响拷贝的其它对象。
实现深拷贝的两种方式: 1实现Cloneable接口,重写clone方法。 2通过对象序列化实现深拷贝(推荐)
方法1 clone方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
class CloneSheep implements Cloneable{ public String string="d"; public String[] ss={"1","2"}; public CloneSheep sheep; @Override protected Object clone() throws CloneNotSupportedException { //若拷贝对象属性都是基本类和属性时,可以直接使用父类拷贝 //若有 对象属性,则需要重写 CloneSheep csheep=(CloneSheep)super.clone(); csheep.sheep=sheep.clone(); //这里麻烦的地方在于,要判断sheep是否为null //而且若对象复杂,则clone方法会很麻烦 return csheep; } }
方法2 对象序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
//创建流对象 ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ByteArrayInputStream bis = null; ObjectInputStream ois = null; try { //序列化 bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this); //当前这个对象以对象流的方式输出 //反序列化 bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); DeepProtoType copyObj = (DeepProtoType)ois.readObject(); return copyObj;
- 创建新的对象比较复杂时,可以利用原型模式简化 对象的创建过程,同时也能够提高效率
- 不用重新初始化对象,而是 动态地获得对象运行时的状态
- 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
- 在实现深克隆的时候可能需要比较复杂的代码
- 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改 其源代码,违背了 ocp 原则
建造者模式
建造者模式在JDK的应用:StringBuilder
Appendable 接口定义了多个append方法(抽象方法), 即Appendable 为抽象建造者, 定义了抽象方法。
AbstractStringBuilder 实现了 Appendable 接口方法,这里的AbstractStringBuilder 已经是建造者,只是不能实例化。
StringBuilder 即充当了指挥者角色,同时充当了具体的建造者,建造方法的实现是由 AbstractStringBuilder 完成, 而StringBuilder 继承了AbstractStringBuilder。
注意:
1 使用程序) 不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对 象 2 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对 象 3 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合 “开闭原则 ” 4 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似, 如果产品之间的差异性很大,则不适合使用建造者模式
第三章-结构型模式
适配器、装饰者、代理、桥接、组合、外观、享元
适配器
- 适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是==兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作==。其别名为包装器(Wrapper)
- 适配器模式属于结构型模式
- 主要分为三类:类适配器模式、对象适配器模式、接口适配器模式
类适配器和对象适配器类似,类适配器是通过继承实现被适配对象调用,对象适配器通过属性实现被适配对象调用。
接口适配器,当 不需要全部实现接口提供的方法时,可先 设计一个抽象类实现 接口,并为该接口中每个方法提供一个 默认实现(空方法),那么该 抽象类的子类可有选择地覆盖父类的某些方法来实现需求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
//在AbsAdapter 我们将 Interface4 的方法进行默认实现 public abstract class AbsAdapter implements Interface4 { //默认实现 public void m1() { } public void m2() { } public void m3() { } } ////////// AbsAdapter absAdapter = new AbsAdapter() { //只需要去覆盖我们 需要使用 接口方法 @Override public void m1() { // TODO Auto-generated method stub System.out.println("使用了m1的方法"); } }; absAdapter.m1();
实际应用
应用一:FutureTask适配Callable 为Runnable
FutureTask把Callable接口适配为Runnable接口。让其作为Runnable类运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
//FutureTask 实现了RunnableFuture接口 public class FutureTask<V> implements RunnableFuture<V> { private Callable<V> callable; public void run() { callable.call(); } } //RunnableFuture 是Runnable接口子类 public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); } ///使用 FutureTask future=new FutureTask(new classble1()); new Thread(future).start();
SpringMVC应用:Handler和HandlerAdapter
DispatcherServlet中的doDispatch方法
根据request请求先去HandelMapping中去寻找对应的handel(Controller)然后返回,
1 2 3 4 5
//根据Controller获取相应的HandlerAdapter处理器适配器 HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); //处理器适配器 调用方法处理请求 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
不管Controller的类型是Controller还是注解,还是RestController,都可以通过相应的HandlerAdapter进行适配然后调用方法处理请求。
装饰者模式
装饰者模式: 动态的将新功能 附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
例子:Decorator /ˈdekəreɪtər 装饰者/ 继承Drink
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// 1. 点一份 LongBlack Drink order = new LongBlack(); System.out.println("费用1=" + order.cost()); System.out.println("描述=" + order.getDes()); // 2. order 加入一份牛奶 order = new Milk(order); System.out.println("order 加入一份牛奶 费用 =" + order.cost()); System.out.println("order 加入一份牛奶 描述 = " + order.getDes()); // 3. order 加入一份巧克力 order = new Chocolate(order); System.out.println("order 加入一份牛奶 加入一份巧克力 费用 =" + order.cost()); System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes());
装饰者模式 实际应用:IO流
public abstract class InputStream implements Closeable{} //是一个抽象类,即Component
public class FilterInputStream extends InputStream { //是一个装饰者类Decorator 其属性protected volatile InputStream in //被装饰的对象 }
class DataInputStream extends FilterInputStream implements DataInput { //FilterInputStream 子类
代理模式
静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类
- 优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展
- 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类
- 一旦接口增加方法,目标对象与代理对象都要维护
JDK动态代理
- 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
- 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
- 动态代理也叫做:JDK代理、接口代理
Cglib动态代理
静态代理和JDK代理模式都要求目标对象是实现一个接口,有时候目标对象只是一个单独的对象,并没有实现任何的接口,这时可使用目标对象子类来实现代理-这就是Cglib代理。
Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截。
注意:
- 在内存中动态构建子类,注意代理的类不能为final,否则报错java.lang.IllegalArgumentException:
- 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
应用
例如Spring AOP(Spring的声明式事务),实现方法拦截。
- 目标对象需要实现接口,用JDK代理
- 目标对象不需要实现接口,用Cglib代理
其它
桥接
组合
外观
享元
第四章-行为型模式
模版方法模式、命令模式、访问者模式、迭代器模式、观察者 模式、中介者模式、备忘录模式、解释器模式(Interpreter模式) 、状态模 式、策略模式、职责链模式(责任链模式)。
模版方法模式
定义:
- 模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
- 简单说, 模板方法模 式 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤。
实际应用:SpringIOC容器中
我们使用的ClassPathXmlApplicationContext的父类为 AbstractXmlApplicationContext 又继承AbstractRefreshableConfigApplicationContext
观察者模式
定义:
观察者模式类似订牛奶业务
- 奶站/气象局:Subject
- 用户/第三方网站:Observer Subject:登记注册、移除和通知
优点
- 观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除 和通知。
- 这样,我们增加观察者(这里可以理解成一个新的公告板),就不需要去修改核 心类WeatherData不会修改代码,遵守了ocp原则。
实际应用:JDK 的Observable类、web开发的监听
- Observable 的作用和地位等价于 我过 们前面讲过Subject
- Observable 是类,不是接口,类中已经实现了核心的方法 , 即管理Observer 法 的方法 add.. delete .. notify…
- Observer 的作用和地位等价于我们前面讲过的 Observer, 有update
- Observable 和 和 Observer 的使用方法和前面讲过的一样,只是Observable 是 类,通过继承来实现观察者模式
职责链模式(责任链模式)
定义
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止.
职责链可以是一条直线,一个环或者树形结构,常见的是直线型,即沿着一条单向的链来传递请求。客户无需关心请求的处理细节以及请求的传递,只需将请求发送到链上。将请求的发送者和处理者解耦,是职责链模式的动机。
Handler中有一个属性,是自己。还有一个方法负责处理请求。当使用时,创建多个Handler的子类,然后设置下级节点(或者形成一个环形链表),根据请求的参数不同,看是由谁处理,调用者不需要关系由谁处理,只需要设置好责任链就好了。
实际应用:OA审批、SpringMVC
如果金额 小于等于 5000, 由教学主任审批 如果金额 小于等于 10000, 由院长审批
DispatcherServlet类中的doDispatch方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { try { ModelAndView mv = null; Object dispatchException = null; try { processedRequest = this.checkMultipart(request); multipartRequestParsed = processedRequest != request; // 获取该请求的handler,每个handler实为HandlerExecutionChain,它为一个处理链,负责处理整个请求 mappedHandler = this.getHandler(processedRequest); if (mappedHandler == null) { this.noHandlerFound(processedRequest, response); return; } HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) { return; } } // 责任链执行预处理方法,实则是将请求交给注册的请求拦截器执行 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 实际的执行逻辑的部分,也就是你加了@RequestMapping注解的方法 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); this.applyDefaultViewName(processedRequest, mv); // 责任链执行后处理方法,实则是将请求交给注册的请求拦截器执行 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception var20) { dispatchException = var20; } catch (Throwable var21) { dispatchException = new NestedServletException("Handler dispatch failed", var21); } // 处理返回的结果,触发责任链上注册的拦截器的AfterCompletion方法,其中也用到了HandlerExecutionChain注册的handler来处理错误结果 this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException); } catch (Exception var22) { // 触发责任链上注册的拦截器的AfterCompletion方法 this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22); } }
从上面的代码中我们可以看到,HandlerExecutionChain主要负责请求的拦截器的执行和请求的处理,但是他本身不处理请求,只是将请求分配给在链上注册的处理器执行,这是一种责任链的实现方式,减少了责任链本身与处理逻辑之间的耦合的同时,规范了整个处理请求的流程,下面我们看一下上面代码中涉及到的方法在HandlerExecutionChain类中对应的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = this.getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request, response, this.handler)) { this.triggerAfterCompletion(request, response, (Exception)null); return false; } } } return true; } void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = this.getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for(int i = interceptors.length - 1; i >= 0; --i) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(request, response, this.handler, mv); } } } void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception { HandlerInterceptor[] interceptors = this.getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for(int i = this.interceptorIndex; i >= 0; --i) { HandlerInterceptor interceptor = interceptors[i]; try { interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable var8) { logger.error("HandlerInterceptor.afterCompletion threw exception", var8); } } } }
其它
命令模式、访问者模式、迭代器模式、、中介者模式、备忘录模式、解释器模式(Interpreter模式) 、状态模 式、策略模式、
文章作者 卢森林
上次更新 2020-08-15