SpringBoot2 - 基础入门(二)
了解自动装配原理
文章目录
- SpringBoot2 - 基础入门(二)
- 了解自动装配原理
- 一、依赖管理
- 1.1 父项目做依赖管理
- 1.2 starer场景启动器
- 2、自动配置
- 2.1 自动配置依赖
- 2.2 组件扫描
- 3、配置文件
- 3.1 各种配置都拥有默认值
- 3.2 按需加载所有的自动配置项
- 二、容器功能
- 1. 添加组件
- 1.1 配置类 -- 本身也是组件
- 1.2 使用@Import导入组件
- 1. 3 查看容器中的组件
- 1.4 条件装配 -- @Conditional
- 1.5 使用配置文件设置组件 -- 和springMVC相同
- 1.6 配置绑定
- 三、自动装配原理
- 1. 引导加载自动配置类
- 1.1 @SpringBootConfiguration
- 1.2 @ComponentScan
- 1.3 @EnableAutoConfiguration
- 2. 按需加载配置
- 3. 查看加载和未加载的配置
- 四、开发小技巧
- 1. Lombok
- 1.1 引入lombok
- 1.2 安装插件
- 1.3 在JavaBean上设置注解@Data -- 编译阶段生成相应的getset方法
- 2. dev-tools
- 2.1 [引入依赖](https://docs.spring.io/spring-boot/docs/2.7.12/reference/html/using.html#using.devtools)
- 2.2 生效修改
- 2.3 热更新-- JRebel -- 收费
- 3. Spring Initializr -- 项目初始化向导
一、依赖管理
1.1 父项目做依赖管理
依赖管理
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.12</version>
</parent>`
spring-boot-starter-parent的父项目:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.12</version>
</parent>
在spring-boot-dependencies中几乎声明了所有开发中常用的依赖配置的版本号
例如对于数据库链接的依赖,spring-boot-dependencies中有以下配置
<mysql.version>8.0.33</mysql.version> ...... <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>${mysql.version}</version> <exclusions> <exclusion> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> </exclusion> </exclusions> </dependency>
- 如何覆盖父项目中的配置
只需要在maven项目的核心配置文件pom.xml中,重写需要的依赖版本,此时maven就会根据就近原则加载相应的依赖
例如对于链接mysql的依赖,只需在pom.xml中添加以下配置
<properties> <mysql.version>想要的版本号</mysql.version> </properties>
- 在spring-boot-dependencies中配置的所有依赖,我们在引入时都可以不设置版本号,称之为版本仲裁
1.2 starer场景启动器
- 以spring-boot-starter-*命名的依赖文件
- 官方文档地址
就某个开发场景而言只要我们引入相应的启动器,这个场景所需要的常规依赖都会帮我们自动引入
以web为例:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
- 可以看一下他的依赖树:
- 所有场景启动器最底层的依赖 – spring boot自动配置的核心依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.7.12</version>
<scope>compile</scope>
</dependency>
2、自动配置
2.1 自动配置依赖
-
自动配置Tomcat
- 引入Tomcat依赖
- 配置
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>2.7.12</version> <scope>compile</scope> </dependency>
-
自动配置SpringMVC
- 引入SpringMVC全套组件
- 自动配号SpringMVC常用组件(功能)
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.3.27</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.27</version> <scope>compile</scope> </dependency>
-
…
-
在主程序类中有以下固定写法
@SpringBootApplication public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); } }
其中SpringApplication.run返回一个ConfigurableApplicationContext对象也就是IOC容器
我们可以通过以下方法查看IOC容器中已装配的组件
@SpringBootApplication public class MainApplication { public static void main(String[] args) { // 返回IOC容器 ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); //获取已装配的组件名称 String[] beanDefinitionNames = run.getBeanDefinitionNames(); //输出 for(String name : beanDefinitionNames){ System.out.println(name); } } }
输出后可以看到包括视图解析器,文件上传,字符编码等,所有web开发所需要的基本组件
2.2 组件扫描
-
为什么只需要设置@Controller组件,springboot就可以帮我们扫描到我们自定义的组件
这是因为springboot所设定的默认规则 – 约定大于配置
只要我们的程序符合以下目录结构,那他就会帮我们自动扫描
com +- example +- myapplication +- MyApplication.java | +- customer | +- Customer.java | +- CustomerController.java | +- CustomerService.java | +- CustomerRepository.java | +- order +- Order.java +- OrderController.java +- OrderService.java +- OrderRepository.java
-
如果想要扫描别的位置那可以在@SpringBootApplication注解中设置属性scanBasePackages,例如:
@SpringBootApplication(scanBasePackages = "com.test")//扫描com.test下的所有组件 public class MainApplication { public static void main(String[] args) { // 返回IOC容器 ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); //获取已装配的组件名称 String[] beanDefinitionNames = run.getBeanDefinitionNames(); //输出 for(String name : beanDefinitionNames){ System.out.println(name); } } }
-
或者设置注解@ComponentScan(“包路径”),@SpringBootApplication就是包含这个注解的所以这两个注解不能同时使用
3、配置文件
3.1 各种配置都拥有默认值
- 默认配置最终都会映射到MultipartProperties
- 配置文件的值最终会绑定到某个类上,这个类最终会在容器中创建对象
3.2 按需加载所有的自动配置项
- 引入了那个场景这个场景的自动配置才会自动开启
- SpringBoot所有的自动配置功能都在以下包中
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.7.12</version>
<scope>compile</scope>
</dependency>
二、容器功能
1. 添加组件
1.1 配置类 – 本身也是组件
- @Configuration – 告诉SpringBoot这是一个配置类 == 配置文件
package com.ywj.boot.config;
import org.springframework.context.annotation.Configuration;
/**
* Author : YWJ
* Date : 2023/5/26
* Name : SpringBootDemo
*/
@Configuration // 告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {
}
- @Bean – 给容器中添加组件,以方法名作为组件的Id,返回类型就是组件的类型,返回的值就是组件在容器中的实例,默认都是单例
package com.ywj.boot.config;
import com.ywj.boot.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Author : YWJ
* Date : 2023/5/26
* Name : SpringBootDemo
*/
@Configuration // 告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {
@Bean
// 也可使用@Bean("id")设置组件id
public User user01(){
return new User("张三",18);
}
}
1.2 使用@Import导入组件
- 给容器中自动创建出导入的组件,默认组件名称就是导入类的全类名
@Import(User.class) // 导入一个User组件
@Configuration(proxyBeanMethods = true) // 告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {
@Bean
public User user01(){
return new User("张三",18);
}
}
1. 3 查看容器中的组件
- 使用ioc的getBean方法
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
// 返回IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
//获取已装配的组件名称
String[] beanDefinitionNames = run.getBeanDefinitionNames();
//输出
for(String name : beanDefinitionNames){
System.out.println(name);
}
// 获取自定义的组件
User user01 = run.getBean("user01", User.class);
System.out.println(user01);
}
}
- 也可使用配置类来执行组件方法
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
// 返回IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
// 获取配置类组件
MyConfig bean = run.getBean(MyConfig.class);
//通过配置类调用组件方法
User user = bean.user01();
User user1 = bean.user01();
System.out.println(user==user1); // ture
}
}
上述方法也是可以返回我们所配置的组件的,并且不管调用多少次他都是单例的,
这是因为在springboot2.0以后的版本中 注解@Configuration 中有一个属性proxyBeanMethods 默认为 true
proxyBeanMethods :是不是代理Bean的方法
如果@Configuration(proxyBeanMethods = true);此时就会使用代理对象调用配置类中的方法,
此时SpringBoot总会检查这个组件是否在容器中存在实例,如果存在则直接复用,因此它总是单例的
如果改成false则不通过代理对象调用,同时也不会是单例。
- true情况下-- 通常用来解决组件依赖问题
- 使用false则会跳过springboot在容器中寻找的过程,大大增加服务速度
1.4 条件装配 – @Conditional
- 示例 @ConditionalOnBean(name=“李四”) 只有在容器中存在name=”李四“的组件时,被标注的组件才能加载进容器
@Configuration(proxyBeanMethods = true) // 告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {
@ConditionalOnBean(name="李四")
@Bean
public User user01(){
return new User("张三",18);
}
}
- 该注解可以标注在一个方法上,也可以标注在一个类中,标注在组件方法上时只对被标注方法生效,标注在类上时,该类中所有的组件方法都会生效
1.5 使用配置文件设置组件 – 和springMVC相同
- 配置文件就和SpringMVC相同使用Bean标签配置
- @ImportResource注解 – 导入一个配置文件
@Import(User.class)
@Configuration
@ImportResource("classpath:beans.xml")
public class MyConfig {
@ConditionalOnBean(name="李四")
@Bean
public User user01(){
return new User("张三",18);
}
}
1.6 配置绑定
-
方法一
在组件类中添加以下注解
@Component
@ConfigurationProperties(prefix = “test”) 其中prefix是组件属性在配置文件中的前缀,例如test.name 中的test -
通过在配置文件中设置相关Bean属性,然后将这些属性绑定到一个组件上
- 配置文件
test.name=张三 test.age=789
- 组件类 – 必须有get set方法
package com.ywj.boot.pojo; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * Author : YWJ * Date : 2023/5/26 * Name : SpringBootDemo */ @Component @ConfigurationProperties(prefix = "test") public class User { private String name ; private Integer age ; public User() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public User(String name, Integer age) { this.name = name; this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } } `
- 配置好后,可使用自动装配
@RestController public class HelloController { @Autowired User user ; @RequestMapping("/user") public User getUser(){ System.out.println(user); return user; } }
-
方法二
在配置类中添加以下注解
@EnableConfigurationProperties(User.class) // 开启User的属性配置功能 // 将这个组件自动注册到容器中
同时在组件上添加以下注解
@ConfigurationProperties(prefix = “test”)
此时不在需要配置@Component注解
三、自动装配原理
1. 引导加载自动配置类
@SpringBootApplication是以下注解的一个组合注解
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} )
1.1 @SpringBootConfiguration
@SpringBootConfiguration注解源码
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration @Indexed public @interface SpringBootConfiguration { @AliasFor( annotation = Configuration.class ) boolean proxyBeanMethods() default true; }
- 可以看到@SpringBootConfiguration本质上也就是一个@Configuration
- @Configuration代表当前是一个配置类,因此主程序类也是一个配置类,可以称之为主配置类
1.2 @ComponentScan
- 指定要扫那些包
1.3 @EnableAutoConfiguration
@EnableAutoConfiguration源码
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
- @AutoConfigurationPackage 自动配置包==@Import({Registrar.class})==
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
通过@Import导入组件registrar
而组件register会将该注解所标注类所在包下的所有组件导入容器
-
@Import({AutoConfigurationImportSelector.class})
- 利用getAutoConfigurationEntry(AnnotationMetadata annotationMetadata),给容器中批量导入一些组件
- 利用List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);获取所有需要导入的组件的全类名
- 利用工厂加载SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);获取 Map<String, List> loadSpringFactories(ClassLoader classLoader)
- 默认扫描我们当前系统里面所有的META-INF/spring.factories
2. 按需加载配置
- springboot会先加载所有的自动配置类
- 每个自动配置类都会按照一定的条件生效,默认都会绑定配置文件指定的值。
- 生效的配置类就会给容器中配置很多组件
- 只要容器中有这些组件,相当与这些功能就有了
- 只要用户有自己的配置,就以用户的为准
- 使用@Bean配置组件
- 修改配置文件
3. 查看加载和未加载的配置
- 只需开启debug模式,在application.properties 文件中添加下列内容之后再运行,就会输出配置加载信息
debug=true
四、开发小技巧
1. Lombok
简化JavaBean开发
1.1 引入lombok
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
1.2 安装插件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sgAxZImw-1685180916916)(C:\Users\YWJ\AppData\Roaming\Typora\typora-user-images\image-20230527171704252.png)]
1.3 在JavaBean上设置注解@Data – 编译阶段生成相应的getset方法
package com.ywj.boot.pojo;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Author : YWJ
* Date : 2023/5/26
* Name : SpringBootDemo
*/
@Data
@ConfigurationProperties(prefix = "test")
public class User {
private String name ;
private Integer age ;
}
添加以下方法会在编译阶段创建相应的方法
@ToString : 生成toString
@AllArgsConstructor :生成所有参数的有参构造器
@NoArgsConstructor:生成无参构造
@EaualsAndHashCode :重写equals和hashcode
@Slf4j :记录日志,会自动注入一个Log对象,通过Log.info(“日志信息”)
2. dev-tools
热更新
2.1 引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
2.2 生效修改
CTRL+F9
该方法对java代码来说是利用重新启动的
对静态页面来说是局部修改
2.3 热更新-- JRebel – 收费
3. Spring Initializr – 项目初始化向导
简化项目创建
在这里插入图片描述