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 方法,返回自定义的子容器。

浙公网安备 33010602011771号