目录
2:自动装配、按需加载
2.2:@Import({Registrar.class}) 注解的源码
2.3:@Import({AutoConfigurationImportSelector.class}) 注解的源码
3.5:@ConfigurationProperties和@EnableConfigurationProperties注解
1:自动装配
1.1:未使用自动装配之前
在传统的springmvc项目中,当我们需要使用mvc模块、mysql数据模块、redis缓存模块的时候。我们需要导入依赖的jar,然后在spring的配置文件中配置dataSource、sqlsessionFactory、internalResourceViewResolver(视图解析器)、multipartResolver(文件上传)、事务管理器等模块。把这些我们需要的模块变成bean,给spring管理。
在了解自动装配之前,我们首先来回顾一下以前的springMVC项目。在MVC中我们使用容器依赖的bean的时候需要在xml中配置声明才能使用,比如springmvc中的以下配置。
在web.xml中配置dispatcherServlet和filter
在bean.xml配置视图解析器、文件上传multipartResolver、数据源等配置
没用使用自动装配之前,这些庞杂的web配置是每一个项目必须的。我们首先在pom中引入相关的jar。然后再xml中一遍一遍的配置这些东西。 十分复杂低效。
1.2:初步了解自动装配
springboot是一个脚手架工具,约定大于配置。我们只需要按照springboot的规范来开发,就能减少很多配置,当需要开发web项目的时候。
第一步:引入web包的starter启动器。这个包就包含了web的所需的主要功能,同时能导入springweb相关的依赖jar。比如spring,springmvc的包和tomcat的包。点击pom可查询。
<!--springboot的web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
第二步: 我们启动springboot项目之后,就能看到所有的bean。这些bean在容器加载的时候就会把bean自动加载进来。
@SpringBootApplication(scanBasePackages = "com.thits")
@ImportResource(value = "bean.xml") //装配xml文件配置的bean
public class SpringBoot1Application {
public static void main(String[] args) {
//返回ioc容器
ConfigurableApplicationContext run = SpringApplication.run(SpringBoot1Application.class, args);
//获取容器中的所有bean
String[] beanDefinitionNames = run.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("bean的名字:"+beanDefinitionName);
}
}
}
截图可以看到控制台的各种bean已经被spring容器管理了。 比如multipartResolver、DispatcherServlet、视图解析器、事务管理器等。我们就可以直接使用了,不需要在xml中配置这些东西了。
2:自动装配、按需加载
2.1:首先来了解springboot的启动类注解
@SpringBootApplication
public class SpringBoot1Application {
//程序的主入口
public static void main(String[] args) {
SpringApplication.run(SpringBoot1Application.class, args);
}
}
在启动类中有一个注解就是@SpringBootApplication,他有三个子注解,子注解又有其他的注解,搞清楚这些注解的意思就能理解自动装配了。
- @SpringBootApplication(有三个子注解,分比为1、2、3)
- 1:@SpringBootConfiguration (有一个子注解:Configuaration)
- 1.1:@Configuaration (有一个子注解:Component)
- 1.1.1:@Component (作用:就是启动类声明为bean)
- 2:@EnableAutoConfiguration (开启自动注入:有两个子注解)
- 2.1:@AutoConfigurationPackage (自动配置包:有一个子注解,import)
- 2.1.1:@Import({Registrar.class}) (没有子注解)
- 2.2:@Import({AutoConfigurationImportSelector.class}) (没有子注解)
- 3:@ComponentScan (组件扫描:没有子注解,作用:包扫描项目包路径)
在这三个注解中,第一和第三都没有太大的意思,所以我们主要看第二个注解就好了。在第二个注解中使用了两个@import注解。就是这两个注解解释了自动装配的含义。
2.2:@Import({Registrar.class}) 注解的源码
@Import({Registrar.class})源码的作用就是:主启动类注册到容器,也就是main方法所在的包下边,将所有的我们自己的代码中的配置注入的bean容器。
2.3:@Import({AutoConfigurationImportSelector.class}) 注解的源码
源码的第一步就是实现ImportSelector接口的ImportSelector方法,根据类的全部限定名字把类注入到容器(参见import注解的用法),注入了那些类呢?接着看源码
此处源码是获取自动装配的配置类。获取了138个,在哪里获取接着看。
这里的代码就能看出来去哪里加载了这些自动装配的类,在factories文件中。
我们查看autoconfigure的jar下边的factories文件就能看到这些指定的自动装配类全类名了。
2.4:自动装配含义
springboot自动装配:
这就是自动装配,通过@SpringBootApplication注解的两个Import子注解 ,把spring-boot-autoconfigure-2.6.7.jar和其他包的spring.factories 文件中,EnableAutoConfiguration配置的所有类都装配到spring容器中了。
2.5:自动装配扩展原理(Condition按需加载)
springboot自动装配的扩展:两个疑难问题讲解
问题1:我们查看spring.factories中有当前版本支持的所有配置类,这些配置类都会注入bean吗?
答:并不是,之后我们需要导入相对的jar包,这个类是真是存在的,然后才可以自动装配这些bean。
举例说明:在spring.factories中有很多配置类,我们截取这几个来说明
举例1:我们没有配置DispatcherServlet,但是会通过自动装配,层层判断来自动创建出DispatcherServlet
举例2:我们Aop的案例
问题2:在spring的IOC注入了这些138个配置类,我们怎么使用?
答:通过配置使用,约定大约配置。
比如我们使用文件上传。我们怎么知道配置文件上传的文件大小、文件路径等信息。在源码中我们可以看到怎么使用。约定大于配置
springboot默认创建StandardServletMultipartResolver,在源码中如下
文件上传约定大约配置,我们都知道ConfigurationProperties注解的作用是绑定配置文件的值。这里就是约定大于配置。我们只需要按照约定来就行了
自动装配总结:
1:springboot默认加载spring.factories中指定的所有AutoConfiguration。
2:根据这些AutoConfiguration的源码,在里边的条件@conditional判断是否真正的使用这些类。
3:导入的对应jar话就使用这些类,并且在绑定的Properties根据前缀中指定,然后在配置文件中配置属性
这就是自动装配了。
3:注解说明
3.1:@Component注解
使用Component注解可以使一个类直接变成spring容器可以管理的bean,
我们知道@controller @service @Repository 的注解里边都有 Component,就是直接声明自己编写的代码是bean。法
3.2:@Configuration和@Bean注解
@Configuration注解也是把类变成bean, @bean注解用来创建具体的bean,代码案例如下。
主要作用
1:是将第三方依赖的jar的指定类变成bean,在第三方中你没法修改代码加bean注解,可以使用此种方式(new 第三方类)
2:解决组件依赖,详细见代码注释
package com.thits.springboot1.configurations;
import com.thits.springboot1.pojo1.Dog;
import com.thits.springboot1.pojo1.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Configuration相当于配置文件bean.xml 用来装配第三方bean
* proxyBeanMethods 有两种模式
* full模式 proxyBeanMethods = true 容器中只有一个bean
* lite模式 proxyBeanMethods = false 获取bean的方法 每次获取的都是一个新的bean
*
*
* ConditionalOnBean(name="bean名字")
* 对方bean存在 自己也就存在(处理依赖条件)
* 跟方法顺序有关系
*
*
* ConditionalOnMissingBean
* 对方bean不存在 自己也就存在(处理依赖条件)
* 缺失了才能创建
*
*
*/
@Configuration(proxyBeanMethods=true)
public class ConfigurationBeans {
/**
* 方法名 就是容器中的bean的名字
* 主要作用有两个:
*
* 1:引入了其他的jar。里边有些类我想让容器管理 变成bean
* 我们无法直接需改人家的源码,加上注解。这个时候使用此方法来new对象,创建外部的bean
*
* 2:结合Configuration注解来实现组件依赖
* 我们创建的user11bean依赖dog的bean,
* 我们就可以设置proxyBeanMethods=true
* 使用的都是容器中存在的单例dog的bean
* @return
*/
@Bean(name = "user11")
@ConditionalOnMissingBean(name = {"dog1"})
//Conditiona注解跟下边的两个方法顺序有关
public User user1(){
//user组件依赖dog组件
Dog dog=dog1();
return new User(1,"李四",dog);
}
@Bean
public Dog dog1(){
return new Dog();
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@Value("1")
private int id;
@Value("张三")
private String name;
private Dog dog;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dog {
@Value("黄色")
private String color;
@Value("大黄狗")
private String name;
}
扩展1:无论@Configuration(proxyBeanMethods=true)还是false,注入的话都是一个bean
//返回ioc容器
ConfigurableApplicationContext run = SpringApplication.run(SpringBoot1Application.class, args);
User bean1 = run.getBean("user11", User.class);
User bean2 = run.getBean("user11", User.class);
System.out.println("咱们使用@Autowired注入的bean,不论false还是true都是单例:"+(bean1==bean2));//true
扩展2:@Configuration(proxyBeanMethods=false)或者true,直接使用方法获调用取bean,才会出现容器检查bean是否存在的情况,用来解决组件依赖
//bean的测试 通过方法获取,注意是直接调用方法
System.out.println("===============获取@Configuration注解的bean================");
ConfigurationBeans configurationBeans = run.getBean("configurationBeans", ConfigurationBeans.class);
//获取ConfigurationBeans @Configuration(proxyBeanMethods = false)
//当true的时候 容器检查存在实例 这里是调用方法获取实例 返回容器中的实例 所以输出结果是相等 ture
//false的时候 容器不会检查是否存在实例 这里是调用方法每次方法获取实例 所以输出结果是不相等 false
User user = configurationBeans.user1();
User user1 = configurationBeans.user1();
System.out.println(user);//通过方法获取bean
System.out.println(user1);
System.out.println("设置proxyBeanMethods比较:" + (user == user1));
System.out.println();
3.3:@Import注解(自动装配的核心底层实现)
Import注解的作用:也是注入第三方类让他变成容器管理的bean,比@Configuration结合@bean功能更加单一。但是更加暴力简单
实现1:@import(类.class) 直接指定类让容器管理
实现2:@import(选择器.class) 实现选择器,让选择器可以使用数组,把很多类都注入到容器中,这就是依赖注入的核心代码
3.4:@ImportResource注解
作用:我们可以使用ImportResource注解,来装配配置文件xml中的bean。
在springboot项目中,都是使用了注解,但是外部可能存在spring的XML文件里边声明了bean。导致无法使用,所用使用importresource注解来装配bean
用法案例:类和bean声明。
测试代码:
3.5:@ConfigurationProperties和@EnableConfigurationProperties注解
@ConfigurationProperties作用:此注解的作用是,将spring的properties或者yml中配置的属性值,注入到bean中必须和@Component注解结合使用
@EnableConfigurationProperties作用:开启配置绑定,将类变成bean,然后结合@ConfigurationProperties 和@EnableConfigurationProperties(Person.class)使用
测试代码:
配置提示依赖代码包
<!-- 自定义bean:person yml中的提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
3.6:@validation注解
作用:主要用来验证前端传递过来的数据,是否合乎规范
使用案例:
第一步:导入jar
<!--JSR 303 效验-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
第二步:在实体类上使用注解validation,属性上使用其他的@notnull@max等注解,限制属性的范围
/**
* 验证yml的值绑定
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component//定义为bean
@ConfigurationProperties(prefix = "person")//绑定配置文件属性
@Validated //jsr 303验证属性
public class PersonYml {
@Max(value = 40,message = "年龄太大了")
private int age;
@NotNull(message = "名字不能为空")
private String name1;
@NotNull
@Email(message = "邮箱格式不合法")
private String email;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date brith;
@AssertFalse
private boolean happy;
private String[] like;
private Cat cat;
private Map<String,Object> maps;
private List<Object> list;
}
测试代码见3.5的截图,里边有注释和测试。