长文干货丨一文搞懂IoC的依赖注入

 一、注解驱动IoC

企业建站必须是能够以充分展现企业形象为主要目的,是企业文化与产品对外扩展宣传的重要窗口,一个合格的网站不仅仅能为公司带来巨大的互联网上的收集和信息发布平台,创新互联公司面向各种领域:柴油发电机成都网站设计全网整合营销推广解决方案、网站设计等建站排名服务。


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

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

1. AnnotationConfigApplicationContext 的注册使用

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

 
 
 
 
  1. @Configuration 
  2. public class DemoConfiguration { 

在xml中声明bean的方式

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

 
 
 
 
  1.  

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

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

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

 
 
 
 
  1. @Bean 
  2. public Person person() { 
  3.     return new Person(); 

2. 注解IoC容器的初始化

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

运行后Person控制台打印结果

 
 
 
 
  1. com.huodd.bean.Person@55536d9e 

3. 组件的注册和扫描

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

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

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

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

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

@Component

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

 
 
 
 
  1. @Component 
  2. public class Person { 

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

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

 
 
 
 
  1. @Component("person1") 
  2. public class Person { 

在xml中相当于

 
 
 
 
  1.  

@ComponentScan

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

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

解决方案1:

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

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

 
 
 
 
  1. @Configuration 
  2. @ComponentScan("com.huodd.bean") 
  3. public class DemoComponentScanConfiguration { 

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

启动类代码如下:

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

解决方案2:

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

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

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

 
 
 
 
  1.  

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

组件注册的补充

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

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

 
 
 
 
  1. @Target({ElementType.TYPE}) 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. @Documented 
  4. @Component 
  5. public @interface Service { 

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

@Configuration

@Configuration 底层也有标注@Component

 
 
 
 
  1. @Target({ElementType.TYPE}) 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. @Documented 
  4. @Component 
  5. public @interface Configuration { ... } 

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

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

4.1 xml引用注解

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

 
 
 
 
  1.  
  2.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  3.        xmlns:context="http://www.springframework.org/schema/context" 
  4.        xsi:schemaLocation="http://www.springframework.org/schema/beans 
  5.         https://www.springframework.org/schema/beans/spring-beans.xsd  
  6.         http://www.springframework.org/schema/context  
  7.         https://www.springframework.org/schema/context/spring-context.xsd"> 
  8.  
  9.      
  10.      
  11.      
  12.      
  13.  

4.2 注解引用XMl

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

 二、IoC的依赖注入

1.Setter属性注入

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

 
 
 
 
  1. @Bean 
  2. public Person person() { 
  3.     Person person = new Person(); 
  4.     person.setId(1); 
  5.     person.setName("PoXing"); 
  6.     person.setAge(18); 
  7.     return person; 

xml中的setter注入

 
 
 
 
  1.  
  2.      
  3.      
  4.      
  5.  

2. 构造器注入

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

 
 
 
 
  1. public Person(Integer id, String name, Integer age) { 
  2.         this.id = id; 
  3.         this.name = name; 
  4.         this.age = age; 

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

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

xml驱动中如下

 
 
 
 
  1.  
  2.      
  3.      
  4.      
  5.      
  6.  

3. 注解式属性注入

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

@Component 下的属性注入

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

 
 
 
 
  1. @Component 
  2. public class Dog { 
  3.     private Integer id; 
  4.     private String name; 
  5.     private Integer age; 
  6.  
  7.    ... 省略 Getter、Setter 
  8.    ... 省略 toString 
  9.  

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

 
 
 
 
  1. @Value("1") 
  2. private Integer id; 
  3.  
  4. @Value("wangcai") 
  5. private String name; 
  6.  
  7. @Value("3") 
  8. private Integer age; 

启动类代码如下

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

控制台打印结果

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

外部配置文件(@PropertySource)

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

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

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

 
 
 
 
  1. dog.id=1 
  2. dog.name=wangcai 
  3. dog.age=3 

2.引入配置文件

 
 
 
 
  1. @PropertySource("classpath:di/dog.properties") 
  2. @ComponentScan("com.huodd.bean") 
  3. @Configuration 
  4. public class DemoComponentScanConfiguration { 

3.Dog类中属性注入 这里@Value需要配合占位符 来获取properties配置文件中的内容

 
 
 
 
  1. @Value("${dog.id}") 
  2. private Integer id; 
  3.  
  4. @Value("${dog.name}") 
  5. private String name; 
  6.  
  7. @Value("${dog.age}") 
  8. private Integer age; 

4.修改一下启动类

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

控制台打印结果如下

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

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

4.自动注入

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

@Autowired

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

方法1 → 在属性上标注

 
 
 
 
  1. @Component 
  2. public class Dog { 
  3.     // ...... 
  4.     @Autowired 
  5.     private Person person; 

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

 
 
 
 
  1. @Component 
  2. public class Dog { 
  3.     // ...... 
  4.     private Person person; 
  5.  
  6.     @Autowired 
  7.     public Dog(Person person) { 
  8.      this.person = person; 
  9.     } 

方法3 → 使用setter方法注入

 
 
 
 
  1. @Component 
  2. public class Dog { 
  3.     // ...... 
  4.     private Person person; 
  5.      
  6.     @Autowired 
  7.     public void setPerson(Person person) { 
  8.         this.person = person; 
  9.     } 

JSR250规范下的@Resource

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

它与@Autowired的区别是:

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

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

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

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

 
 
 
 
  1. @Component 
  2. public class Dog { 
  3.     // ...... 
  4.     @Autowired 
  5.     @Qualifier("xiaowang") 
  6.     private Person person; 

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

 
 
 
 
  1. @Component 
  2. public class Dog { 
  3.     // ...... 
  4.     @Resource(name="xiaowang") 
  5.     private Person person; 

JSR330规范下的@Inject

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

 
 
 
 
  1.  
  2.  
  3.     javax.inject 
  4.     javax.inject 
  5.     1 
  6.  

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

 
 
 
 
  1. @Component 
  2. public class Dog { 
  3.      
  4.     @Inject // 等同于@Autowired 
  5.     @Named("xiaowang") // 等同于@Qualifier 
  6.     private Person person; 

它与@Autowired的区别是:

  • @Autowired所在的包为 org.springframework.beans.factory.annotation.Autowired 即为 SpringFramework 提供的
  • @Inject所在的包为 javax.inject.Inject 属于JSR的规范 也就是说如果不使用SpringFramework时可以使用该注解

5. 复杂类型注入

Array注入

 
 
 
 
  1.  
  2.      
  3.         PoXing 
  4.         LaoWang 
  5.      
  6.  

List注入

 
 
 
 
  1.  
  2.      
  3.         13000000000 
  4.         13000000001 
  5.      
  6.  

Set注入-

 
 
 
 
  1.  
  2.  
  3. --- 
  4.  
  5.  
  6.      
  7.          
  8.          
  9.      
  10.  

Map注入

 
 
 
 
  1.  
  2.      
  3.          
  4.           
  5.          
  6.          
  7.               
  8.          
  9.      
  10.  

Properties注入

 
 
 
 
  1.  
  2.      
  3.         男 
  4.         18 
  5.      
  6.  

面试题

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

  1. 先拿属性对应的类型,去IoC容器中找相应的Bean
  2. 如果没有找到 直接抛出NoUniqueBeanDefinitionException异常
  3. 如果找到一个 直接返回
  4. 如果找到多个相同类型的bean 再拿属性名去与这多个bean的id进行对比
  5. 如果有多个或者没有 则会抛出NoUniqueBeanDefinitionException异常
  6. 如果只有一个 直接返回

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

3.自动注入的注解对比

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

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

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

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

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

分享名称:长文干货丨一文搞懂IoC的依赖注入
网站链接:http://www.36103.cn/qtweb/news35/3085.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联