前言

该文档主要介绍如何创建一个简单的、能够正常运行的spring项目。为往后项目的开发提供便利

创建spring项目创建maven项目

使用sts开发软件创建项目,STS是Spring Tool Suite的简称,该软件是一个基于Eclipse环境的,用于开发spring应用程序的软件。其提供了很多spring相关的辅助工具,为开发项目提供诸多便利。

导入spring-mvc jar包

编辑spring项目的pom.xml文件,加入spring-mvc容器 相关jar依赖、spring-ioc容器相关的jar

<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.bear.simple_spring</groupId> <artifactId>simple_spring</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- 导入springmvc jar包 begin --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.1.2.RELEASE</version> </dependency> <!-- 导入springmvc json与对象转换jar【@RequestBody、@ResponseBody ...注解生效】--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.fasterxml</groupId> <artifactId>classmate</artifactId> <version>1.4.0</version> </dependency> </dependencies></project>配置servlet容器web.xml编辑web.xml文件,让servlet容器启动的时候加载spring-mvc配置文件及spring-ioc的配置文件,以启动spring-mvc容器与spring-ioc容器

<?xml version="1.0" encoding="UTF-8"?><web-app id="WebApp_ID" version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <!-- 配置servlet 容器加载 spring-mvc配置文件,使servlet容器启动的时候同时启动spring-mvc容器 --> <servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <!-- 配置spring-mvc路径 --> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Map all requests to the DispatcherServlet for handling --> <servlet-mapping> <servlet-name>springDispatcherServlet</servlet-name> <!-- 配置 spring-mvc拦截的请求 --> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 配置servlet 容器加载 spring-ioc配置文件,使servlet容器启动的时候同时启动spring-ioc容器 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-beans.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener></web-app>创建spring-mvc容器的配置文件【spring-mvc.xml】

创建spring-mvc的配置文件

<?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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-4.3.xsd"> <!-- 配置spring 扫描包 --> <context:component-scan base-package="com.bear.simple.spring" use-default-filters="false"> <!-- 配置 spring-mvc 只扫描的注解Controller【处理http请求注解】、 ControllerAdvice【处理异常的注解】--> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/> </context:component-scan> <!-- 配置spring jsp 的试图解析器 【如不需要用到模板,可不配置】--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"></property> <property name="suffix" value=".jsp"></property> </bean> <!-- 配置支持静态文件访问 --> <mvc:default-servlet-handler/> <!-- 配置注解驱动,让RequestMapping 、异常处理等注解生效 --> <mvc:annotation-driven></mvc:annotation-driven></beans>创建spring-ioc容器的配置文件【spring-beans.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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-4.3.xsd"> <!-- 配置spring-ioc容器扫描的包,并排除 扫描Controller、ControllerAdvice【这两个注解交由spring-mvc容器扫描】--> <context:component-scan base-package="com.bear.simple.spring"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/> </context:component-scan></beans>测试spring-mvc项目是否创建成功

编写controller

package com.bear.simple.spring.controller;import javax.servlet.http.HttpServletRequest;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import com.bear.simple.spring.entity.Account;import com.bear.simple.spring.service.AccountService;@RestController@RequestMapping("account")public class AccountController {@Autowiredprivate AccountService accountService;/** * 获取帐号信息 * @param id * @param request * @return */@RequestMapping("/get")public Account getAccount(@RequestParam String id,HttpServletRequest request) { if(null == id || "".equals(id)) throw new RuntimeException("请求参数缺少id"); Account result = accountService.loadAccountById(id); return result;}}

编写实体层

