正确使用@Autowired

目录

  • 一、前言
  • 二、跟着官方文档,学习正确使用@Autowired
    • 0、实验环境
    • 1、通过构造方法进行注入
      • 1.1 问题1:那万一没有这个CustomerPreferenceDao对象,会报错吗?
    • 2、通过setter方法注入
    • 3、通过方法注入(这个方法可以是任意名称,有任意参数)
    • 4、通过字段注入 【非常常见的做法,简洁】
    • 5、字段注入和构造方法注入可以混用。
    • 6、解释
    • 7、注入一组依赖(通过:字段 或 方法)
      • 7.1 通过字段
      • 7.2 通过方法
    • 8、按顺序注入一组依赖【以字段注入为例】
    • 9、还能注入Map<String, xxx>

一、前言

  • Spring框架两大核心特性:IoC容器(对开发者来说,就是希望Spring帮助注入依赖) + AOP
  • 因此,熟练使用Spring框架之一便是熟练使用依赖注入。
  • 之前介绍了“正确使用@Resource”,本文重点介绍“正确使用@Autowired”

二、跟着官方文档,学习正确使用@Autowired

0、实验环境

@ComponentScan
public class Application {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Application.class);Arrays.stream(applicationContext.getBeanDefinitionNames()).forEach(System.out::println);System.out.println("---------------------------------------------------------------");MovieRecommender movieRecommender = applicationContext.getBean(MovieRecommender.class);movieRecommender.sayHello();}
}
public class CustomerPreferenceDao {public void sayHello() {System.out.println("Hello, I am " + this.getClass().getSimpleName());}
}@Configuration
public class LearnAutowiredConfig {@Beanpublic CustomerPreferenceDao customerPreferenceDao() {return new CustomerPreferenceDao();}
}

1、通过构造方法进行注入

@Component
public class MovieRecommender {private final CustomerPreferenceDao customerPreferenceDao;@Autowiredpublic MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {this.customerPreferenceDao = customerPreferenceDao;}public void sayHello() {customerPreferenceDao.sayHello();System.out.println("Hello, I am " + this.getClass().getSimpleName());}
}
  • 从Spring 4.3开始,如果组件只有一个构造方法,那么没必要给这个构造方法带上@Autowired。
    • 道理也很简单,Spring为MovieRecommender类创建对象时,少不了调用构造方法。它发现需要一个CustomerPreferenceDao对象,然后在IoC容器里面确实有这个对象,那就自动帮咱注入了。

1.1 问题1:那万一没有这个CustomerPreferenceDao对象,会报错吗?

  • 会报错!

个人觉得,注解是一个非常好的标识,即使能省略,也别省略。

  • 为啥会报错呢?
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {/*** Declares whether the annotated dependency is required.* <p>Defaults to {@code true}.*/boolean required() default true;}
  • 表面上省略了@Autowired,但实际上仍然和带上@Autowired的效果一致。默认要求必须注入依赖,如果待注入的依赖不存在,则报错。
  • 那我改成@Autowired(required = false)可以吗?–> 依然报错
Inconsistent constructor declaration on bean with name 'movieRecommender': single autowire-marked constructor flagged as optional - this constructor is effectively required since there is no default constructor to fall back to: public com.forrest.learnspring.autowired.example1.bean.MovieRecommender(com.forrest.learnspring.autowired.example1.dao.CustomerPreferenceDao)
  • 大意:Spring认为提供的构造方法不符合需求(因为找不到可用的CustomerPreferenceDao的bean),然后发现这个构造方法是可选的,那我就选其他的吧。结果没其他可选了,那只能报错了。
  • 补一个空参构造方法就行:
public MovieRecommender() {this.customerPreferenceDao = null;
}@Autowired(required = false)
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {this.customerPreferenceDao = customerPreferenceDao;
}
  • 如果bean具有多个构造方法,那么不能省略@Autowired,否则Spring在创建bean的时候,不知道用哪个构造方法。

2、通过setter方法注入

@Component
public class MovieRecommender {private CustomerPreferenceDao customerPreferenceDao;@Autowiredpublic void setCustomerPreferenceDao(CustomerPreferenceDao customerPreferenceDao) {this.customerPreferenceDao = customerPreferenceDao;}...
}

3、通过方法注入(这个方法可以是任意名称,有任意参数)

