缥帙各舒散,前后互相逾。这篇文章主要讲述最全的Spring依赖注入方式,你都会了吗?相关的知识,希望能为你提供帮助。
前言
文章图片
Spring 正如其名字,给开发者带来了春天,Spring 是为解决企业级应用开发的复杂性而设计的一款框架,其设计理念就是:简化开发。
Spring 框架中最核心思想就是:
- IOC(控制反转):即转移创建对象的控制权,将创建对象的控制权从开发者转移到了 Spring 框架。
- AOP(切面编程):将公共行为(如记录日志,权限校验等)封装到可重用的模块中,而使原本的模块内只需关注自身的个性化行为。
控制反转 IOC就 IOC 本身而言,其并不是什么新技术,只是一种思想理念。IOC 的核心就是原先创建一个对象,我们需要自己直接通过 new 来创建,而 IOC 就相当于有人帮们创建好了对象,需要使用的时候直接去拿就行,IOC 主要有两种实现方式:
- DL(Dependency Lookup):依赖查找。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/application-context.xml");
Object bean = applicationContext.getBean("object");
- DI(Dependency Inject):依赖注入。
依赖注入 DI通过 xml 的注入方式我们不做讨论,在这里主要讨论基于注解的注入方式,基于注解的常规注入方式通常有三种:
- 基于属性注入
- 基于 setter 方法注入
- 基于构造器注入
接下来就让我们分别介绍一下三种常规的注入方式。
- 属性注入
@Service
public class UserService {
@Autowired
private Wolf1Bean wolf1Bean; //通过属性注入
}
- setter 方法注入
@Service
public class UserService {
private Wolf3Bean wolf3Bean;
@Autowired//通过setter方法实现注入
public void setWolf3Bean(Wolf3Bean wolf3Bean) {
this.wolf3Bean = wolf3Bean;
}
}
- 构造器注入
@Service
public class UserService {
private Wolf2Bean wolf2Bean;
@Autowired //通过构造器注入
public UserService(Wolf2Bean wolf2Bean) {
this.wolf2Bean = wolf2Bean;
}
}
接口注入
在上面的三种常规注入方式中,假如我们想要注入一个接口,而当前接口又有多个实现类,那么这时候就会报错,因为 Spring 无法知道到底应该注入哪一个实现类。比如我们上面的三个类全部实现同一个接口 IWolf,那么这时候直接使用常规的,不带任何注解元数据的注入方式来注入接口 IWolf。
@Autowired
private IWolf iWolf;
此时启动服务就会报错:
文章图片
image.png
这个就是说本来应该注入一个类,但是 ?
?Spring?
?
找到了三个,所以没法确认到底应该用哪一个。这个问题如何解决呢?解决思路主要有以下 ?
?5?
?
种:- 通过配置文件和 @ConditionalOnProperty 注解实现
@Component
@ConditionalOnProperty(name = "lonely.wolf",havingValue = "https://www.songbingjia.com/android/test1")
public class Wolf1Bean implements IWolf{
}
当然,这种配置方式,编译器可能还是会提示有多个 Bean,但是只要我们确保每个实现类的条件不一致,就可以正常使用。
- 通过其他 @Condition 条件注解
- @ConditionalOnBean:当存在某一个 Bean 时,初始化此类到容器。
- @ConditionalOnClass:当存在某一个类时,初始化此类的容器。
- @ConditionalOnMissingBean:当不存在某一个 Bean 时,初始化此类到容器。
- @ConditionalOnMissingClass:当不存在某一个类时,初始化此类到容器。
- …
不过上面介绍的这些方法似乎每次都只能固定注入一个实现类,那么如果我们就是想多个类同时注入,不同的场景可以动态切换而又不需要重启或者修改配置文件,又该如何实现呢?
- 通过 @Resource 注解动态获取
@Component
public class InterfaceInject {
@Resource(name = "wolf1Bean")
private IWolf iWolf;
}
如上所示则只会注入 BeanName 为 wolf1Bean 的实现类。
- 通过集合注入
@Component
public class InterfaceInject {
@Autowired
List< IWolf> list;
@Autowired
private Map< String,IWolf> map;
}
上面的两种形式都会将 IWolf 中所有的实现类注入集合中。如果使用的是 List 集合,那么我们可以取出来再通过 instanceof 关键字来判定类型;而通过 Map 集合注入的话,Spring 会将 Bean 的名称(默认类名首字母小写)作为 key 来存储,这样我们就可以在需要的时候动态获取自己想要的实现类。
- @Primary 注解实现默认注入
@Component
@Primary
public class Wolf1Bean implements IWolf{
}
通过这种方式,Spring 就会默认注入 wolf1Bean,而同时我们仍然可以通过上下文手动获取其他实现类,因为其他实现类也存在容器中。
- 手动获取 Bean 的几种方式
(1)直接注入
最简单的一种方法就是通过直接注入的方式获取 ApplicationContext 对象,然后就可以通过 ApplicationContext 对象获取 Bean :
@Component
public class InterfaceInject {
@Autowired
private ApplicationContext applicationContext; //注入
public Object getBean(){
return applicationContext.getBean("wolf1Bean"); //获取bean
}
}
- (2)通过 ApplicationContextAware 接口获取
@Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 通过名称获取bean
*/
public static < T> T getBeanByName(String beanName){
return (T) applicationContext.getBean(beanName);
}
/**
* 通过类型获取bean
*/
public static < T> T getBeanByType(Class< T> clazz){
return (T) applicationContext.getBean(clazz);
}
}
封装之后,我们就可以直接调用对应的方法获取 Bean 了:
Wolf2Bean wolf2Bean = SpringContextUtil.getBeanByName("wolf2Bean");
Wolf3Bean wolf3Bean = SpringContextUtil.getBeanByType(Wolf3Bean.class);
- (3)通过 ApplicationObjectSupport 和 WebApplicationObjectSupport 获取
同样的,下面这个工具类也需要增加注解,以便交由 Spring 进行统一管理:
@Component
public class SpringUtil extends /*WebApplicationObjectSupport*/ ApplicationObjectSupport {
private static ApplicationContext applicationContext = null;
public static < T> T getBean(String beanName){
return (T) applicationContext.getBean(beanName);
}
@PostConstruct
public void init(){
applicationContext = super.getApplicationContext();
}
}
有了工具类,在方法中就可以直接调用了:
@RestController
@RequestMapping("/hello")
@Qualifier
public class HelloController {
@GetMapping("/bean3")
public Object getBean3(){
Wolf1Bean wolf1Bean = SpringUtil.getBean("wolf1Bean");
return wolf1Bean.toString();
}
}
- (4)通过 HttpServletRequest 获取
@RestController
@RequestMapping("/hello")
@Qualifier
public class HelloController {
@GetMapping("/bean1")
public Object getBean1(HttpServletRequest request){
//直接通过方法中的HttpServletRequest对象
ApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
Wolf1Bean wolf1Bean = (Wolf1Bean)applicationContext.getBean("wolf1Bean");
return wolf1Bean.toString();
}
@GetMapping("/bean2")
public Object getBean2(){
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); //手动获取request对象
ApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
Wolf2Bean wolf2Bean = (Wolf2Bean)applicationContext.getBean("wolf2Bean");
return wolf2Bean.toString();
}
}
- (5)其他方式获取
谈谈 @Autowrite 和 @Resource 以及 @Qualifier 注解的区别
上面我们看到了,注入一个 Bean 可以通过 @Autowrite,也可以通过 @Resource 注解来注入,这两个注解有什么区别呢?
- @Autowrite:通过类型去注入,可以用于构造器和参数注入。当我们注入接口时,其所有的实现类都属于同一个类型,所以就没办法知道选择哪一个实现类来注入。
- @Resource:默认通过名字注入,不能用于构造器和参数注入。如果通过名字找不到唯一的 Bean,则会
通过类型去查找。如下可以通过指定 name 或者 type 来确定唯一的实现:
@Resource(name = "wolf2Bean",type = Wolf2Bean.class)
private IWolf iWolf;
而 @Qualifier 注解是用来标识合格者,当 @Autowrite 和 @Qualifier 一起使用时,就相当于是通过名字来确定唯一:
@Qualifier("wolf1Bean")
@Autowired
private IWolf iWolf;
那可能有人就会说,我直接用 @Resource 就好了,何必用两个注解结合那么麻烦,这么一说似乎显得 @Qualifier 注解有点多余?
@Qualifier 注解是多余的吗
我们先看下面声明 Bean 的场景,这里通过一个方法来声明一个 Bean (MyElement),而且方法中的参数又有 Wolf1Bean 对象,那么这时候 Spring 会帮我们自动注入 Wolf1Bean:
@Component
public class InterfaceInject2 {
@Bean
public MyElement test(Wolf1Bean wolf1Bean){
return new MyElement();
}
}
然而如果说我们把上面的代码稍微改一下,把参数改成一个接口,而接口又有多个实现类,这时候就会报错了:
@Component
public class InterfaceInject2 {
@Bean
public MyElement test(IWolf iWolf){//此时因为IWolf接口有多个实现类,会报错
return new MyElement();
}
}
而 @Resource 注解又是不能用在参数中,所以这时候就需要使用 @Qualifier 注解来确认唯一实现了(比如在配置多数据源的时候就经常使用 @Qualifier 注解来实现):
@Component
public class InterfaceInject2 {
@Bean
public MyElement test(@Qualifier("wolf1Bean") IWolf iWolf){
return new MyElement();
}
}
【最全的Spring依赖注入方式,你都会了吗()】
推荐阅读
- 从HarmonyOS过渡到OpenHarmony应用开发指南&埋坑
- M-SQL(超强的多任务表示学习方法)
- 深入剖析RocketMQ源码-NameServer
- win7系统玩cf卡、FPS不高的处理措施
- win7系统怎样读取u盘,u盘无法打开的处理办法
- 准备安装win10,win7系统怎样清盘?
- 如何更改win7系统睡眠时间?
- 设置低也不是问题,怎样使win7系统变快?
- win7系统怎样删除脱机文件 完全关闭脱机文件服务的技巧