欢迎关注方志朋的博客,回复”666“获面试宝典

前言

Spring 正如其名字,给开发者带来了春天,Spring 是为解决企业级应用开发的复杂性而设计的一款框架,其设计理念就是:简化开发。

Spring 框架中最核心思想就是:

  • IOC(控制反转): 即转移创建对象的控制权,将创建对象的控制权从开发者转移到了 Spring 框架。

  • AOP(切面编程): 将公共行为(如记录日志,权限校验等)封装到可重用的模块中,而使原本的模块内只需关注自身的个性化行为。

本文,将主要介绍 Spring 中 IOC 的依赖注入,

控制反转 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 {@Autowiredprivate Wolf1Bean wolf1Bean;//通过属性注入
}

setter 方法注入

除了通过属性注入,通过 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;

此时启动服务就会报错:

这个就是说本来应该注入一个类,但是 Spring 找到了三个,所以没法确认到底应该用哪一个。这个问题如何解决呢?

解决思路主要有以下 5 种:

通过配置文件和 @ConditionalOnProperty 注解实现

通过 @ConditionalOnProperty 注解可以结合配置文件来实现唯一注入。下面示例就是说如果配置文件中配置了 lonely.wolf=test1,那么就会将 Wolf1Bean 初始化到容器,此时因为其他实现类不满足条件,所以不会被初始化到 IOC 容器,所以就可以正常注入接口:

@Component
@ConditionalOnProperty(name = "lonely.wolf",havingValue = "test1")
public class Wolf1Bean implements IWolf{
}

当然,这种配置方式,编译器可能还是会提示有多个 Bean,但是只要我们确保每个实现类的条件不一致,就可以正常使用。

通过其他 @Condition 条件注解

除了上面的配置文件条件,还可以通过其他类似的条件注解,如:

  • @ConditionalOnBean:当存在某一个 Bean 时,初始化此类到容器。

  • @ConditionalOnClass:当存在某一个类时,初始化此类的容器。

  • @ConditionalOnMissingBean:当不存在某一个 Bean 时,初始化此类到容器。

  • @ConditionalOnMissingClass:当不存在某一个类时,初始化此类到容器。

类似这种实现方式也可以非常灵活的实现动态化配置。

不过上面介绍的这些方法似乎每次都只能固定注入一个实现类,那么如果我们就是想多个类同时注入,不同的场景可以动态切换而又不需要重启或者修改配置文件,又该如何实现呢?

通过 @Resource 注解动态获取

如果不想手动获取,我们也可以通过 @Resource 注解的形式动态指定 BeanName 来获取:

@Component
public class InterfaceInject {@Resource(name = "wolf1Bean")private IWolf iWolf;
}

如上所示则只会注入 BeanName 为 wolf1Bean 的实现类。

通过集合注入

除了指定 Bean 的方式注入,我们也可以通过集合的方式一次性注入接口的所有实现类:

@Component
public class InterfaceInject {@AutowiredList<IWolf> list;@Autowiredprivate Map<String,IWolf> map;
}

上面的两种形式都会将 IWolf 中所有的实现类注入集合中。如果使用的是 List 集合,那么我们可以取出来再通过 instanceof 关键字来判定类型;而通过 Map 集合注入的话,Spring 会将 Bean 的名称(默认类名首字母小写)作为 key 来存储,这样我们就可以在需要的时候动态获取自己想要的实现类。

@Primary 注解实现默认注入

除了上面的几种方式,我们还可以在其中某一个实现类上加上 @Primary 注解来表示当有多个 Bean 满足条件时,优先注入当前带有 @Primary 注解的 Bean:

@Component
@Primary
public class Wolf1Bean implements IWolf{
}

通过这种方式,Spring 就会默认注入 wolf1Bean,而同时我们仍然可以通过上下文手动获取其他实现类,因为其他实现类也存在容器中。

手动获取 Bean 的几种方式

