当前位置: 首页 > news >正文

Spring核心机制:@Bean注解与依赖注入的终极实践指南

一、方法注解

1.1、@Bean

        在之前的讲解中,我们了解到通过类注解可以将类交由Spring管理。然而,当我们使用第三方库中的类时,由于无法直接添加类注解或其他限制条件,这时候就需要借助@Bean方法注解来实现同样的功能。

public class BeanConfig {@Beanpublic User user(){User user=new User();user.setName("路明非");user.setAge(18);return user;}
}

尝试获取bean对象中的user

@SpringBootApplication
public class SpringDemo3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(SpringDemo3Application.class, args);BeanConfig beanConfig=context.getBean(BeanConfig.class);User user=beanConfig.user();System.out.println(user);}
}

        运行结果:获取不到该对象

        为什么呢??? 

         如下:

        控制台正常输出我们预期的结果了 

 1.2、定义多个对象

1.2.1、构造方法注解

  @AllArgsConstructor @NoArgsConstructor 是用于自动生成类构造方法的注解,功能类似于 @Data 注解。

回顾@Data注解功能:

        在类上添加 @Data 注解时,Lombok 会在编译期自动生成以下方法

                (1)toString()

                (2)equals(Object object)

                (3)hashCode()

                (4)所有字段的 getter 方法

                (5)所有非 final 字段的 setter 方法

                (6)一个包含使用 final 字段的构造器

        我们可以看到 @Data 注解为我们自动添加 Getter 、Setter等方法、但没有我们想要的构造方法 ,所以我们为User类添加该注解

1.2.2、获取Bean对象 

        此时,User类就存在两个对象了,我们尝试获取Bean对象,运行下述代码

@SpringBootApplication
public class SpringDemo3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(SpringDemo3Application.class, args);User student1=context.getBean(User.class);System.out.println(student1);}
}

         运行结果显示:没有可用类型为...合适的Bean,期望匹配单个Bean,但发现两个“user、user1”,从报错信息中可以看到@Bean注解的bean,bean的名称就是它的方法名

        那么我们根据bean的名称来获取bean对象 

@SpringBootApplication
public class SpringDemo3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(SpringDemo3Application.class, args);User student1=(User) context.getBean("user1");System.out.println(student1);User student2=(User) context.getBean("user");System.out.println(student2);}
}

         可以看出@Bean可以针对同一个类,定义多个对象

1.3、重命名Bean 

1.3.1、替换原始类名 
@Configuration
public class BeanConfig {@Bean("getuser2")public User getuser(){return new User("路明非",18);}@Beanpublic User getuser1(){return new User("陈墨瞳",18);}
}
@SpringBootApplication
public class SpringDemo3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(SpringDemo3Application.class, args);User student1=(User) context.getBean("getuser1");System.out.println(student1);User student2=(User) context.getBean("getuser");System.out.println(student2);}
}

         我们给路明非的Bean对象进行了重命名的操作,再次按照方法名尝试获取Bean对象:

        从代码中可以看到,我们可以成功获取名为 getuser1 的 Bean 对象。需要注意的是,要获取路明非的信息,必须使用重命名操作。此外,观察上述代码可以发现小奥奇修改了方法名。这是因为在测试过程中发现,即使替换了原始的 user 名称,执行后续代码时仍会生成参数为空的 User 对象。这是由于 Spring 框架自动创建了 User 实例,但未提供任何初始化值,所以保留了 Java 的默认值(null 和 0)

   1.3.2、多名替换

        我们可以对类进行多次重命名,当获取Bean对象时,使用任意一个名称都能成功获取该对象。这是因为:

二、DI详解

        依赖注入是⼀个过程,是指 IoC 容器在创建 Bean 时,去提供运行时所依赖的资源,而资源指的就是对象,在上面程序案例中,我们使用了 @Autowired 这个注解,完成了依赖注入的操作
简单来说,就是把对象取出来放到某个类的属性中,关于依赖注入,Spring 给我们提供了如下三种方式:

2.1、属性注入

        UserController类:

@org.springframework.stereotype.Controller
public class UserController {@Autowiredprivate UserService service;public void print(){System.out.println("hello Controller....");service.print();}
}

        UserService类:

@org.springframework.stereotype.Service
public class UserService {public void print(){System.out.println("hello Service....");}
}

         获取UserController中的print()方法:

@SpringBootApplication
public class SpringDemo3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(SpringDemo3Application.class, args);UserController userController=context.getBean(UserController.class);userController.print();}
}

        运行结果: 

2.2、构造方法注入

        构造方法注入是在类的构造方法中实现注入,如下代码所示:

@org.springframework.stereotype.Controller
public class UserController {private UserService service;@Autowiredpublic UserController(UserService service) {this.service=service;}public void print(){System.out.println("hello Controller....");service.print();}
}

        注意如果类中只有一个构造方法,上述代码中的 @Autowired 注解是可以省略的,但是如果存在多个构造方法,我们就必须加上 @Autowired 注解指定那个是默认的构造方法

        我们移除无参构造方法,添加两个有参构造方法且不使用 @Autowired 注解,然后观察运行结果:

@org.springframework.stereotype.Controller
public class UserController {private UserService service;private UserConfig config;public UserController(UserService service) {this.service=service;}
//    @Autowiredpublic UserController(UserService service, UserConfig config) {this.service=service;this.config=config;}public void print(){System.out.println("hello Controller....");service.print();config.print();}
}

        我们使用 @Autowired 注解指定后发现就能输出预期结果了:

2.3、Setter注入

        Setter注入和属性注入类似,就是在生成 set 方法时加上 @Autowired 注解

