Spring Boot
是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。
说白了就是为我们的开发简化了大量配置的一个框架。
前提:JDK1.8
1.进入 SpringBoot官网以Spring Initializr
的方式创建。
2. 使用 IDE
工具以Spring Initializr
方式创建。
3. 以Maven
(引入SpringBoot-starter-parent
坐标)方式创建。
无论是那种方式创建SpringBoot
项目,核心是项目中的pom
文件引入了SpringBoot-starter-parent
坐标。
下面是我用IDEA IntelliJ
以Spring Initializr
的方式创建的SpringBoot
项目pom
文件:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.darian</groupId>
<artifactId>darian-springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>darian-springboot</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<--! 这是我手动引入的 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
spring-boot-starter-parent :是一个特殊的starter
,它用来提供相关的Maven
默认依赖。使用它之后,常用的包依赖可以省去version
标签。Maven
的用户可以通过继承spring-boot-starter-parent
项目来获得一些合理的默认配置。
spring-boot-starter-web :是SpringWeb
的核心组件。
spring-boot-maven-plugin:Maven
的相关操作需要。
@RestController
public class TestController {
private static final String TEST = "This is test... hello";
@GetMapping("/test")
public String test(){
return TEST;
}
}
在Controller
上使用@RestController
注解,表示修饰该Controller
所有的方法返回JSON
格式数据。(@RestController
= @Controller
+ @ResponseBody
可以点击注解进去看,实际就是两个注解的集合体)
启动方式1
@SpringBootApplication
public class DarianSpringbootApplication {
public static void main(String[] args) {
// @SpringBootApplication + 这句话的作用就是标识为启动类
SpringApplication.run(DarianSpringbootApplication.class, args);
}
}
启动好,访问浏览器:localhost:8080/test
就能访问我们的Controller
了
@SpringBootApplication
注解+一句话就能启动程序,是因为SpringBoot
内置了很多默认配置,由SpringBoot
为我们自动配置了。对SpringBoo
t自动配置原理感兴趣的话可以点击@SpringBootApplication
注解进去看一看。
启动方式2
// @ComponentScan(...) 扫包范围
@ComponentScan(basePackages = "com.darian.darianspringboot.controller")
@EnableAutoConfiguration
public class DarianSpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(DarianSpringbootApplication.class, args);
}
}
还有别的启动方式就不探讨了
我们可以点击@SpringBootApplication
注解进去看到若干注解,核心了解@EnableAutoConfiguration
即可。
@EnableAutoConfiguration 思想原理
注解的作用在于让 Spring Boot
根据应用所声明的依赖来对 Spring
框架进行自动配置,这个注解告诉Spring Boot
根据添加的jar
依赖猜测你想如何配置Spring
。由于spring-boot-starter-web
添加了Tomcat
和Spring MVC
,所以auto-configuration
将假定你正在开发一个web应用并相应地对Spring
进行设置。
SpringBoot
默认提供静态资源目录位置需置于classpath
下,目录名需符合如下规则:
举个栗子: 在src/main/resources/
目录下创建static
文件夹,放入一张图片,然后启动项目:http://localhost:8080/1.png
访问图片。
当然,在企业项目中,大多数都是前后端分离,且项目中的图片都是上传到某专用服务器进行存放,通过一些API去访问的,你有兴趣可以去了解阿里的OSS图片储存。
@ControllerAdvice
public class GlobalExceptionHandler {
@ResponseBody
// 可以是自定义的异常类型,根据需求设计
@ExceptionHandler(RuntimeException.class)
public Map<String, Object> exceptionHandler() {
Map<String, Object> map = new HashMap<>();
map.put("code", "500");
map.put("msg", "系統错误");
return map;
}
}
@ExceptionHandler :表示要拦截异常类型
@ControllerAdvice
该类作为全局异常处理类,还可以(basePackages
熟悉)指定范围。
@ControllerAdvice
约定了几种可行的返回值,如果是直接返回model
类的话,需要使用 @ResponseBody
进行 json
转换。
String
(表示跳到某个 view
)ModelAndView
Model
+ @ResponseBody
使用@RestController
处理请求,返回的都是JSON格式数据。如何渲染HTML页面呢?
模板引擎技术
Spring Boot
提供默认配置的模板引擎主要有以下几种:
Thymeleaf
、FreeMarker
、Velocity
、Groovy
等,JSP
也可以用,但是SpringBoot
不推荐使用,如果坚持使用JSP
你可能得不到SpringBoot
的某些特性支持。
SpringBoot
默认的模板配置路径为:src/main/resources/templates
。
下面我们使用FreeMarker
模板引擎技术渲染Web视图作为示范:
<!-- freemarker模板引擎依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
src/main/resources/
创建一个templates
文件夹,HTML文件的后缀为*.ftl
index.ftl
)内容如下<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<title></title>
</head>
<body>
<h1>index欢迎,${name}</h1>
</body>
</html>
模板引擎相关的表达式语法可以去自己使用的模板引擎官网学习,就不介绍了。
Controller
代码如下@Controller
public class TestVeiwController {
@RequestMapping("/1")
public String ftl1(ModelMap modelMap) {
System.out.println("----1----");
modelMap.addAttribute("name", "darian");
return "index";
}
@RequestMapping("/2")
// @RequestParam 属性不清楚的话 百度一下就好了
public ModelAndView ftl2(ModelAndView modelAndView,@RequestParam(value = "name",required = false, defaultValue = " ") String name){
System.out.println("----2----");
modelAndView.addObject("name", name);
modelAndView.setViewName("index");
return modelAndView;
}
@RequestMapping("/3")
public String ftl3(Model model, @RequestParam("name") String name) {
System.out.println("----3----");
model.addAttribute("name", name);
return "index";
}
@RequestMapping("/4")
public String ftl4(Map<String, Object> params) {
System.out.println("----4----");
params.put("name","darian");
return "index";
}
}
application.yml
格式)spring:
freemarker:
suffix: .ftl #后缀
content-type: text/html
enabled: true
cache: false #缓存页面是否?
charset: UTF-8 #编码格式
这样就OK了,如果要使用JSP
的话,把FreeMarker
模板引擎的依赖更换成JSP
依赖:
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
在/WEB-INF/
下创建jsp文件夹,添加jsp
页面,再修改application.yml
文件为
spring:
mvc:
view:
# 存放jsp页面的文件路径
prefix: /WEB-INF/jsp/
# 后缀
suffix: .JSP
SpringBoot
整合了:JdbcTemplate
、MyBatis
、SpringDataJpa
…等持久层框架。
下面简单做个使用步骤回顾,不多赘述了。
Mysql/Oracl
) 依赖JdbcTemplate
、MyBatis
、SpringDataJpa
…)依赖application.yml
(核心配置文件)增加数据源配置JdbcTemplate: 在Service类中注入JdbcTemplate
使用即可。
MyBatis:额外多了Mapper.xml
文件的SQL
编写(generate
逆向工程可生成)。
SpringDataJpa: (jpa
是规范,data jpa
是对hibernate
的封装实现)
hibernate
使用一样,使用相关注解(@Entity、@Id、@GeneratedValue、@Column
等注解)对对象/属性做映射。Dao
层接口需要继承JpaRepository
《Spring事务理解》
springboot
默认集成事物,使用声明式事务(@Transactional 注解
)即可。
多数据源是指:在一个项目中操纵两个及以上的数据库,就需要在项目中配置(要操作数的数据源)/个数。
目前比较常见的两种方式:分包/AOP+自定义注解方式。
使用多数据源,在一次业务逻辑中操作多个数据源的情况要注意事务的管理,因为此时事务是分布的(分布式事物),需要谨慎处理。
常见分布式事务解决方案: 使用 springboot
+ jta
+ atomikos
、两段提交协议、 MQ推送,等解决方案。
门面类的日志框架(没有具体实现,只是一个规范):如 Commons Logging、Slf4j...
具体的日志框架实现:如 Log4j、Log4j2、Logback、Jul...
性能来说:推荐 Slf4j + Logback
性能上会好一些(网上很多相关性能比较文章)。
下面用 Slf4j + Logback
举例:
resource
目录下新建logback-spring.xml
文件logback-spring.xml
文件(网上很多供参考,用到实际生成中的话请务必按照生产规范的标准书写)<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--使用spring扩展profile支持,要以logback-spring.xml命名-->
<!-- 彩色日志 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
<property name="FILE_LOG_PATTERN" value="%-12(%d{yyyy-MM-dd HH:mm:ss.SSS}) |-%-5level [%thread] %c [%L] -| %msg%n" />
<!-- 控制台彩色日志输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--日志文件输出的位置,不指定会出现Linux无法输出文件-->
<File>${user.dir}/logs/logs.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${user.dir}/logs/pool.log.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>20MB</MaxFileSize>
</triggeringPolicy>
</appender>
<logger name="io.bytom.pool" level="DEBUG"/>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</configuration>
哪里要使用就在声明Logger
对象(项目用了lombok
,直接在类上使用@Slf4j
注解,直接使用log
记录也是一样的噢)
使用方式(下方举例)
@Controller
public class TestVeiwController {
private static final Logger log = LoggerFactory.getLogger(TestVeiwController.class);
@RequestMapping("/1")
public String ftl1(ModelMap modelMap) {
// 推荐使用占位符的方式
log.info("----{}----",1);
modelMap.addAttribute("name", "darian");
return "index";
}
}
使用AOP统一处理请求日志
因为我依赖了lombok
依赖,使用@Slf4j
注解后就能直接使用log
记录日志了。
@Slf4j
@Aspect
@Component
public class RequestLog {
@Pointcut("execution(public * com.darian.darianspringboot.controller..*.*(..))")
public void logPoint() {
}
@Before("logPoint()")
public void requestBefore() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录请求内容
log.info("URL : " + request.getRequestURL().toString());
log.info("HTTP_METHOD : " + request.getMethod());
log.info("IP : " + request.getRemoteAddr());
Enumeration<String> enu = request.getParameterNames();
while (enu.hasMoreElements()) {
String name = enu.nextElement();
log.info("name:{},value:{}", name, request.getParameter(name));
}
}
@AfterReturning(returning = "result", pointcut = "logPoint()")
public void requestAfterReturning(Object result) {
// 处理完请求,返回内容
log.info("RESPONSE : " + result);
}
}
缓存分为两种:JVM缓存/内存缓存(Redis、MemCache...
)
以Ehcache
缓存简单举例:首先引入Ehcache
依赖,然后resource
目录下创建ehcache.xml
文件(内容百度上面找一下),启动类使用@EnableCaching
注解开启缓存功能。
@RestController
public class UserController {
@Autowired
private UserServiceImpl userService;
@GetMapping("/user/{id}")
// @Cacheable缓存数据,可用在类/方法上
@Cacheable(value = "customCache",key = "#id")
public User user(@PathVariable Integer id){
return userService.getUser(id);
}
@GetMapping("/del/{id}")
// 缓存清除
@CacheEvict(value = "customCache", key = "#id")
public void delUser(@PathVariable Integer id){
// userService.deleteUserById(id);
}
}
@EnableScheduling
注解,开启定时任务。@Scheduled
注解。下面是模拟定时任务例子,实际中并不会这么使用(每五秒打印当前时间):
@Component
public class ScheduledTasks {
// SimpleDateFormat线程不安全
private static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
// cron、fixedDelay等属性省略解释
@Scheduled(fixedRate = 5000)
public void printCurrentTime() {
System.out.println("time:" + sdf.format(new Date()));
}
}
@EnableAsync
注解,开启异步调用。@Async
注解。下面是模拟异步方法的代码举例:
@Async
public void longTimeDoSomething(){
// 模拟该方法会和DB层做很长时间的持久化工作
try {
Thread.sleep(3000);
} catch (Exception ex){
}
System.out.println("tips:longTimeDoSomething() is OK");
}
上面的方法需要至少花费3秒以上,为了不被耽误响应给客户端,我们可以让该方法成为异步方法被异步调用,来提高响应速度。
自定义参数,在SpringBoot
项目中,我们通过在application.yml
文件中自定义一些命名规则的参数,再通过某种方式去获取它们。
方式1 通过 @Value 获取:
#自定义参数
custom:
name: darian
love: 100
获取application.yml
文件中自定义的参数值
@RestController
public class TestController {
@Value("${custom.name}")
private String name;
@Value("${custom.love}")
private Integer love;
@GetMapping("/custom")
public String custom(){
return name;
}
}
方式2 通过@ConfigurationProperties(prefix = “…”)注解:
@Component
@ConfigurationProperties(prefix = "自定义参数的统一前缀")
public class customParams {
private String customName;
private Integer customLove;
// 省略属性的get/set方法...
}
方式3 @Autowired注入 Environment 类(隶属 freemarker.core.Environment包):
以下的例子是我再对接阿里云短信接口的写的发送短信工具类的方法,自定义了很多参数,通过Environment
来获取。(有很多种方式,效果都一样,看个人喜好)
@Component
public class SmsUtil{
@Autowired
private Environment environment;
public void sendSms(String phoneNumbers, String code) {
String accessKeyId = environment.getProperty("aliyun.sms.accessKeyId");
String accessKeySecret = environment.getProperty("aliyun.sms.accessKeySecret");
String domain = environment.getProperty("aliyun.sms.domain");
String signName = environment.getProperty("aliyun.sms.signName");
String templateCode = environment.getProperty("aliyun.sms.templateCode");
// 省略其他代码... ...
}
}
还有其他方式就不探讨了,前面两种就够用了。
实际开发中,我们会有不同的开发环境(如 test测试环境、prd生产环境、等等环境),那么区分呢?
项目的核心配置文件是application.properties形式的:
如果你是项目中使用的application.properties
形式的配置文件,那么你需要创建多个环境的配置文件,且按照application-[标识].properties
的方式命名文件。
application.properties
为主配置文件,其他的文件为副文件,如下所示:
那么你想要随时切换环境的话,需要在主配置文件中加入如下代码:
spring.profiles.active=pre
项目的核心配置文件是application.yml形式的:
相对来说会轻松很多,不需要创建那么多配置文件,只需在配置文件中指定环境即可:
但是注意,要使用---
分隔环境。
# 指定环境
spring:
profiles:
active: dev
# 本地开发环境
spring:
profiles: env
# 省略本地开发环境的各种配置(如redis、datasource...)
# 生产环境
spring:
profiles: prd
#省略正式生产环境的各种配置(如redis、datasource...)
---
# 测试环境
spring:
profiles: test
#省略测试环境的各种配置(如redis、datasource...)
在IDE开发工具中Terminal
打包,或者CMD
窗口进入项目路径打包也行。
将SpringBoot
项目打包,一般都有两个步骤:清除(mvn clean
),打包。清除是为了清除target
文件下编译产生的文件。
maven
命令:mvn package
打包的方式可以不用(compile
)编译,打包好的jar
文件会在项目target
目录下maven
命令:mvn install
打包的方式会将打包好的jar包部署到本地,放到你本地的.m2
仓库中。打包过程中如果遇到没有主清单的问题,是因为pom
文件缺少了maven
插件。添加下方的代码到pom
文件即可:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
如果你想运行已经打包的文件,CMD
(Win+R
键)黑窗口进入jar
所在文件路径,使用java - jar 包名
就能启动项目了。
此外在使用java - jar 包名
方式启动项目时,还能指定许多参数,如:java - jar xxx.jar --server.prot=8888
,指定了项目的端口号,其他更多的参数你可以百度了解更多。
如果你觉得不安全,或者不喜欢,可以上官网查询相关文档,设置关闭即可。
小技巧
Maven
的打包之前会经过编译、测试,项目比较大的时候,下面这个命令就比较舒服了。
mvn package -Dmaven.test.skip=true
打包(跳过编译和测试环节)
多看官方文档,这也是一个小技巧噢,比如Maven的命令,想知道更多命令,可以上官网查看。
PS: 文章比较长,属于回顾型,存在错误或概念错误或者不严谨的地方请多多指出,谢谢观看。