公司的很多项目都陆陆续续引入了Spring Boot,通过对Spring Boot的接触了解发现其真的是大大地简化了开发、简化了依赖配置,很多功能注解一下就可以实现,真的是太方便了。下面记录了一个Spring Boot的入门程序实现,包括过滤器、servlet、定时器、全局异常处理、日志、Druid数据源的SQL监控配置。

1,pom.xml文件:

  
4.0.0
  
com.zws
  
spring-boot
  
0.0.1-SNAPSHOT
  
jar
  
spring-boot
  
http://maven.apache.org
  
    
UTF-8
  
    
    
org.springframework.boot
    
spring-boot-starter-parent
    
1.5.10.RELEASE
  
  
    
org.springframework.boot
    
spring-boot-starter-web
    
        
           
org.springframework.boot
           
spring-boot-starter-logging
        
    
org.springframework.boot
    
spring-boot-starter-aop
    
org.springframework.boot
    
spring-boot-starter-jdbc
    
org.mybatis.spring.boot
    
mybatis-spring-boot-starter
    
1.3.1
    
com.alibaba
    
druid-spring-boot-starter
    
1.1.0
       
mysql
       
mysql-connector-java
  
    
commons-logging
    
commons-logging
    
1.2
    
org.slf4j
    
slf4j-api
    
log4j
    
log4j
    
1.2.17
    
com.alibaba
    
fastjson
    
1.2.46
    
junit
    
junit
    
test
  
    
      
         
      
org.apache.maven.plugins
  
maven-compiler-plugin
  
      
1.8       
1.8
  
  
          
              
org.springframework.boot
              
spring-boot-maven-plugin
          
      
  

可以看到这个文件内容并不多,但是却引入了很多需要的依赖。这里引入的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=8080server.context-path=/appspring.mvc.throw-exception-if-no-handler-found=truespring.resources.add-mappings=false#tomcat访问日志server.tomcat.basedir=logsserver.tomcat.accesslog.enabled=trueserver.tomcat.accesslog.directory=accessserver.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=falsedatasource.druid.username=rootdatasource.druid.password=rootdatasource.druid.driver-class-name=com.mysql.jdbc.Driverdatasource.druid.initialSize=5datasource.druid.minIdle=5datasource.druid.maxActive=20datasource.druid.maxWait=60000datasource.druid.minEvictableIdleTimeMillis=300000datasource.druid.validationQuery=select 'x'datasource.druid.testOnBorrow=falsedatasource.druid.testOnReturn=falsedatasource.druid.testWhileIdle=truedatasource.druid.poolPreparedStatements=falsedatasource.druid.maxPoolPreparedStatementPerConnectionSize=100datasource.druid.filters=stat,wall,log4jmybatis.config-location=classpath:mybatis/mybatis-config.xmlmybatis.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:

此配置文件的路径已经配置在了resources/application.properties文件内。

4,resources/mybatis/UserMapper.xml:

    
        
        
        
        
        
       select id, name, pass, create_date from user            
     select id, name, pass, create_date from user where id = #{value}    

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@EnableSchedulingpublic 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
 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;@Servicepublic class UserService {	@Autowired	private UserMapper userMapper;		public List
 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;}

启动后访问

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;@Componentpublic 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 */@RestControllerAdvicepublic 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, errorLoglog4j.appender.stdout = org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout = org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.conversionPattern = %d{yyyy-MM-dd HH\:mm\:ss}|%p|%C|%M|%L|%m%nlog4j.appender.infoLog=com.zws.be.log.MyRollingFileAppenderlog4j.appender.infoLog.File=logs/info/info.loglog4j.appender.infoLog.Threshold = infolog4j.appender.infoLog.layout=org.apache.log4j.PatternLayoutlog4j.appender.infoLog.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss}|%p|%C|%M|%L|%m%nlog4j.appender.errorLog=com.zws.be.log.MyRollingFileAppenderlog4j.appender.errorLog.File=logs/error/error.loglog4j.appender.errorLog.Threshold = errorlog4j.appender.errorLog.layout=org.apache.log4j.PatternLayoutlog4j.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
{ 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
> 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
> 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/1Params:Method:com.zws.be.user.controller.UserController.helloReturn:{"createDate":"2018-03-04 15:44:25","id":1,"name":"zhangsan","pass":"zhangsan@123"}---END---

13,运行Application,访问

1.png

查看druid的监控:

0.png

项目目录结构图:

2.png