@org.springframework.stereotype.Controller
public class UserController {private UserService service;@Autowiredpublic void setService(UserService service) {this.service = service;}public void print(){System.out.println("hello Controller....");service.print();}
}

        当然若不加注解会报错:

2.4、三种方式的优缺点分析


属性注入

        优点简洁,使用方便

        缺点

                功能性问题:无法注入一个不可变的对象(final 修饰的对象)

                通用性问题:只能适应于 IoC 容器

                设计原则问题:更容易违背单一设计原则


构造方法注入

        优点

                可注入不可变对象

                注入的对象不会被修改

                注入对象会被完全初始化(因为依赖对象是在构造方法中执行的,而构造方法是在对象创建之初执行的,因此被注入的对象在使用之前,会被完全初始化)

                通用性更好

        缺点

                注入多个对象时,代码会比较繁琐


Setter 注入

        优点

                符合单一职责的设计原则,可以对该对象重新进行配置或者注入

        缺点

                不能注入不可变对象(final 修饰的对象)

                注入的对象可被修改

 2.5、@Autowired引发的问题

        定义一个学生类:

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Student {String name;int age;
}

        在 AllStudent 类中创建路明非的学生实例,使用@Bean注解将该对象交由Spring容器管理,并对bean进行重命名

@Component
public class AllStudent {@Bean({"s2","s3"})public Student S1(){return new Student("路明非",18);}
}

        在 ControllerDemo1 类中定义 Student 类并完成注入,需注意此处学生对象命名为 "s1"。

@Controller
public class ControllerDemo1 {@Autowiredprivate Student s1;public void print(){System.out.println("hello controller...");System.out.println(s1);}
}

        最后尝试获取Bean对象 

@SpringBootApplication
public class SpringDemo3Application {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(SpringDemo3Application.class, args);ControllerDemo1 demo1 = (ControllerDemo1) context.getBean(ControllerDemo1.class);demo1.print();}
}

        测试运行结果显示成功获取了路明非对象,这表明代码中的变量名"s1"并不影响最终结果。当系统中仅存在一个学生对象时(学生类型的Bean,或者说没有候选Bean),Spring框架会自动根据类型进行依赖注入,变量命名可以自由选择。

         那么,当我们再次添加一个学生实例后,此时注入的学生是哪一个呢???

@Component
public class AllStudent {@Bean({"s2","s3"})public Student S1(){return new Student("路明非",18);}@Beanpublic Student S4(){return new Student("陈墨瞳",18);}
}

         我们点击运行后,观察结果:

        报错信息下方提示:"Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed"。这表明可以通过以下方式解决:

  1. 使用@Primary注解标记默认Bean
  2. 使用@Qualifier指定具体Bean 

        我们将按照这些建议进行测试验证

2.5.1、 @Primary
 2.5.2、@Qualifier

        在需要注入的对象上方添加 @Qualifier 注解,可以明确指定要传入的具体对象。但需注意,@Qualifier不能单独使用,必须与@Autowired配合使用

@Controller
public class ControllerDemo1 {@Qualifier("S4")@Autowiredprivate Student s1;public void print(){System.out.println("hello controller...");System.out.println(s1);}
}

        该注解比较适用于参数注入 :

 2.5.3、@Resource

        使用 @Resource 注解时,会按照bean名称进行注入。可以通过name属性来指定需要注入的bean名称

@Controller
public class ControllerDemo1 {@Resource(name="S4")private Student s1;public void print(){System.out.println("hello controller...");System.out.println(s1);}
}

2.6、Autowired装配顺序 

http://www.dtcms.com/a/294132.html

相关文章:

  • ubuntu24的一些小问题
  • 分布式限流算法与组件
  • latex中既控制列内容位置又控制列宽,使用>{\centering\arraybackslash}p{0.85cm}
  • 修改 Lucide-React 图标样式的方法
  • rust嵌入式开发零基础入门教程(四)
  • webrtc整体架构
  • 重塑优化建模与算法设计:2025年大模型(LLM)在优化领域的应用盘点 - 2
  • Java中IO多路复用技术详解
  • Three.js 材质全解析:从 MeshBasicMaterial 到 MeshStandardMaterial
  • 通缩浪潮中的 “测量防线”:新启航如何用国产 3D 白光干涉仪筑牢半导体成本护城河?
  • 7月23日华为机考真题第二题-200分
  • 从机械操作到智能流程:火语言 RPA 在多场景中的效率提升实践
  • 如何提升AI收录?如何免费增加AI搜索推荐你的网站?有哪些免费好用的检测工具推荐?
  • Kafka使用场景与设计原理
  • 【金融机器学习】第五章:最优投资组合——Bryan Kelly, 修大成(中文翻译)
  • 量化金融简介(附电子书资料)
  • 大规模金融数据相关性并行计算系统设计与实现
  • MySQL金融级数据一致性保障:从原理到实战
  • Web开发基础与RESTful API设计实践指南
  • Linux内核设计与实现 - 第11章 定时器和时间管理
  • static 关键字的 特殊性
  • 【AI智能体】Dify 开发与集成MCP服务实战操作详解
  • Elasticsearch Circuit Breaker 全面解析与最佳实践
  • 【Word Press基础】创建一个动态的自定义区块
  • JS逆向基础( AES 解密密文WordArray和Uint8Array实战②)
  • 【无标题】word 中的中文排序
  • Pycharm2025 安装教程 免费分享 没任何套路
  • PDF转Word的简单方法
  • CSP-J 2021 入门级 第一轮(初赛) 阅读程序(3)
  • Android组件化实现方案深度分析