【Spring】IoC详解:方法Bean的存储、Bean重命名、扫描路径@Component(下)
1.方法Bean的存储
1.1 方法Bean介绍
类注解是添加到某个类上的, 但是存在两个问题:
1.使用外部包⾥的类, 没办法添加类注解
2.一个类, 需要多个对象, ⽐如多个数据源这种场景, 我们就需要使用方法注解
相比类注解(@Component等)定义对象的身份,方法注解更像“操作指令”,告诉容器如何处理方法或其返回值。
@Bean
是Spring
中最核心的方法注解,通常用在@Configuration
标记的配置类中。它的作用是告诉IoC容器:“这个方法的返回值是一个Bean,请帮我管理它。”
1.2 示例
1.2.1 错误1:方法注解没和类注解一起用
创建一个UserInfo类:
@Data
public class UserInfo {
private int id;
private String name;
private int age;
}
创建BeanConfig类,把类中的方法放进容器中:
public class BeanConfig {
@Bean
public UserInfo userInfo1(){
UserInfo userInfo = new UserInfo();
userInfo.setId(1);
userInfo.setName("liming");
userInfo.setAge(18);
return userInfo;
}
@Bean
public UserInfo userInfo2(){
UserInfo userInfo =new UserInfo();
userInfo.setId(2);
userInfo.setName("张三");
userInfo.setAge(20);
return userInfo;
}
}
从容器中取出Bean:
@SpringBootApplication
public class Test1Application {
public static void main(String[] args) {
// 获取上下文
ApplicationContext context = SpringApplication.run(Test1Application.class, args);
//获取userInfo1
UserInfo userInfo1 = (UserInfo)context.getBean("userInfo1");
System.out.println(userInfo1);
//获取userInfo2
UserInfo userInfo2 = (UserInfo)context.getBean("userInfo2");
System.out.println(userInfo2);
}
}
运行结果:
原因:
方法注解要和类注解一起使用,否则方法无法放到容器中
修改后的代码:
@Configuration
public class BeanConfig {
@Bean
public UserInfo userInfo1(){
UserInfo userInfo = new UserInfo();
userInfo.setId(1);
userInfo.setName("liming");
userInfo.setAge(18);
return userInfo;
}
@Bean
public UserInfo userInfo2(){
UserInfo userInfo =new UserInfo();
userInfo.setId(2);
userInfo.setName("张三");
userInfo.setAge(20);
return userInfo;
}
}
运行结果:
1.2.2 错误2:没有设置可以传参的Bean
上面写的方法Bean中的userInfo
是固定的,怎么设置可以变动的userInfo
呢?
在方法中加入参数:
@Configuration
public class BeanConfig {
@Bean
public UserInfo userInfo1(String name){
UserInfo userInfo = new UserInfo();
userInfo.setId(1);
userInfo.setName(name);
userInfo.setAge(18);
return userInfo;
}
}
获取userInfo1的Bean:
@SpringBootApplication
public class Test1Application {
public static void main(String[] args) {
// 获取上下文
ApplicationContext context = SpringApplication.run(Test1Application.class, args);
//获取userInfo1
UserInfo userInfo1 = (UserInfo)context.getBean("userInfo1");
System.out.println(userInfo1);
}
}
运行结果:
在BeanConfig类中添加一个Bean方法,修改后:
@Configuration
public class BeanConfig {
@Bean //添加一个返回String类型的Bean
public String name(){
return "liming";
}
@Bean
public UserInfo userInfo1(String name){
UserInfo userInfo = new UserInfo();
userInfo.setId(1);
userInfo.setName(name);
userInfo.setAge(18);
return userInfo;
}
}
运行结果:
我们可以发现,上面的name方法的名字和userInfo(String name)中的name一样,如果再创建一个返回String类型的Bean呢?会发生什么呢?
@Configuration
public class BeanConfig {
@Bean
public String name(){
return "liming";
}
@Bean
public String name2(){
return "zhangsan";
}
@Bean
public UserInfo userInfo1(String name){
UserInfo userInfo = new UserInfo();
userInfo.setId(1);
userInfo.setName(name);
userInfo.setAge(18);
return userInfo;
}
}
运行结果:
userInfo(String name)中的参数name的匹配是不是与上面方法name()、name2()的上下顺序有关呢?
把name()和name2()位置变换:
@Configuration
public class BeanConfig {
@Bean
public String name2(){
return "zhangsan";
}
@Bean
public String name(){
return "liming";
}
@Bean
public UserInfo userInfo1(String name){
UserInfo userInfo = new UserInfo();
userInfo.setId(1);
userInfo.setName(name);
userInfo.setAge(18);
return userInfo;
}
}
运行结果:
把name()方法去掉,留下name2()方法:
@Configuration
public class BeanConfig {
@Bean
public String name2(){
return "zhangsan";
}
@Bean
public String name(){
return "liming";
}
@Bean
public UserInfo userInfo1(String name){
UserInfo userInfo = new UserInfo();
userInfo.setId(1);
userInfo.setName(name);
userInfo.setAge(18);
return userInfo;
}
}
运行结果:
1.2.3错误3:多个名字不一样的Bean传参
把返回String类型的Bean存入容器:
@Configuration
public class BeanConfig {
@Bean
public String name3(){
return "liming";
}
@Bean
public String name2(){
return "zhangsan";
}
@Bean
public UserInfo userInfo1(String name){
UserInfo userInfo = new UserInfo();
userInfo.setId(1);
userInfo.setName(name);
userInfo.setAge(18);
return userInfo;
}
}
获取userInfo1的Bean:
@SpringBootApplication
public class Test1Application {
public static void main(String[] args) {
// 获取上下文
ApplicationContext context = SpringApplication.run(Test1Application.class, args);
//获取userInfo1
UserInfo userInfo1 = (UserInfo)context.getBean("userInfo1");
System.out.println(userInfo1);
}
}
运行结果:
可以发现,方法Bean的名称就是方法名。
需要约定传参(注入):
@Configuration
public class BeanConfig {
@Bean
public String name3(){
return "liming";
}
@Bean
public String name2(){
return "zhangsan";
}
@Bean
public UserInfo userInfo1( @Qualifier("name3") String name){
UserInfo userInfo = new UserInfo();
userInfo.setId(1);
userInfo.setName(name);
userInfo.setAge(18);
return userInfo;
}
}
1.2.4总结
总结:
(1)方法Bean注解要和类Bean注解一起使用
(2)方法Bean需要传参时要设置可以传参的方法Bean
(3)传参时优先传参参数名和Bean名字一样的
(4)如果没有名字一样但是类型有多个的Bean,需要约定哪个Bean传参(注入)。
2.Bean重命名
可以通过设置 name 属性给 Bean 对象进⾏重命名操作,如下代码所示:
@Configuration
public class BeanConfig {
@Bean
public String name1(){
return "liming";
}
//下面三种方法都可以使用
//@Bean({"u1","userInfo1"})
//@Bean("u1")
@Bean(name={"u1","userInfo1"})//起别名u1
public UserInfo userInfo1( @Qualifier("name1") String name){
UserInfo userInfo = new UserInfo();
userInfo.setId(1);
userInfo.setName(name);
userInfo.setAge(18);
return userInfo;
}
}
在容器中取名字为userInfo1
和u1
的Bean
@SpringBootApplication
public class Test1Application {
public static void main(String[] args) {
// 获取上下文
ApplicationContext context = SpringApplication.run(Test1Application.class, args);
//获取userInfo1
UserInfo userInfo1 = (UserInfo)context.getBean("userInfo1");
System.out.println(userInfo1);
//获取userInfo1
UserInfo u1 = (UserInfo)context.getBean("u1");
System.out.println(u1);
}
}
运行后的结果:
结论:给Bean起别名后,Bean的新名字和旧名字都可以使用。
3.扫描路径
3.1示例
使用前面学习的四个注解声明的bean,⼀定会⽣效吗?
答: 不⼀定(原因:bean想要⽣效,还需要被Spring扫描)
下面是启动类默认的路径:
运行时,可以正常启动。
下⾯我们通过修改项⽬⼯程的⽬录结构,来测试bean对象是否⽣效:
再次运行,发生报错:
为什么呢?
3.2 @ComponentScan注解
使⽤五⼤注解声明的bean,要想⽣效, 还需要配置扫描路径, 让Spring扫描到这些注解
也就是通过@ComponentScan
来配置扫描路径.
@SpringBootApplication
注解的父注解有@ComponentScan
:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
......
}
@ComponentScan()
中的意思是:
默认会扫描主类所在包及其子包下的所有 @Component
类,但 Spring Boot 的自动配置类(如 spring-boot-autoconfigure 包下的类)不应该被直接扫描注册,因为它们由 @EnableAutoConfiguration 按需加载
但是我们也可以自定义设置扫描路径:
启动类放的位置:
启动类代码:
@ComponentScan("org.example.test1")//扫描的包
@SpringBootApplication
public class Test1Application {
public static void main(String[] args) {
// 获取上下文
ApplicationContext context = SpringApplication.run(Test1Application.class, args);
//获取userInfo1
UserInfo userInfo1 = (UserInfo)context.getBean("userInfo1");
System.out.println(userInfo1);
//获取userInfo1
UserInfo u1 = (UserInfo)context.getBean("u1");
System.out.println(u1);
}
}
运行后,结果: