环境:Spring5.3.26
SpringMVC使用相信大家都会使用,别人项目工程搭建后,你只需负责写Controller即可,那你是否想过自己能否把环境搭建出来呢?而且还不借助网络;本篇教大家如何通过注解快速搭建SpringMVC运行环境。
传统SpringMVC配置
本节:回顾传统SpringMVC的基本配置原理。
DispatcherServlet需要一个WebApplicationContext(一个普通ApplicationContext的扩展)用于它自己的配置。WebApplicationContext有一个链接到ServletContext和它所关联的Servlet。它还绑定到ServletContext,这样应用程序就可以在需要访问WebApplicationContext时使用RequestContextUtils上的静态方法来查找它。
对于许多应用程序来说,只有一个WebApplicationContext就足够简单了。也可以有一个上下文层次结构,其中一个根WebApplicationContext在多个DispatcherServlet(或其他Servlet)实例之间共享,每个实例都有自己的子WebApplicationContext配置。有关上下文层次结构特性的更多信息,请参阅ApplicationContext的附加功能。
根WebApplicationContext通常包含基础设施bean,例如需要跨多个Servlet实例共享的数据存储库和业务服务。这些bean被有效地继承,并且可以在特定于Servlet的子WebApplicationContext中被重写(即重新声明),该子WebApplicationContext通常包含给定Servlet的本地bean。下图显示了这种关系:
web.xml中配置:
ContextLoaderListener:该监听器用来创建Root 容器,该容器就是用来配置基础的Bean,如DAO,Service等。
DispatcherServlet:对应一个web 容器,也就是子容器。该容器用来配置Controller。在Controller中会应用到Service,那么该子容器就会从父容器中查找相应的Bean。如下父子关系配置:
- public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
- protected WebApplicationContext initWebApplicationContext() {
- // 获取父容器,该父容器是在ContextLoaderListener监听器中创建并保存到ServletContext中
- WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
- WebApplicationContext wac = null;
- if (this.webApplicationContext != null) {
- wac = this.webApplicationContext;
- if (wac instanceof ConfigurableWebApplicationContext) {
- ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
- if (!cwac.isActive()) {
- if (cwac.getParent() == null) {
- cwac.setParent(rootContext);
- }
- configureAndRefreshWebApplicationContext(cwac);
- }
- }
- }
- if (wac == null) {
- // 创建子容器并设置父容器
- wac = createWebApplicationContext(rootContext);
- }
- return wac;
- }
- }
以上就是SpringMVC的基本配置。
Servlet注册
既然是基于注解的方式配置SpringMVC,那么我们需要先了解Servlet的注册方式有哪些。
方式1:
web.xml中注册
- <servlet>
- <servlet-name>DemoServlet</servlet-name>
- <servlet-class>com.pack.servlet.DemoServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>DemoServlet</servlet-name>
- <url-pattern>/demo</url-pattern>
- </servlet-mapping>
方式2:
基于注解方式
- @WebServlet(name = "demoServlet", urlPatterns = "/demo")
- @WebServlet(value = {"/demo","/demo1"})
- @WebServlet(value = "/demo")
- @WebServlet("/demo")
- public class DemoServlet extends HttpServlet {
- // ...
- }
方式3:
通过SPI技术,这也是今天要使用的方式
Servlet3.0以上的版本开始,可以通过SPI方式注册Servlet,Filter,Listener三大组件。
第一步:在项目中建立如下文件
- META-INF/service/javax.servlet.ServletContainerInitializer
文件名:javax.servlet.ServletContainerInitializer
第二步:自定义类实现ServletContainerInitializer
- @HandlesTypes({CustomHandler.class})
- public class CustomContainerInitializer implements ServletContainerInitializer {
- // 这里的set集合就是当前环境中所有CustomHandler的子类
- @Override
- public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
- if (set!=null&&set.size()>0){
- set.stream().forEach(cls->{
- try {
- CustomHandler o = (CustomHandler)cls.newInstance();
- o.onStartup();
- } catch (Exception e) {
- e.printStackTrace();
- }
- });
- }
- //注入Servlet
- ServletRegistration.Dynamic userServlet = servletContext.addServlet("DemoServlet", DemoServlet.class);
- userServlet.addMapping("/demo");
- }
- }
SpringMVC注解配置
接下来就是要使用上面介绍的Servlet注册方式的第三种方式来实现SpringMVC的注册。
在Spring中已经提供了相应的实现:
在spring-web包中:
内容:
- org.springframework.web.SpringServletContainerInitializer
- @HandlesTypes(WebApplicationInitializer.class)
- public class SpringServletContainerInitializer implements ServletContainerInitializer {
- }
这里我们只需要实现WebApplicationInitializer接口即可,不过Spring已经为我们定义好了该接口的抽象模版,我们只需继承该抽象类即可:
- public class SpringMVCConfig 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[] {"/"} ;
- }
- }
RootConfig.java
- @Configuration
- public class RootConfig {
- }
WebConfig.java
- @Configuration
- @ComponentScan(basePackages = {"com.pack.controller"})
- public class WebConfig {
- }
测试controller
- @RestController
- @RequestMapping("/demo")
- public class DemoController {
- @GetMapping("")
- public Object index() {
- Map<String, Object> result = new HashMap<>() ;
- result.put("code", 0) ;
- result.put("data", "你好") ;
- return result ;
- }
- }
测试:
只是通过如上配置,SpringMVC环境基本上是可以使用了,但是我们看上面Controller接口,是基于REST full,所以当你访问该接口时会出现如下错误:
这是因为默认情况下RequestMappingHandlerAdapter无法处理,服务器端无法提供与 Accept-Charset 以及 Accept-Language 消息头指定的值相匹配的响应。
这时候就需要为其配置相应的消息转换器:
- @Bean
- public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
- RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter() ;
- adapter.getMessageConverters().add(new MappingJackson2HttpMessageConverter()) ;
- return adapter ;
- }
再次方法正常:
完毕!!!
原文地址:https://mp.weixin.qq.com/s/uQB-eDTuiXL7ODqfBgH2qg