spring boot在配置上相比spring要简单很多,其核心在于spring-boot-starter,在使用spring boot来搭建一个项目时,只需要引入官方提供的starter,就可以直接使用,免去了各种配置。
starter简单来讲就是引入了一些相关依赖
和一些初始化的配置
- 命名规范:
- 官方的starter: spring-boot-starter-xxx
例如:spring-boot-starter-web
- 第三方的starter:xxx-spring-boot-starter
例如:mybatis-spring-boot-starter
- 官方的starter: spring-boot-starter-xxx
原理
Spring Boot之所以能够帮我们简化项目的搭建和开发过程,主要基于它提供的起步依赖和自动配置
起步依赖
将具备某种功能的坐标打包到一起,可以简化依赖导入的过程。
例如:mybatis-spring-boot-starter
自动配置
即无须手动配置xml,自动配置并管理bean,可以简化开发过程。
实例分析
Spring boot默认扫描启动类所在的包下的主类与子类的所有组件,但并没有包括依赖包中的类
以mybatis-spring-boot-starter为例分析
起步依赖
- 在pom文件中,添加坐标
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
SpringBoot是如何知道要加载哪些配置的
最先关注到的是@SpringBootApplication
注解
进入到@SpringBootApplication注解
内部
- @SpringBootConfiguration:标识这个一个配置类,内部实际是一个@Configuration
- @EnableAutoConfiguration:表示启用自动配置
进入到@EnableAutoConfiguration注解
内部:
- @Import:将类交给spring容器管理
进入AutoConfigurationImportSelector对象
内部
- getCandidateConfiguration:获取待加载的配置类,读取各starter的
META_INF/spring.factory
中的bean的全类名,用于加载这些bean并完成实例化的创建工作
Starter内部是如何进行自动配置的
找到
mybatis-spring-boot-autoconfigure�
中的META_INF/spring.factory
打开META_INF/spring.factory
具体配置的是org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
进入MybatisAutoConfiguration
- 通过@Configuration+@Bean注解,将返回的对象交给spring容器管理,用来替代传统的xml配置文件
自动配置条件依赖
在上面的
MybatisAutoConfiguration
中看到有很多注解,这些注解的用途如下
注解 | 用途 |
---|---|
@ConditionalOnBean | 仅当当前上下文中存在某个bean时,才会实例化这个bean |
@ConditionalOnClass | 某个class位于类路径上,才会实例化这个bean |
@ConditionalOnExpression | 当表达式为true的时候,才会实例化这个bean |
@ConditionalOnMissingBean | 仅在当前上下文中不存在某个bean时,才会实例化这个bean |
@ConditionalOnMissingClass | 某个class在类路径上不存在时,才会实例化这个bean |
@ConditionalOnNotWebApplication | 不是web应用时,才会实例化这个Bean |
@AutoConfigureAfter | 在某个bean完成自动配置后,才会实例化这个Bean |
@AutoConfigureBefore | 某个bean完成自动配置前,才会实例化这个Bean |
如何将一个普通类交给Spring容器管理
- 使用@Configuration + @Bean注解
- 使用@Controller、@Service、@Repository、@Component
- 使用@Import方法
实现
spring-boot-starter是为了在项目初始化的时候,向spring容器中注入一些bean。
这些bean在注入的时候可以读取配置文件,进行动态的调整
案例一
向spring容器中注入一个
SimpleService
,通过SimpleService读取配置(simple.name/simple.address)
- 创建一个maven工程,并添加坐标
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--案例1-第一步:引入坐标-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.14</version>
</parent>
<groupId>com.tzcxyh</groupId>
<artifactId>simple-spring-boot-starter</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--案例1-第一步:引入坐标-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!--对配置生成原数据,写配置文件的时候,会有提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
- 创建SimpleProperties对象,读取配置
package com.tzcxyh.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @Description 配置属性类,读取配置文件中的参数信息
**/
@ConfigurationProperties(prefix = "simple")
public class SimpleProperties {
private String name;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
- 创建SimpleService对象,根据配置输出
package com.tzcxyh.service;
/**
* @Description 服务类
**/
public class SimpleService {
private String name;
private String address;
public SimpleService(String name, String address) {
this.name = name;
this.address = address;
}
public String sayHello(){
return "你好!我的名字叫" + this.name + ", 我来自" + this.address;
}
}
- 创建SimpleAutoConfiguration,将SimpleService注入到Spring容器中
package com.tzcxyh.config;
import com.tzcxyh.service.SimpleService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Description 自动配置类
* 读取SimpleProperties配置 + 自动配置SimpleService
**/
@Configuration
//启用SimpleProperties对象,读取配置文件
@EnableConfigurationProperties(SimpleProperties.class)
public class SimpleServiceAutoConfiguration {
private SimpleProperties simpleProperties;
/**
* 以构造函数的方式注入配置
* @param simpleProperties
*/
public SimpleServiceAutoConfiguration(SimpleProperties simpleProperties) {
this.simpleProperties = simpleProperties;
}
@Bean//与@Configuration一起,将返回bean交给spring容器管理
@ConditionalOnMissingBean//外部没有定义的时候才会创建这个bean
public SimpleService simpleService(){
return new SimpleService(simpleProperties.getName(), simpleProperties.getAddress());
}
}
- 在META-INF/spring.factories中添加SimpleAutoConfiguration全类名
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tzcxyh.config.SimpleServiceAutoConfiguration
案例二
自定义日志注解,通过拦截器的方式,计算方法调用时长。在案例一的基础上添加
- 创建一个maven工程,并添加坐标
<!--案例2-第一步:引入坐标-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--对外隐藏-->
<optional>true</optional>
</dependency>
- 创建@SimpleLog注解
package com.tzcxyh.log;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SimpleLog {
/**
* 方法描述
* @return
*/
String desc() default "";
}
- 创建SimpleLogInterceptor�对象,继承HandlerInterceptorAdapter�,重写preHandler、postHandler方法
package com.tzcxyh.log;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
/**
* @Description 日志拦截器
**/
public class SimpleLogInterceptor extends HandlerInterceptorAdapter {
/**
* 存储开始时间的时间戳,用于计算方法调用时间
*/
private static final ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
SimpleLog simpleLog = method.getAnnotation(SimpleLog.class);
if(simpleLog != null){
//存在注释,记录开始时间
long startTime = System.currentTimeMillis();
startTimeThreadLocal.set(startTime);
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
SimpleLog simpleLog = method.getAnnotation(SimpleLog.class);
if(simpleLog != null){
long endTime = System.currentTimeMillis();
Long startTime = startTimeThreadLocal.get();
long optTime = endTime - startTime;
String requestURI = request.getRequestURI();
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
String methodDesc = simpleLog.desc();
System.out.println("请求URI:" + requestURI);
System.out.println("请求方法名:" + methodName);
System.out.println("方法描述:" + methodDesc);
System.out.println("方法执行时间:" + optTime + "ms");
}
super.postHandle(request, response, handler, modelAndView);
}
}
- 创建SimpleLogAutoConfiguration�,实现WebMvcConfigurer�,将SimpleLogInterceptor加入拦截器中
package com.tzcxyh.config;
import com.tzcxyh.log.SimpleLogInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Description 自动配置类,把SimpleLogInterceptor加入拦截器
**/
@Configuration
public class SimpleLogAutoConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SimpleLogInterceptor());
}
}
- 在META-INF/spring.factories中追加SimpleAutoConfiguration全类名
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tzcxyh.config.SimpleServiceAutoConfiguration,\
com.tzcxyh.config.SimpleLogAutoConfiguration
测试
将simple-spring-boot-starter打包后,在项目中引入
<dependency>
<groupId>com.tzcxyh</groupId>
<artifactId>simple-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
- 添加配置
simple.name=xyh
simple.address=杭州
- 创建IndexController,测试
package com.tzcxyh.lulu.controller;
import com.tzcxyh.log.SimpleLog;
import com.tzcxyh.service.SimpleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
@Autowired
SimpleService simpleService;
@GetMapping("/index")
@SimpleLog(desc = "测试方法")
public String index(){
return simpleService.sayHello();
}
}
- 访问ip:端口号/index
- 控制台输出