Springboot自动配置 + mybatis集成实战

一、Spring Boot 自动配置原理

使用 Spring Boot 之后,做一个整合了 Spring MVC 的 Web 工程开发,变的无比简单,那些繁杂的配置都消失不见了,这是如何做到的?一切魔力的开始,都是从我们的 main 函数来的,所以我们再次来看下启动类:

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

从上面代码我们发现特别的地方有两个:

  • 注解:@SpringBootApplication

  • run方法:SpringApplication.run()。

1、了解 @SpringBootApplication

点击进入,查看源码:

这里重点的注解有3个:

  • @SpringBootConfiguration

  • @EnableAutoConfiguration

  • @ComponentScan

1.1、@SpringBootConfiguration

通过这段我们可以看出,在这个注解上面,又有一个 @Configuration 注解。通过上面的注释阅读我们知道:这个注解的作用就是声明当前类是一个配置类,然后 Spring 会自动扫描到添加了 @Configuration 的类,并且读取其中的配置信息。而 @SpringBootConfiguration 是来声明当前类是 Spring Boot 应用的配置类,项目中只能有一个。所以一般我们无需自己添加。

1.2、@ComponentScan

我们的 @SpringBootApplication 注解声明的类就是 main 函数所在的启动类,因此扫描的包是该类所在包及其子包。因此,一般启动类会放在一个比较前的包目录中。

1.3、@EnableAutoConfiguration

@EnableAutoConfiguration 的作用,告诉 Spring Boot 基于你所添加的依赖,去“猜测”你想要如何配置 Spring。比如我们引入了 spring-boot-starter-web,而这个启动器中帮我们添加了 tomcatSpringMVC 的依赖。此时自动配置就知道你是要开发一个 Web 应用,所以就帮你完成了 Web 及 Spring MVC 的默认配置了!

Spring Boot 内部对大量的第三方库或 Spring 内部库进行了默认配置,这些配置是否生效,取决于我们是否引入了对应库所需的依赖,如果有,那么默认配置就会生效。那么带来新的问题,如下:

  • 这些默认配置是在哪里定义的呢?

  • 为何依赖引入就会触发配置呢?

2、如何自动配置

@EnableAutoConfiguration 注解中导入了一个 AutoConfigurationImportSelector 配置类,该类中有个 getCandidateConfigurations 方法,方法的作用是委托 SpringFactoriesLoader 去读取 jar 包中的 META-INF/spring.factories 文件,并加载里面配置的自动配置对象,包括:AOP,PropertyPlaceholder,FreeMarker,HttpMessageConverter,Jackson,DataSourceDataSourceTransactionManager,DispatcherServlet 等等。

其实在我们的项目中,已经自动引入了一个依赖:spring-boot-autoconfigure,上面说到的这些自动配置类,都来自该包。

虽然 spring.factories 文件中定义了很多配置类,但并不是所有配置类都会生效,只有符合条件的是才会自动装配。

我们来看一个我们熟悉的,例如 Spring MVC,查看其自动配置类:

打开 WebMvcAutoConfiguration:

我们看到这个类上的 4 个注解:

  • @Configuration:声明这个类是一个配置类

  • @ConditionalOnWebApplication(type = Type.SERVLET)

ConditionalOn,翻译就是在某个条件下,此处就是满足项目的类是是 Type.SERVLET 类型,我们现在的项目就满足了,就是一个 Web 工程。

  • @ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })

这里的条件是 OnClass,也就是满足以下类存在:Servlet、DispatcherServlet、WebMvcConfigurer。这里就是判断你是否引入了 Spring MVC相关依赖,引入依赖后该条件成立,当前类的配置才会生效!

  • @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)

这个条件与上面不同,OnMissingBean,是说环境中没有指定的 bean 这个才生效。其实这就是自定义配置的入口,也就是说,如果我们自己配置了一个 WebMVCConfigurationSupport 的 bean,代表容器里已经存在该 bean 了,那么这个默认配置就会失效!

接着,我们查看 WebMvcAutoConfiguration 该类中定义了什么:

视图解析器:

