Spring MVC
设计良好的控制器本身只处理很少甚至不处理工作,而是将业务逻辑委托给一个或多个服务对象。
使用Java类配置Spring MVC Servlet 3.0 查找 javax.servlet.ServletContainerInitializer
的实现类来配置 Servlet 容器;Spring 提供了该接口的实现:SpringServletContainerInitializer
, 并且又会查找实现WebApplicationInitializer
的类来进行配置,并引入了一个AbstractAnnotationConfigDispatcherServletInitializer
的基础实现,所以在Spring中通过Java代码进行配置的时候,需要扩展该基础实现类。
这种配置DispatcherServlet的方式只能部署到支持Servlet 3.0的服务器中才能工作。
UML 图示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Delegate the ServletContext to any WebApplicationInitializer implementations present on the application classpath. Because this class declares @HandlesTypes (WebApplicationInitializer .class ), Servlet 3.0+ containers will automatically scan the classpath for implementations of Spring 's WebApplicationInitializer interface and provide the set of all such types to the webAppInitializerClasses parameter of this method . Assuming that one or more WebApplicationInitializer types are detected , they will be instantiated (and sorted if the @@Order annotation is present or the Ordered interface has been implemented ). Then the WebApplicationInitializer .onStartup (ServletContext ) method will be invoked on each instance , delegating the ServletContext such that each instance may register and configure servlets such as Spring 's DispatcherServlet , listeners such as Spring 's ContextLoaderListener , or any other Servlet API componentry such as filters . /* Servlet 3.0+ 会自动在应用的 classpath 中寻找 WebApplicationInitializer 的实现类,并对每一个实现类进行实例化,将 ServletContext 委托给他们,对每一个实例调用 WebApplicationInitializer .onStartup (ServletContext ), 加载和注册所有的 Servlet 、Listener 、Filter */ If no WebApplicationInitializer implementations are found on the classpath , this method is effectively a no -op . An INFO -level log message will be issued notifying the user that the ServletContainerInitializer has indeed been invoked but that no WebApplicationInitializer implementations were found . // org .springframework .web .SpringServletContainerInitializer @HandlesTypes (WebApplicationInitializer .class ) public class SpringServletContainerInitializer implements ServletContainerInitializer { public void onStartup (@Nullable Set<Class<?>> webAppInitializerClasses, @NotNull ServletContext servletContext) throws ServletException { } }
Root Application Context As such, it typically contains middle-tier services, data sources, etc.
Servlet Application Context As such, it typically contains controllers, view resolvers, locale resolvers, and other web-related beans.
Root Application Context 用来初始化 ContextLoaderListener 对象,并将该对象注册到 Servlet 容器中
Servlet Application Context 用来初始化 DispatcherServlet 对象,并将该对象注册到 Servlet 容器中,随后添加 Filters, 添加 Mapping 映射, 当然用户还可以通过 customizeRegistration(ServletRegistration.Dynamic registration) 来进行自定义之后的操作,如配置 Multipart 处理的参数
DispatcherServlet 1 2 3 4 5 6 7 8 setContextClass(Class) / 'contextClass' setContextConfigLocation(String) / 'contextConfigLocation' setContextAttribute(String) / 'contextAttribute' setNamespace(String) / 'namespace' setContextInitializerClasses(String) / 'contextInitializerClasses'
编写控制器方法
1 2 3 4 5 6 7 8 @RequestMapping (value = "/messageList" , method = RequestMethod.GET)public String message (@RequestParam(value = "count" , defaultValue = "20" ) int count, Model model) { model.addAttribute("messageList" , repository.getMessageList(count)); return "message" ; }
1 2 3 4 5 6 7 8 @RequestMapping (value = "/messageList/{name}" , method = RequestMethod.GET)public String messageListByName (@PathVariable String name, Model model) { model.addAttribute("messageList" , repository.getMessageListByName(name)); return "message" ; }
1 2 3 4 5 6 7 8 9 10 @RequestMapping (value = "/register" , method = RequestMethod.POST)public String processRegistration (@Valid User user, Errors errors) { if (errors.hasErrors()) { errors.getAllErrors().forEach(error -> printError(error)); return "registerFormSpringForm" ; } System.out.println("--------" + user.getUsername()); repository.saveUser(user); return "redirect:/blog/" + user.getUsername(); }
==C:\Users\guo.IntelliJIdea2018.3\system\tomcat\Unnamed_spring_web_java_config\work\Catalina\localhost\ROOT==
表单数据校验 在使用validator对表单数据进行校验时,需要
Spring对Java校验API(Java Validation API,又称JSR-303)的支持。从Spring 3.0开始,在Spring MVC中提供了对Java校验API的支持。在Spring MVC中要使用Java校验API的话,并不需要什么额外的配置。只要保证在类路径下包含这个Java API的实现即可,比如Hibernate Validator。
java的 JSR 303: Bean Validation 只是一个规范,并没有具体的实现,hibernate-validator 项目是对其的一个实现,因此,具体使用要引入相应的JAR包:validation-api(定义校验注解) hibernate-validator(具体实现)
1 2 3 4 5 6 7 8 9 10 <dependency > <groupId > javax.validation</groupId > <artifactId > validation-api</artifactId > <version > 2.0.1.Final</version > </dependency > <dependency > <groupId > org.hibernate.validator</groupId > <artifactId > hibernate-validator</artifactId > <version > 6.0.13.Final</version > </dependency >
==并且如果之前没有添加,后来添加了,要生效,必须重新打包部署,即重新生产 Artifacts, 并部署到tomcat上,否则就不会起作用!!==
使用spring-test进行测试 在用spring-test进行测试的时候,需要和spring其他组件的jar包版本一致,否则就可能会出现NoSuchMethodError
1 java.lang.NoSuchMethodError: org.springframework.util.StreamUtils.emptyInput()Ljava/io/InputStream;
Apache Common Lang包来实现equals()
和hashCode()
方法。
对“/spittles/12345”发起GET
请求要优于对“/spittles/show?spittle_id=12345”发起请求。前者能够识别出要查询的资源,而后者描述的是带有参数的一个操作——本质上是通过HTTP发起的RPC。