说实话,如果不是spring-boot项目可以通过 ‘java -jar *’ 的方式来运行的话,我个人是不偏向使用spring-boot来开发项目的。个人感觉,如果不是对spring-mvc有相对深入了解的话,使用spring-boot这么一个黑盒工具会有很多问题。就算是那些对spring-mvc有相对多了解的人,在一开始使用spring-boot的时候也会有一种无从下手的感觉。当然了,如果对spring-mvc熟悉之后又学会了spring-boot,其实也会感觉省去很多麻烦,毕竟,spring-boot自己也说了:Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.
本文所涉及的那些文件我打了一个包,当成了本文的资源,本意是让大家可以0积分下载,有个方便的地方直接开始,但是目前csdn不支持手动设置积分,默认就是5积分,所以大家看需求下载吧。那个资源包里的文件跟本文所涉及的文件是一样的,本文就是根据那个资源包里的文件来写的。本文所涉及开发工具为eclipse,项目结构为maven普通java项目,也就是那个maven-archetype-quickstart的选项。
package com.szq.base;
import org.springframework.boot.SpringApplication;
import com.szq.base.config.AppConfig;
public class App {
public static void main(String[] args) {
SpringApplication.run(AppConfig.class, args);
}
}
这个类没什么需要解释的,spring-boot项目标准启动方式,固定这么写就行了。
package com.szq.base.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication(scanBasePackages = { "com.szq.base.controller", "com.szq.base.service.impl", "com.szq.base.springcomponent" })
@ServletComponentScan
@MapperScan("com.szq.base.dao")
public class AppConfig extends SpringBootServletInitializer implements WebMvcConfigurer {
@Bean
protected WebSecurityConfigurerAdapter getWebSecurityConfigurerAdapter() {
return new WebSecurityConfigurerAdapter() {
@Override
protected void configure(HttpSecurity http) throws Exception {
//禁用spring-security的csrf功能
http.csrf().disable();
//启用spring-security的cors功能,这里这样写,不加其他代码,则默认调用spring-mvc的cors相关功能
http.cors();
//防止spring-security拦截spring-boot actuator的相关请求
http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest().permitAll();
}
};
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*");
}
}
这个类是spring-boot项目配置类,虽然spring-boot支持多个配置类,但是, 个人建议:只使用这一个配置类,将所有配置集中管理是比较好的。 这个类是个重头戏,解释如下:
package com.szq.base.constants;
public interface AppConstants {
String APP_AUTORELOADABLE_FILE_NAME = "app-autoreloadable.properties";
}
这个类没什么解释的,一个常量类。
package com.szq.base.constants;
import java.util.HashMap;
import java.util.Map;
public class ReturnConstants {
public static final Map<String, Object> SUCCESS;
public static final Map<String, Object> FAIL;
public static final Map<String, Object> SYSTEM_ERROR;
static {
SUCCESS = new HashMap<>(2);
SUCCESS.put("code", 0);
SUCCESS.put("msg", "success");
FAIL = new HashMap<>(2);
FAIL.put("code", 1);
FAIL.put("msg", "fail");
SYSTEM_ERROR = new HashMap<>(2);
SYSTEM_ERROR.put("code", 2);
SYSTEM_ERROR.put("msg", "System error");
}
}
这个类没什么解释的,也是一个常量类。
package com.szq.base.controller.api;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.configuration2.ImmutableConfiguration;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.szq.base.constants.AppConstants;
import com.szq.base.constants.ReturnConstants;
import com.szq.base.service.UserService;
import com.szq.base.springcomponent.GlobalHttpLinkPool;
import com.szq.base.springcomponent.GlobalPropertiesConfig;
/**
* 常规spring-mvc中的controller层,这个controller有个比较特殊的地方就是实现了ApplicationContextAware借口,下文有介绍
* @author Administrator
* @comment
*/
@RestController
@RequestMapping(value = "/api", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class ApiController implements ApplicationContextAware {
private ApplicationContext applicationContext;
/**
* 全局单例工具类,读取配置文件,基于Apache Commons Configuration库
* 该读取工具会自动重载配置文件而无需重启服务器
* 可以将一些开关类型的变量配置在一个配置文件中,用该工具类来读取
* 从而实现在不停机的前提下改变服务的功能
* 该项目所用配置文件名为:app-autoreloadable.properties
*/
@Autowired
private GlobalPropertiesConfig globalPropertiesConfig;
/**
* 全局单例工具类,用来发送http请求,基于Apache HttpClient库
*/
@Autowired
private GlobalHttpLinkPool globalHttpLinkPool;
/**
* 常规spring-mvc中的service
*/
@Autowired
private UserService userService;
/*
* 通过实现ApplicationContextAware借口,获取applicationContext
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 很多基于spring-boot的开发者不知道怎么手动优雅的关闭spring-boot项目
* 当然了,在实际生产环境中是不需要考虑优雅关闭spring-boot项目的,因为如果是通过'java -jar'的方式运行的
* 直接'ctrl + c'就行,如果是传统war包方式部署的spring-boot项目,则servlet容器有自己的关闭方式
* 但是,如果是在开发的时候呢?如何优雅的关闭spring-boot项目呢?在eclipse中,只有一个terminate按钮,是没有stop按钮的!
* 其实方法很简单,就是下面这个接口中所描述的方法。
* @return
* @throws Exception
*/
@RequestMapping(value = "/shutdown", method = RequestMethod.GET)
private Map<String, Object> appShutdown() throws Exception {
//一定要另起一个线程,不然该方法可能无法正常执行完毕
new Thread(new Runnable() {
@Override
public void run() {
//spring-boot项目退出,固定这么写就行了
System.exit(SpringApplication.exit(applicationContext));
}
}).start();
return ReturnConstants.SUCCESS;
}
/**
* 常规接口,测试数据库操作是否正确
* @return
* @throws Exception
*/
@RequestMapping(value = "/user/save", method = RequestMethod.GET)
private Map<String, Object> saveUser() throws Exception {
String userName = "userName1";
String userPassword = "userPassword1";
userService.saveUser(userName, userPassword);
return ReturnConstants.SUCCESS;
}
/**
* 常规接口,测试全局配置文件读取工具是否正常工作
* @return
* @throws Exception
*/
@RequestMapping(value = "/property/test", method = RequestMethod.GET)
private Map<String, Object> testPropertyFunction() throws Exception {
ImmutableConfiguration configuration = globalPropertiesConfig.getConfiguration(AppConstants.APP_AUTORELOADABLE_FILE_NAME);
Map<String, Object> rm = new HashMap<>(ReturnConstants.SUCCESS);
{
rm.put("data", configuration.getString("msg"));
}
return rm;
}
/**
* 常规接口,测试全局http请求工具是否正常工作
* @return
* @throws Exception
*/
@RequestMapping(value = "/httplinkpool/test", method = RequestMethod.GET)
private Map<String, Object> testHttpLinkPoolFunction() throws Exception {
CloseableHttpClient closeableHttpClient = globalHttpLinkPool.getCloseableHttpClient();
HttpPost post = new HttpPost("https://www.baidu.com");
CloseableHttpResponse response = closeableHttpClient.execute(post);
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
Map<String, Object> rm = new HashMap<>(ReturnConstants.SUCCESS);
{
rm.put("data", responseString);
}
return rm;
}
}
这个类也没什么好解释的,都在代码中的注释里。其中那个优雅关闭spring-boot项目的方法算是一个干货吧。
package com.szq.base.controller.page;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 常规spring-mvc中的controller层,用来返回页面
* @author Administrator
* @comment
*/
@Controller
public class PageController {
/**
* 工具方法,从请求url中提取页面名称,
* @param servletPath
* @return
*/
private String getHTMLName(String servletPath) {
String pageName = servletPath.substring(1);
if (pageName.endsWith(".html")) {
pageName = pageName.substring(0, pageName.length() - 5);
}
return pageName;
}
/**
* 常规接口,测试freemarker是否正常工作
* @param request
* @return
* @throws Exception
*/
@RequestMapping("/freemarkerIndex.html")
private String getIndexPage(HttpServletRequest request) throws Exception {
return getHTMLName(request.getServletPath());
}
}
没什么要解释的,都在代码注释里。
package com.szq.base.dao;
import com.szq.base.dio.UserDIO;
/**
* 常规spring-mvc的dao层,这个接口会被mybatis自动扫描
* 对应于AppConfig.java配置类中的那个@MapperScan("com.szq.base.dao")配置
* @author Administrator
* @comment
*/
public interface UserDAO {
int saveUser(UserDIO user);
}
这个也没什么解释的,看代码注释。
package com.szq.base.dio;
public class UserDIO {
private int id;
private String userName;
private String userPassword;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
@Override
public String toString() {
return "UserDIO [id=" + id + ", userName=" + userName + ", userPassword=" + userPassword + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + ((userName == null) ? 0 : userName.hashCode());
result = prime * result + ((userPassword == null) ? 0 : userPassword.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
UserDIO other = (UserDIO) obj;
if (id != other.id)
return false;
if (userName == null) {
if (other.userName != null)
return false;
} else if (!userName.equals(other.userName))
return false;
if (userPassword == null) {
if (other.userPassword != null)
return false;
} else if (!userPassword.equals(other.userPassword))
return false;
return true;
}
}
没什么解释的,就是一个bean。
package com.szq.base.service;
public interface UserService {
int saveUser(String userName, String userPassword) throws Exception;
}
没什么解释的。
package com.szq.base.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.szq.base.dao.UserDAO;
import com.szq.base.dio.UserDIO;
import com.szq.base.service.UserService;
/**
* 常规spring-mvc的service层,没什么特殊的地方
* @author Administrator
* @comment
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDAO userDAO;
@Transactional(rollbackFor = Exception.class)
@Override
public int saveUser(String userName, String userPassword) throws Exception {
UserDIO user = new UserDIO();
{
user.setUserName(userName);
user.setUserPassword(userPassword);
}
int i = 0;
for (int j = 0; j < 20; j++) {
//这个if是为了测试上面的@Transactional注解是否起到了作用
if (j == 5) {
throw new IllegalStateException("-- transaction exception and global exception handler test");
}
userDAO.saveUser(user);
}
return i;
}
}
没什么解释。
package com.szq.base.springcomponent;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import com.alibaba.fastjson.JSON;
import com.szq.base.constants.ReturnConstants;
/**
* 这个类会被spring自动扫描
* 对应于AppConfig配置类的@SpringBootApplication(scanBasePackages = { "com.szq.base.controller", "com.szq.base.service.impl", "com.szq.base.springcomponent" })配置
* 全局统一异常处理,请仔细看ApiController类,里面的每个接口都直接把异常给抛出了
* 如:private Map appShutdown() throws Exception
* 那个抛出的异常就会跑到这里进行统一的处理
* @author Administrator
* @comment
*/
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
private Logger logger = LoggerFactory.getLogger(getClass().getSimpleName());
@ExceptionHandler(Exception.class)
protected final ResponseEntity<String> handleNonSpringStandardException(Exception e) {
logger.warn("", e);
Map<String, Object> rm = new HashMap<>(ReturnConstants.SYSTEM_ERROR);
{
rm.put("err_msg", e.getMessage());
}
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON_UTF8).body(JSON.toJSONString(rm));
}
}
这个类算是一个干货,与其在每个接口中处理异常,不如写这么一个类全局统一处理异常,能少写很多代码。原文链接如下:
https://docs.spring.io/spring/docs/4.3.15.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/#mvc-ann-controller-advice。文档写的可能不是很清楚,大致意思是@RestControllerAdvice注解的类里面可以使用@ExceptionHandler注解,@ExceptionHandler又可以用来处理controller的异常,综合一下就可以整出来这么一个全局统一异常处理的工具来。
package com.szq.base.springcomponent;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.net.ssl.SSLContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.springframework.stereotype.Component;
/**
* http请求工具,作为spring的一个单例bean来使用,会被spring自动扫描
* 对应于AppConfig配置类的@SpringBootApplication(scanBasePackages = { "com.szq.base.controller", "com.szq.base.service.impl", "com.szq.base.springcomponent" })配置
* @author Administrator
* @comment
*/
@Component
public class GlobalHttpLinkPool {
private PoolingHttpClientConnectionManager connectionManager;
@PostConstruct
private void initMethod() {
TrustStrategy trustStrategy = new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
//返回true就会信任所有凭证,某些应用只是需要https的加密传输功能而不需要进行实际的认证,这时候就需要return true
return true;
}
};
SSLContext sslContext = null;
try {
sslContext = SSLContextBuilder.create().loadTrustMaterial(trustStrategy).build();
} catch (Exception e) {
//出异常,connectionManager置null
connectionManager = null;
}
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext);
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.INSTANCE).register("https", sslConnectionSocketFactory).build();
connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
connectionManager.setMaxTotal(500); //设置连接池总大小
connectionManager.setDefaultMaxPerRoute(50); //设置每个URL最多可同时有多少个连接
connectionManager.setValidateAfterInactivity(1000); //设置检测失效连接的时间间隔,单位:毫秒
}
@PreDestroy
private void destroyMethod() {
connectionManager.shutdown();
}
/**
* 获取连接客户端
* @return
*/
public CloseableHttpClient getCloseableHttpClient() {
return HttpClients.custom().setConnectionManager(connectionManager).build();
}
}
没什么解释的,一个工具类。
package com.szq.base.springcomponent;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.PreDestroy;
import org.apache.commons.configuration2.ConfigurationUtils;
import org.apache.commons.configuration2.ImmutableConfiguration;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Parameters;
import org.apache.commons.configuration2.builder.fluent.PropertiesBuilderParameters;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.reloading.PeriodicReloadingTrigger;
import org.springframework.stereotype.Component;
/**
* 配置文件读取工具,会自动重载配置文件,作为spring的一个单例bean来使用,会被spring自动扫描
* 对应于AppConfig配置类的@SpringBootApplication(scanBasePackages = { "com.szq.base.controller", "com.szq.base.service.impl", "com.szq.base.springcomponent" })配置
* @author Administrator
* @comment
*/
@Component
public class GlobalPropertiesConfig {
private Map<String, BuilderAndTrigger> btMap = new HashMap<>();
@PreDestroy
private void destroyMethod() {
Set<Entry<String, BuilderAndTrigger>> entries = btMap.entrySet();
for (Entry<String, BuilderAndTrigger> entry : entries) {
entry.getValue().getTrigger().shutdown();
}
}
/**
* 获取配置
* @param fileName 配置文件名,配置文件放在类目录下
* @return
* @throws ConfigurationException
*/
public ImmutableConfiguration getConfiguration(final String fileName) throws ConfigurationException {
BuilderAndTrigger bt = btMap.get(fileName);
PropertiesConfiguration configuration = null;
if (bt == null) { //builder不存在,生成新builder
ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration> builder = createConfiguration(fileName);
configuration = builder.getConfiguration(); //检测builder是否成功生成,未成功生成则这里会抛出异常
PeriodicReloadingTrigger trigger = new PeriodicReloadingTrigger(builder.getReloadingController(), null, 5, TimeUnit.SECONDS); //声明配置重载触发器,5秒钟检测一次重载
trigger.start(); //启动触发器
btMap.put(fileName, new BuilderAndTrigger(builder, trigger)); //将文件名和对应的builder和trigger保存到btMap
} else {
configuration = bt.getBuilder().getConfiguration(); //若builder存在,则直接取该builder
}
return ConfigurationUtils.unmodifiableConfiguration(configuration); //返回不可修改的configuration
}
/**
* 移除不再使用的配置
* @param fileName
*/
public void removeConfiguration(final String fileName) {
BuilderAndTrigger bt = btMap.get(fileName);
if (bt != null) { //有builder和trigger存在
bt.getTrigger().shutdown(); //停止trigger
btMap.remove(fileName); //移除builder和trigger
}
}
/**
* 根据配置文件名生成新配置
* @param fileName
* @return
*/
private ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration> createConfiguration(final String fileName) {
PropertiesBuilderParameters parameters = new Parameters().properties().setFile(new File(fileName));
return new ReloadingFileBasedConfigurationBuilder<>(PropertiesConfiguration.class).configure(parameters);
}
/**
* @author Administrator 内部类,表示builder和其对应的trigger
* @comment
*/
private static class BuilderAndTrigger {
private ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration> builder;
private PeriodicReloadingTrigger trigger;
public BuilderAndTrigger(ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration> builder, PeriodicReloadingTrigger trigger) {
this.builder = builder;
this.trigger = trigger;
}
public ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration> getBuilder() {
return builder;
}
public PeriodicReloadingTrigger getTrigger() {
return trigger;
}
@Override
public String toString() {
return "BuilderAndTrigger [builder=" + builder + ", trigger=" + trigger + "]";
}
}
}
一个工具类。
<mapper namespace="com.szq.base.dao.UserDAO">
<insert id="saveUser">
INSERT INTO t_user ( u_name, u_password )
VALUES
(
#{userName},
#{userPassword}
)
insert>
mapper>
mybatis的sql映射文件。这个文件在mybatis-mapper目录下,这个目录是在app-autoreloadable.properties配置文件中配置的,下文有解释。
alert('-- freemarkerIndex page js');
测试spring-boot静态资源是否正常加载,这个文件在static目录下,这个目录spring-boot默认静态资源目录。
<html>
<head>
<title>indextitle>
head>
<body>
<h2>this is index pageh2>
<script type="text/javascript" src="index.js">script>
body>
html>
同15。
alert('-- this is index page');
同15。
<html>
<head>
<title>indextitle>
head>
<body>
<h2>this is freemarker index pageh2>
<p>${300000}p>
<p>${300000000?number_to_datetime}p>
<script type="text/javascript" src="freemarkerIndex.js">script>
body>
html>
freemarker测试页面,测试freemarker是否正常工作,看注释,有一些干货小技巧。这个文件在templates目录下,这个目录是spring-boot的freemarker starter默认目录。
#该配置文件配置会自动重载,无需重启服务器
msg=this is a auto-reload config test msg.1
GlobalPropertiesConfig.java所用的那个配置文件,没什么解释的。
#这一组配置表示是否开启spring-boot内置的debug和trace级别的日志
debug=false
trace=false
#这一组配置用来配置spring-boot内置servlet容器的一些配置
#配置内置server监听的网络地址,这里配置为0.0.0.0表示监听任意网络地址
server.address=0.0.0.0
server.port=8080
server.tomcat.accesslog.enabled=true
#这一组配置表示spring-boot-starter-freemarker的相关属性
#这个配置表示freemarker模板文件的后缀
spring.freemarker.suffix=.html
#这个配置表示freemarker数字显示格式
spring.freemarker.settings.number_format=#
#这个配置表示freemarker日期时间显示格式
spring.freemarker.settings.datetime_format=yyyy-MM-dd HH:mm:ss
#这一组配置表示spring-boot内置文件上传功能的相关属性
#奇怪的是,spring-boot不支持使用commons fileupload库,推荐使用内置的文件上传功能,而spring-mvc则拥抱commons fileupload库,不知道为什么,可能是这两个spring项目的开发人员理念不一致吧
#原文链接分别如下:
#spring-boot:https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/htmlsingle/#howto-multipart-file-upload-configuration
#spring-mvc:https://docs.spring.io/spring/docs/4.3.15.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/#mvc-multipart-resolver-commons
#这个配置表示上传的每个文件的最大大小
spring.servlet.multipart.max-file-size=1MB
#这个配置表示每个请求的最大大小
spring.servlet.multipart.max-request-size=10MB
#这组配置表示阿里巴巴druid数据库连接池的配置,粘贴复制改改账户名密码和URL,拿来用就行了,每个项的意思参考我这篇文章
#https://blog.csdn.net/lianjunzongsiling/article/details/80112971
spring.datasource.druid.username=root
spring.datasource.druid.password=123456
spring.datasource.druid.url=jdbc:mysql://localhost:3306/learn?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE&serverTimezone=UTC
spring.datasource.druid.initial-size=1
spring.datasource.druid.max-active=200
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-wait=60000
spring.datasource.druid.validation-query=select 1;
spring.datasource.druid.test-on-borrow=false
spring.datasource.druid.test-on-return=false
spring.datasource.druid.test-while-idle=true
spring.datasource.druid.time-between-eviction-runs-millis=25200000
spring.datasource.druid.min-evictable-idle-time-millis=3600000
#这组配置表示mybatis相关配置,mybatis有一个spring-boot的starter,这个配置就是给那个starter用的
#这个配置表示mybatis的sql映射文件的位置,支持ant形式
mybatis.mapper-locations=mybatis-mapper/*.xml
#这个配置表示mybatis的type alias的位置,具体参考我的这篇文章
#https://blog.csdn.net/lianjunzongsiling/article/details/74783674
mybatis.type-aliases-package=com.szq.base.dso
application.properties是spring-boot的默认配置文件,每个配置的意思请看上面配置中的注释。干货:server.address=0.0.0.0,表示监听所有网络地址。有些人将server.address配置成localhost或者127.0.0.1,这样的话springboot内置服务器就只会监听配置的那个固定的地址,导致别人无法访问。
<Configuration status="WARN" monitorInterval="5">
<Properties>
<Property name="logDir" value="logs/base/" />
<Property name="logFileName" value="app" />
<Property name="genericPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
<Property name="genericFilePattern" value="%d{yyyy-MM-dd}-%i" />
Properties>
<Appenders>
<Console name="consoleAppender" target="SYSTEM_OUT">
<PatternLayout pattern="${genericPattern}" />
Console>
<RollingRandomAccessFile name="rollingRandomAccessFileAppender" fileName="${logDir}/${logFileName}.log" filePattern="${logDir}/${logFileName}-${genericFilePattern}.log" append="true">
<PatternLayout pattern="${genericPattern}" />
<Policies>
<TimeBasedTriggeringPolicy interval="1" />
<SizeBasedTriggeringPolicy size="100 MB" />
Policies>
<DefaultRolloverStrategy max="1000000" compressionLevel="9" />
RollingRandomAccessFile>
Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="consoleAppender" />
Root>
Loggers>
Configuration>
log4j2的配置文件,log4j2的默认配置文件名原本是log4j2.xml,但是spring-boot推荐使用log4j2-spring.xml这个名字,原文链接如下:https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/htmlsingle/#boot-features-custom-log-configuration。log4j2的教程可以参考我这篇文章:https://blog.csdn.net/lianjunzongsiling/article/details/78848844。
<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.0modelVersion>
<groupId>com.szqgroupId>
<artifactId>baseartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>warpackaging>
<name>basename>
<url>http://maven.apache.orgurl>
<properties>
<maven-jar-plugin.version>3.1.1maven-jar-plugin.version>
properties>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.6.RELEASEversion>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-loggingartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-log4j2artifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.18version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.0version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.58version>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-configuration2artifactId>
<version>2.5version>
dependency>
<dependency>
<groupId>commons-beanutilsgroupId>
<artifactId>commons-beanutilsartifactId>
<version>1.9.3version>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.6version>
dependency>
dependencies>
<build>
<finalName>basefinalName>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
pom.xml文件,里面有必要的注释。通过修改packaging属性为 jar 或者 war ,可以分别打出来jar包或者war包。其实,按照这个pom.xml的配置,加上前文中的重头戏1,打出来的 war 包既可以通过 ‘java -jar *.war’ 包的形式来运行,也可以像传统的war包部署那样来运行,也就是说 jar 包这种形式是不需要的,因为按照本文章所介绍, jar 包有的功能,war 包都有,jar 包没有的功能,war 包也有。
如果你浏览了本文章,按照章节一中图片所示的结构创建了项目,然后把章节二中的代码和配置文件都对应地粘贴复制保存好,那么你现在就有了一个功能齐备的可以直接用来开发的项目了,这个项目整合了常用框架,有现成配置文件读取工具和http请求工具,有现成的spring-security整合,可以直接通过override相关接口的相关方法来配置spring-mvc和spring-security的相关功能,数据库操作功能也是现成的,事务支持也是现成的。如果该文章让你感觉很困惑,那么你需要从基础的部分学习spring,然后再来看本文章。