spring之我见--从Filter机制看设计模式(责任链模式,装饰器模式)

最近为公司一个项目写一个简单的网关,其实也就是一个filter做一些token解析/校验。但是光这一个filter就不简单,现在就说说里面运用的设计模式。

Filter的核心-责任链模式

在这里插入图片描述
Filter: 属于javaweb中一个重要的组件,对请求资源进行处理(request和response)。
FilterChain: 管理着所有filter的顺序和执行,就像一个链条。通过这种链式串联,我们就可以对同一种对象资源实现不同业务场景的处理,达到业务解耦。

再看看spring里的源码实现:

FilterChain实现:

public final class ApplicationFilterChain implements FilterChain {

    /**
     * Filters.
     */
    private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
    
    /**
     * The int which is used to maintain the current position
     * in the filter chain.
     */
    private int pos = 0;

    /**
     * The int which gives the current number of filters in the chain.
     */
    private int n = 0;
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {
            internalDoFilter(request,response);
    }


private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {

        // Call the next filter if there is one
        if (pos < n) {
                 ApplicationFilterConfig filterConfig = filters[pos++];
                Filter filter = filterConfig.getFilter();
                filter.doFilter(request, response, this);
       }  
}
    
}

Filter的一个实现:

public class GateWayFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
       //这里填充业务代码
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

我删除了绝大部分跟涉及模式无关的代码,这个FilterChain 和 Filter 做了什么就很清晰了。
FilterChain 拥有 所有filter集合的配置类(ApplicationFilterConfig),每次请求都会 初始化 pos(filter数组下标) 和 n (filter过滤器总数)。 然后每次走完第一个 filter,都会执行 filterChain对象 的 doFilter继续执行下一个filter。

filterChain.doFilter(servletRequest, servletResponse);

责任链模式让我们的request 和response经过数十个filter的处理后(各个filter之间不耦合,可拓展性强),最终到达spring 的endpoint层,体会到了设计的精妙。

Filter的得力助手-装饰器模式

还是以具体场景为例,我在写网关过滤器的时候需要在过滤器中临时新加一些header信息,本来以为request会提供addHeader的方法,但是很遗憾,这个方法需要自己来实现。很明显的是,我们需要增强HttpServletRequest接口。这时候HttpServletRequestWrapper装饰器类登场了,它的构造器只接收一个HttpServletRequest对象,并且可以方便覆写HttpServletRequest的方法。

public class HttpServletRequestWrapper extends ServletRequestWrapper implements
        HttpServletRequest {

    /**
     * Constructs a request object wrapping the given request.
     *
     * @param request The request to wrap
     *
     * @throws java.lang.IllegalArgumentException
     *             if the request is null
     */
    public HttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }
    
	。。。。。。
 }

我们写一个继承HttpServletRequestWrapper的类。增加一个map变量,然后新增一个putHeader方法,用来实现我们增加header信息的目的,但是这样别人调用getHeaders方法还是取不到我们新增的键值对,我们需要复写getHeaders方法来实现目的。

class MutableHttpServletRequest extends HttpServletRequestWrapper {
        private final Map<String, String> customHeaders;

        public MutableHttpServletRequest(HttpServletRequest request) {
            super(request);
            this.customHeaders = new HashMap<>();
        }

        public void putHeader(String name, String value) {
            this.customHeaders.put(name, value);
        }

        @Override
        public Enumeration<String> getHeaders(String name) {
            Enumeration<String> headers = super.getHeaders(name);
            if (null == headers || !headers.hasMoreElements()) {
                String value = customHeaders.get(name);
                if (StringUtils.isNotBlank(value)) {
                    return Collections.enumeration(Arrays.asList(value));
                }
            } else {
                return headers;
            }
            return null;
        }

        @Override
        public String getHeader(String name) {
            // check the custom headers first
            String headerValue = customHeaders.get(name);

            if (StringUtils.isNotBlank(headerValue)) {
                return headerValue;
            }
            // else return from into the original wrapped object
            return super.getHeader(name);
        }

        @Override
        public Enumeration<String> getHeaderNames() {
            // create a set of the custom header names
            Set<String> set = new HashSet<>(customHeaders.keySet());

            // now add the headers from the wrapped request object
            @SuppressWarnings("unchecked")
            Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
            while (e.hasMoreElements()) {
                // add the names of the request headers into the list
                String n = e.nextElement();
                set.add(n);
            }

            // create an enumeration from the set and return
            return Collections.enumeration(set);
        }
    }