@Component
public class MovieRecommender {private CustomerPreferenceDao customerPreferenceDao;@Autowiredpublic void prepare(CustomerPreferenceDao customerPreferenceDao) {this.customerPreferenceDao = customerPreferenceDao;}public void sayHello() {customerPreferenceDao.sayHello();System.out.println("Hello, I am " + this.getClass().getSimpleName());}
}
  • 这个prepare方法会被Spring调用,咱不用管。咱需要注意的是:
    • 入参必须是能够在IoC容器中找得到的bean。(String s, CustomerPreferenceDao customerPreferenceDao)这是不可以的。因此,IoC容器中,没有s这个bean。
    • 对访问修饰符没有要求。
      • 我猜测是,Spring创建好MovieRecommender的bean后,由这个bean去调用自己的方法。因此,哪怕方法是private的都没问题。

4、通过字段注入 【非常常见的做法,简洁】

@Component
public class MovieRecommender {@Autowiredprivate CustomerPreferenceDao customerPreferenceDao;...
}
  • 但IDEA不建议字段注入。【但写的代码少了,一般都采用这种,哈哈】

5、字段注入和构造方法注入可以混用。

@Component
public class MovieRecommender {@Autowiredprivate CustomerPreferenceDao customerPreferenceDao;private final UserProcessor userProcessor;@Autowiredpublic MovieRecommender(UserProcessor userProcessor) {this.userProcessor = userProcessor;}...
}

6、解释

官方文档:
Make sure that your target components (for example, MovieCatalog or CustomerPreferenceDao) are consistently declared by the type that you use for your @Autowired-annotated injection points. Otherwise, injection may fail due to a “no type match found” error at runtime.
For XML-defined beans or component classes found via classpath scanning, the container usually knows the concrete type up front. However, for @Bean factory methods, you need to make sure that the declared return type is sufficiently expressive. For components that implement several interfaces or for components potentially referred to by their implementation type, consider declaring the most specific return type on your factory method (at least as specific as required by the injection points referring to your bean).

咋一看,这都是啥啊… 但还是要理解下啊

  • 解释:
    • 从上面的例子可知,不管@Autowired用在构造方法、setter方法、还是字段上,我们都能知道需要的依赖是什么类型的。
    • Spring就会根据这个类型,去IoC容器中找:
      • 因此,我们要保证类型别写错了,否则Spring会找不到。Make sure that your target components are consistently declared by the type (类型兼容)
  • 正例(可以向上转型):
@Controller
public class UserController {@Autowiredprivate UserService userService;...
}
@Configuration
public class LearnAutowiredConfig {// 很显然,这么写相当于:UserService userServie = new UserServiceImpl();// UserController的userService = userServie = new UserServiceImpl(); 也是成立的。@Beanpublic UserService userService() {return new UserServiceImpl();}
}// 同理这么写也行
@Configuration
public class LearnAutowiredConfig {@Beanpublic UserServiceImpl userService() {return new UserServiceImpl();}
}
  • 反例(不能向下转型):
@Controller
public class UserController {@Autowiredprivate UserServiceImpl userService;...
}@Configuration
public class LearnAutowiredConfig {// UserService userService = new UserServiceImpl();// UserController的userService = userService; 这是不行的。属于向下转型。@Beanpublic UserService userService() {return new UserServiceImpl();}
}
  • 报错:No qualifying bean of type ‘com.forrest.learnspring.autowired.example2.service.impl.UserServiceImpl’ available
  • 向下转型需要强转,否则会报错,等同于:
    在这里插入图片描述
  • 推荐】注入依赖,依赖的类型尽可能抽象(有接口用接口!)。

7、注入一组依赖(通过:字段 或 方法)

7.1 通过字段

@Controller
public class UserController {@Autowiredprivate List<UserService> userServices;...
}@Configuration
public class LearnAutowiredConfig {@Beanpublic UserService userService() {return new UserServiceImpl();}@Beanpublic UserService userProxyService() {return new UserProxyServiceImpl();}
}

7.2 通过方法

@Controller
public class UserController {private List<UserService> userServices;@Autowiredpublic void prepare(List<UserService> userServices) {this.userServices = userServices;}...
}

8、按顺序注入一组依赖【以字段注入为例】

  • 办法:
  • (1) implement the org.springframework.core.Ordered interface
  • (2) @Order
  • (3) @Priority
  • 以@Order为例子
@Configuration
public class LearnAutowiredConfig {@Bean@Order(2)public UserService userService() {return new UserServiceImpl();}@Bean@Order(1)public UserService userProxyService() {return new UserProxyServiceImpl();}
}/*
1 : UserProxyServiceImpl
2 : UserServiceImpl
*/
@Order(1)
@Service
public class UserProxyServiceImpl implements UserService {...
}@Order(2)
@Service
public class UserServiceImpl implements UserService {...
}

9、还能注入Map<String, xxx>

  • 以字段注入为例:
