底层注解
@Configuration
把一个类变为配置类,回想下,之前spring中的 xxx.xml Bean配置文件,@Configuration代替了 之前的xml配置文件,那之前xml中的 <bean id="xxx" class="com.lxc.xxx.xxx" /> 在类中如何表示? 在一个方法上用注解 @Bean 来表示 <Bean> 标签,默认方法名表示之前Bean标签上的id,也就是组件的名字默认是方法名称,class值(返回类型)就是方法的返回值,也就是组件的类型。
配置类
此时,两个组件(我的理解就是两个对象而已)Person和Cat会被加载到IOC的容器里边,在启动入口类中,通过 app.getBean() 的方式可以获取到任何一个组件!!!
@Configuration(proxyBeanMethods = true)
public class ConfigBean {
/**
* @Bean 相当于 之前
* <Bean id="person" class="com.lxc.xxx.person">
* </Bean>
* @return User
*/
@Bean // 方法名就是之前xml中 id的值,也可以自定义 @Bean(name="alisName")
public Person person() {
return new Person(); // 返回值 相当于class 返回类型
}
@Bean
public Cat cat () {
return new Cat();
}
}
Cat
public class Cat {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
Person
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
测试
@SpringBootApplication
public class BootAndVueProjectApplication {
private static final Logger LOG = LoggerFactory.getLogger(BootAndVueProjectApplication.class);
public static void main(String[] args) {
// 返回IOC容器
ConfigurableApplicationContext app = SpringApplication.run(BootAndVueProjectApplication.class, args);
Person person = app.getBean("person", Person.class);
Cat cat = app.getBean("cat", Cat.class);
System.out.println(person);
System.out.println(cat);
}
}
几个提示点
(1)通过@Configuration配置一个类,里边的所有注册的组件默认是单例模式(调用多少次都会从缓存中获取除了第一次调用)。
@SpringBootApplication
public class BootAndVueProjectApplication {
private static final Logger LOG = LoggerFactory.getLogger(BootAndVueProjectApplication.class);
public static void main(String[] args) {SpringApplication(BootAndVueProjectApplication.class);
ConfigurableApplicationContext app = SpringApplication.run(BootAndVueProjectApplication.class, args);
Person person = app.getBean("person", Person.class);
Person person1 = app.getBean("person", Person.class);
System.out.println(person == person1);
}
}
(2)配置类本身也是一个组件
@SpringBootApplication
public class BootAndVueProjectApplication {
public static void main(String[] args) {
ConfigurableApplicationContext app = SpringApplication.run(BootAndVueProjectApplication.class, args);
ConfigBean configBean = app.getBean(ConfigBean.class);
System.out.println(configBean);
}
(3)springboot 2.0 @Configuration多了一个 这样的属性 proxyBeanMethod :是否代理Bean的方法,默认为true,什么意思?
很简单,在外部调用配置类中两个组件的注册方法(person和Cat)时,如果 proxyBeanMethod 属性取默认值true,那么不管调用方法多少次,都会取容器中的单实例对象。
@SpringBootApplication
public class BootAndVueProjectApplication {
public static void main(String[] args) {
ConfigurableApplicationContext app = SpringApplication.run(BootAndVueProjectApplication.class, args);
ConfigBean configBean = app.getBean(ConfigBean.class);
System.out.println(configBean.person() == configBean.person());
}
}
如果 proxyBeanMethod 属性为false,每次调用,都会拿到一个新对象
@Configuration(proxyBeanMethods = false)
public class ConfigBean {// ··· ···}
@SpringBootApplication
public class BootAndVueProjectApplication {
public static void main(String[] args) {
ConfigurableApplicationContext app = SpringApplication.run(BootAndVueProjectApplication.class, args);
ConfigBean configBean = app.getBean(ConfigBean.class);
System.out.println(configBean.person() == configBean.person());
}
}
(3)有上边 proxyBeanMethod 属性为true,引出一个组件依赖的问题。
假设Person类中有Cat cat属性,可以理解为这个人 A 想领养了一只猫 B,下边来测试下,在外部通过person来创建这个猫 B,和调用 cat方法来创建猫A,是不是同一只猫呢?
@Configuration(proxyBeanMethods = true)
public class ConfigBean {
@Bean
public Person person() {
Person person = new Person();
person.setCat(cat());
return person;
}
@Bean
public Cat cat () {
return new Cat();
}
}
public class Person {
private Cat cat;
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
}
public class Cat {
}
测试:
很明显,是同一只猫,这个人领养的这只猫就是B
@SpringBootApplication
public class BootAndVueProjectApplication {
public static void main(String[] args) {
ConfigurableApplicationContext app = SpringApplication.run(BootAndVueProjectApplication.class, args);
Person person = app.getBean("person", Person.class);
Cat cat = app.getBean("cat", Cat.class);
System.out.println(person.getCat() == cat);
}
}
当 proxyBeanMethod 属性为false时,如果组件间存在依赖关系(一个类中属性引用了别的类),每次外部在调用时,都会产生一个新的对象。
实际上平时开发,如果配置类中的只是单纯的注册组件的话,通常会把proxyBeanMethod 属性设置为false,如果 proxyBeanMethod = true ,在springboot启动时,每次都会去检查注册组件的方法 在容器中是否存在,启动很慢;如果设置为false,在启动时,会跳过检查容器,启动速度非常快。
向容器注册组件,除了上边@Bean注解,还有以下注解:
@Component、@Controller、@Service、@Repository
@Import
@import注解与上边几个一样,都是把组件注册到IOC容器里边,注解属性默认是数组,里边元素必须为Class类型。
@Import({Person.class, Cat.class})
@Configuration(proxyBeanMethods = true)
public class TestConfig {
}
public class Cat {
}
public class Person {
}
@ComponentScan 包扫描
@ComponentScan(basePackages="包全路径")
@Conditional 条件装配
条件装配:满足 @Conditional 指定的条件时,则进行组件注入,基于@Conditional 注解衍生出非常多的条件判断注解,springboo源码中有很多基于这个注解的文件类,存在一个按需加载的概念,就是用@Conditional 注解和衍生注解来做的。
下边以 @ConditionalOnBean 为例 记录下。
// 当容器中存在名字为 user 的组件时,下边所有注册的主键才能生效,否则不生效!
@ConditionalOnBean(name = {"user"})
@Import({Person.class, Cat.class})
@Configuration(proxyBeanMethods = true)
public class TestConfig {
@Bean
public Person person () {
return new Person();
}
@Bean
public Cat cat () {
return new Cat();
}
}
@ImportResource("classpass:beans.xml") 导入spring的xml文件
一些老项目,都会使用xml的方式向容器中注册组件 , 我们可以使用@ImportResource("classpass:beans.xml") 注解的方式去加载xml配置文件来注册组件。
配置绑定(获取配置文件中的内容)
有两种方式:
方式一:
如何使用java读取到properties文件中的内容,并把它封装到JavaBean 中,以供随时使用。在springboot中可以使用以下注解非常方便的拿到properties配置文件中的键值对:
@ConfigurationProperties(prefix="xxx")
prefix:在配置文件中的属性前缀
Person.java
// 为了方便,不在使用 @Configuration+@Bean方式注册组件,可以直接在类上添加@Component注解
// ,spring会把这个类直接注册到IOC容器里
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private String address;
public String getName() {
return name;
}
public String getAddress() {
return address;
}
}
测试
@SpringBootApplication
public class SprintBoot01Application {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(SprintBoot01Application.class, args);
Person person = ctx.getBean("person", Person.class);
System.out.println(person.getName());
System.out.println(person.getAddress());
}
}
测试时,发现报了一个错误,仔细琢磨下边这个报错:
在名为person的组件中 绑定属性失败,下边是配置文件中的键值对,最后一行Reason划重点,原因:address这个属性没有setter方法,原来如此,猜测:springboot底层为每个属性赋值时,使用的是每个属性的settter方法为其赋值,加上属性的settter方法,结果输出了。
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private String address;
public void setAddress(String address) {
this.address = address;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
}
方式二:
在配置文件中 添加 @EnableConfigurationProperties 注解,开启配置文件与 类的绑定。
// (1)开启配置绑定:把配置文件中的属性与Person类绑定,使用这个注解
@EnableConfigurationProperties(Person.class)
@Configuration(proxyBeanMethods = true)
public class TestConfig {
@Bean
public Person person () {
return new Person();
}
}
Person.java
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private String address;
public void setAddress(String address) {
this.address = address;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
}