WebMvcAutoConfiguration 中使用了 @AutoConfigureAfter 注解,意为指定的类加载完了后,再加载本类。

@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })

而 DispatcherServletAutoConfiguration 中又做了很多事情,比如配置了前端控制器:

@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
    DispatcherServlet dispatcherServlet = new DispatcherServlet();
    dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
    dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
    dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
    dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
    return dispatcherServlet;
}

3、总结

  • @SpringBootApplication 注解内部是3大注解功能的集成

    • @ComponentScan:开启组件扫描

    • @SpringBootConfiguration:作用等同于 @Configuration 注解,也是用于标记配置类

    • @EnableAutoConfiguration:内部导入 AutoConfigurationImportSelector,该类中有个 getCandidateConfigurations 方法,读取 jar 包中 META-INF/spring.factories 文件中配置类,再根据条件进行加载和配置,比如:AOP,PropertyPlaceholder,FreeMarker,HttpMessageConverter,Jackson,DataSourceDataSourceTransactionManager,DispatcherServlet,WebMvc 等等

  • SpringApplication.run(..)的作用

    • 启动 Spring Boot 应用

    • 加载自定义的配置类,完成自动配置功能

    • 把当前项目配置到嵌入的 Tomcat 服务器

    • 启动嵌入的 Tomcat 服务器

二、Spring Boot 实战

现在我们来把之前上课的项目改造为 Spring Boot 项目。

1、迁移 ssm 项目

1.1、快速创建 Spring Boot 项目

1.2、添加依赖

如果是普通 Maven 项目,需要手动添加。

<!-- 打包方式 jar 包 -->
<packaging>jar</packaging>
​
<!-- 指定父工程 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.3.RELEASE</version>
</parent>
​
<dependencies>
    <!-- spring boot Web 包 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- spring boot Test 包 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>

1.3、添加启动类

如果是普通 Maven 项目,需要手动添加。

@SpringBootApplication
public class RbacApplication {
    public static void main(String[] args) {
        SpringApplication.run(RbacApplication.class, args);
    }
}

1.4、拷贝项目代码

  • 把 src\main\java 中的 java 代码全部拷贝到新项目中的 src\main\java 里面。

  • 把 src\main\resources 中存放 mapper.xml 的目录也拷贝到新项目中的 src\main\resources 里面。

注意:按照下面步骤用什么拷贝什么。

2、配置数据库连接池

2.1、添加依赖

<!-- MySQL 驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

2.2、自动配置方式

application.properties

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///rbac?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=admin

此时运行测试获取的数据库连接池对象可以执行成功,并且我们看到了使用的连接池是 Hikari,全称是 Hikaricp。

其实,在 Spring Boot 2.0 之后,采用的默认连接池就是 Hikari,号称“史上最快的连接池”,所以我们没有添加依赖也能直接用,Spring Boot 的自动配置中含有 DataSourceAutoConfiguration 配置类,会先检查容器中是否已经有连接池对象,没有则会使用默认的连接池,并根据特定的属性来自动配置连接池对象,用到的属性值来源于 DataSourceProperties 对象。

2.3、配置 Druid 连接池

当然如果我们在项目中还是想要使用 Druid 作为连接池也是可以的。只需要添加依赖即可,此时加的是 Druid 的 druid-spring-boot-starter 自动配置包, 里面包含了 DruidDataSourceAutoConfigure 自动配置类,会自动创建 Druid 的连接池对象,所以 Spring Boot 发现已经有连接池对象了,则不会再使用 Hikari

<!-- druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.21</version>
</dependency>

注意:如果添加的依赖是以前那种普通包,只有 Druid 自身的依赖,并不是自动配置包,则需要进行以下配置:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.19</version>
</dependency>
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

所以一般如果已经提供了 Spring Boot 相关的自动配置包,直接使用自动配置的会更方便些。

对于 Hikari 以及 Druid 两款都是开源产品,阿里的 Druid 有中文的开源社区,交流起来更加方便,并且经过阿里多个系统的实验,想必也是非常的稳定,而 Hikari 是 Spring Boot 2.0 默认的连接池,全世界使用范围也非常广,对于大部分业务来说,使用哪一款都是差不多的,毕竟性能瓶颈一般都不在连接池。

