第1章 Spring Boot 基础
1.1 Spring Boot 工程的创建
1)jar与war区别
jar包使用springBoot内置tomcat服务器
war包部署时,需要外部的 web 容器,例如JBOSS、WebLogic 等
1.2 SpringBoot 的配置文件
1)yml配置文件
server:
port: 8088
servlet:
context-path: /test
2)properties配置文件
server.port=8088
server.servlet.context-path=/test
1.3 Actuator 监控器
-
导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
-
修改properties配置文件
# Actuator的端口号及上下文路径 management.server.port=9999 management.server.servlet.context-path=/first # 指定默认路径 management.endpoint.web.base-path=/base
-
访问测试—health 监控终端
localhost:9999/first/base/health
第2章 Spring Boot 工程应用
2.1 自定义异常页面
-
在src/main/resources 目录下再定义新的目录public/error,必须是这个目录名称。
-
在error 目录中定义异常页面。这些异常页面的名称必须为相应的状态码,扩展名为html。例如:404.html(3/0时出现)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>404</title> </head> <body> 404 错误 </body> </html>
2.2 单元测试
2.3 多环境选择
1)application.yml
spring:
profiles:
active: dev # dev开发环境
# active: prod #prod生产环境
2)application-dev.yml
server:
port: 8081
servlet:
context-path: /dev
3)application-prod.yml
server:
port: 8082
servlet:
context-path: /prod
2.4 读取自定义配置
1)读取单个属性
@Value("${server.port}")
private int port;
2)读取多个配置文件
@Component
@PropertySource(value = "classpath:custom.properties", encoding = "utf-8") //properties格式配置
//@PropertySource(value = "classpath:custom1.yml", encoding = "utf-8", factory = YamlPropertySourceF.class) //yml格式配置----需要配置
@ConfigurationProperties(prefix = "student") //指定
@Data
public class Student {
private String name; //变量名必须与配置文件中的名称一致,否则读不到数据
private int age;
private double score;
}
2.5 Spring Boot 下使用JSP 页面
-
在src/main 下创建webapp 目录,用于存放jsp 文件。这就是一个普通的目录,无需执行Mark Directory As。
-
在spring boot 工程中若要创建jsp 文件,一般是需要在src/main 下创建webapp 目录,然后在该目录下创建jsp 文件。但通过Alt + Insert 发现没有创建jsp 文件的选项。此时,需要打开Project Structrue 窗口,选择Facets->Web->添加Web Resoure Directory,将webapp 目录指定为web 资源目录,然后才可以创建jsp文件。
-
index.jsp文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <meta charset="utf-8"> <title>测试标题</title> </head> <body> <form action="test/jsp" method="post"> 年龄:<input type="text" name="age"><br> 姓名:<input type="text" name="name"><br> <input type="submit" name="注册"> </form> </body> </html>
-
welcome.jsp
-
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> name=${name}<br> age=${age}<br> </body> </html>
-
在pom 中添加一个Tomcat 内嵌的jsp 引擎jasper 依赖。jsp 引擎是用于解析jsp 文件的,
即将jsp 文件解析为Servlet 是由jsp 引擎完成的。embed,嵌入。<dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency>
-
在pom 文件中将webapp 目录注册为资源目录。
<build> <resources> <!--注册webapp 目录为资源目录--> <resource> <directory>src/main/webapp</directory> <targetPath>META-INF/resources</targetPath> <includes> <include>**/*.*</include> </includes> </resource> </resources> </build>
-
controller类
@Controller //注意不是@RestController @RequestMapping("/test") public class JspController { @PostMapping("/jsp") public String jsp(String name, int age, Model model){ model.addAttribute("name", name); model.addAttribute("age", age); return "/jsp/welcome.jsp"; // return "jsp/welcome"; 配置下面文件 } }
-
修改主配置文件 (非必要)
# 配置controller类返回值前后缀 mvc: view: prefix: / suffix: .jsp
-
实体类
@Data public class User { private String name; private int age; }
2.6 Spring Boot 中使用MyBatis
1)总步骤
-
导入三个pom依赖:mybatis 与Spring Boot 整合依赖、mysql 驱动依赖与Druid 数据源依赖。
<!--mybatis 与spring boot 整合依赖--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!--mysql 驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!-- druid 驱动 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.12</version> </dependency>
-
在pom注册资源
<resources> <!--注册webapp 目录下的jsp为资源目录--> <resource> <directory>src/main/webapp</directory> <targetPath>META-INF/resources</targetPath> <includes> <include>**/*.*</include> </includes> </resource> <!--默认resource目录下的xml,其他需注册dao 包下mybatis 映射文件为资源目录--> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources>
-
修改主配置文件(注册xml文件,实体类,以及数据源)
# 注册xml和实体类 mybatis: mapper-locations: classpath:com/monkey/springboot/mapper/*.xml type-aliases-package: com.monkey.springboot.entity # 注册数据源 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&autoReconnect=true&allowMultiQueries=true&useSSl=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true username: root password: 1234
2.7 Spring Boot 的事务支持
-
若工程直接或间接依赖于spring-tx,则框架会自动注入DataSourceTransactionManager事务管理器;
-
若依赖于spring-boot-data-jpa,则会自动注入JpaTransactionManager。
@Service
public class UserService {
@Autowired
private UserDao userDao;
// spring 默认事提交方式,发生异常回滚
@Transactional(rollbackFor = Exception.class, isolation = Isolation.REPEATABLE_READ)
public void addUser(User user) {
userDao.addUser(user);
int a = 5/0; //异常回滚
userDao.addUser(user);
}
}
2.8 Spring Boot 对日志的控制
1) logback 日志技术介绍
-
Spring Boot 中使用的日志技术为logback。其与 Log4J 都出自同一人,性能要优于Log4J,是Log4J 的替代者。
-
在Spring Boot 中若要使用logback,则需要具有spring-boot-starter-logging 依赖,而该依赖被spring-boot-starter-web 所依赖,即不用直接导入spring-boot-starter-logging 依赖。
# 日志控制 logging: # console表示显示在控制台 # logs-自定义前缀,level等级,msg信息,n换行 pattern: console: logs-%level %msg%n level: root: info #级别,减少项目日志 com.monkey.springboot.dao: debug #指定dao层日志
-
添加logback.xml日志配置文件
<?xml version="1.0" encoding="UTF-8"?> <configuration> <appender name="log" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%-5level - %msg%n</pattern> </encoder> </appender> <root level="warn"> <appender-ref ref="log"/> </root> <logger name="com.monkey.springboot.dao" level="debug"/> </configuration>
2)日志等级
tarce < debug < info < warn < error < fatal
2.9 Spring Boot 中SSRM 整合应用
1)redis分类:两类
- (注解实现) DB更新后,Redis 缓存中的数据就要马上清除,以保证将来缓存中的数据与DB 中的数据的绝对一致性,这是一类数据;
- (设置过期时效) 还有一类,对数据准确性要求不是很高,只要与DB 中的数据差别不大就可以,所以这类数据一般会为其设置过期时效。
2)举例
1. 当前工程完成让用户在页面中输入要查询学生的id,其首先会查看Redis 缓存中是否存在,
2. 若存在,则直接从Redis 中读取;若不存在,则先从DB 中查询出来,然后再存放到Redis缓存中。
3. 但用户也可以通过页面注册学生,一旦有新的学生注册,则需要将缓存中的学生信息清空。
4. 根据id 查询出的学生信息要求必须是实时性的,其适合使用注解方式的Redis 缓存。
5. 同时,通过页面还可以查看到总学生数,但对其要求是差不多就行,无需是实时性的。
6. 对于Spring Boot 工程,其适合使用API 方式的Redis 缓存,该方式方便设置缓存的到期时限。
3)整合步骤
-
修改pom 文件
<!--redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
修改yml配置文件
spring: # 单机redis redis: host: 127.0.0.1 port: 6379 database: 0 timeout: 6000ms password: cache: type: redis # 指定缓存类型 cache-names: redisCache #指定缓存区域名称(注解中使用value属性) # #redis集群 # sentinel: # master: mymaster # nodes: sentinel1:6307,sentinel2:6307,sentinel3:6307
-
代码实现-------访问: http://localhost:8080/index.jsp
// controller类 @Controller @RequestMapping("/redis") public class UserRedisController { @Autowired private UserRedisService userRedisService; @PostMapping("/addUser") public String addUser(User user, Model model){ model.addAttribute("name", user.getName()); model.addAttribute("age", user.getAge()); userRedisService.addUser(user); return "/jsp/userRedis.jsp"; } @PostMapping("/selectAge") public String addUser(int age, Model model){ model.addAttribute("age", age); userRedisService.selectAge(age); return "/jsp/userRedis.jsp"; } @GetMapping("/selectCount") public String selectCount(){ int count = userRedisService.selectCount(); return "/jsp/userRedis.jsp"; } } // sevice层 @Service public class UserRedisService { @Autowired private UserRedisDao userRedisDao; @Autowired private RedisTemplate redisTemplate; // 清除缓存 @CacheEvict(value = "redisCache", allEntries = true) public void addUser(User user) { userRedisDao.addUser(user); } // 缓存查询 @Cacheable(value = "redisCache", key = "#age") public List<User> selectAge(int age) { return userRedisDao.selectAge(age); } // 双重检测锁 public int selectCount() { // 获取redis对象 BoundValueOperations ops = redisTemplate.boundValueOps("count"); // 获取值 Object count = ops.get(); System.out.println("redis:" + count); // 双重检测 if (ObjectUtils.isEmpty(count)){ //查询数据库 count = userRedisDao.selectCount(); System.out.println("数据库:" + count); //设置缓存的过期时效 ops.set(count, 100, TimeUnit.SECONDS); } return userRedisDao.selectCount(); } } // dao层 @Mapper public interface UserRedisDao { void addUser(@Param("param") User user); List<User> selectAge(int age); int selectCount(); } // xlm文件 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.monkey.springboot.dao.UserRedisDao"> <insert id="addUser"> insert into user(name, age) values (#{param.name}, #{param.age}) </insert> <select id="selectAge" resultType="com.monkey.springboot.entity.User"> select id,name,age from user where age <= #{age} </select> <select id="selectCount" resultType="java.lang.Integer"> select count(*) from user </select> </mapper> // 实体类 @Data public class User implements Serializable { //必须序列化 private String id; private String name; private int age; } //index.jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <meta charset="utf-8"> <title>测试标题</title> </head> <body> <%--redis测试--%> <form action="redis/addUser" method="post"> 年龄:<input type="text" name="age"><br> 姓名:<input type="text" name="name"><br> <input type="submit" value="添加用户"> </form> <hr> <form action="redis/selectAge" method="post"> 输入最大年龄:<input type="text" name="age"><br> <input type="submit" value="查询信息"> </form> <hr> <a href="redis/selectCount">查询总人数</a> </body> </html> // userRedis.jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <meta charset="UTF-8"> <title>添加用户</title> </head> <body> student=${user}<br> selectAge = ${userList}<br> count = ${count}<br> </body> </html>
4)redis高并发问题
-
缓存穿透:
问题:查询结果经常为空,需要访问数据库 解决:当null时,设置一个默认值,存入缓存
-
缓存雪崩:
问题:大量缓存同时到期,需要同时更新 解决:提前规划好缓存到期时间
-
热点缓存:
问题:某个缓存到期,同时大量访问 解决:双重检查锁机制
5)简单过程
- 在pom 中添加Redis 与Spring Boot 整合依赖
- 在配置文件中注册Redis 连接信息
- 实体类实现序列化接口
- 在启动类上添加@EnableCaching (没必须,其他依赖中可能包含该注解)
- 在查询方法上添加@Cacheble,在增删改方法上添加@CacheEvict 【注解实现】
- 若使用API 方式操作Redis,则需要注入RedisTemplate,然后通过RedisTemplate 获取到Redis 操作对象后就可以对Redis 进行操作了。【双重检查锁机制】
6)redis五大数据类型
-
Redis字符串(String)
String是redis最基本的类型,你可以理解成与Memcached一样的类型,一个key对应一个value。 方法: set 设置值,get获取值,del删除值
-
Redis列表(List)
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部或者尾部。它的底层是一个链表
-
Redis集合(Set)
Redis的Set的histring类型的无序集合。他是通过HashTable实现的。
-
Redis哈希(Hash)
Redis hash是一个键值对集合。Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。 类似Java里面Map<String, Object>
-
Redis有序集合Zset(sorted set)
Redis zset和set一样也是string类型元素的集合,而且不允许重复的成员。 不同的是每个元素都会关联一个double类型的分数。 redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
7)注解@Cacheable和@CacheEvict
-
@Cacheable属性,一般用于查询
# 属性 1. value属性是必须指定的,其表示当前方法的返回值是会被缓存在哪个Cache上的,对应Cache的名称。其可以是一个Cache也可以是多个Cache,当需要指定多个Cache时其是一个数组。 2. key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key。 自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。下面是几个使用参数作为key的示例。 #root.methodName获取方法名称 3. condition属性指定发生的条件 例如:@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0") 当id为偶数生效
-
@CacheEvict,一般用于增删改
# 属性 1. allEntries是boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。 当指定了allEntries为true时,Spring Cache将忽略指定的key。 有的时候我们需要Cache一下清除所有的元素,这比一个一个清除元素更有效率。 2. beforeInvocation属性 清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。 使用beforeInvocation可以改变触发清除操作的时间,当我们指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。
2.10 Spring Boot 中Dubbo 的整合应用
- 后续补
2.11 Spring Boot 下使用拦截器(Token)
在非Spring Boot 工程中若要使用SpringMVC 的拦截器,在定义好拦截器后,需要在Spring配置文件中对其进行注册。但Spring Boot 工程中没有了Spring 配置文件,那么如何使用拦截器呢?
Spring Boot 对于原来在配置文件配置的内容,现在全部体现在一个类中,该类需要继承自WebMvcConfigurationSupport 类,并使用**@Configuration 进行注解**,表示该类为一个JavaConfig 类,其充当配置文件的角色
1)步骤
-
定义拦截器
public class DefineInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("拦截请求" + request.getRequestURI()); return true; } }
-
定义WebMvc 配置类,其需要继承自WebMvcConfigurationSupport,且需要使用@Configuration 进行注解
@Configuration public class IntercptorConfig extends WebMvcConfigurationSupport { @Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new DefineInterceptor()) // .addPathPatterns("/first/**"); //拦截first开头的url .excludePathPatterns("/first/**"); //拦截除first开图的url } }
-
controller类,无需修改主配置文件
@RestController
public class InterceptorController {
@GetMapping("/first/request")
public String first(){
return "first request";
}
@GetMapping("/second/request")
public String second(){
return "second request";
}
@GetMapping("/third/request")
public String third(){
return "third request";
}
2.12 Spring Boot 中使用Servlet
在Spring Boot 中使用Servlet,根据Servlet 注册方式的不同,有两种使用方式。
- 若使用的是Servlet3.0+版本,则两种方式均可使用;
- 若使用的是Servlet2.5 版本,则只能使用配置类方式。
2.13 Spring Boot 中使用Filter
在Spring Boot 中使用Filter 与前面的使用Servlet 相似,根据Filter 注册方式的不同,有两种使用方式。
- 若使用的是Servlet3.0+版本,则两种方式均可使用;
- 若使用的是Servlet2.5版本,则只能使用配置类方式
补充: 整合myabtis-plus
第3章 模板引擎Thymeleaf
3.1 Thymeleaf 简介
Thymeleaf[taɪm lif],百里香叶,是一个流行的模板引擎,该模板引擎采用Java 语言开发。
Java 中常见的模板引擎有Velocity、Freemaker、Thymeleaf 等。不同的模板引擎都会具有自
己的特定的标签体系,而Thymeleaf 以HTML 标签为载体,在HTML 的标签下实现对数据的
展示。
Thymeleaf 本身与SpringBoot 是没有关系的,但SpringBoot 官方推荐使用Thymeleaf 作
为前端页面的数据展示技术,SpringBoot 很好地集成了这种模板技术。
Thymeleaf 的官网为: http://www.thymeleaf.org
3.2 Spring Boot 集成Thymeleaf
-
导入如下依赖
<!--Thymeleaf--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
-
controller类
@Controller public class ThymeleafController { @GetMapping("/thymeleaf") public String thymeleaf(Model model){ model.addAttribute("test", "hello world!"); //显示html页面,不用写后缀名 return "index"; } }
-
index.html文件(必须在resource/template下创建html)
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <p th:text="${test}">显示test值</p> <div th:text="${test}">显示test值</div> <span th:text="${test}">显示test值</span> </body> </html>
-
yaml配置文件(建议关闭缓存)
# 建议关闭缓存,否则可能会出现数据未更新情况 spring: thymeleaf: cache: false
3.3 Thymeleaf 标准表达式
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 变量表达式${...} -->
<p th:text="${student}">显示test值</p>
<p th:text="${student.name}">显示test值</p>
<p th:text="${student.age}">显示test值</p>
<hr>
<!-- 选择表达式*{...} -->
<div th:object="${student}">
<p th:text="*{name}">显示test值</p>
<p th:text="*{age}">显示test值</p>
</div>
<hr>
<!-- URL 表达式@{...} -->
<a th:href="@{'http://localhost:8080/thymeleaf1/' + ${student.name}}">跳转1</a><br>
<a th:href="@{|http://localhost:8080/thymeleaf1/${student.name}|}">跳转2</a><br>
<a th:href="@{|/thymeleaf1/${student.name}|}">跳转3</a>
</body>
</html>
3.4 Thymeleaf 常见属性
1)th:if 或th:switch/th:case
该属性用于逻辑判断,类似于JSTL 中的<c:if/>。
2)th:each
该属性用于遍历数组、List、Set、Map,类似于JSTL 中的<c:forEach/>。
3)th:text/th:utext
1. th:utext 会解析文本中的HTML 标签,
2. th:text 则是原样显示。
4)th:id/th:name
这两个属性可以获取标签的动态id 与name 属性,以便在js 中使用。
5)th:style
该属性用于获取标签的css 动态样式。
6)th:onclick
该属性用于获取标签的单击事件所触发的动态事件,即单击事件所触发的方法名。这些js 事件属性很多,都是以th:on 开头。
7)内联属性th:inline
1. 其应用场景是,在HTML 某标签显示文本内部存在部分数据来自于动态数据,或JS 内部需要使用动态数据的场景。在该场景中,可以使用[[${...}]]或[(${...})]的方式将动态数据嵌入到指定的文本或脚本内部。
2. th:inline 的取值有四个:text, javascript、css 和 non。分别表示内联文本、内联脚本、内联css,与禁止内联,即不解析[[${...}]]。不过,th:inline=”text”可以省略不写。
8)万能属性th:attr
很多HTML 标签的属性都有其对应的Thymeleaf 的th 命名空间中属性,但并不是所有的都存在对应属性。若没有其对应的th 命名空属性,但又想让其获取动态值,就需要使用该属性了
3.5 Thymeleaf 运算基础
字面量,即字面常量。Thymeleaf 中有四种字面量:文本、数字、布尔,及null。
第4章 Spring Boot 源码解析
4.1 自动配置源码解析
1)解析@SpringBootApplication
-
前四个是专门(即只能)用于对注解进行注解的,称为元注解。
1.@Target(ElementType.TYPE) //Target注解可以修饰,如果注解超出了注解的修饰范围就会报错 TYPE, // 类,接口,枚举声明 FIELD, // 枚举值 METHOD, // 方法 PARAMETER, // 形参 CONSTRUCTOR, // 构造器 LOCAL_VARIABLE, // 局部变量 ANNOTATION_TYPE, // 注解 PACKAGE, // 包 2.@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME) // 注解保留位置 @Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含 @Retention(RetentionPolicy.CLASS) //默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得 @Retention(RetentionPolicy.RUNTIOME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到 3.@Documented //avadoc工具会将此注解标记元素的注解信息包含在javadoc中。默认,注解信息不会包含在Javadoc中 4.@Inherited //表示注解会被自动继承。
2) @SpringBootConfiguration
- 查看该注解的源码注解可知,该注解与@Configuration 注解功能相同,仅表示当前类为一个JavaConfig 类,其就是为Spring Boot 专门创建的一个注解。
3) @ComponentScan
- 顾名思义,用于完成组件扫描。不过需要注意,其仅仅是指定了要扫描的包,并没有装配其中的类,这个真正装配这些类是**@EnableAutoConfiguration** 完成的。
4) @EnableAutoConfiguration
@EnableXxx 注解一般用于开启某一项功能,是为了简化代码的导入。其是组合注解,一般情况下@EnableXxx 注解中都会组合一个**@Import** 注解,而该@Import 注解用于导入指定的类,而被导入的类一般有三种:
-
A、 配置类
@Import 中指定的类一般为Configuration 结尾,且该类上会注解@Configuration,表示当前类为JavaConfig 类。 -
B、 选择器
@Import 中指定的类一般以Selector 结尾,且该类实现了ImportSelector 接口,表示当前类会根据条件选择导入不同的类。 -
C、 注册器
@Import 中指定的类一般以Registrar 结尾,且该类实现了ImportBeanDefinitionRegistrar接口,用于导入注册器,该类可以在代码运行时动态注册指定类的实例。