SpringBoot自动装配原理之Configuration以及@Bean注解的使用
组件Configuration以及@Bean注解的使用
该知识点在Spring中应该学过,没有学过或者遗忘的的朋友需要预习或温习前置知识点。SpringBoot其实就是Spring的进一步简化,所以前置知识点还是有必要的学习的,这样更能明白其底层的原理。
好了,废话不多说,开始!
算了,还是给朋友们补充一下知识点,就是这么随意。
结构目录:
pojo--User:
package com.xbhog.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
private String name;
private int age;
}
config-MyConfig:
package com.xbhog.config;
import com.xbhog.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
@Bean
public User user(){
return new User("xbhog",18);
}
}
controller-Mycontroller:
package com.xbhog.controller;
import com.xbhog.config.MyConfig;
import com.xbhog.pojo.User;
import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Mycontroller {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = context.getBean("user", User.class);
System.out.println(user.toString());
}
}
前面这三个文件埋了一个坑,使用SpringBoot启动的话是找不到Bean的,因为我们必须把文件放到与主启动程序同一目录下,这样才能找到,可以这样:这是由于SpringBootApplication的扫描路径决定的
但是当我们把Myapp放入主程序文件夹时:发现并没有找到相应的组件信息
在不改变原来的程序的情况下,我们可以使用手动扫描的方式,设置自定义扫名路径:
package com.xbhog.springboot1times;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
//@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.xbhog") //设置扫描路径
public class Myapp {
public static void main(String[] args) {
/*1. 返回我们IOC容器*/
ConfigurableApplicationContext run = SpringApplication.run(Myapp.class, args);
/*2.查看容器里面的组件*/
String[] names = run.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
}
}
效果显示:
单实例问题:
- 判断组件在容器中是否为单实例:
package com.xbhog.springboot1times;
import com.xbhog.pojo.Pet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
//@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.xbhog")
public class Myapp {
public static void main(String[] args) {
/*1. 返回我们IOC容器*/
ConfigurableApplicationContext run = SpringApplication.run(Myapp.class, args);
/*2.从容器中获取组件*/
Pet tom1 = run.getBean("tom11", Pet.class);
Pet tom2 = run.getBean("tom11", Pet.class);
System.out.println("组件是否为单实例:"+(tom1== tom2));
}
}
组件是否为单实例:true
Myconfig调用问题:
因为配置类也属于组件,如果我们获取配置类组件后,通过实例化对象在调用其中的bean,是调用普通方法呢,还是调用容器中的相应的组件?
package com.xbhog.springboot1times;
import com.xbhog.config.MyConfig;
import com.xbhog.pojo.Pet;
import com.xbhog.pojo.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
//@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.xbhog")
public class Myapp {
public static void main(String[] args) {
/*1. 返回我们IOC容器*/
ConfigurableApplicationContext run = SpringApplication.run(Myapp.class, args);
/*2.查看容器里面的组件*/
String[] names = run.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
/*3.从容器中获取组件*/
Pet tom1 = run.getBean("tom11", Pet.class);
Pet tom2 = run.getBean("tom11", Pet.class);
System.out.println("组件是否为单实例:"+(tom1== tom2));
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
User user1 = bean.user();
User user2 = bean.user();
System.out.println("测试通过Myconfig类调用的user组件:"+(user1==user2));
}
}
效果如下:
组件是否为单实例:true
com.xbhog.config.MyConfig$$EnhancerBySpringCGLIB$$9b1ae7c2@6c0905f6
测试通过Myconfig类调用的user组件:true
上面的效果也就是Configuration(proxyBeanMethods=true)的作用,保证每个@Bean方法被调用多少次返回的组件都是单实例的
实际上就是proxyBeanMethods:代理bean的方法(true),外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象,也叫:FULL模式.
当proxyBeanMethods=true
时:
MyConfig返回的Bean本身就是代理对象,CGLIB,并且测试通过Myconfig类调用的user组件:true.
com.xbhog.config.MyConfig$ \(EnhancerBySpringCGLIB\)$9b1ae7c2@6c0905f6
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
//如果@Configuration(proxyBeanMethods = true)代理对象调用方法。
// SpringBoot总会检查这个组件是否在容器中有。
User user1 = bean.user();
User user2 = bean.user();
System.out.println("测试通过Myconfig类调用的user组件:"+(user1==user2));
当:proxyBeanMethods=false
时(Lite模式-轻量级):
MyCnfig返回的就不是代理模式,是单纯的对象,测试通过Myconfig类调用的user组件:false
总结:
proxyBeanMethods:代理bean的方法 ;
- Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
- Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
- 组件依赖必须使用Full模式默认。其他默认是否Lite模式
使用场景:
现在向pojo.User中添加Pet对象:
package com.xbhog.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
private String name;
private int age;
private Pet pet;
}
在proxyBeanMethods=true
模式下才是正确的;
package com.xbhog.config;
import com.xbhog.pojo.Pet;
import com.xbhog.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration //告诉SpringBoot这是一个配置类 == application.xml
public class MyConfig {
@Bean //给容器添加组件,以方法名作为组件的id,返回类型就是组件类型。返回的值,就是组件在容器中的实例
public User user(){
User user = new User("xbhog", 18);
//user组件依赖与Pet组件,用户中的宠物与容器中的宠物时一样的
user.setPet(tomcat());
return user;
}
@Bean("tom11") //设置bean别名--》id的名字
public Pet tomcat(){
return new Pet("tomcat");
}
}
User user = run.getBean("user", User.class);
Pet tom3 = run.getBean("tom11", Pet.class);
System.out.println("用户的宠物"+(user.getPet() == tom3));
用户的宠物true;
在proxyBeanMethods=false
模式后,就不会扫描容器,直接创建对象:
组件是否为单实例:true
com.xbhog.config.MyConfig@330c1f61
测试通过Myconfig类调用的user组件:false
用户的宠物false