一、注解驱动IoC

xml驱动的IoC容器使用的是ClassPathXmlApplicationContext读取xml内bean信息

注解驱动的IoC容器使用的是AnnotationConfigApplicationContext读取Java类中的bean信息

1. AnnotationConfigApplicationContext 的注册使用

相比于xml文件作为驱动, 注解驱动需要指明配置类 一个配置类可以理解为"相当于"一个xml  配置类只需要在类上标注注解 @Configuration

@Configuration
public class DemoConfiguration {
}

在xml中声明bean的方式

<bean id="person" class="com.huodd.bean.Person"></bean>

在配置类中使用的是@Bean注解

@Bean
public Person person() {return new Person();
}

说明:  向IoC容器注册一个类型为Persion,id为Person的Bean

方法名表示的是bean的id 返回值表示的是注册的bean的类型

@Bean注解也可以显示的声明bean的id  如 @Bean("person1")

2. 注解IoC容器的初始化

public class AnnotationConfigApplication {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(DemoConfiguration.class);Person person = ctx.getBean(Person.class);System.out.println(person);}
}

运行后Person控制台打印结果

com.huodd.bean.Person@55536d9e

3. 组件的注册和扫描

上述初始化时  我们在使用AnnotationConfigApplicationContext时传递了参数 Class<?>... componentClasses

翻看AnnotationConfigApplicationContext的构造方法可以发现还可以传递参数的参数类型还有 String... basePackages

这里就涉及到组件的注册和扫描

这里可以思考一个问题, 如果我们要注册的组件特别多, 那进行编写这些@Bean的时候代码工作量也会特别多,这时候该如何解决呢?

Spring 给我们提供了几个注解,可以帮助我们快速注册需要的组件, 这些注解被称为模式注解(stereotype annotations)

@Component

@Component可以说是所有组件注册的根源 在类上标注 @Component 代表该类被注册到IoC容器中作为一个Bean

@Component
public class Person {
}

如果未指定 Bean 的名称 默认规则是 "类名称首字母小写"  上面的bean名称默认会是 person

如果要自定义bean的名称 可以在@Component声明value的值即可  如

@Component("person1")
public class Person {
}

在xml中相当于

<bean id="person1" class="com.huodd.bean.Person"/>

@ComponentScan

这个时候 如果我们直接运行启动类 获取Person的bean对象,会报错NoSuchBeanDefinitionException 这是为什么呢?

因为我们只是声明了组件,而后直接启动了IoC容器,这样容器是感知不到有@Component存在的,

解决方案1:

我们需要在写配置类时再额外标注一个新的注解@ComponentScan

目的是告诉IoC容器 我要扫描哪个包下面的带有@Component注解的类

@Configuration
@ComponentScan("com.huodd.bean")
public class DemoComponentScanConfiguration {
}

注: 如果不指定扫描路径, 则默认扫描本类所在包及所有子包下带有@Component的组件

启动类代码如下:

public class AnnotationConfigApplication {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(DemoComponentScanConfiguration.class);Person person = ctx.getBean(Person.class);System.out.println(person);}
}

解决方案2:

这里也可以不写@ComponentScan 而直接在AnnotationConfigApplicationContext方法参数内传入String类型的包扫描路径 代码如下

public class AnnotationConfigApplication {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext("com.huodd.bean");Person person = ctx.getBean(Person.class);System.out.println(person);}
}

PS: 组件扫描并非是注解驱动IoC所特有的, 其实在xml驱动的IoC模式下 同样可以启用组件扫描, 只需要在xml中声明一个标签即可

<context:component-scan base-package="com.huodd.bean"/>

这里需要注意下: 如需要扫描多个路径,需要写多个标签 也就是 一个标签只能声明一个根包

组件注册的补充

SpringFramework 提供了在进行Web开发三层架构时的扩展注解: 分别为 @Controller@Service@Repository 小伙伴有没有很熟悉?

分别代表 表现层业务层持久层  这三个注解的作用与 @Component完全一样  扒开源码我们可以看到  底层在这三个注解类上又添加了 @Component

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
}

这样 我们在进行符合三层架构的开发时 对于相应的如 ServiceImpl等 就可以直接标注 @Service 等注解了

@Configuration

@Configuration 底层也有标注@Component

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration { ... }

由此可以说明,配置类不是向我们所想的那样,只是单纯的做一个配置而已, 它也会被视为 bean,也被注册到IoC容器里面

4. 注解驱动与xml驱动互相引用

4.1 xml引用注解

需开启注解配置 再注册相应配置类

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!-- 开启注解配置 --><context:annotation-config /><!-- 注册配置类 --><bean class="com.huodd.config.AnnotationConfigConfiguration"/>
</beans>