在 Spring 项目中,手动获取 Bean 需要通过 ApplicationContext 对象,这时候可以通过以下 5 种方式进行获取:

直接注入

最简单的一种方法就是通过直接注入的方式获取 ApplicationContext 对象,然后就可以通过 ApplicationContext 对象获取 Bean :

@Component
public class InterfaceInject {@Autowiredprivate ApplicationContext applicationContext;//注入public Object getBean(){return applicationContext.getBean("wolf1Bean");//获取bean}
}

通过 ApplicationContextAware 接口获取

通过实现 ApplicationContextAware 接口来获取 ApplicationContext 对象,从而获取 Bean。需要注意的是,实现 ApplicationContextAware 接口的类也需要加上注解,以便交给 Spring 统一管理(这种方式也是项目中使用比较多的一种方式):

@Component
public class SpringContextUtil implements ApplicationContextAware {private static ApplicationContext applicationContext = null;@Overridepublic 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);

通过 ApplicationObjectSupport 和 WebApplicationObjectSupport 获取

这两个对象中,WebApplicationObjectSupport 继承了 ApplicationObjectSupport,所以并无实质的区别。

同样的,下面这个工具类也需要增加注解,以便交由 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);}@PostConstructpublic 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();}
}

通过 HttpServletRequest 获取

通过 HttpServletRequest 对象,再结合 Spring 自身提供的工具类 WebApplicationContextUtils 也可以获取到 ApplicationContext 对象,而 HttpServletRequest 对象可以主动获取(如下 getBean2 方法),也可以被动获取(如下 getBean1 方法):

@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();}
}

其他方式获取

当然,除了上面提到的方法,我们也可以使用最开始提到的 DL 中代码示例去手动 new 一个 ApplicationContext 对象,但是这样就意味着重新初始化了一次,所以是不建议这么去做,但是在写单元测试的时候这种方式是比较适合的。

谈谈 @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 {@Beanpublic MyElement test(Wolf1Bean wolf1Bean){return new MyElement();}
}

然而如果说我们把上面的代码稍微改一下,把参数改成一个接口,而接口又有多个实现类,这时候就会报错了:

@Component
public class InterfaceInject2 {@Beanpublic MyElement test(IWolf iWolf){//此时因为IWolf接口有多个实现类,会报错return new MyElement();}
}

 @Resource 注解又是不能用在参数中,所以这时候就需要使用 @Qualifier 注解来确认唯一实现了(比如在配置多数据源的时候就经常使用 @Qualifier 注解来实现):

@Component
public class InterfaceInject2 {@Beanpublic MyElement test(@Qualifier("wolf1Bean") IWolf iWolf){return new MyElement();}
}

总结

本文主要讲述了如何在 Spring 中使用灵活的方式来实现各种场景的注入方式,并且着重介绍了当一个接口有多个实现类时应该如何注入的问题,最后也介绍了常用几个注入注解的区别,通过本文,相信大家对如何使用 Spring 中的依赖注入会更加的熟悉。

来源:blog.csdn.net/zwx900102/article/

details/115023405

热门内容:
  • 面试官:有了 for 循环 为什么还要 forEach ?

  • 网传铁饭碗职业排名,公务员仅排第八!

  • 为什么 Java 中“1000==1000”为false,而”100==100“为true?

  • 别再写 main 方法测试了,太 Low!这才是专业 Java 测试方法!

  • 新来的同事问我 where 1=1 是什么意思

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

明天见(。・ω・。)ノ♡

最全的 Spring 依赖注入方式,你都会了吗?相关推荐

  1. Spring 依赖注入方式详解

    平常的Java开发中,程序员在某个类中需要依赖其它类的方法. 通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理. Spring提出了依赖注入的思想,即依赖类不由 ...

  2. Spring依赖注入方式

    一.依赖注入(DI)简介     依赖注入背后的基本原理是对象之间的依赖关系,可以通过以下几种方式来实现:构造器的参数.工厂方法的参数,或给由构造函数或者工厂方法创建的对象设置属性.因此,容器的工作就 ...

