SpringMVC中的RootApplicationContext上下文和WebApplicationContext上下文,通过注解配置SpringMVC的完整解决方案 您所在的位置:网站首页 servletname报错 SpringMVC中的RootApplicationContext上下文和WebApplicationContext上下文,通过注解配置SpringMVC的完整解决方案

SpringMVC中的RootApplicationContext上下文和WebApplicationContext上下文,通过注解配置SpringMVC的完整解决方案

2023-10-27 02:11| 来源: 网络整理| 查看: 265

注解配置SpringMVC原理简述 1. 准备知识1.1 两个应用上下文1.2 ServletContext配置方法(Configuration Methods)1.3 运行时插拔1.4 SpringServletContainerInitializer1.4.1 AbstractContextLoaderInitializer1.4.2 AbstractDispatcherServletInitializer1.4.3 AbstractAnnotationConfigDispatcherServletInitializer 2. SpringMVC配置案例2.1 自定义的Intializer类,继承了AbstractAnnotationConfigDispatcherServletInitializer2.2编写@Configuration类2.3 把@Controller类也贴出来2.4 引入自定义Servlet2.5 其他

1. 准备知识 1.1 两个应用上下文

在SpringMVC中,有两个应用上下文:RootApplicationContext、WebApplicationContext。其中,WebApplicationContext是DispatcherServlet专属的上下文,用来加载Controller、ViewResolver、HandlerMapping等web相关的Bean。RootApplicationContext则是加载数据库、@Service等中间件中的Bean。其中,WebApplicationContext继承了RootApplicationContext中的所有Bean,以便在@Controller中注入@Service等依赖。 记住这一点,接下来就可以分析SpringMVC的加载原理了。 1.2到1.4内容比较多,也可以直接阅读第二节: 2. SpringMVC配置案例。

1.2 ServletContext配置方法(Configuration Methods)

在Servlet3.0之后,Servlet新增了3个配置方法,可以通过编程的方式动态地装配Servlet、Filter、Listener。其中每种方法又有对应的重载版本。

addServlet ServletRegistration.Dynamic addServlet(String servletName, String className); ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet); ServletRegistration.Dynamic addServlet(String servletName, Class servletClass); addFilter FilterRegistration.Dynamic addFilter(String filterName, String className); FilterRegistration.Dynamic addFilter(String filterName, Filter filter); FilterRegistration.Dynamic addFilter(String filterName, Class filterClass); addLister public void addListener(String className); public void addListener(T t); void addListener(Class listenerClass); 1.3 运行时插拔

注意:以上2个配置方法,只能在ServletContextListener#contextInitialized或ServletContainerInitializer#onStartup方法中被调用。 ServletContextListener用于监听servlet上下文的生命周期

当web应用或者容器启动时,ServletContainerInitializer#onStartup方法将被回调。并通过@HandleTypes注解指定要处理的类型,对应类型的子类(包括抽象类)组成的集合作为onStartup方法的第一个参数

package javax.servlet; import java.util.Set; public interface ServletContainerInitializer { //第二个参数是,ServletContext void onStartup(Set var1, ServletContext var2) throws ServletException; } 1.4 SpringServletContainerInitializer

SpringServletContainerInitializer继承了ServletContainerInitializer 。 @HandlesTypes({WebApplicationInitializer.class}元注解表明了在容器或应用启动时,动态执行WebApplicationInitializer实现类的onStartup方法。

@HandlesTypes({WebApplicationInitializer.class}) public class SpringServletContainerInitializer implements ServletContainerInitializer { public SpringServletContainerInitializer() { } public void onStartup(@Nullable Set webAppInitializerClasses, ServletContext servletContext) throws ServletException { List initializers = new LinkedList(); Iterator var4; if (webAppInitializerClasses != null) { var4 = webAppInitializerClasses.iterator(); while(var4.hasNext()) { Class waiClass = (Class)var4.next(); if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { initializers.add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance()); } catch (Throwable var7) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7); } } } } if (initializers.isEmpty()) { servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); } else { servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); AnnotationAwareOrderComparator.sort(initializers); var4 = initializers.iterator(); while(var4.hasNext()) { WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next(); initializer.onStartup(servletContext); } } } }

WebApplicationInitializer接口及相关的3个实现类(都是抽象类) 在这里插入图片描述

package org.springframework.web; import javax.servlet.ServletContext; import javax.servlet.ServletException; public interface WebApplicationInitializer { void onStartup(ServletContext var1) throws ServletException; } 1.4.1 AbstractContextLoaderInitializer

protected访问修饰符的方法:createRootApplicationContext(),就是实例化RootApplication。AbstractContextLoaderInitializer可以替换以web.xml中配置ContextLoaderListener的方式,不过spring官方并不推荐这种方式(因为核心前端控制器DispatcherServlet相关逻辑还需要开发人员来实现),咱们接着往下看AbstractDispatcherServletInitializer。

