07.自定义子容器

1.使用监听器传入自定义的子容器

//监听器
public class addWebApplicationContextListener implements ServletContextListener {
    // 子容器在ServletContext中的唯一标识(全局常量,避免写错)
    public static final String MVC_CONTEXT_KEY = "SPRING_MVC_CONTEXT";

    public void contextInitialized(ServletContextEvent sce) {
        XmlWebApplicationContext mvcContext = null;
        try {
            // 创建xml型Web容器(适配传统xml配置)
            mvcContext = new XmlWebApplicationContext();
            // 指定子容器的配置文件(resources根目录下的spring-mvc.xml)
            mvcContext.setConfigLocation("classpath:spring-mvc.xml");
            // 关联ServletContext
            mvcContext.setServletContext(sce.getServletContext());
            // 刷新容器(核心,触发Bean扫描、初始化)
            mvcContext.refresh();
            // 将子容器绑定到ServletContext供DispatcherServlet获取
            sce.getServletContext().setAttribute(MVC_CONTEXT_KEY, mvcContext);
        } catch (Exception e) { // 捕获异常,避免监听器静默失败
            e.printStackTrace();
            throw new RuntimeException("创建Spring MVC子容器失败:" + e.getMessage(), e);
        }
    }
}

2.版本一:自定义DispatcherServlet版本:重写FrameworkServlet里的initWebApplicationContext方法

public class CustomDispatcherServlet extends DispatcherServlet {
    // 自定义标记,确保onRefresh仅执行一次
    private boolean onRefreshInvoked = false;
    // 国家之间同步锁,保证onRefresh执行时线程安全
    private final Object customOnRefreshLock = new Object();

    protected WebApplicationContext initWebApplicationContext() {
        // 自定义查找根容器,由ContextLoaderListener创建,否则为null
        WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext mvcContext = null;

        // 核心:从ServletContext获取监听器创建的子容器
        mvcContext = (WebApplicationContext) getServletContext().getAttribute(容器唯一标识);
        // 非空校验,子容器为null直接抛出异常
        if (mvcContext == null) throw new RuntimeException("获取子容器失败!请检查监听器是否注册");

        // 为子容器绑定父容器
        if (mvcContext instanceof XmlWebApplicationContext) {
            XmlApplicationContext xmlContext = (XmlApplicationContext) mvcContext;
            if (xmlContext.getParent() == null) xmlContext.setParent(rootContext);
        }

        // 调用onRefresh初始化MVC核心组件
        if (!onRefreshInvoked) synchronized (this.customOnRefreshLock) {
            onRefresh(mvcContext); // 触发HandlerMapping等组件初始化
            onRefreshInvoked = true;
        }

        // 将子容器绑定到ServletContext供Spring MVC内部全局获取
        String attrName = FrameworkServlet.SERVLET_CONTEXT_PREFIX + getServletName();
        getServletContext().setAttribute(attrName, mvcContext);

        // 返回有效容器供DispatcherServlet使用
        return mvcContext;
    }
}

3.版本二:自定义DispatcherServlet版本2:重写init(ServletConfig config)

public class CustomDispatcherServlet2 extends DispatcherServlet {
    private WebApplicationContext mvcContext;

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("=====自定义DispatcherServlet初始化");
        mvcContext = (WebApplicationContext) config.getServletContext().getAttribute(addWebApplicationContextListener.MVC_CONTEXT_KEY);
        if (mvcContext == null) {
            new ServletException("获取子容器失败");
        }
        System.out.println("获取子容器成功"+mvcContext.getId());
        super.init(config);//重点,保存ServletConfig对象
    }

    @Override
    protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
        return mvcContext;
    }
    //这个方法不用重写,重写它只是为了增强一个日日志功能
    @Override
    protected void initStrategies(ApplicationContext context) {
        super.initStrategies(context); 
        System.out.println("初始化完成,使用子容器");
    }
}

没有重写 initWebApplicationContext ,会默认调用 findWebApplicationContext() ,从 ServletContext 查找子容器,但是该方法是按照固定的Key( FrameworkServlet.SERVLET_CONTEXT_PREFIX + Servlet名称去找子容器 ),大概率会返回 null ,故而肯定会进入 createWebApplicationContext 方法,返回自定义的子容器。

posted @ 2025-12-02 21:45  那就改变世界吧  阅读(7)  评论(0)    收藏  举报