@Controller
public class UserController {@Autowiredprivate Map<String, UserService> userServiceMap;...
}/*
userProxyServiceImpl: UserProxyServiceImpl
userServiceImpl: UserServiceImpl
*/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/598217.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Anritsu MS9740B与MS9740A 光谱分析仪 的区别?

MS9740B与MS9740A的主要区别在于测量处理时间的缩短和对高速信号处理技术的应用。MS9740B在保持原有功能和性能的同时&#xff0c;将测量处理时间缩短了一半。这一点通过提高生产效率和减少测量及检查时间来实现&#xff0c;从而提高了光有源设备制造商的生产力。此外&#xff…

可以监控员工电脑的软件有哪些

“可以监控员工电脑的软件是什么&#xff1f;” 那必然是“电脑监控软件”了&#xff01;&#xff01;&#xff01; 电脑已成为现代企业中不可或缺的工具。然而&#xff0c;电脑使用不当或滥用可能会带来数据泄露、工作效率低下等问题&#xff0c;严重影响企业的运营和声誉。…

vue实现验证码验证登录

先看效果&#xff1a; 代码如下&#xff1a; <template><div class"container"><div style"width: 400px; padding: 30px; background-color: white; border-radius: 5px;"><div style"text-align: center; font-size: 20px; m…

数据结构面试题报错调试方法记录

栈和队列报错调试 1.用栈实现队列 232. 用栈实现队列 - 力扣&#xff08;LeetCode&#xff09; 此题解题思路如下&#xff1a; 先将数据放在pushst栈里面&#xff0c;popst栈为空再把pushst栈里面的数据放进popst栈里面去&#xff0c;不为空则不执行。不为空时候直接拿取栈…

测试框架pytest学习与实践

pytest是一个专业的测试框架&#xff0c;可以帮助我们对python项目进行测试&#xff0c;提高测试的效率。 pytest官网手册&#xff1a;pytest: helps you write better programs — pytest documentation 中文手册&#xff1a;Pytest 教程 入门学习 安装pytest pip install…

机器人开启私聊配置自定义接口的方式

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 今天给大家介绍一下&#xff0c;如何在机器人中开启私聊回复。 前提条件&#xff1a;机器人已经启动好了&#xff0c;且功能也都可以正常使用&#xff0c;如果没有启动&#xff0c;可以联…

《QT实用小工具·十八》高亮发光按钮控件

1、概述 源码放在文章末尾 该项目实现了高亮发光按钮控件 可设置文本&#xff0c;居中显示。可设置文本颜色。可设置外边框渐变颜色。可设置里边框渐变颜色。可设置背景色。可直接调用内置的设置 绿色、红色、黄色、黑色、蓝色 等公有槽函数。可设置是否在容器中可移动&#…

生成式AI产品汇总网站

在 AIHungry.com&#xff0c;我们处于人工智能发展的最前沿&#xff0c;为您带来最新的人工智能。深入了解我们对最佳人工智能工具的全面评论&#xff0c;并随时了解我们对人工智能新闻的报道。 人工智能新闻&#xff1a;随时了解人工智能领域的最新发展和突破。工具评论&…

国内估值最高的大模型公司,“国产大模型五虎”系列之——智谱AI

前言&#xff1a; 上次我们介绍了同为“国产大模型五虎”的MiniMax&#xff0c;今天就继续来盘点一下国内估值最高的大模型企业智谱AI&#xff0c;同时也是五虎中的另外一虎。 “国产大模型五虎”指的是由阿里投资的五家大模型独角兽&#xff1a;智谱 AI、百川智能、月之暗面…

人工智能大模型+智能算力,企商在线以新质生产力赋能数字化转型

2024 年3月28 日&#xff0c;由中国互联网协会主办、中国信通院泰尔终端实验室特别支持的 2024 高质量数字化转型创新发展大会暨铸基计划年度会议在京召开。作为新质生产力代表性企业、数算融合领导企业&#xff0c;企商在线受邀出席大会主论坛圆桌对话&#xff0c;与行业专家共…

C++初级----string类(STL)

1、标准库中的string 1.1、sring介绍 字符串是表示字符序列的类&#xff0c;标准的字符串类提供了对此类对象的支&#xff0c;其接口类似于标准字符容器的接口&#xff0c;但是添加了专门用于操作的单字节字符字符串的设计特性。 string类是使用char&#xff0c;即作为他的字符…

【蓝桥杯嵌入式】12届程序题刷题记录及反思

一、题目解析 按键短按LCD显示两个界面LED指示灯PWM脉冲输出 二、led控制 控制两个led灯&#xff0c;两种状态 //led void led_set(uint8_t led_dis) {HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOC,led_dis << 8,GPIO_PIN_RESET);HAL…