public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer { protected final Log logger = LogFactory.getLog(this.getClass()); public AbstractContextLoaderInitializer() { } public void onStartup(ServletContext servletContext) throws ServletException { this.registerContextLoaderListener(servletContext); } protected void registerContextLoaderListener(ServletContext servletContext) { WebApplicationContext rootAppContext = this.createRootApplicationContext(); if (rootAppContext != null) { ContextLoaderListener listener = new ContextLoaderListener(rootAppContext); listener.setContextInitializers(this.getRootApplicationContextInitializers()); servletContext.addListener(listener); } else { this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context"); } } @Nullable protected abstract WebApplicationContext createRootApplicationContext(); @Nullable protected ApplicationContextInitializer[] getRootApplicationContextInitializers() { return null; } } 1.4.2 AbstractDispatcherServletInitializer

很明显,它继承了AbstractContextLoaderInitializer抽象类,装配了dispatcherServlet,但是并没有实现父类的createRootApplicationContext(),那么createRootApplicationContext是什么时候实现的呢,接着往下看AbstractAnnotationConfigDispatcherServletInitializer。 注意:AbstractDispatcherServletInitializer适用于Spring XML来驱动spring配置。

public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer { 。。。其他方法略 public void onStartup(ServletContext servletContext) throws ServletException { super.onStartup(servletContext); this.registerDispatcherServlet(servletContext); } //重点看下registerDispatcherServlet方法 protected void registerDispatcherServlet(ServletContext servletContext) { String servletName = this.getServletName(); Assert.hasLength(servletName, "getServletName() must not return null or empty"); WebApplicationContext servletAppContext = this.createServletApplicationContext(); Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null"); //实例化servletAppContext FrameworkServlet dispatcherServlet = this.createDispatcherServlet(servletAppContext); Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null"); //初始化专属于Dispatcher的WebApplicaitonContext dispatcherServlet.setContextInitializers(this.getServletApplicationContextInitializers()); //动态装配DispatcherServlet Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet); if (registration == null) { throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. Check if there is another servlet registered under the same name."); } else { registration.setLoadOnStartup(1); registration.addMapping(this.getServletMappings()); registration.setAsyncSupported(this.isAsyncSupported()); Filter[] filters = this.getServletFilters(); if (!ObjectUtils.isEmpty(filters)) { Filter[] var7 = filters; int var8 = filters.length; for(int var9 = 0; var9 return new DispatcherServlet(servletAppContext); } } 1.4.3 AbstractAnnotationConfigDispatcherServletInitializer

显然,这个类又继承了AbstractDispatcherServletInitializer,它实现了父类的createServletApplicationContext方法,并且实现了AbstractContextLoaderInitializer#createRootApplicationContext()方法。又通过模板方法模式,暴露了2个抽象方法:getRootConfigClasses、getServletConfigClasses。只需要引入RootConfig和ServletConfig对应的@Configuration类就可以完成配置。注解大法好!!! 看到类中的Annotation这个单词我们就能知道,这个类适用于通过注解来驱动Spring配置。

public abstract class AbstractAnnotationConfigDispatcherServletInitializer extends AbstractDispatcherServletInitializer { @Nullable protected WebApplicationContext createRootApplicationContext() { Class[] configClasses = this.getRootConfigClasses(); if (!ObjectUtils.isEmpty(configClasses)) { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(configClasses); return context; } else { return null; } } protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); Class[] configClasses = this.getServletConfigClasses(); if (!ObjectUtils.isEmpty(configClasses)) { context.register(configClasses); } return context; } @Nullable protected abstract Class[] getRootConfigClasses(); @Nullable protected abstract Class[] getServletConfigClasses(); } 2. SpringMVC配置案例 2.1 自定义的Intializer类,继承了AbstractAnnotationConfigDispatcherServletInitializer public class PreventPneumoniaWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class[] getRootConfigClasses() { return new Class[]{RootConfig.class}; } @Override protected Class[] getServletConfigClasses() { return new Class[]{WebConfig.class}; } @Override protected String[] getServletMappings() { return new String[]{"/"}; } } 2.2编写@Configuration类

RootConfig.java

@Configuration @ComponentScan(basePackages = {"cn.xxx.dao","cn.xxx.service"}, excludeFilters = {@Filter(type=FilterType.ANNOTATION, value= EnableWebMvc.class)}) @ImportResource("classpath:spring/spring-all.xml") public class RootConfig { }

WebConfig.java

//表明这是一个java配置类 @Configuration //开启springMvc @EnableWebMvc //开启组件扫描,不用显式配置控制器 @ComponentScan({"cn.xxx.control"}) public class WebConfig extends WebMvcConfigurerAdapter { //配置JSP视图解析器 @Bean public ViewResolver viewResolver(){ InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/src/module/pneumonia/"); resolver.setSuffix(".html"); resolver.setExposeContextBeansAsAttributes(true); return resolver; } //配置静态资源的处理 @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } } 2.3 把@Controller类也贴出来 /** * @author java_shj * @desc 简单的一个springMvc控制器 * @createTime 2019/9/30 11:29 **/ @Controller //类级别的请求处理 public class HomeController { private static Logger logger = LoggerFactory.getLogger(HomeController.class); //方法级别的请求处理 @RequestMapping(method = RequestMethod.GET,value = {"/home","/"}) public String home(){ logger.info("首页方法触发了"); return "index"; } @RequestMapping(method = RequestMethod.GET, value = {"/index"}) public String homeNew() { return "preventPneumonia"; } } 2.4 引入自定义Servlet /** * @author java_shj * @desc 空谷项目自定义Initializer,来注册自定义的Servlet、Filter、Listener,并映射到对应的url * @createTime 2020/1/10 17:36 **/ public class EvServletInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { // 注册想要引入的Servlet Dynamic validateCodeServlet = servletContext.addServlet("validateCodeServlet", ValidateCodeServlet.class); // 映射Servlet validateCodeServlet.addMapping("/validateCodeServlet"); } } 2.5 其他

操作数据库是,可能需要引入spring-mybatis等配置文件,见RootConfig.java中引入xml文件,引入其他xml文件同理。

@ImportResource("classpath:spring/spring-all.xml")

至此,不需要web.mxl配置文件,大功告成。写个前台页面,就可以开搞了。注解驱动真香!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有