3、集成 MyBatis

3.1、添加依赖

<!-- Mybatis 集成到 SpringBoot 中的依赖 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.1</version>
</dependency>

3.2、配置 Mapper 对象

扫描 Mapper 接口只要在配置类上贴个注解 @MapperScan 并指定扫描的包路径即可。

@SpringBootApplication
@MapperScan("cn.mapper")
public class SsmApplication {
    public static void main(String[] args) {
        SpringApplication.run(SsmApplication.class, args);
    }
}

3.3、配置属性

在 application.properties 配置以前在 XML 配置了的那些 MyBatis 的属性,属性前缀 mybatis。

# 配置别名
mybatis.type-aliases-package=cn.domain

# 打印 SQL 日志
logging.level.cn.mapper=trace

4、事务管理

4.1、添加依赖

<!-- Spring JDBC 和 TX -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

4.2、XML方式

采取配置类和 XML 混用的策略,在配置类上使用 @ImportResource("classpath:spring-tx.xml")。

4.3、注解方式

直接在业务层实现类上或者其方法上直接贴 @Transactional 注解即可。

Spring Boot 自动配置中提供了 TransactionAutoConfiguration 事务注解自动配置类,引入了事务的依赖后,可直接使用 @Transactional 注解。

4.4、配置代理实现

Spring Boot 默认优先选择 CGLIB 代理,如果需要改为优先使用 JDK 代理,需要做以下配置:

spring.aop.proxy-target-class=false

4.5、测试验证

在测试类上添加方法,打印业务对象看下其真实类型。

@Test
public void testSave() {
    System.out.println(departmentService.getClass());
}

5、集成 Web

5.1、添加依赖

<!-- spring boot web 启动器(之前已添加了) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

5.1、修改端口

在 application.properties 配置,如下:

server.port=80

5.2、目录结构

拷贝静态资源及模板文件

5.3、静态资源处理

  • 默认情况下,Spring Boot 会从 classpath 下的 /static,/public,/resources,/META-INF/resources 下加载静态资源。

  • 可以在 application.properties 中配置 spring.resources.staticLocations 属性来修改静态资源加载地址。

  • 因为应用是打成 jar 包,所以之前的 src/main/webapp 就作废了,如果有文件上传,那么就的必须去配置图片所在的路径。