package com.bear.simple.spring.entity;/*** 登陆帐号信息* @author bear**public class Account {//登陆帐号IDprivate String id;//登陆帐号private String acccountNo;//帐号密码private String password;//帐号描述private String desc;//帐号是否启用 0 禁用 1启用,默认为启用private int enable = 1;public Account(String id, String acccountNo, String password) { super(); this.id = id; this.acccountNo = acccountNo; this.password = password;}public Account(String id, String acccountNo, String password, String desc, int enable) { super(); this.id = id; this.acccountNo = acccountNo; this.password = password; this.desc = desc; this.enable = enable;}public Account() { super();}public String getId() { return id;}public void setId(String id) { this.id = id;}public String getAcccountNo() { return acccountNo;}public void setAcccountNo(String acccountNo) { this.acccountNo = acccountNo;}public String getPassword() { return password;}public void setPassword(String password) { this.password = password;}public int getEnable() { return enable;}public void setEnable(int enable) { this.enable = enable;}public Boolean isEnable() { return 1 == this.enable;}@Overridepublic int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((acccountNo == null) ? 0 : acccountNo.hashCode()); result = prime * result + ((desc == null) ? 0 : desc.hashCode()); result = prime * result + enable; result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((password == null) ? 0 : password.hashCode()); return result;}@Overridepublic boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Account other = (Account) obj; if (acccountNo == null) { if (other.acccountNo != null) return false; } else if (!acccountNo.equals(other.acccountNo)) return false; if (desc == null) { if (other.desc != null) return false; } else if (!desc.equals(other.desc)) return false; if (enable != other.enable) return false; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; if (password == null) { if (other.password != null) return false; } else if (!password.equals(other.password)) return false; return true;}@Overridepublic String toString() { return "Account [id=" + id + ", acccountNo=" + acccountNo + ", password=" + password + ", desc=" + desc + ", enable=" + enable + "]";}}

编写服务层

package com.bear.simple.spring.service;import java.util.HashMap;import java.util.Map;import java.util.Optional;import org.springframework.stereotype.Service;import com.bear.simple.spring.entity.Account;@Servicepublic class AccountService {private static final Map<String, Account> cache = new HashMap<String, Account>();static { cache.put("1", new Account("1", "黄大仙", "123456")); cache.put("2", new Account("2", "刘不三", "12345678")); cache.put("3", new Account("3", "刘不四", "8888888"));}/** * 加载帐号,不存在报异常 * * @param id * @return */public Account loadAccountById(String id) { Optional<Account> accountOpt = findAccountById(id); if (!accountOpt.isPresent()) throw new RuntimeException(String.format("帐号【%s】不存在", id)); return accountOpt.get();}/** * 查找帐号 * * @param id * @return */public Optional<Account> findAccountById(String id) { if (null == id || "".equals(id)) return Optional.empty(); Account account = cache.get(id); return Optional.ofNullable(account);}}

测试请求处理

使用postman软件测试

统一返回结果

编写返回结果实体类【该类包含成功返回,异常返回与错误返回】