4.2 注解引用XMl

需在配置类上标注 @ImportResource 并声明配置文件的路径

@Configuration
@ImportResource("classpath:annotation/demo-beans.xml")
public class ImportXmlAnnotationConfiguration {
}

二、IoC的依赖注入

1.Setter属性注入

创建对象 将属性值set进去 之后返回对象

@Bean
public Person person() {Person person = new Person();person.setId(1);person.setName("PoXing");person.setAge(18);return person;
}

xml中的setter注入

<bean id="person" class="com.huodd.bean.Person"><property name="id" value="1"/><property name="name" value="PoXing"/><property name="age" value="18"/>
</bean>

2. 构造器注入

使用构造器注入,需要在bean本身添加有参构造方法, 如在Person中添加有参构造方法如下

public Person(Integer id, String name, Integer age) {this.id = id;this.name = name;this.age = age;
}

注解驱动中,我们创建bean的时候注入属性时  就需要同时指定参数值

@Bean
public Person person() {return new Person(1, "PoXing", 18);
}

xml驱动中如下

<bean id="person" class="com.huodd.bean.Person"><!-- index: 表示构造器的参数索引value: 表示对应的参数值--><constructor-arg index="0" value="1"/><constructor-arg index="1" value="PoXing"/><constructor-arg index="2" value="18"/>
</bean>

3. 注解式属性注入

这里先说明一下,为何会有注解式属性值注入. 细心的小伙伴可能会发现 上面我们谈到的 Setter属性注入构造器注入 好像在只能是在使用 @Bean注解的时候时候使用, 但是 如果是通过标注 @Component注解的组件呢(像前面我们的Person类中标注了@Component注解),怎么给它设定属性值, 该节主要就是说一下这部分

@Component 下的属性注入

这里我们使用Dog类做为演示(这里我悄悄的添加了@Component注解 自己尝试的小伙伴要注意哦 否则会报错的)

@Component
public class Dog {private Integer id;private String name;private Integer age;... 省略 Getter、Setter... 省略 toString}

这里要实现注解式属性注入,可以直接在要注入的字段上标注 @Value注解 如

@Value("1")
private Integer id;@Value("wangcai")
private String name;@Value("3")
private Integer age;

启动类代码如下

public class DiApplication {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext("com.huodd.bean");Dog dog = ctx.getBean(Dog.class);System.out.println(dog);}
}

控制台打印结果

Dog{id=1, name='wangcai', age=3}

外部配置文件(@PropertySource)

这里主要是解决上面的@Value中注入 我们把属性值直接固定写死了,如果要修改 还要去Java代码中去修改,很不符合开发规范,

SpringFramework为我们扩展了新的注解@PropertySource 主要用来导入外部配置文件

  1. 这里我们创建一个 dog.properties

dog.id=1
dog.name=wangcai
dog.age=3
  1. 引入配置文件

@PropertySource("classpath:di/dog.properties")
@ComponentScan("com.huodd.bean")
@Configuration
public class DemoComponentScanConfiguration {
}
  1. Dog类中属性注入 这里@Value需要配合占位符 来获取properties配置文件中的内容

@Value("${dog.id}")
private Integer id;@Value("${dog.name}")
private String name;@Value("${dog.age}")
private Integer age;
  1. 修改一下启动类

public class DiApplication {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(DemoComponentScanConfiguration.class);Dog dog = ctx.getBean(Dog.class);System.out.println(dog);}
}

控制台打印结果如下

Dog{id=1, name='wangcai', age=3}

此时配置文件的属性已经注入成功

4.自动注入

在xml模式中有ref属性 可以将一个bean注入到另外一个bean中, 注解模式中也同样可以

@Autowired

给Dog的bean中注入 Person的Bean (即  给dog指定它的主人)

方法1 → 在属性上标注

@Component
public class Dog {// ......@Autowiredprivate Person person;
}

方法2 → 使用构造器注入方式

@Component
public class Dog {// ......private Person person;@Autowiredpublic Dog(Person person) {this.person = person;}
}

方法3 → 使用setter方法注入

@Component
public class Dog {// ......private Person person;@Autowiredpublic void setPerson(Person person) {this.person = person;}
}

JSR250规范下的@Resource

@Resource也是用来属性注入的注解

它与@Autowired的区别是:

  • @Autowired是按照类型注入

  • @Resource是按照属性名(也就是bean的名称)注入

@Resource 注解相当于标注 @Autowired@Qualifier

@Qualifier这里简要说明下,为指定bean的名称而存在,如果存在多个相同的bean,而bean的名称不同,我们可以使用@Autowired 配置 @Qualifier注解

