最近重新整理了项目架构,所以把SpringMCV的抽离出来单独聊一聊,首先SpringMVC是目前大小厂最流行的MVC架构框架,这点应该可以肯定的,谁让他有个爸爸叫Spring!因此这个儿子也继承他爹的全部优点!
废话就不说了,进入主题!
maven引入必要的依赖(本项目依然使用Tomcat8插件,不明白的小伙伴自行学习)
1.maven配置文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zwd</groupId>
<artifactId>ssm</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>ssm Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--junit4-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- 懒人Bean -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<!-- 数据验证(JSR-303) -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.0.Final</version>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<!--EL表达式-->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- Spring依赖 -->
<!--1. Spring核心依赖,上面已经有日志了 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!--spring的aop jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!--这两个jar包是阿帕奇的-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!--3. Spring WEB依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!--4.Spring-test相关依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
</dependencies>
<build>
<finalName>ssm</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat8-maven-plugin</artifactId>
<version>3.0-r1655215</version>
<configuration>
<port>8080</port>
<path>/</path>
<uriEncoding>${project.build.sourceEncoding}</uriEncoding>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<!--配置Tomcat8仓库-->
<pluginRepositories>
<pluginRepository>
<id>alfresco-public</id>
<url>https://artifacts.alfresco.com/nexus/content/groups/public</url>
</pluginRepository>
<pluginRepository>
<id>alfresco-public-snapshots</id>
<url>https://artifacts.alfresco.com/nexus/content/groups/public-snapshots</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
</snapshots>
</pluginRepository>
<!--这个google的用不了-->
<!--<pluginRepository>
<id>beardedgeeks-releases</id>
<url>http://beardedgeeks.googlecode.com/svn/repository/releases</url>
</pluginRepository>-->
</pluginRepositories>
</project>
2.springmvc的配置文件(resources目录下创建springmvc.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="com.zwd.*"/>
<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler/>
<!-- 这里因为我们使用hibernate的JSR303数据验证,所以需要配置相关信息 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<property name="validationMessageSource" ref="messageSource"/>
</bean>
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>classpath:validation</value><!-- 自定义的配置 -->
<value>classpath:org.hibernate.validator.ValidationMessages</value>
</list>
</property>
<property name="useCodeAsDefaultMessage" value="false"/>
<property name="defaultEncoding" value="UTF-8"/>
<property name="cacheSeconds" value="60"/>
</bean>
<mvc:annotation-driven validator="validator">
<mvc:message-converters>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json</value>
</list>
</property>
<property name="features">
<list>
<value>QuoteFieldNames</value> <!-- 输出key时是否使用双引号,默认为true -->
<value>WriteMapNullValue</value> <!-- 是否输出值为null的字段,默认为false -->
<!--
<value>DisableCircularReferenceDetect</value>
<value>WriteDateUseDateFormat</value>
<value>WriteNullStringAsEmpty</value> 字符类型字段如果为null,输出为"",而非null
<value>WriteNullNumberAsZero</value> 数值字段如果为null,输出为0,而非null
<value>WriteNullBooleanAsFalse</value> Boolean字段如果为null,输出为false,而非null
<value>WriteNullListAsEmpty</value> List字段如果为null,输出为[],而非null
-->
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--过滤静态资源-->
<mvc:resources location="/img/" mapping="/img/**"/>
<mvc:resources location="/js/" mapping="/js/**"/>
<mvc:resources location="/css/" mapping="/css/**"/>
<!--配置视图解析器-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="suffix" value=".jsp"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
</bean>
</beans>
其他没什么好说的,需要注意的是使用Hibernate Validation 我们需要自己手动进行配置实现,因为springmcv本身并没有具体的实现.
3.web.xml配置启动springmvc
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>spring-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--加载springmvc.xml配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--启动服务器时,创建该servlet-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置中文乱码过滤器-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
</web-app>
以上都是springmvc的标配
4.随便定义一个controller
package com.zwd.controller;
import com.zwd.model.UserVo;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping(value = "findAll", method = RequestMethod.POST)
public UserVo findAll(@RequestBody @Valid UserVo UserVo) {
return UserVo;
}
}
很简单,没有任何的业务逻辑,我们用实体类UserVo接受客户端的数据并原封不动返回就行了,逻辑非常清晰,不拖泥带水,这里只负责处理客户端的请求.
如果执行过程发生错误,或者数据验证失败等情况,则交给各种另外两兄弟handler处理
首先来一个全局异常处理ControllerExceptionHandler,顾名思义他就是来处理我们遇到的异常情况的
package com.zwd.handler;
import com.zwd.model.ResponseBean;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
@RestControllerAdvice
public class ControllerExceptionHandler {
/**
* 通用异常处理
*/
@ExceptionHandler(Exception.class)
public ResponseBean handleServiceMessageException(HttpServletRequest request, Exception ex) {
//这里可以通用日志记录下错误,我就不多做处理了
ex.printStackTrace();
return ResponseBean.error("未知错误");
}
/**
* 用于处理校验失败
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseBean bindException(MethodArgumentNotValidException e) {
BindingResult bindingResult = e.getBindingResult();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("校验失败:");
for (FieldError fieldError : bindingResult.getFieldErrors()) {
stringBuilder.append(fieldError.getDefaultMessage() + ",");
}
return ResponseBean.valid(stringBuilder.toString());
}
}
这个类专门处理各种异常,报错自定义异常等,只要你喜欢,你想写啥都行
再来一个ControllerResponseHandler,这个是统一封装返回结果处理,用来规范返回响应参数格式的
package com.zwd.handler;
import com.zwd.model.ResponseBean;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice
public class ControllerResponseHandler implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
System.out.println("supports");
// 支持所有的返回值类型
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
System.out.println("beforeBodyWrite");
if (body instanceof ResponseBean) {
return body;
} else {
// 所有没有返回 ResponseBean 结构的结果均认为是成功的
return ResponseBean.success(body);
}
}
}
这里的ResponseBean是我自定义的数据类,也一并给大家展示了
package com.zwd.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResponseBean implements Serializable {
private Integer status;
private String msg;
private Object data;
public static ResponseBean build() {
return new ResponseBean();
}
public static ResponseBean success(Object obj) {
return new ResponseBean(200, "success", obj);
}
public static ResponseBean success(String msg, Object obj) {
return new ResponseBean(200, msg, obj);
}
public static ResponseBean error(Object obj) {
return new ResponseBean(500, "fail", obj);
}
public static ResponseBean error(String msg, Object obj) {
return new ResponseBean(500, msg, obj);
}
public static ResponseBean valid(Object obj) {
return new ResponseBean(404, "vaild", obj);
}
}
package com.zwd.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.Range;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class UserVo {
@NotBlank(message = "姓名不能为空")
private String name;
@Range(min = 1, max = 99, message = "年龄范围必须是1至99")
private int age;
}
项目大致流程就差不多了,我们来几个测试结果展示:
1.首先是正确的请求
2.然后是验证不能通过的请求
3.中间再处理我抛出一个运行时异常Runtimeexception
整个流程一气呵成,没有任何一点瑕疵,真正做到了高内聚,低耦合,这都归功于spring 的AOP,今天就到此为止吧!