# 告诉 Spring Boot 什么访问的路径是找静态资源
spring.mvc.static-path-pattern=/static/**

5.3、前端控制器映射路径配置

在 Spring Boot 自动配置中,WebMvcAutoConfiguration 自动配置类导入了 DispatcherServletAutoConfiguration 配置对象,会自动创建 DispatcherServlet 前端控制器,默认的映射路径是 /,Spring Boot 多数用于前后端分离和微服务开发,默认支持 RESTFul 风格,所以一般都是使用默认的即可,不做改动。

# 在匹配模式时是否使用后缀模式匹配
spring.mvc.pathmatch.use-suffix-pattern=true

6、集成 Thymeleaf

6.1、添加依赖

<!-- 引入 Thymeleaf 依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

6.2、相关配置

# Thymelea 模板配置
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.thymeleaf.encoding=UTF-8

# 热部署文件,页面不产生缓存,及时更新
spring.thymeleaf.cache=false

6.3、修改模板文件

使用 Thymeleaf 的语法替换之前 JSP 中的 EL 表达式和 JSTL。

7、统一异常处理

7.1、框架自带方式

Spring Boot 默认情况下,会把所有错误都交给 BasicErrorController 类完成处理,错误的视图导向到 classpath:/static/error/ 和 classpath:/templates/error/ 路径上,HTTP 状态码就是默认视图的名称。如:

  • 出现 404 错误 -> classpath:/static/error/404.html

  • 出现 5xx 错误 -> classpath:/static/error/5xx.html

7.2、控制器增强器方式

自己定义一个控制器增强器,专门用于统一异常处理,该方式一般用于 5xx 类错误。

@ControllerAdvice // 控制器增强器
public class ExceptionControllerAdvice {
    @ExceptionHandler(RuntimeException.class) // 处理什么类型的异常
    public String handlException(RuntimeException e, Model model) {
        e.printStackTrace(); // 记得这行代码保留,不然项目后台出异常,开发工具控制台看不多错误信息
        return "errorView"; // 指定错误页面视图名称
    }
}

8、添加拦截器

在传统的 XML 方式中,我们需要在 <mvc:interceptors> 标签中去注册我们自定义的拦截器。在 Spring Boot 中,提供了 WebMvcConfigurer 配置接口,是使用 JavaConfig 配置 Spring MVC 的标准,如果我们需要对 Spring MVC 做配置,则需要让我们自定义配置类实现该接口,若是需要注册拦截器,则实现接口中的 addInterceptors 方法即可。

8.1、编写拦截器

让 Spring 管理拦截器 bean 对象,在拦截器类上贴 @Component 注解。

@Component
public class LoginInterceptor implements HandlerInterceptor { 
    // ...
}
@Component
public class PermissionInterceptor implements HandlerInterceptor {
    // ...
}

8.2、配置义拦截器

定义一个配置类,实现 WebMvcConfigurer 接口,在 addInterceptors 方法注册拦截器。

@Configuration
public class MvcJavaConfig implements WebMvcConfigurer {
​
    @Autowired
    private LoginInterceptor loginInterceptor;
​
    @Autowired
    private PermissionInterceptor permissionInterceptor;
​
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册登录拦截器
        registry.addInterceptor(loginInterceptor)
            // 对哪些资源起过滤作用
            .addPathPatterns("/**")
            // 对哪些资源起排除作用
            .excludePathPatterns("/login", "/static/**");
        // 注册登录拦截器
        registry.addInterceptor(permissionInterceptor)
            // 对哪些资源起过滤作用
            .addPathPatterns("/**")
            // 对哪些资源起排除作用
            .excludePathPatterns("/login", "/static/**");
    }
}

三、系统日志

1、为什么要用日志

  • 比起 System.out.println,日志框架更为灵活,可以把日志的输出和代码分离。

  • 日志框架可以方便的定义日志的输出环境,控制台,文件,数据库。

  • 日志框架可以方便的定义日志的输出格式和输出级别。

2、Spring Boot 中的日志介绍

  • Spring Boot 默认已经开启日志,其默认的日志格式为:时间 日志级别 线程ID 线程名称 日志类 日志说明。

  • Spring Boot 的日志分为:系统日志和应用日志。

  • 日志级别,级别越高,输出的内容越少,如果设置的级别为 info,则 debug 以及 trace 级别的都无法显示,日志级别由低到高 trace < debug < info < warn < error。

  • Spring Boot 默认选择 Logback 作为日志框架,也能选择其他日志框架,但是没有必要。

3、输出日志

在我们自定义的类中可以使用日志框架来输出。

3.1、方式一

在类中定义一个静态 Logger 对象,传入当前类的作用是方便输出日志时可以清晰地看到该日志信息是属于哪个类的。

private static final Logger log = LoggerFactory.getLogger(当前类.class);

3.2、方式二

使用 Lombok 提供的 @Slf4j 注解来简化代码,其实和方式一的作用是一样的。

@Slf4j
@Service
public class PermissionServiceImpl implements IPermissionService {}

在需要输出日志的地方使用日志的输出方法

log.info(...);
log.error(...);
...
// 输出日志中有变量可以使用 {} 作为占位符
log.info("删除id为{}的数据", id);

4、日志级别

我们来尝试一下日志级别:

log.debug("权限插入成功:{}",expression);
log.info("权限插入成功:{}",expression);
log.warn("权限插入成功:{}",expression);

执行权限加载功能后,发现控制台出现 info 与 warn 的信息,debug 的没有显示,原因是因为 Spring Boot 默认的日志级别是 info,所以 debug 低于 info 级别,就不会显示出来了。

若要修改日志级别,最快速的方式是在 application.properties 配置,配置如下

# 把日志级别修改为 debug,不过我们一般不会更改,除非要调试找 bug,不然控制台显示的内容太多也容易乱
logging.level.root=debug

5、Logback 配置文件的使用

Logback 框架默认会自动加载 classpath:logback.xml,作为框架的配置文件,在 Spring Boot 中使用时,还会额外的支持自动加载classpath:logback-spring.xml,在 Spring Boot 中推荐使用 logback-spring.xml,功能更强大些。

样板文件:

<?xml version="1.0" encoding="UTF-8"?>
<!--
    scan:开启日志框架的热部署,默认值 true 表示开启
    scanPeriod:热部署的频率,默认值 60 second
    debug:设置输出框架内部的日志,默认值 false
-->
<configuration scan="true" scanPeriod="60 second" debug="false">
    <property name="appName" value="springboot demo" />
    <contextName>${appName}</contextName>
    
    <!-- appender:日志输出对象,配置不同的类拥有不同的功能
        ch.qos.logback.core.ConsoleAppender:日志输出到控制台        
    -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd-HH:mm:ss} %level [%thread]-%logger{35} >> %msg %n</pattern>
     </encoder>
   </appender>
    
    <!-- ch.qos.logback.core.FileAppender:日志输出到文件中
    <appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
        <encoder>
            <pattern>%-4relative [%thread] %level %logger{35} - %msg %n</pattern>
       </encoder>
        <append>true</append>
        <file>mylog.log</file>
   </appender>
    -->
    
    <!-- 
        root 是项目通用的 logger,一般情况下都是使用 root 配置的日志输出
        level:按照级别输出日志,日志级别,级别越高,输出的内容越少
    -->
   <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
    
    <!-- 自定义的 logger,用于专门输出特定包中打印的日志
    <logger name="cn.crm.mapper" level="trace">
    </logger>
    -->
</configuration>

参考日志格式:

  • %d{yyyy-MM-dd-HH:mm:ss} %level [%thread]-%class:%line >> %msg %n

格式中的标识符组成:

  • %logger{n}:输出 Logger 对象类名,n 代表长度

  • %class{n}:输出所在类名

  • %d{pattern} 或者 date{pattern}:输出日志日期,格式同 Java

  • %L/line:日志所在行号

  • %m/msg:日志内容

  • %method:所在方法名称

  • %p/level:日志级别

  • %thread:所在线程名称

四、其他功能

1、修改 banner

Spring Boot 提供了一些扩展点,比如修改 banner:在 resources 根目录中放入 banner.txt 文件,替换默认的 banner。

# application.properties
# 关闭 banner
spring.main.banner-mode=off 

2、热部署插件

除了使用 JRebel 来实现热部署,还可以使用 Spring Boot 提供的 spring-boot-devtools 包来完成 Springboot 应用热部署。

<!-- Spring Boot 热部署插件 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

Spring Boot 重启是 reload 重启,通过监控 classpath 的变化,如果 classpath 中的文件发生变化,即触发重启。Spring Boot 通过两个 classpath 来完成 reload,一个 basic classloader 中加载不变的类(jar 包中的类),一个 restart classloader 中加载 classpath 中的类(自己写的类),重启的时候,restart classloader 中的类丢弃并重新加载。

# 默认排除的资源
spring.devtools.restart.exclude=static/**,templates/**,public/**       
​
# 增加额外的排除资源
# 处理默认配置排除之外的
spring.devtools.restart.additional-exclude=public/** 
​
# 禁用自动重启
spring.devtools.restart.enabled=false 

3、切换运行环境

在实际开发中,一个系统是有多套运行环境的,如开发时有开发的环境,测试时有测试的环境,不同的环境中,系统的参数设置是不同的,如:连接开发数据和测试数据库的 URL 绝对是不同的,那么怎么快速的切换系统运行的环境呢?我们需要为不同的环境创建不同的配置文件,如下:

# application-dev.properties
server.port=8081
# application-test.properties
server.port=8082
# 在 application.properties 中指定需要使用的环境即可
spring.profiles.active=dev 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值