如: 下面表示该Dog类注入的主人Bean是名称为 xiaowang的,  而当前容器内可能存在多个 主人bean对象  比如 xiaoli、xiaoming ....

@Component
public class Dog {// ......@Autowired@Qualifier("xiaowang")private Person person;
}

下面如果使用@Resource 可以更方便些 代码如下

@Component
public class Dog {// ......@Resource(name="xiaowang")private Person person;
}

JSR330规范下的@Inject

@Inject注解也是按照类型注入,与@Autowire的策略一样, 不过如要使用@Inject 需要额外的导入依赖

<!-- jsr330 -->
<dependency><groupId>javax.inject</groupId><artifactId>javax.inject</artifactId><version>1</version>
</dependency>

后面的使用方法就与SpringFramework 原生的 @Autowire + @Qualifier 相同了

@Component
public class Dog {@Inject // 等同于@Autowired@Named("xiaowang") // 等同于@Qualifierprivate Person person;

它与@Autowired的区别是:

  • @Autowired所在的包为 org.springframework.beans.factory.annotation.Autowired 即为 SpringFramework 提供的

  • @Inject所在的包为 javax.inject.Inject 属于JSR的规范 也就是说如果不使用SpringFramework时可以使用该注解

5. 复杂类型注入

Array注入

<property name="names"><array><value>PoXing</value><value>LaoWang</value></array>
</property>

List注入

<property name="tels"><list><value>13000000000</value><value>13000000001</value></list>
</property>

Set注入-

<!-- 已经提前声明好的Dog -->
<bean id="wangcai" class="com.huodd.bean.ext.Dog"/>
---<property name="dogs"><set><bean class="com.huodd.bean.Dog"/><ref bean="wangcai"/></set>
</property>

Map注入

<property name="homesMap"><map><entry key="1" value="main"><ref bean="myHome1" /></entry><entry key="2" value="other"><ref bean="myHome2" /></entry></map>
</property>

Properties注入

<property name="props"><props><prop key="sex">男</prop><prop key="age">18</prop></props>
</property>

面试题

1.@Autowired注入原理是什么?

  1. 先拿属性对应的类型,去IoC容器中找相应的Bean

  2. 如果没有找到 直接抛出NoUniqueBeanDefinitionException异常

  3. 如果找到一个 直接返回

  4. 如果找到多个相同类型的bean 再拿属性名去与这多个bean的id进行对比

  5. 如果有多个或者没有 则会抛出NoUniqueBeanDefinitionException异常

  6. 如果只有一个 直接返回

2.依赖注入的方式有哪些,都有什么区别

注入方式 被注入对象是否可改变 是否依赖IOC框架的API 使用场景
构造器注入 不可变 不可变的固定注入
参数注入 不可变 注解配置类中 @Bean方法注册 bean
属性注入(注解式属性注入) 不可变 是(只能通过标注注解来侵入式注入) 通常用于不可变的固定注入
setter注入 可变 可选属性的注入

3.自动注入的注解对比

注解 注入方式 是否支持@Primary 来源 Bean不存在时处理
@Autowired 根据类型注入 SpringFramework原生注解 可指定required=false来避免注入失败
@Resource 根据名称注入 JSR250规范 容器中不存在指定Bean会抛出异常
@Inject 根据类型注入 JSR330规范 ( 需要导jar包 ) 容器中不存在指定Bean会抛出异常

@Qualifier :如果被标注的成员/方法在根据类型注入时发现有多个相同类型的 Bean ,则会根据该注解声明的 name 寻找特定的 bean

@Primary :如果有多个相同类型的 Bean 同时注册到 IOC 容器中,使用 “根据类型注入” 的注解时会注入标注 @Primary 注解的 bean 即默认策略

4.使用依赖注入有什么优缺点

  • 依赖注入作为 IOC 的实现方式之一,目的就是解耦,我们不需要直接去 new 那些依赖的类对象就可以直接从容器中去取来使用, 如果组件存在多级依赖,依赖注入可以将这些依赖的关系简化。

  • 依赖对象的可配置:通过 xml 或者注解声明,可以指定和调整组件注入的对象,借助 Java 的多态特性,可以不需要大批量的修改就完成依赖注入的对象替换

一码不扫,

何以扫天下?

长文干货丨一文搞懂IoC的依赖注入相关推荐

  1. 长文干货! 一文搞懂IoC的依赖注入

    有梦想,有情怀,有温度,干货满满: 微信搜索[ PoXing ] 关注一下我吧. 一.注解驱动IoC xml驱动的IoC容器使用的是ClassPathXmlApplicationContext读取xm ...