package com.bear.simple.spring.entity;import java.io.PrintWriter;import java.io.StringWriter;public class JsonData {//是否正确返回private final boolean ret;//状态码private String code = "000";//数据private Object data;//提示信息private String msg;//异常详细信息private String detail;private JsonData(boolean ret) { this.ret = ret;} /** * 创建错误返回结果 * @param msg 提示信息 * @return */public static JsonData error(String msg) { JsonData result = new JsonData(false); result.msg = msg; result.code = "999"; return result;}public static JsonData error(String msg,Exception e,String code) { JsonData result = new JsonData(false); result.msg = msg; result.code = code; result.detail = getStackTraceInfo(e); return result;}public static JsonData error(Exception e) { JsonData result = new JsonData(false); result.msg = e.getMessage(); result.code = "999"; result.detail = getStackTraceInfo(e); return result;}public static JsonData error(Exception e,String code) { JsonData result = new JsonData(false); result.msg = e.getMessage(); result.code = code; result.detail = getStackTraceInfo(e); return result;}public static JsonData success(Object data,String msg) { JsonData result = new JsonData(true); result.msg = msg; result.data = data; return result;}public static JsonData success(Object data) { return success(data, null);}public static JsonData success() { return success(null,null);}public String getCode() { return code;}public Object getData() { return data;}public String getMsg() { return msg;}public String getDetail() { return detail;}public boolean isRet() { return ret;}/** * 打印异常堆栈信息 * @param e * @return */public static String getStackTraceInfo(Exception e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); try { e.printStackTrace(pw); pw.flush(); sw.flush(); return sw.toString(); } finally { try { pw.close(); } catch (Exception ex) { ex.printStackTrace(); } try { sw.close(); } catch (Exception ex) { ex.printStackTrace(); } } }}

统一异常处理

编写异常处理控制器(统一处理系统发生的所有异常)

package com.bear.simple.spring.controller;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.ResponseBody;import com.bear.simple.spring.entity.JsonData;@ControllerAdvicepublic class ExceptionController { /** * 处理特定异常 * @param ex * @return */ @ExceptionHandler @ResponseBody public JsonData handleUserNotExistException(UserNotExistException ex) { if(null == ex.getMessage() || "".equals(ex.getMessage())) return JsonData.error("用户不存在", ex, "998"); return JsonData.error(ex, "998"); } /** * 处理上述遗漏的异常 * @param ex * @return */ @ExceptionHandler @ResponseBody public JsonData handleUserNotExistException(Exception ex) { if(null == ex.getMessage() || "".equals(ex.getMessage())) return JsonData.error("系统发生未知异常", ex, "999"); return JsonData.error(ex, "999"); }}

添加日志处理

日志记录是项目中不可或缺的一部分。它的存在让我们定位问题更为方便。目前使用比较普遍的日志框架有log4j、logback等。当前项目将采用logback。引用logback相应jar包

<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.1.7</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.1.7</version> </dependency>

编写日志配置文件【logback.xml(名称可自定义)】

<?xml version="1.0" encoding="UTF-8"?><!-- scan 为 true 扫描logback.xml文件,一旦发现文件更新,则重新加载,scanPeriod 为扫描时间间隔 --><configuration scan="true" scanPeriod="60 seconds" debug="false"><!-- 变量定义 在logback.xml文件中使用${}应用 --><property name="appName" value="simpleSpringLog" /><!-- 定义日志输出路径 --><property name="logFilePath" value="C:/sts_workspace/log" /><!-- logback上下文名称定义 --><contextName>${appName}</contextName><!-- 控制台日志输出配置 --><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <!-- encoder 默认配置为PatternLayoutEncoder --> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n </pattern> </encoder></appender><!-- 文件日志输出配置 --><appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>${logFilePath}/testFile.log</file> <append>true</append> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n </pattern> </encoder></appender><!-- 滚动日志输出,每天生成一个日志文件 --><appender name="ROLL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 配置滚动策略 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${logFilePath}/%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> <!-- 配置日志输出格式 --> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n </pattern> </encoder></appender><appender name="TIMEOUT_ROLL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 定义日志过滤器 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <!-- 配置滚动策略 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${logFilePath}/timeout/%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> <!-- 配置日志输出格式 --> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n </pattern> </encoder> </appender><!-- 定义指定类路径输出日志 --><logger name="com.bear.simple.spring.service" level="INFO" additivity="false"> <appender-ref ref="STDOUT" /> <appender-ref ref="ROLL_FILE" /></logger><logger name="org" level="INFO"/><!-- 设置root的打印配置,将等级【level="INFO"】大于INFO的日志打印到控制台【ref="STDOUT"】 --><root level="INFO"> <appender-ref ref="STDOUT" /> <appender-ref ref="ROLL_FILE" /></root></configuration>

使用springmvc启动加载日志配置文件,编辑web.xml文件使用配置监听器加载日志配置文件,只需在web.xml文件中添加如下代码段即可

<context-param> <param-name>logbackConfigLocation</param-name> <!-- 日志配置文件地址 --> <param-value>C:/developer/apache-tomcat-8.0.32/web_log/logback.xml</param-value></context-param><listener> <listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class></listener>

配置统一编码过滤器

配置springmvc接收的所有请求的编码格式为utf-8,只需在web.xml文件中添加字符集过滤器即可,添加的内容如下

<!-- 统一字符编码过滤器 --><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> <!-- 是否强制所有请求都使用该字符编码 --> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param></filter><filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping>