公司的很多项目都陆陆续续引入了Spring Boot,通过对Spring Boot的接触了解发现其真的是大大地简化了开发、简化了依赖配置,很多功能注解一下就可以实现,真的是太方便了。下面记录了一个Spring Boot的入门程序实现,包括过滤器、servlet、定时器、全局异常处理、日志、Druid数据源的SQL监控配置。
1,pom.xml文件:
<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.zws</groupId>
<artifactId>spring-boot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-boot</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.46</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
可以看到这个文件内容并不多,但是却引入了很多需要的依赖。这里引入的Spring Boot版本为1.5.10.RELEASE,其中spring-boot-maven-plugin插件是Spring Boot提供的打包用的插件。mybatis-spring-boot-starter为MyBatis集成Spring Boot的依赖。druid-spring-boot-starter为阿里开源数据源Druid集成Spring Boot的依赖,它会下载对应的druid jar包,注意这里并没有直接引入Druid依赖,如果直接引入Druid依赖jar包,则druid的监控页面无法显示sql。
2,Spring Boot配置文件resources/application.properties:
server.port=8080
server.context-path=/app
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
#tomcat访问日志
server.tomcat.basedir=logs
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.directory=access
server.tomcat.accesslog.pattern=%t %a "%r" %s %b (%D ms)
datasource.druid.url=jdbc:mysql://localhost/db_boot?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false
datasource.druid.username=root
datasource.druid.password=root
datasource.druid.driver-class-name=com.mysql.jdbc.Driver
datasource.druid.initialSize=5
datasource.druid.minIdle=5
datasource.druid.maxActive=20
datasource.druid.maxWait=60000
datasource.druid.minEvictableIdleTimeMillis=300000
datasource.druid.validationQuery=select 'x'
datasource.druid.testOnBorrow=false
datasource.druid.testOnReturn=false
datasource.druid.testWhileIdle=true
datasource.druid.poolPreparedStatements=false
datasource.druid.maxPoolPreparedStatementPerConnectionSize=100
datasource.druid.filters=stat,wall,log4j
mybatis.config-location=classpath:mybatis/mybatis-config.xml
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
application.properties文件为Spring Boot的总配置文件,Spring Boot默认会加载resources目录下的application.properties文件。Spring Boot默认内嵌Tomcat作为web容器,server.port为端口,server.context-path为上下文路径。datasource开头的为druid数据源的配置。
3,MyBatis的配置文件resources/mybatis/mybatis-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="cacheEnabled" value="true" />
<setting name="lazyLoadingEnabled" value="true" />
<setting name="multipleResultSetsEnabled" value="true" />
<setting name="useColumnLabel" value="true" />
<setting name="useGeneratedKeys" value="false" />
<setting name="autoMappingBehavior" value="PARTIAL" />
<setting name="autoMappingUnknownColumnBehavior" value="WARNING" />
<setting name="defaultExecutorType" value="SIMPLE" />
<setting name="defaultStatementTimeout" value="25" />
<setting name="defaultFetchSize" value="100" />
<setting name="safeRowBoundsEnabled" value="false" />
<setting name="mapUnderscoreToCamelCase" value="false" />
<setting name="localCacheScope" value="SESSION" />
<setting name="jdbcTypeForNull" value="OTHER" />
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString" />
</settings>
<typeAliases>
<package name="com.zws.be.user.bean" />
</typeAliases>
</configuration>
此配置文件的路径已经配置在了resources/application.properties文件内。
4,resources/mybatis/UserMapper.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.zws.be.user.mapper.UserMapper" >
<resultMap id="UserMap" type="User">
<id column="id" property="id" jdbcType="BIGINT" />
<result column="name" property="name" jdbcType="VARCHAR" />
<result column="pass" property="pass" jdbcType="VARCHAR" />
<result column="create_date" property="createDate" />
</resultMap>
<select id="getAll" resultMap="UserMap">
select id, name, pass, create_date from user
</select>
<select id="getUserById" resultMap="UserMap" parameterType="long">
select id, name, pass, create_date from user where id = #{value}
</select>
</mapper>
5,Sping Boot启动类Application.java:
package com.zws.be;
import javax.sql.DataSource;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
import com.alibaba.druid.pool.DruidDataSource;
@SpringBootApplication
@MapperScan(basePackages = {"com.zws.be.*.mapper"})
@ServletComponentScan
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean("dataSource")
@ConfigurationProperties("datasource.druid")
public DataSource getDruidDataSource() {
return DataSourceBuilder.create().type(DruidDataSource.class).build();
}
}
这个是程序启动的入口,里面有mian函数,运行此类即可启动Spring Boot。注解@MapperScan(basePackages = {"com.zws.be.*.mapper"})指定了映射mapper文件的接口所在的包。@EnableScheduling注解用于开启Spring的定时组件,下面会有例子。@ServletComponentScan注解搭配@WebServlet和@WebFilter注解使得定义servlet和filter时非常方便。
6,下面是定义一个简单的restful接口所需的各类:
com.zws.be.user.bean.User:
package com.zws.be.user.bean;
import java.util.Date;
import org.apache.ibatis.type.Alias;
@Alias("User")
public class User {
private Long id;
private String name;
private String pass;
private Date createDate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", pass=" + pass + ", createDate=" + createDate + "]";
}
}
com.zws.be.user.mapper.UserMapper:
package com.zws.be.user.mapper;
import java.util.List;
import com.zws.be.user.bean.User;
public interface UserMapper {
List<User> getAll();
User getUserById(Long id);
}
com.zws.be.user.service.UserService:
package com.zws.be.user.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.zws.be.user.bean.User;
import com.zws.be.user.mapper.UserMapper;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> getUsers() {
return userMapper.getAll();
}
public User getUser(Long id) {
return userMapper.getUserById(id);
}
}
com.zws.be.user.controller.UserController:
package com.zws.be.user.controller;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.zws.be.user.bean.User;
import com.zws.be.user.service.UserService;
@RestController
@RequestMapping("/user")
public class UserController {
public Logger logger = Logger.getLogger(UserController.class);
@Autowired
private UserService userService;
@RequestMapping(value="/{id}", method=RequestMethod.GET, produces = "application/json")
public String hello(@PathVariable Long id) {
User user = userService.getUser(id);
return JSON.toJSONString(user, SerializerFeature.WriteDateUseDateFormat);
}
}
7,配置Druid的sql监控servlet:
package com.zws.be.filter;
import javax.servlet.annotation.WebServlet;
import com.alibaba.druid.support.http.StatViewServlet;
/**
* DRUID的sql监控
* @author wensh.zhu
* @2018-03-11
*/
@WebServlet(name="monitorSrvlet", urlPatterns= {"/druid/*"})
public class MonitorServlet extends StatViewServlet {
private static final long serialVersionUID = -2211104135110049275L;
}
启动后访问http://localhost:8080/app/druid即可进入Druid的sql监控的监控页面。
8,过滤器例子:
package com.zws.be.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import org.apache.log4j.Logger;
@WebFilter(filterName="filterDemo", urlPatterns = {"/*"})
public class FilterDemo implements Filter{
private Logger logger = Logger.getLogger(FilterDemo.class);
@Override
public void destroy() {}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain chain)
throws IOException, ServletException {
logger.info("FilterDemo is working...");
chain.doFilter(arg0, arg1);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
9,定时器例子:
package com.zws.be.timer;
import org.apache.log4j.Logger;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class TimerDemo {
private Logger logger = Logger.getLogger(getClass());
@Scheduled(cron = "0/30 * * * * *")
public void timer() {
logger.info("定时器例子");
}
}
10,全局异常处理:
package com.zws.be.exception;
import org.apache.log4j.Logger;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理
* @author wensh.zhu
* @2018-03-11
*/
@RestControllerAdvice
public class ExceptionAdvice {
private Logger logger = Logger.getLogger(getClass());
private String pattern = "抱歉,异常了:{0}";
@ExceptionHandler(value = {Exception.class})
@ResponseBody
public String handleAllException(Exception e, HttpServletRequest req, HttpServletResponse resp) {
logger.error("接口调用异常", e);
if (e instanceof NoHandlerFoundException) {
String reqURI = req.getRequestURI().toString();
return MessageFormat.format(pattern, "资源" + reqURI + "不存在!!!");
}
return MessageFormat.format(pattern, e.getMessage());
}
}
11,log4j配置文件resources/log4j.properties:
log4j.rootLogger=INFO, stdout, infoLog, errorLog
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.conversionPattern = %d{yyyy-MM-dd HH\:mm\:ss}|%p|%C|%M|%L|%m%n
log4j.appender.infoLog=com.zws.be.log.MyRollingFileAppender
log4j.appender.infoLog.File=logs/info/info.log
log4j.appender.infoLog.Threshold = info
log4j.appender.infoLog.layout=org.apache.log4j.PatternLayout
log4j.appender.infoLog.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss}|%p|%C|%M|%L|%m%n
log4j.appender.errorLog=com.zws.be.log.MyRollingFileAppender
log4j.appender.errorLog.File=logs/error/error.log
log4j.appender.errorLog.Threshold = error
log4j.appender.errorLog.layout=org.apache.log4j.PatternLayout
log4j.appender.errorLog.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss}|%p|%C|%M|%L|%m%n
自定义日志生成文件名称:
package com.zws.be.log;
import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;
import org.apache.log4j.Priority;
import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.helpers.CountingQuietWriter;
import org.apache.log4j.spi.LoggingEvent;
public class MyRollingFileAppender extends RollingFileAppender{
private long nextRollover = 0L;
public void rollOver() {
File target, file;
if (qw != null) {
long size = ((CountingQuietWriter) qw).getCount();
nextRollover = size + maxFileSize;
}
boolean renameSuccessed = true;
if (maxBackupIndex > 0) {
file = new File(getRollingFileName(fileName, maxBackupIndex));
if (file.exists()) {
renameSuccessed = file.delete();
}
for (int i = maxBackupIndex - 1; i >= 1 && renameSuccessed; i --) {
file = new File(getRollingFileName(fileName, i));
if (file.exists()) {
target = new File(getRollingFileName(fileName, i + 1));
renameSuccessed = file.renameTo(target);
}
}
if (renameSuccessed) {
target = new File(getRollingFileName(fileName, 1));
closeFile();
renameSuccessed = file.renameTo(target);
if (!renameSuccessed) {
try {
setFile(fileName, true, bufferedIO, bufferSize);
} catch (IOException e) {
if (e instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
}
}
}
}
}
private String getRollingFileName(String fileName, int index) {
String fName = "";
String newIndex = index < 10 ? ("0" + index) : String.valueOf(index);
if (index > 0) {
fName = fileName.replace(".log", "") + "_" + newIndex + ".log";
} else {
fName = fileName;
}
return fName;
}
@Override
protected void subAppend(LoggingEvent event) {
super.subAppend(event);
if (fileName != null && qw != null) {
long size = ((CountingQuietWriter) qw).getCount();
if (size >= maxFileSize && size >= nextRollover) {
rollOver();
}
}
}
@Override
public boolean isAsSevereAsThreshold(Priority priority) {
String level = priority.toString();
if ("ERROR".equals(level)) {
return getThreshold().equals(priority);
}
return super.isAsSevereAsThreshold(priority);
}
}
12,Spring MVC请求参数以及返回信息自动日志的辅助类:
package com.zws.be.log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.MessageFormat;
import org.apache.log4j.Logger;
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;
/**
* 用于请求参数、响应信息的自动日志。
* @author wensh.zhu
* @2018-03-11
*/
@ControllerAdvice(basePackages = {"com.zws.be"})
public class LogResponseBodyAdvice implements ResponseBodyAdvice<String>{
private String pattern = "\n---BEGIN---\nReqURI:{0}\nParams:{1}\nMethod:{2}\nReturn:{3}\n---END---";
private Logger logger = Logger.getLogger(LogResponseBodyAdvice.class);
@Override
public String beforeBodyWrite(String rtnMsg, MethodParameter param, MediaType arg2,
Class<? extends HttpMessageConverter<?>> arg3, ServerHttpRequest req, ServerHttpResponse resp) {
String clazName = param.getDeclaringClass().getName();
String mtdName = param.getMethod().getName();
String reqURI = req.getURI().toString();
String reqParams = getReqParams(req);
String log = getLog(reqURI, reqParams, clazName + "." + mtdName, rtnMsg);
logger.info(log);
return rtnMsg;
}
@Override
public boolean supports(MethodParameter arg0, Class<? extends HttpMessageConverter<?>> arg1) {
return true;
}
private String getLog(String reqURI, String reqParams, String proMethod, String rtnMsg) {
return MessageFormat.format(pattern, reqURI, reqParams, proMethod, rtnMsg);
}
private String getReqParams(ServerHttpRequest req) {
StringBuilder body = new StringBuilder();
String line = null;
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(req.getBody(), "UTF-8"));
while ((line = reader.readLine()) != null) {
body.append(line);
}
} catch (IOException e) {
logger.error("I/O error ", e);
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
logger.error("I/O error ", e);
}
}
return body.toString();
}
}
此类功能为将Spring MVC接受到请求的参数、处理请求对应的方法、请求返回内容自动打印到日志文件内,在接口出现问题的时候方便排查,日志输出样例:
---BEGIN---
ReqURI:http://localhost:8080/app/user/1
Params:
Method:com.zws.be.user.controller.UserController.hello
Return:{"createDate":"2018-03-04 15:44:25","id":1,"name":"zhangsan","pass":"zhangsan@123"}
---END---
13,运行Application,访问http://localhost:8080/app/user/1,如下图:
查看druid的监控:
项目目录结构图:
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。