  2. 干货丨一文看懂什么是知识图谱!

    来源: THU数据派 概要:在维基百科的官方词条中:知识图谱是Google用于增强其搜索引擎功能的知识库. 什么是知识图谱? 在维基百科的官方词条中:知识图谱是Google用于增强其搜索引擎功能的知识 ...

  3. 干货丨一文读懂深度学习(附学习资源,据说点赞2W+)

    文章来源:新智元 人工智能(AI)和机器学习(ML)都属于目前最热门的话题. 在日常生活中,AI这个术语我们随处可见.你或许会从立志高远的开发者那里听说她(他)们想要学习AI.你又或许会从运营者那里听 ...

  4. 干货丨一文看懂人工智能、机器学习和深度学习的区别与联系

    人工智能的浪潮正在席卷全球,诸多词汇时刻萦绕在我们耳边:人工智能(Artificial Intelligence).机器学习(Machine Learning).深度学习(Deep Learning) ...

  5. 干货丨一文看懂什么是“自然语言处理”

    作者:武汉飔拓科技 一.什么是自然语言处理 简单地说,自然语言处理(NaturalLanguage Processing,简称NLP)就是用计算机来处理.理解以及运用人类语言(如中文.英文等),它属于 ...

  6. 干货丨一文读懂深度学习与机器学习的差异

    作者:oschina 如果你经常想让自己弄清楚机器学习和深度学习的区别,阅读该文章,我将用通俗易懂的语言为你介绍他们之间的差别.机器学习和深度学习变得越来越火.突然之间,不管是了解的还是不了解的,所有 ...

  7. 干货丨一文看懂生成对抗网络:从架构到训练技巧

    文章来源:机器之心 论文地址:https://arxiv.org/pdf/1710.07035.pdf 生成对抗网络(GAN)提供了一种不需要大量标注训练数据就能学习深度表征的方式.它们通过反向传播算 ...

  8. 干货!一文搞懂无状态服务

    来源 | 机智的程序员小熊 责编 | 寇雪芹 头图 | 下载于视觉中国 事故的发生是量的积累的结果,任何事情都没有表面看起来那么简单,在软件运行的过程中,随着用户量的增加,不考虑高可用,迟早有一天会发 ...

  9. 都2021年了,再不学ES6你就out了 —— 一文搞懂ES6

    JS干货分享 -- 一文搞懂ES6 导语:ES6是什么?用来做什么? 1. let 与 const 2. 解构赋值 3. 模板字符串 4. ES6 函数(升级后更爽) 5. Class类 6. Map ...

最新文章

  1. 再见,Kafka!再见,RocketMQ!
  2. kali用Squid简单配置搭建http代理服务器
  3. 字符串静态顺序结构C/C++实现(数据结构严蔚敏版)
  4. Eclipse(javaweb)刚换工作空间之后,应该做哪几件事
  5. Distributed Systems笔记-NFS、AFS、GFS
  6. 018:Django商城部署和数据库读写分离
  7. php非,通过非数字和字符的方式实现PHP WebShell
  8. 福音!微信个人公众号可以改名了!
  9. Knative 核心概念介绍:Build、Serving 和 Eventing 三大核心组件
  10. python电影推荐系统的设计与实现_一种电影推荐系统的设计与实现
  11. WCF分布式安全开发实践(1):传输安全模式之匿名客户端:Transport_None_WSHttpBinding
  12. element时间选择器限制到时分秒_element-ui 日期时间选择器限制日期以及时间范围...
  13. CodeSmith 创建Ado.Net自定义模版(一)
  14. TAOCP-Reading-计算机程序设计艺术阅读-1-1
  15. 昨天熬夜整了一波,值了!
  16. PHP 接入微信公众账号API
  17. python登录豆瓣_手把手教你用python模拟登录豆瓣
  18. H310C,B365,M.2 NVME SSD,USB3.0,安装 WIN7 64 位
  19. pure-ftpd安装与使用
  20. 微信小程序开发工具第一次使用,网络连接不上怎么办

热门文章

  1. Unity引擎中的C#语言学习的笔记(1)
  2. 北京ui设计工资一般多少?北京ui设计公司排名?
  3. 图像处理3:Sobel边缘检测
  4. irreader RSS 订阅源阅读器工具软件 - 一款强大的网络内容阅读器
  5. [转载]惯性导航专业相关书籍180多部
  6. 登录、注册页面及后台代码
  7. 聊一聊Java中的悲观锁和乐观锁
  8. vue中的@代表什么
  9. 手把手学会LoadRunner参数化【LoadRunner】
  10. 51nod 1597 有限背包计数问题 DP 根号分治