本篇文章将学习SpringBoot2中的容器功能,何为容器呢?
在学习Spring时有讲过IOC容器,Spring会帮我们创建好对象存储在IOC容器中,这也是创建对象的另一种方式
组件添加
1、@Configuration
@Configuration的作用是告诉SpringBoot这是一个配置类,相当于Spring中的xml配置文件,在xml配置文件中注入对象的属性,Spring帮我们自动创建对象并存入IOC容器中
先来看一下@Configuration的使用方法,配合@Bean来使用,@Bean以方法名作为组件id,如果希望自定义可以使用@Bean(“xxx”)
@Configuration //告诉springboot这是这是一个配置类
public class MyConfig {
@Bean //给容器添加组件,以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
public Person tom(){
Person tom = new Person(1, "Tom");
return tom;
}
@Bean
public Pet cat(){
return new Pet("tomcat");
}
}
还是来看我们的主程序
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
//返回ioc容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class);
//查看容器中的组件
String[] beanDefinitionNames = run.getBeanDefinitionNames();
for (String name:beanDefinitionNames) {
System.out.println(name);
}
//获取容器中的组件
/*
如果@Configuration(proxyBeanMethods = true)代理对象调用方法
springboot总会检查这个容器中是否存在组件,保持组件单实例
如果@Configuration(proxyBeanMethods = false)创建的MyConfig对象就不再是一个代理对象
*/
Person tom1 = run.getBean("tom", Person.class);
Person tom2 = run.getBean("tom", Person.class);
System.out.println("判断是否为单实例:" + (tom1 == tom2)); //true
//com.yellowstar.boot.config.MyConfig$$EnhancerBySpringCGLIB$$76dd3309@27e32fe4
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
Pet cat1 = bean.cat();
Pet cat2 = bean.cat();
System.out.println("宠物:" + (cat1==cat2) ); //true
}
}
下面有一个重点知识需要掌握,SpringBoot2中的@Configuration和SpringBoot相比,多了一个属性proxyBeanMethods,大致意思为是否创建一个代理类默认值为true,保证每个@Bean方法被调用多少次返回的组件都是单实例的,所以测试的返回值为true,将他设置为false来看一下
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
//返回ioc容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class);
//查看容器中的组件
String[] beanDefinitionNames = run.getBeanDefinitionNames();
for (String name:beanDefinitionNames) {
System.out.println(name);
}
//获取容器中的组件
Person tom1 = run.getBean("tom", Person.class);
Person tom2 = run.getBean("tom", Person.class);
System.out.println("判断是否为单实例:" + (tom1 == tom2));
//com.yellowstar.boot.config.MyConfig@4a31c2ee 已经不是一个代理对象了
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
Pet cat1 = bean.cat();
Pet cat2 = bean.cat();
System.out.println("宠物:" + (cat1==cat2) ); //false
}
}
2、@Bean、@Component、@Controller、@Service、@Repository
这几个Spring中常用的注解,也延伸到了SpringBoot中,是可以正常使用的,这里不做过多介绍
3、@ComponentScan、@Import
@ComponentScan定义扫描注解的范围
@Import来重点认识一下,@Import给容器中自动创建两个类型的组件,默认组件名称为全类名,我们将其定义在MyConfig类上,其属性为我们需要注入到IOC容器中的对象类型
@Import({Person.class,Pet.class}) //给容器中自动创建两个类型的组件,默认组件名称为全类名
@Configuration(proxyBeanMethods = false) //告诉springboot这是这是一个配置类
public class MyConfig {
}
看一下控制台输出,发现这两个类型已经被注入到IOC容器中了,并且组件名称为全类名
4、@Conditional
条件装配,这个需要重点关注一下,在之后学习源码时,会经常见到他,标注在类或方法上,如果满足条件才执行此方法或类注入IOC容器。@Conditional衍生了非常多的子类
举例介绍@ConditionalOnBean,如果存在满足条件的组件,则执行方法
细心的同学会发现,我将cat组件的顺序排到了tom组件之前,这也是小黄在实操过程中发现的,如果按照原先顺序,因为在执行tom方法前ioc容器中并没有cat组件,所以不会注入tom组件
@Configuration(proxyBeanMethods = false) //告诉springboot这是这是一个配置类
@ImportResource("classpath:bean.xml") //根据xml文件自动创建两个类型的组件
public class MyConfig {
@Bean
public Pet cat(){
return new Pet("tomcat");
}
@Bean //给容器添加组件,以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
@ConditionalOnBean( name = "cat") //有加载顺序,必须要在执行注解标注方法之前存在名为cat的组件,标注在类上必须要在加载该类之前存在cat组件
public Person tom(){
Person tom = new Person(1, "Tom");
return tom;
}
}
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
//返回ioc容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class);
//containsBean("xxx")查看容器中是否存在名为xxx的组件
boolean cat = run.containsBean("cat");
System.out.println("容器中cat组件:" + cat);
boolean tom = run.containsBean("tom");
System.out.println("容器中tom组件:" + tom);
}
}
原生配置文件引入
在实际项目中会出现以下这种情况:该项目之前都是用SSM框架构建的,项目中配置了大量的xml文件来注入bean,现要使用SpringBoot来改造该项目,即使是维护这些Bean也需要大量的时间,那么SpringBoot为我们提供了另外一个注解,可以让我们引入原生的配置文件
@ImportResource
先来创建原生的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="make" class="com.yellowstar.boot.bean.Person">
<property name="id" value="1"></property>
<property name="name" value="make"></property>
</bean>
<bean id="tiger" class="com.yellowstar.boot.bean.Pet">
<property name="name" value="tiger"></property>
</bean>
</beans>
在MyConfig类上标注@ImportResource注解,这时候我们在运行主程序会发现这两个组件已经被存储到IOC容器中了
@Configuration(proxyBeanMethods = false) //告诉springboot这是这是一个配置类
@ImportResource("classpath:bean.xml") //根据xml文件自动创建两个类型的组件
public class MyConfig {
}
配置绑定
如何使用Java读取到properties文件中的内容,并且封装到JavaBean中,这里我们就引入一个配置绑定的概念
创建一个Car类,我们希望自动注入ioc容器并且引用外部文件来配置该类的属性
public class Car {
private String brand;
private Integer price;
//基本方法省略
}
application.properties配置文件
mycar.brand=BYD
mycar.price=100000
我们介绍以下两种方法
1、@Component + @ConfigurationProperties
@Component,被改注解标识的类会被注入IOC容器
@ConfigurationProperties,该注解有一个prefix属性,通过指定的前缀,绑定配置文件中的配置,该注解可以放在类上,也可以放在方法上
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
private String brand;
private Integer price;
}
通过自动装配获取car看一下car的属性,此时就做到了配置绑定
@Controller
@ResponseBody
public class HelloController {
@Autowired
Car car;
@RequestMapping("/car")
public Car getCar(){
System.out.println(car); //Car{brand='BYD', price=100000}
return car;
}
}
2、@EnableConfigurationProperties + @ConfigurationProperties
@EnableConfigurationProperties替换@Component,标识在配置类上,开启car的配置绑定功能,并把car组件注入到IOC容器中,还是执行以上方法,发现输出结果一样
@EnableConfigurationProperties(Car.class)
public class MyConfig {}