结语

虽然filter并不是spring的内容,但是设计模式处处都有,所以多看一些优秀的源码来提高自己的编码水平还是有道理的

<think>嗯,用户问的是在Java开发中常用的设计模式有哪些,面试的时候被问到这个问题该怎么回答。首先,我得回想一下常见的设计模式,以及它们在Java开发中的应用场景。可能用户是想准备面试,需要列出最常用的一些模式,同时给出具体的例子,这样面试时能更有说服力。 首先,单例模式肯定是常见的,比如Spring中的Bean默认就是单例。不过要注意线程安全和几种实现方式,比如双重检查锁、静态内部类或者枚举。需要解释清楚为什么用单例,以及不同实现方式的优缺点。 然后是工厂模式,分为简单工厂、工厂方法和抽象工厂。Spring的BeanFactory就是一个很好的例子,用工厂来解耦对象的创建和使用。可能需要举例说明工厂方法如何让子类决定实例化哪个类,而抽象工厂处理产品族的问题。 接下来是建造者模式,比如Lombok的@Builder注解,或者Java中的StringBuilder。适用于构造复杂对象,尤其是参数多且有可选参数的情况。需要说明和工厂模式的区别,工厂关注对象创建,建造者关注构造过程。 代理模式也很重要,特别是动态代理,Spring AOP就是基于动态代理实现的。要区分静态代理和动态代理,以及JDK动态代理和CGLIB的区别。比如JDK基于接口,CGLIB基于继承。 观察者模式,Java中的事件监听机制,或者Spring的事件发布订阅。需要提到Subject和Observer接口,以及如何实现松耦合的事件处理。 适配器模式,比如Java中的InputStreamReader,将字节流转换为字符流。还有Spring MVC中的HandlerAdapter,适配不同的处理器。说明如何让不兼容的接口一起工作。 装饰器模式,Java IO流中的BufferedInputStream,通过装饰器动态添加功能。和继承的不同之处在于可以在运行时组合功能,更灵活。 策略模式,比如Collections.sort()中的Comparator接口,不同的排序策略可以互换。Spring中的ResourceLoader根据不同策略加载资源。说明如何封装算法,使得它们可以相互替换。 模板方法模式,JdbcTemplate中的execute方法定义了流程,具体步骤由子类实现。或者Servlet中的doGet和doPost方法。强调定义算法骨架,具体步骤延迟到子类。 责任链模式,Servlet中的Filter链,或者Spring Security的过滤器链。每个处理器决定是否处理请求或传递给下一个。需要说明如何动态组合处理流程。 组合模式,处理树形结构,比如Java AWT中的Container和Component,或者XML解析中的节点结构。说明部分-整体的层次结构,统一对待单个对象和组合对象。 外观模式,比如SLF4J作为日志门面,封装了不同日志框架的复杂性。提供一个简化的接口,降低系统间的依赖。 状态模式,比如订单状态的变化,或者线程状态的转换。通过改变状态对象来改变行为,避免大量的条件判断。 可能还需要提到其他的模式,但用户可能更关注最常用的。需要结合实际项目经验,举出具体的例子,比如在某个功能中使用了哪种模式,解决了什么问题。这样回答更有深度,也能展示实际应用能力。 需要注意避免只是罗列模式名称,而是要解释每个模式的定义、适用场景、在Java中的典型应用,以及自己的使用经验。这样面试官会觉得你不仅知道理论,还能实际运用。 另外,可能用户还希望知道如何选择合适的设计模式,或者不同模式之间的区别,比如工厂方法和抽象工厂的区别,装饰器和代理模式的不同。但根据问题,可能主要需要列出常用模式,并给出简明扼要的解释和例子。 最后,总结时应该强调理解设计模式的原则,比如开闭原则、单一职责等,而不仅仅是模式本身。这样能展示更全面的设计思想。</think>在Java开发中,设计模式是面试高频考点。以下是常用设计模式的分类、核心思想及典型应用场景,结合代码示例说明: --- ### 一、**创建型模式** #### 1. **单例模式(Singleton)** - **作用**:确保一个类只有一个实例,并提供全局访问点。 - **实现方式**:双重校验锁(线程安全)、静态内部类、枚举。 - **Java应用**:Spring默认Bean作用域、数据库连接池。 ```java // 双重校验锁实现 public class Singleton { private volatile static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } ``` #### 2. **工厂模式(Factory)** - **简单工厂**:通过参数决定创建哪种对象(违反开闭原则)。 - **工厂方法**:定义接口,由子类决定实例化哪个类(Spring的`BeanFactory`)。 - **抽象工厂**:创建产品族(如GUI组件库)。 ```java // 工厂方法示例 public interface LoggerFactory { Logger createLogger(); } public class FileLoggerFactory implements LoggerFactory { public Logger createLogger() { return new FileLogger(); } } ``` #### 3. **建造者模式(Builder)** - **适用场景**:构造参数多且可选的对象(如`StringBuilder`)。 - **优势**:避免构造函数参数爆炸,支持链式调用。 ```java // Lombok @Builder简化实现 @Builder public class User { private String name; private int age; } // 使用:User user = User.builder().name("Alice").age(25).build(); ``` --- ### 二、**结构型模式** #### 4. **代理模式(Proxy)** - **动态代理**:Spring AOP基于JDK动态代理(接口)或CGLib(类)。 - **静态代理**:手动编写代理类,硬编码增强逻辑。 ```java // JDK动态代理示例 public class LogInvocationHandler implements InvocationHandler { private Object target; public LogInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before method: " + method.getName()); Object result = method.invoke(target, args); System.out.println("After method: " + method.getName()); return result; } } ``` #### 5. **适配器模式(Adapter)** - **类适配器**:通过继承适配(不推荐,破坏封装)。 - **对象适配器**:通过组合适配(推荐)。 - **应用**:`InputStreamReader`适配字节流到字符流。 ```java // 对象适配器示例 public class Adapter implements Target { private Adaptee adaptee; public Adapter(Adaptee adaptee) { this.adaptee = adaptee; } @Override public void request() { adaptee.specificRequest(); } } ``` #### 6. **装饰器模式(Decorator)** - **特点**:动态扩展功能,避免子类膨胀(如Java IO流)。 - **与代理模式区别**:装饰器关注增强功能,代理控制访问。 ```java // Java IO示例 InputStream in = new BufferedInputStream(new FileInputStream("test.txt")); ``` --- ### 三、**行为型模式** #### 7. **观察者模式(Observer)** - **应用**:事件驱动模型(如Spring的`ApplicationEvent`)。 - **实现方式**:`java.util.Observable`(已过时),推荐自定义实现。 ```java // Spring事件机制示例 @Component public class CustomEventPublisher { @Autowired private ApplicationEventPublisher publisher; public void publishEvent(String message) { publisher.publishEvent(new CustomEvent(this, message)); } } ``` #### 8. **策略模式(Strategy)** - **核心**:封装算法族,支持运行时切换(如`Comparator`)。 - **优势**:消除条件语句,符合开闭原则。 ```java // Collections.sort()策略应用 List<Integer> numbers = Arrays.asList(3, 1, 4); Collections.sort(numbers, (a, b) -> b - a); // 降序策略 ``` #### 9. **模板方法模式(Template Method)** - **实现**:定义算法骨架,子类重写步骤(如`JdbcTemplate`)。 ```java public abstract class AbstractTemplate { public void execute() { step1(); step2(); // 由子类实现 step3(); } private void step1() { /* 通用逻辑 */ } protected abstract void step2(); private void step3() { /* 通用逻辑 */ } } ``` --- ### 四、**高频考点扩展** 1. **如何选择设计模式?** - 创建对象复杂 → 工厂/建造者 - 扩展功能 → 装饰器/代理 - 算法切换 → 策略 - 解耦事件 → 观察者 2. **实际项目案例** - **订单状态流转** → 状态模式 - **权限校验链** → 责任链模式 - **兼容新旧接口** → 适配器模式 --- ### 五、**回答建议** - **结构化**:按分类(创建型/结构型/行为型)列举,突出常用模式。 - **结合项目**:举例说明在项目中如何应用某个模式解决具体问题。 - **深入原理**:说明模式背后的设计原则(如开闭原则、单一职责)。 示例回答: > "在项目中常用工厂模式解耦对象创建,例如支付模块中,根据不同的支付渠道(支付宝/微信)通过工厂创建对应的处理器。另外,使用策略模式实现优惠券计算规则,通过注入不同策略实现满减、折扣等算法的灵活切换。"
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值