设计模式之美笔记13 忘是亡心i 2022-11-30 12:27 144阅读 0赞 > 记录学习王争的设计模式之美 课程 笔记和练习代码,以便回顾复习,共同进步 ### 文章目录 ### * * 策略模式 * * 策略模式的原理和实现 * * 1. 策略的定义 * 2. 策略的创建 * 3. 策略的使用 * 如何利用策略模式避免分支判断 * 文件排序 * * 问题和解决思路 * 代码实现和分析 * 代码优化和重构 * 责任链模式 * * 原理和实现 * * 第一种实现 * 第二种实现 * 责任链模式的应用场景 * servlet filter * spring Interceptor ## 策略模式 ## 实际项目开发中,策略模式较为常用,利用它避免冗长的if-else或者switch分支判断,还包括提供框架的扩展点等。 ### 策略模式的原理和实现 ### 策略模式,Strategy Design Pattern。定义: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. 定义一族算法类,将每个算法分别封装起来,让他们可以互相替换,策略模式可使得算法的变化独立于使用它的客户端(指的是使用算法的代码)。 工厂模式是解耦对象的创建和使用,观察者模式是解耦观察者和被观察者。策略模式类似,也起到解耦的作用,解耦的是策略的定义、创建、使用三部分。 #### 1. 策略的定义 #### 较为简单,包含一个策略接口和一组实现该接口的策略类。因为所有的策略类都实现相同的接口,所有,客户端代码基于接口而非实现编程,可灵活的替换不同的策略。 public interface Strategy { void algorithmInterface(); } public class ConcreteStrategyA implements Strategy { @Override public void algorithmInterface() { //...具体的算法... } } public class ConcreteStrategyB implements Strategy { @Override public void algorithmInterface() { //...具体的算法... } } #### 2. 策略的创建 #### 策略模式会包含一组策略,使用时,通过类型type判断创建哪种策略使用,为封装创建逻辑,需要对客户端代码屏蔽创建细节,可把根据type创建策略的逻辑抽离出来,放到工厂类。 public class StrategyFactory { private static final Map<String, Strategy> strategies = new HashMap<>(); static { strategies.put("A",new ConcreteStrategyA()); strategies.put("B",new ConcreteStrategyB()); } public static Strategy getStrategy(String type){ if (type == null || type.isEmpty()){ throw new IllegalArgumentException("type should not be empty"); } return strategies.get(type); } } 如果策略类是无状态的,不包含成员变量,只是纯粹的算法实现,这样的策略对象是可被共享使用的,不需要再每次调用getStrategy()的时候,都创建一个新的策略对象,针对这种情况,可使用上面的这种工厂类的实现方式,事先创建好每个策略对象,缓存到工厂类,用的时候直接返回;相反,如果策略类是有状态的,根据业务场景的需要,希望每次从工厂方法中,获得的是新创建的策略对象,而不是缓存好可共享的策略对象,代码如下: public class StrategyFactory { public static Strategy getStrategy(String type){ if (type == null || type.isEmpty()){ throw new IllegalArgumentException("type should not be empty."); } if (type.equals("A")){ return new ConcreteStrategyA(); }else if (type.equals("B")){ return new ConcreteStrategyB(); } return null; } } #### 3. 策略的使用 #### 客户端代码如何确定使用哪个策略?最常见的是运行时动态确定使用哪种策略,这也是策略模式最典型的应用场景。 运行时动态指的是,事先不知道会使用哪种策略,而是在程序运行时,根据配置、用户输入、计算结果等不确定因素,动态决定使用哪种策略。 public class UserCache { private Map<String, User> cacheData = new HashMap<>(); private EvictionStrategy eviction; public UserCache(EvictionStrategy eviction){ this.eviction = eviction; } //... } public class Application { //运行时动态确定,根据配置文件的配置决定使用哪种策略 public static void main(String[] args) throws Exception { EvictionStrategy evictionStrategy = null; Properties properties = new Properties(); properties.load(new FileInputStream("./config.properties")); String type = properties.getProperty("eviction_type"); evictionStrategy = EvictionStrategyFactory.getEvictionStrategy(type); UserCache userCache = new UserCache(evictionStrategy); //... } } 从上面代码看出,非运行时动态确定,也就是第二个Application中的使用方式,并不能发挥策略模式的优势,这种应用场景下,策略模式退化为面向对象的多态特性 或 基于接口而非实现编程原则。 ### 如何利用策略模式避免分支判断 ### 能移除分支判断逻辑的模式不仅有策略模式,还有状态模式,对于使用哪种模式,具体看应用场景。策略模式适用于根据不同类型的状态,决定使用哪种策略这种应用场景。 先看if-else货switch是如何产生的,如下: public class OrderService { public double discount(Order order){ double discount = 0.0; OrderType type = order.getType(); if (type.equals(OrderType.NORMAL)){ //普通订单 //...省略折扣计算算法代码 }else if (type.equals(OrderType.GROUPON)){ //团购订单 ...省略折扣计算算法代码 }else if (type.equals(OrderType.PROMOTION)){ //促销订单 ...省略折扣计算算法代码 } return discount; } } 那如何移除分支判断逻辑?策略模式,对代码重构,将不同类型的订单的打折策略设计为策略类,并由工厂类负责创建策略对象,具体代码: public interface DiscountStrategy { double calDiscount(Order order); } //省略NormalDiscountStrategy GrouponDiscountStrategy PromotionDiscountStrategy的创建 //策略的创建 public class DiscountStrategyFactory { private static final Map<OrderType,DiscountStrategy> strategies = new HashMap<>(); static { strategies.put(OrderType.NORMAL,new NormalDiscountStrategy()); strategies.put(OrderType.GROUPON,new GrouponDiscountStrategy()); strategies.put(OrderType.PROMOTION,new PromotionDiscountStrategy()); } public static DiscountStrategy getDiscountStrategy(OrderType type){ return strategies.get(type); } } public class OrderService { public double discount(Order order){ OrderType type = order.getType(); DiscountStrategy discountStrategy = DiscountStrategyFactory.getDiscountStrategy(type); return discountStrategy.calDiscount(order); } } 在策略工厂,用map缓存策略,根据type直接从map中获取对应的策略,避免if-else分支判断逻辑。本质上是借助“查表法”,根据type查表替代根据type分支判断。 但是,如果业务场景需要每次都创建不同的策略对象,就要用另一种工厂类的实现方式 public class DiscountStrategyFactory { public static DiscountStrategy getDiscountStrategy(OrderType type){ if (type == null){ throw new IllegalArgumentException("type should not be null"); } if (type.equals(OrderType.NORMAL)){ return new NormalDiscountStrategy(); }else if (type.equals(OrderType.GROUPON)){ return new GrouponDiscountStrategy(); }else if (type.equals(OrderType.PROMOTION)){ return new PromotionDiscountStrategy(); } return null; } } 这种情况下,实际并未真正移除if-else逻辑,而是将其移到工厂类中。 ### 文件排序 ### #### 问题和解决思路 #### 假设有个需求,希望写个小程序,实现对一文件进行排序的功能,文件只包含整型数,且相邻的数字通过逗号区分。如果你编写这个小程序,如何实现? 可能会觉得很简单,将文件的内容读出来,通过逗号分割为一个一个的数字,放到内存数组中,然后编写某种排序算法(如快排),或者直接用编程语言提供的排序函数。对数组排序,最后写入文件。 但如果文件很大呢?如有10GB,而内存有限(如只有8GB),没办法一次性加载文件的所有数据到内存,这时,利用外部排序算法了。 如果文件更大,如100GB,为利用CPU多核的优势,可在外排的基础上优化,加入多线程并发排序的功能,类似单机版的MapReduce。 如果文件非常大,如1TB,即便单机多线程程序,也算比较慢,这时可用MapReduce框架,利用多机处理能力,提高排序的效率。 #### 代码实现和分析 #### 先实现最简单直接的方式 public class Sorter { private static final long GB = 1000*1000*1000; public void sortFile(String filePath){ //省略校验逻辑 File file = new File(filePath); long fileSize = file.length(); if (fileSize < 6*GB){ quickSort(filePath); }else if (fileSize < 10*GB){ externalSort(filePath); }else if (fileSize < 100*GB){ concurrentExternalSort(filePath); }else { mapreduceSort(filePath); } } private void mapreduceSort(String filePath) { //利用MapReduce排序 } private void concurrentExternalSort(String filePath) { //多线程外排 } private void externalSort(String filePath) { //外部排序 } private void quickSort(String filePath) { //快排 } } public class SortingTool { public static void main(String[] args) { Sorter sorter = new Sorter(); sorter.sortFile(args[0]); } } 为避免sortFile()方法过长,把每种排序算法从sortFile()方法中抽离出来,分为4个独立的排序方法。 如果正在开发一个大型项目,排序文件只是其中一个功能模块,就要在代码设计和代码质量上下功夫。 刚如果自己实现算法的逻辑,会比较复杂,代码行数过多,所有排序算法的代码都堆在Sorter类,导致该类代码很多,此外,所有的排序算法都设计为Sorter的私有函数,也影响代码的复用性。 #### 代码优化和重构 #### 拆分重构后: public interface ISortAlg { void sort(String filePath); } public class QuickSort implements ISortAlg { @Override public void sort(String filePath) { //... } } public class ExternalSort implements ISortAlg { @Override public void sort(String filePath) { //... } } public class ConcurrentExternalSort implements ISortAlg { @Override public void sort(String filePath) { //... } } public class MapReduceSort implements ISortAlg { @Override public void sort(String filePath) { //... } } public class Sorter { private static final long GB = 1000*1000*1000; public void sortFile(String filePath){ //省略校验逻辑 File file = new File(filePath); long fileSize = file.length(); ISortAlg sortAlg; if (fileSize < 6*GB){ sortAlg = new QuickSort(); }else if (fileSize < 10*GB){ sortAlg = new ExternalSort(); }else if (fileSize < 100*GB){ sortAlg = new ConcurrentExternalSort(); }else { sortAlg = new MapReduceSort(); } sortAlg.sort(filePath); } } 拆分后,每个类的代码都不会太多,每个类的逻辑都不会太复杂,代码的可读性、可维护性提高。此外,排序算法设计为独立的类,跟具体的业务逻辑解耦,让排序算法可复用,实际是策略模式的第一步,解耦出策略的定义。 实际上,可继续优化,每种排序类都是无状态的,没必要每次使用时,重新创建一个新的对象。所有,可使用工厂模式对对象的创建进行封装,重构后: public class SortAlgFactory { private static final Map<String, ISortAlg> algs = new HashMap<>(); static { algs.put("QuickSort",new QuickSort()); algs.put("ExternalSort",new ExternalSort()); algs.put("ConcurrentExternalSort",new ConcurrentExternalSort()); algs.put("MapReduceSort",new MapReduceSort()); } public static ISortAlg getSortAlg(String type){ if (type == null || type.isEmpty()){ throw new IllegalArgumentException("type should not be empty"); } return algs.get(type); } } public class Sorter { private static final long GB = 1000*1000*1000; public void sortFile(String filePath){ //省略校验逻辑 File file = new File(filePath); long fileSize = file.length(); ISortAlg sortAlg; if (fileSize < 6*GB){ sortAlg = SortAlgFactory.getSortAlg("QuickSort"); }else if (fileSize < 10*GB){ sortAlg = SortAlgFactory.getSortAlg("ExternalSort"); }else if (fileSize < 100*GB){ sortAlg = SortAlgFactory.getSortAlg("ConcurrentExternalSort"); }else { sortAlg = SortAlgFactory.getSortAlg("MapReduceSort"); } sortAlg.sort(filePath); } } 两次重构后,代码已经符合策略模式的代码结构了,但Sorter类的sortFile()函数还是有一堆if-else逻辑,如果特别想一出,可基于查表法,其中algs是表。 public class Sorter { private static final long GB = 1000*1000*1000; private static final List<AlgRange> algs = new ArrayList<>(); static { algs.add(new AlgRange(0,6*GB, SortAlgFactory.getSortAlg("QuickSort"))); algs.add(new AlgRange(6*GB,10*GB, SortAlgFactory.getSortAlg("ExternalSort"))); algs.add(new AlgRange(10*GB,100*GB, SortAlgFactory.getSortAlg("ConcurrentExternalSort"))); algs.add(new AlgRange(100*GB,Long.MAX_VALUE, SortAlgFactory.getSortAlg("MapReduceSort"))); } public void sortFile(String filePath) { //省略校验逻辑 File file = new File(filePath); long fileSize = file.length(); ISortAlg sortAlg = null; for (AlgRange algRange:algs){ if (algRange.inRange(fileSize)){ sortAlg = algRange.getAlg(); break; } } sortAlg.sort(filePath); } private static class AlgRange{ private long start; private long end; private ISortAlg alg; public AlgRange(long start, long end, ISortAlg alg) { this.start = start; this.end = end; this.alg = alg; } public ISortAlg getAlg() { return alg; } public boolean inRange(long size){ return size >= start && size < end; } } } 即便如此,当添加新的排序算法时,还要修改代码,不完全符合开闭原则,如何满足?通过反射来避免对策略工厂的类的修改。具体:通过一个配置文件或自定义的annotation标注有哪些策略类;策略工厂类读取配置文件或搜索被annotation标注的策略类,反射动态加载这些策略类、创建策略对象;当新添加一个策略的时候,只要将该策略类添加到配置文件或用annotation标注即可。 对sorter来说,同样避免修改,通过将文件大小区间和算法之间的对应关系放到配置文件。当添加新的排序算法,只需要改动配置文件,不用改代码。 ## 责任链模式 ## 模板模式、策略模式和责任链模式的相同作用:复用和扩展。 ### 原理和实现 ### 责任链,Chain Of Responsibility Design Pattern。定义:Avoid coupling the sender of a request to its receive by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. 将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,沿着这条链传递这个请求,直到链上的某个接收对象能处理它为止。 在责任链中,多个处理器(也就是定义中的接收对象)依次处理同一个请求。一个请求先经过A处理器处理,然后把请求传递给B处理器,B处理器处理完后再传递给C处理器,依次类推,形成链。链上的每个处理器各自承担各自的处理职责,所以叫责任链模式。 先看代码实现,有两种。 #### 第一种实现 #### Handle是所有处理器的抽象父类,handle()是抽象方法,每个具体的处理器类(HandleA、HandleB)的handle()方法的代码结构类似,如果它能处理该请求,就不继续往下传递;如果不能,交由后面的处理器来处理(也就是调用successor.handle())。HandleChain是处理器链,从数据结构上看,就是个记录了链头、链尾的链表。其中,记录链尾是为了方便添加处理器。 public abstract class Handler { protected Handler successor = null; public void setSuccessor(Handler successor){ this.successor = successor; } public abstract void handle(); } public class HandlerA extends Handler { @Override public void handle() { boolean handled = false; //... if (!handled && successor != null){ successor.handle(); } } } public class HandlerB extends Handler { @Override public void handle() { boolean handled = false; //... if (!handled && successor != null){ successor.handle(); } } } public class HandlerChain { private Handler head = null; private Handler tail = null; public void addHandler(Handler handler){ handler.setSuccessor(null); if (head == null){ head = handler; tail = handler; return; } tail.setSuccessor(handler); tail = handler; } public void handle(){ if (head != null){ head.handle(); } } } public class Application { public static void main(String[] args) { HandlerChain chain = new HandlerChain(); chain.addHandler(new HandlerA()); chain.addHandler(new HandlerB()); chain.handle(); } } 上面的代码实现不够优雅,处理器的handle()方法,不仅包含自身的业务逻辑,还包含对下一个处理器的调用,也即是代码中的successor.handle()。很可能其他人在handle()方法调用它,导致代码出现bug。 针对该问题,重构,将调用它的逻辑从具体的处理器类中剥离出来,放到抽象父类中,具体的处理器类只需实现自己的业务逻辑即可。 public abstract class Handler { protected Handler successor = null; public void setSuccessor(Handler successor){ this.successor = successor; } public final void handle(){ boolean handled = doHandle(); if (!handled && successor != null){ successor.handle(); } } protected abstract boolean doHandle(); } public class HandlerA extends Handler { @Override protected boolean doHandle() { boolean handled = false; //... return handled; } } public class HandleB extends Handler { @Override protected boolean doHandle() { boolean handled = false; //... return handled; } } #### 第二种实现 #### 更加简单,HandlerChain类用数组而非链表保存所有的处理器,并需要在HandlerChain的handle()方法,依次调用每个处理器的handle()方法。 public interface IHandler { boolean handle(); } public class HandlerA implements IHandler { @Override public boolean handle() { boolean handled = false; //... return handled; } } public class HandlerB implements IHandler { @Override public boolean handle() { boolean handled = false; //... return handled; } } public class HandlerChain { private List<IHandler> handlers = new ArrayList<>(); public void addHandler(IHandler handler){ this.handlers.add(handler); } public void handle(){ for (IHandler handler:handlers){ boolean handled = handler.handle(); if (handled){ break; } } } } public class Application { public static void main(String[] args) { HandlerChain chain = new HandlerChain(); chain.addHandler(new HandlerA()); chain.addHandler(new HandlerB()); chain.handle(); } } 在GoF的定义中,如果责任链上某个处理器能够处理该请求,不会继续往下传递请求。实际,还有一种变体,就是请求会被所有处理器都处理一遍,不会中途终止。这种变体也有两种实现方式:用链表存储处理器和用数组存储处理器,和上面的两种实现方式类似,只需稍微修改即可。 只给出第一种实现方式: public abstract class Handler { protected Handler successor = null; public void setSuccessor(Handler successor){ this.successor = successor; } public final void handle(){ doHandle(); if (successor != null){ successor.handle(); } } protected abstract void doHandle(); } public class HandlerA extends Handler { @Override protected void doHandle() { //... } } public class HandlerB extends Handler { @Override protected void doHandle() { //... } } public class HandlerChain { private Handler head = null; private Handler tail = null; public void addHandler(Handler handler){ handler.setSuccessor(null); if (head == null){ head = handler; tail = handler; return; } tail.setSuccessor(handler); tail = handler; } public void handle(){ if (head != null){ head.handle(); } } } public class Application { public static void main(String[] args) { HandlerChain chain = new HandlerChain(); chain.addHandler(new HandlerA()); chain.addHandler(new HandlerB()); chain.handle(); } } ### 责任链模式的应用场景 ### 实际案例,对于支持UGC(User Generated Content,用户生成内容)的应用(如论坛),用户生成的内容可能会包含一些敏感词(如涉黄、广告、反动等词汇),针对该应用场景,可利用责任链模式过滤这些敏感词。 对于包含敏感词的内容,有两种处理方式,一种是直接禁止发布,另一种是给敏感词打马赛克(如用\*\*\*替换敏感词)之后再发布。第一种符合GoF的责任链的定义,第二种算是它的变体。 只给出第一种方式的代码案例,另外,只给出了代码实现的骨架,具体的敏感词过滤算法并没有给出。 public interface SensitiveWordFilter { boolean doFilter(Content content); } public class SexyWordFilter implements SensitiveWordFilter { @Override public boolean doFilter(Content content) { boolean legal = true; //... return legal; } } public class PoliticalWordFilter implements SensitiveWordFilter { @Override public boolean doFilter(Content content) { boolean legal = true; //... return legal; } } public class AdsWordFilter implements SensitiveWordFilter { @Override public boolean doFilter(Content content) { boolean legal = true; //... return legal; } } public class SensitiveWordFilterChain { private List<SensitiveWordFilter> filters = new ArrayList<>(); public void addFilter(SensitiveWordFilter filter){ this.filters.add(filter); } //return true if content doesn't contain sensitive words public boolean filter(Content content){ for (SensitiveWordFilter filter:filters){ if (!filter.doFilter(content)){ return false; } } return true; } } public class ApplicationDemo { public static void main(String[] args) { SensitiveWordFilterChain filterChain = new SensitiveWordFilterChain(); filterChain.addFilter(new AdsWordFilter()); filterChain.addFilter(new SexyWordFilter()); filterChain.addFilter(new PoliticalWordFilter()); boolean legal = filterChain.filter(new Content()); if (!legal){ //不发表 }else { //发表 } } } 如果像下面这种也能实现,而且更简单,为啥还要用责任链模式呢? public class SensitiveWordFilter { //return true if content doesn't contain sensitive words. public boolean filter(Content content){ if (!filterSexyWord(content)){ return false; } if (!filterAdsWord(content)){ return false; } if (!filterPoliticalWord(content)){ return false; } return true; } private boolean filterSexyWord(Content content){ //... } private boolean filterAdsWord(Content content){ //... } private boolean filterPoliticalWord(Content content){ //... } } 应用设计模式主要是为了应对代码的复杂性,让其满足开闭原则,提高代码的扩展性。 * 首先看,责任链模式如何应对代码的复杂性 将大块代码逻辑拆分为方法,将大类拆分为小类,第应对代码复杂性的常用方法,应用责任来呢模式,把各个敏感词过滤函数继续拆分,设计为独立的类,进一步简化SensitiveWordFilter类,让其代码不至于过多。 * 其次,责任链模式如何让代码满足开闭原则,提高代码的扩展性 当要扩展新的过滤算法,如还要过滤特殊符号,按照非责任链模式的代码实现,需要修改SensitiveWordFilter的代码,违反开闭原则,而责任链模式更加优雅,只需添加一个filter类,且通过addFilter()方法将其添加到FilterChain即可,其他代码完全不用改。 即使用责任链模式,当新添加过滤算法,还要修改客户端代码ApplicationDemo,没有完全符合开闭原则。其实细化,可把代码分为两类:框架代码和客户端代码。客户端代码属于使用框架的代码。 此外,利用责任链模式相对于不用的方式,还有好处是,配置过滤算法更灵活,可选择只用某几个过滤算法。 ### servlet filter ### servlet filter是java servlet规范中定义的组件,过滤器,可实现对http请求的过滤功能,如鉴权、限流、记录日志、验证参数等。因为是servlet规范的而一部分,所有tomcat、jetty等web容器都支持过滤器功能。 实际项目如何使用servlet filter?简单示例,添加一个过滤器,只需定义一个实现javax.sevlet.Filter接口的过滤器类,并将其配置到web.xml配置文件中。web容器启动时,读取web.xml的配置,创建过滤器对象。当有请求到来,先经过过滤器,再由servlet处理。 public class LogFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { //创建filter时自动调用 //其中filterConfig包含这个filter的配置参数,如name之类 } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("拦截客户端发来的请求"); filterChain.doFilter(servletRequest,servletResponse); System.out.println("拦截发送给客户端的请求"); } @Override public void destroy() { //销毁filter时自动调用 } } //web.xml的配置 <filter> <filter-name>logFilter</filter-name> <filter-class>com.ai.doc.chain.filter.v1.LogFilter</filter-class> </filter> <filter-mapping> <filter-name>logFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 添加过滤器非常方便,不需要改任何代码,那servlet如何做到的呢?用责任链模式。责任链的实现包含处理器接口IHandler或抽象类Handler,以及处理器链HandlerChain,对应到servlet filter,javax.servlet.Filter是处理器接口,FilterChain是处理器链,看FilterChain如何实现。以tomcat提供的实现类ApplicationFilterChain为例 public class ApplicationFilterChain implements FilterChain { private int pos = 0;//当前执行到哪个filter private int n;//filter的个数 private ApplicationFilterConfig[] filters; private Servlet servlet; @Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if (pos < n){ ApplicationFilterConfig filterConfig = filters[pos++]; Filter filter = filterConfig.getFilter(); filter.doFilter(request,response,this); }else { //filter都处理完毕,执行servlet servlet.service(request, response); } } public void addFilter(ApplicationFilterConfig filterConfig){ for (ApplicationFilterConfig filter:filters){ if (filter == filterConfig){ return; } } if (n==filters.length){ //扩容 ApplicationFilterConfig[] newFilters = new ApplicationFilterConfig[n+n]; System.arraycopy(filters,0,newFilters,0,n); filters = newFilters; } filters[n++] = filterConfig; } } ApplicationFilterChain的doFilter()方法的代码实现比较有技巧,实际是个递归调用,可用每个Filter(如LogFilter)的doFilter()的代码实现,直接替换ApplicationFilterChain的第12行代码即可。 @Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if (pos < n){ ApplicationFilterConfig filterConfig = filters[pos++]; Filter filter = filterConfig.getFilter(); System.out.println("拦截客户端发来的请求"); chain.doFilter(request,response);//chain就是this System.out.println("拦截发送给客户端的响应"); }else { //filter都处理完毕,执行servlet servlet.service(request, response); } } 这样实现是为了在一个doFilter()方法中,支持双向拦截,既能拦截客户端发来的请求,也能拦截发给客户端的响应。 ### spring Interceptor ### 拦截器,也是用来实现对http请求进行拦截处理。不同之处是,servlet filter是servlet规范的一部分,实现依赖于web容器。spring interceptor是spring mvc框架的一部分,由spring mvc框架来提供实现。客户端的请求,先经过servlet filter,再经过spring interceptor,最后到达具体的业务代码。 如何使用呢?如下,LogInterceptor实现的功能跟刚才的LogFilter一样,只是实现方式不同。LogFilter对请求和响应的拦截在doFilter()实现,而LogInterceptor对请求的拦截在preHandle()中实现,对响应的拦截在postHandle()中实现。 public class LogInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { System.out.println("拦截客户端发来的请求"); return true;//继续后续的处理 } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { System.out.println("拦截发给客户端的响应"); } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { System.out.println("这里总是被执行"); } } //spring mvc配置文件中的配置 <mvc:interceptors> <mvc:interceptor> <bean class="com.ai.doc.chain.filter.LogInterceptor"/> </mvc:interceptor> </mvc:interceptors> spring Interceptor底层如何实现?基于责任链模式,HandlerExecutionChain类是责任链模式的处理器链。实现相较于tomcat的ApplicationFilterChain,逻辑更清晰,不用递归,因为将请求和响应的拦截,拆分到两个方法中实现。源码如下: public class HandlerExecutionChain { private final Object handler; private HandlerInterceptor[] interceptors; public void addInterceptor(HandlerInterceptor interceptor){ initInterceptorList().add(interceptor); } boolean applyPreHandler(HttpServletRequest request, HttpServletResponse response){ handlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)){ for (int i=0;i<interceptors.length;i++){ HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request,response,this.handler)){ triggerAfterCompletion(request,response,null); return false; } } } return true; } void applyPostHandle(HttpServletRequest request,HttpServletResponse response){ handlerInterceptor[] interceptors = 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){ HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)){ for (int i=interceptors.length-1;i>=0;i--){ HandlerInterceptor interceptor = interceptors[i]; try{ interceptor.afterCompletion(request,response,this.handler,ex); }catch (Throwable ex2){ logger.error("HandlerInterceptor.afterCompletion throw exception",ex2); } } } } } 在spring框架,DsipatcherServlet的doDispatch()方法分发请求,在真正的业务逻辑执行前后,执行HandlerExecutionChain的applyPreHandle()和applyPostHandle()方法,用来实现拦截的功能。具体代码很简单。
相关 设计模式之美笔记16 > 记录学习王争的设计模式之美 课程 笔记和练习代码,以便回顾复习,共同进步 文章目录 解释器模式 解释器模式的原理和实现 深藏阁楼爱情的钟/ 2022年12月01日 11:53/ 0 赞/ 135 阅读
相关 设计模式之美笔记15 > 记录学习王争的设计模式之美 课程 笔记和练习代码,以便回顾复习,共同进步 文章目录 访问者模式 访问者模式的诞生 我就是我/ 2022年12月01日 05:16/ 0 赞/ 142 阅读
相关 设计模式之美笔记14 > 记录学习王争的设计模式之美 课程 笔记和练习代码,以便回顾复习,共同进步 文章目录 状态模式 背景 什么 水深无声/ 2022年11月30日 15:51/ 0 赞/ 146 阅读
相关 设计模式之美笔记13 > 记录学习王争的设计模式之美 课程 笔记和练习代码,以便回顾复习,共同进步 文章目录 策略模式 策略模式的原理和实现 忘是亡心i/ 2022年11月30日 12:27/ 0 赞/ 145 阅读
相关 设计模式之美笔记12 > 记录学习王争的设计模式之美 课程 笔记和练习代码,以便回顾复习,共同进步 文章目录 观察者模式 原理及应用场景剖析 深碍√TFBOYSˉ_/ 2022年11月30日 04:18/ 0 赞/ 173 阅读
相关 设计模式之美笔记11 > 记录学习王争的设计模式之美 课程 笔记和练习代码,以便回顾复习,共同进步 文章目录 门面模式 门面模式的原理和实现 ゞ 浴缸里的玫瑰/ 2022年11月28日 13:41/ 0 赞/ 155 阅读
相关 设计模式之美笔记10 > 记录学习王争的设计模式之美 课程 笔记和练习代码,以便回顾复习,共同进步 文章目录 序言 代理模式 桥接模式 柔情只为你懂/ 2022年11月28日 10:36/ 0 赞/ 149 阅读
相关 设计模式之美笔记9 > 记录学习王争的设计模式之美 课程 笔记和练习代码,以便回顾复习,共同进步 文章目录 工厂模式 1. 简单工厂 待我称王封你为后i/ 2022年11月28日 00:41/ 0 赞/ 148 阅读
相关 设计模式之美笔记8 > 记录学习王争的设计模式之美 课程 笔记和练习代码,以便回顾复习,共同进步 文章目录 单例模式 1. 为什么要使用单例 柔光的暖阳◎/ 2022年11月26日 07:52/ 0 赞/ 153 阅读
相关 设计模式之美笔记7 > 记录学习王争的设计模式之美 课程 笔记和练习代码,以便回顾复习,共同进步 文章目录 实战1:id生成器的重构 1. 需求背景 女爷i/ 2022年11月25日 13:19/ 0 赞/ 186 阅读
还没有评论,来说两句吧...