  3. spring四种依赖注入方式

    平常的java开发中,程序员在某个类中需要依赖其它类的方法,通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理. spring提出了依赖注入的思想,即依赖不由程序 ...

  4. Spring中IoC两种接口和两种依赖注入方式的比较

    spring是一个开源框架,是为了解决企业应用程序开发的复杂性而创建的,为J2EE应用程序开发提供集成的框架.简单来说,spring是一个轻量级的控制反转IOC和面向切面AOP的容器框架.spring ...

  5. Spring依赖注入的方式、类型、Bean的作用域、自动注入、在Spring配置文件中引入属性文件...

    1.Spring依赖注入的方式 通过set方法完成依赖注入 通过构造方法完成依赖注入 2.依赖注入的类型 基本数据类型和字符串 使用value属性 如果是指向另一个对象的引入 使用ref属性 User ...

  6. Spring 依赖注入的理解及三种注入方式

    Spring 依赖注入概念和三种注入方式(理解及应用) 什么是注入 要了解Spring的三种注入方式首先前提是得先了解一下什么是注入,相信很多人对这个概念都是模糊不清的,网上的解释是这样的: 依赖注入 ...

  7. spring依赖注入的4种方式

    Spring 依赖注入的4种方式 一.Set注入 必须要有setter方法 public class UserDao {public void test(){System.out.println(&q ...

  8. Spring依赖注入(注解方式)

    Spring依赖注入(注解方式) 在Spring中,尽管使用XML配置文件就可以实现Bean的装配工作,但如果应用中Bean的数量较多,会导致XML配置文件过于臃肿,从而给程序的维护与升级带来一定的困 ...

  9. spring依赖注入的四种方式

    平常的java开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理,spring提出了依赖注入的思想,即依赖类不由程 ...

最新文章

  1. ceph auth get_服装百科|get到羽绒服的“绒”干货,你就能做好设计
  2. 马斯克与SEC再次达成和解协议 未被罚款也未被削权
  3. vue ---- 生命周期
  4. POJ 1325 Machine Schedule 解题报告
  5. 使用cdn和npm引入的区别_带你体验 Vue2 和 Vue3 开发组件有什么区别
  6. 【三维激光扫描】第四章:点云数据处理
  7. 学校预付费云平台系统 的设计与应用
  8. 生成对抗神经网络基本思想
  9. 文献检索方法,如何确定被ei收录,如何确定被sci收录,被引用次数
  10. 中台建设利器-SPI插件机制
  11. 苹果拍照怎么显示地点和时间_2020年康复理疗师证报名时间怎么报考考试地点...
  12. ROS2极简总结-新增概念
  13. 疫情当下,选择代理加盟互联网广告项目的优势
  14. 除了苹果耳机外哪个无线耳机好?苹果蓝牙耳机平替推荐
  15. 演讲者模式投影到幕布也看到备注_ppt备注怎么用在放映时怎么可以不在投影仪上显示...
  16. monaco-editor(code编辑器插件)使用及常用配置与方法
  17. 计算机打开虚拟机,电脑就蓝屏
  18. 英雄联盟客户端美化(一)客户端载入图美化
  19. 鉴客 Android Intent 用法全面总结
  20. 显卡温度过高怎么办,解决显卡温度过高办法?

热门文章

  1. 如何读懂MEMS惯性器件的精度指标
  2. ubuntu16创建开机启动服务
  3. week6 10 后端backend server和mongoDB通信
  4. WebView通过loadDataWithBaseURL加载本地页面卡死
  5. how to write Makefile
  6. 全面解读WEB 2.0
  7. 中国电子学会青少年编程能力等级测试图形化四级编程题:排序
  8. 谢文睿:西瓜书 + 南瓜书 吃瓜系列 7. 支持向量机
  9. Jupyter Notebook 快捷键(基本)
  10. Openpose+Tensorflow 这样实现人体姿态估计 | 代码干货