快速了解SpringBoot

SpringBoot 介绍

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。

说白了就是为我们的开发简化了大量配置的一个框架。

SpringBoot 特性

  • 创建独立的Spring应用程序
  • 直接嵌入Tomcat,Jetty或Undertow(无需部署WAR文件)
  • 提供大量整合好的启动器依赖项,以简化构建配置
  • 尽可能自动配置Spring
  • 提供生产就绪的功能,例如指标,运行状况检查和外部配置
  • 完全没有代码生成,也不需要XML配置

创建SpringBoot项目的方式

前提:JDK1.8

1.进入 SpringBoot官网Spring Initializr的方式创建。
2. 使用 IDE 工具以Spring Initializr方式创建。
3. 以Maven(引入SpringBoot-starter-parent坐标)方式创建。

无论是那种方式创建SpringBoot项目,核心是项目中的pom文件引入了SpringBoot-starter-parent坐标。

下面是我用IDEA IntelliJSpring 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-pluginMaven的相关操作需要。

@RestController注解

@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为我们自动配置了。对SpringBoot自动配置原理感兴趣的话可以点击@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添加了TomcatSpring MVC,所以auto-configuration将假定你正在开发一个web应用并相应地对Spring进行设置。

静态资源访问

SpringBoot默认提供静态资源目录位置需置于classpath下,目录名需符合如下规则:

  • /static
  • /public
  • /resources
  • /META-INF/resources

举个栗子: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 转换。

  1. 返回 String(表示跳到某个 view
  2. 返回 ModelAndView
  3. 返回 Model + @ResponseBody

渲染Web页面(模板引擎)

使用@RestController处理请求,返回的都是JSON格式数据。如何渲染HTML页面呢?

模板引擎技术
Spring Boot 提供默认配置的模板引擎主要有以下几种:
ThymeleafFreeMarkerVelocityGroovy 等,JSP也可以用,但是SpringBoot不推荐使用,如果坚持使用JSP你可能得不到SpringBoot的某些特性支持。

SpringBoot 默认的模板配置路径为:src/main/resources/templates

下面我们使用FreeMarker 模板引擎技术渲染Web视图作为示范:

  1. 首先添加依赖
<!-- freemarker模板引擎依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
  1. src/main/resources/创建一个templates文件夹,HTML文件的后缀为*.ftl
    HTML文件(index.ftl)内容如下
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8" />
    <title></title>
</head>
<body>
    <h1>index欢迎,${name}</h1>
</body>
</html>

模板引擎相关的表达式语法可以去自己使用的模板引擎官网学习,就不介绍了。

  1. 编写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";
    }
}
  1. 配置文件代码如下(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整合了:JdbcTemplateMyBatisSpringDataJpa…等持久层框架。
下面简单做个使用步骤回顾,不多赘述了。

  1. 引入DB驱动 (Mysql/Oracl) 依赖
  2. 引入框架(JdbcTemplateMyBatisSpringDataJpa…)依赖
  3. application.yml(核心配置文件)增加数据源配置

JdbcTemplate: 在Service类中注入JdbcTemplate 使用即可。

MyBatis:额外多了Mapper.xml文件的SQL编写(generate 逆向工程可生成)。

SpringDataJpa: (jpa是规范,data jpa 是对hibernate的封装实现)

  1. 实体类需要hibernate使用一样,使用相关注解(@Entity、@Id、@GeneratedValue、@Column等注解)对对象/属性做映射。
  2. Dao层接口需要继承JpaRepository

事务管理

《Spring事务理解》
springboot 默认集成事物,使用声明式事务(@Transactional 注解)即可。

整合多数据源

多数据源是指:在一个项目中操纵两个及以上的数据库,就需要在项目中配置(要操作数的数据源)/个数。
目前比较常见的两种方式:分包/AOP+自定义注解方式。

使用多数据源,在一次业务逻辑中操作多个数据源的情况要注意事务的管理,因为此时事务是分布的(分布式事物),需要谨慎处理。

常见分布式事务解决方案: 使用 springboot + jta + atomikos 、两段提交协议、 MQ推送,等解决方案。

日志管理

门面类的日志框架(没有具体实现,只是一个规范):如 Commons Logging、Slf4j...
具体的日志框架实现:如 Log4j、Log4j2、Logback、Jul...
性能来说:推荐 Slf4j + Logback 性能上会好一些(网上很多相关性能比较文章)。
下面用 Slf4j + Logback 举例:

  1. 在项目的resource目录下新建logback-spring.xml文件
  2. 编辑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>
  1. 哪里要使用就在声明Logger对象(项目用了lombok,直接在类上使用@Slf4j注解,直接使用log记录也是一样的噢)

  2. 使用方式(下方举例)

@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);
    }
}

定时任务

  1. 启动类上加@EnableScheduling 注解,开启定时任务。
  2. 作为定时任务执行的方法上使用@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()));
    }

}

异步调用

  1. 启动类加上@EnableAsync注解,开启异步调用。
  2. 需要变成异步执行的方法上使用@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为主配置文件,其他的文件为副文件,如下所示:

  • application-dev.properties 标识本地开发环境
  • application-test.properties 标识测试环境
  • application-prd.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文件下编译产生的文件。

  1. 使用maven命令:mvn package 打包的方式可以不用(compile)编译,打包好的jar文件会在项目target目录下
  2. 使用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: 文章比较长,属于回顾型,存在错误或概念错误或者不严谨的地方请多多指出,谢谢观看。


版权声明:本文为QingHe97原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。