2.容器功能
2.1组件添加
1. @Configuration(告诉springboot这是一个配置类==配置文件)
1)基本模式
Full模式和Lite模式
示例
最佳实践:
配置类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式
实际案例,新建两个实体类,Pet和User,User依赖了Pet
Pet类
package com.ch.boot.bean;
public class Pet {
public Pet() {
}
public Pet(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Pet{" +
"name='" + name + '\'' +
'}';
}
}
User类
package com.ch.boot.bean;
public class User {
private String name;
private int age;
private Pet pet;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Pet getPet() {
return pet;
}
public void setPet(Pet pet) {
this.pet = pet;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", pet=" + pet +
'}';
}
}
再创建一个配置类,
package com.ch.boot.config;
import com.ch.boot.bean.Pet;
import com.ch.boot.bean.User;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
1.配置类里面使用@Bean标注在方法上给容器注册组件,默认是单实例的
2.配置类本身也是一个组件
3.proxyBeanMethods:代理bean的方法
Full(proxyBeanMethods = true) 全量模式
lite(proxyBeanMethods = false) 轻量模式
组件依赖
* */
@Configuration(proxyBeanMethods = true)//告诉springboot这是一个配置类==配置文件
public class MyConfig {
@Bean //给容器中添加组件 以方法名作为组件的id,
// 返回类型就是组件类型,返回的只,就是组件在容器中的实例
//外部无论对配置类中的组件注册方法调用多少遍,获取的都是之前注册容器中的单实例
public User user01() {
User zhansan = new User("zhansan", 18);
//user组件依赖了pet组件
zhansan.setPet(tomcatPet());
return zhansan;
}
@Bean("tom")
public Pet tomcatPet() {
return new Pet("tomcat");
}
// Pet tom=ru
}
接下来是在主运行程序中,调用相关组件
package com.ch.boot;
import com.ch.boot.bean.Pet;
import com.ch.boot.bean.User;
import com.ch.boot.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
/*
主程序类,所有启动的入口
@SpringBootApplication:这是一个springboot应用
*/
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
//返回我们的IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
String[] names = run.getBeanDefinitionNames();
for (String name:names) {
System.out.println(""+name);
}
Pet tom01=run.getBean("tom",Pet.class);
Pet tom02=run.getBean("tom",Pet.class);
System.out.println("组件:"+(tom01==tom02));
//com.ch.boot.config.MyConfig$$EnhancerBySpringCGLIB$$c1085e98@4a699efa
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
//如果@Configuration(proxyBeanMethods = true)代理对象调用方法,springboot总会检查,这个组件是否在容器中有,
//保持组件单实例
User user = bean.user01();
User user1 = bean.user01();
System.out.println(user==user1);
User user01 = run.getBean(User.class);
Pet tom=run.getBean("tom",Pet.class);
System.out.println("用户中的宠物"+(user.getPet()==tom));
}
}
proxyBeanMethods:代理bean的方法,当proxyBeanMethods = true时,运行结果如下,返回的组件都是单实例的,因为springboot会检查容器中是否存在该组件,有则调用,无则生成
组件:true
com.ch.boot.config.MyConfig$$EnhancerBySpringCGLIB$$bf8f635b@1136b469
true
用户中的宠物t
proxyBeanMethods:代理bean的方法,当proxyBeanMethods =false时,运行结果如下,返回的组件都是多实例,每次生成组件都会new一个
组件:true
com.ch.boot.config.MyConfig@65327f5
false
用户中的宠物false
如果只是给容器中注册组件,其他组件也不依赖这些组件,那么就用false轻量模式,启动和加载会比较快;
如果组件被依赖,那么就是用true全量模式
2. @Bean,@Component(组件),@Controller(控制器),@Service(业务逻辑组件),@Repository(数据库层组件)
3.@ComponentScan(包扫描),
@Import(导入组件)
@Import({User.class, DBHelper.class})
给容器中自动创建出这两个类型的组件,默认组件的名字就是全类名
String[] beanNamesForType = run.getBeanNamesForType(User.class); System.out.println("=========="); for (String name2 : beanNamesForType) { System.out.println("user类型的组件名字"+name2); } DBHelper bean1 = run.getBean(DBHelper.class); System.out.println("=========="+bean1);
测试结果如下:
user类型的组件名字com.ch.boot.bean.User
user类型的组件名字user01
==========ch.qos.logback.core.db.DBHelper@4905c46b
4.@Conditional
条件装配,满足@Conditional指定的条件,才进行组件注入,ctrl+n查找类,ctrl+h打开继承树
派生注解:
@ConditionalOnBean,容器中存在指定的组件是,才做什么
@ConditionalOnMissBean,容器中不存在指定的组件,才做什么
@ConditionalOnClass,容器中存在指定的类,才做什么,
@ConditionalOnMissingClass,容器中不存在某些指定的类,才做什么
@ConditionalOnResource,容器类路径中存在某些指定的资源,才做什么
@ConditionalOnJava,指定某个Java版本号,才做什么
@ConditionalOnWebApplication,当应用是web应用的时候,才做什么
@ConditionalOnNotWebApplication,当应用不是web应用的时候,才做什么
案例,原先的配置类修改成这样,注解掉tom组件
package com.ch.boot.config;
import ch.qos.logback.core.db.DBHelper;
import com.ch.boot.bean.Pet;
import com.ch.boot.bean.User;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/*
1.配置类里面使用@Bean标注在方法上给容器注册组件,默认是单实例的
2.配置类本身也是一个组件
3.proxyBeanMethods:代理bean的方法
Full(proxyBeanMethods = true) 全量模式
lite(proxyBeanMethods = false) 轻量模式
组件依赖
4.@Import({User.class, DBHelper.class})
给容器中自动创建出这两个类型的组件,默认组件的名字就是全类名
* */
@Import({User.class, DBHelper.class})
@Configuration(proxyBeanMethods = true)//告诉springboot这是一个配置类==配置文件
public class MyConfig {
@Bean //给容器中添加组件 以方法名作为组件的id,
// 返回类型就是组件类型,返回的只,就是组件在容器中的实例
//外部无论对配置类中的组件注册方法调用多少遍,获取的都是之前注册容器中的单实例
public User user01() {
User zhansan = new User("zhansan", 18);
//user组件依赖了pet组件
zhansan.setPet(tomcatPet());
return zhansan;
}
// @Bean("tom")
public Pet tomcatPet() {
return new Pet("tomcat");
}
// Pet tom=ru
}
MainApplication中加入以下测试代码
boolean tome=run.containsBean("tom");
System.out.println("容器中Tom组件"+tome);
boolean user01=run.containsBean("user01");
System.out.println("容器中user组件"+user01);
结果如下
容器中Tom组件false
容器中user组件true
然后尝试给MyConfig中的User组件加入以下注解
@ConditionalOnBean(name="tom")
再运行,结果如下
容器中Tom组件false
容器中user组件false
因为条件中必须含有名为“tom”的组件,才会注入组件User
2)@ConditionalOnBean(name="tom")加在类上,结果都不存在tom和user组件,因为这个条件必须成立,类里面的组件才会都执行,否则都不添加组件
3)当条件装配改成ConditionalOnMissBean(name="tom"),然后MyConfig的Pet容器的名称改为tom22,执行上面程序,结果是都存在user01和tom都存在,因为满足了条件装配,不存在组件tom,故生成组件user01和tom22
2.2原生配置文件的引入
1.@ImportResource(导入资源文件)
在Resource目录下新建一个配置文件,beans.xml的spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="haha" class="com.ch.boot.bean.User">
<property name="name" value="zhangsan"></property>
<property name="age" value="18"></property>
</bean>
<bean id="hehe" class="com.ch.boot.bean.Pet">
<property name="name" value="tomcat"></property>
</bean>
</beans>
在MainApplication中测试
boolean haha=run.containsBean("haha"); boolean hehe=run.containsBean("hehe"); System.out.println("hahah"+haha); System.out.println("hehe"+hehe);
结果
hahahfalse
hehefalse
容器中没有这两个组件,然后在MyConfig类中引入资源文件
再次运行得到结果
hahahtrue
hehetrue
2.3配置管理
如何使用java读取到properties文件中的内容,并把它们封装到JavaBean中,以供随时使用
1)使用@ConfigurationProperties+@Component注解进行绑定,以下prefix是前缀,在配置文件中寻找所有前缀为mycar的配置绑定Car中,
创建一个car类来测试注解
package com.ch.boot.bean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/*
只有在容器中的组件,才拥有在springboot提供的强大功能
* */
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
private String brand;
private Integer price;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
'}';
}
}
Controller中加入测试
package com.ch.boot.controller;
import com.ch.boot.bean.Car;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/*@ResponseBody:代表直接写给浏览器,而不是跳转到某个页面
@Controller*
RestController :是上面两个注解的合体
/
*/
@RestController
public class HelloController {
@Autowired
Car car;
@RequestMapping("/hello")
public String handler01() {
return "Hello Spring boot"+"中文";
}
@RequestMapping("/car")
public Car car(){
return car;
}
}
结果如下,成功绑定
2)第二种资源配置方式@ConfigurationProperties+@EnableConfigurationProperties(必须在配置里面配置)
运行结果跟上面1)的结果一样