目录
3.3.2消除歧义性-------@Primary和@Quelifier
继续总结
3.3依赖注入
那之前啊,我们将Bean装配到SpringIoC容器当中,从而通过容器去获取Bean对象,在真实的程序中难免会有俩个Bean之间的依赖关系,例如电力工人和工具,电力工人工作的前提是要有工具,为了解决这种依赖关系,我们就要学习今天的依赖注入(dependency injection)简称DI
为了更好的理解我们引入以下示例
第一步:创建两个接口(电力工人接口,电力工具接口)
第二步:创建两个实现类(电力工人实现类,电力工具实现类)
第三步:通过@Component接口将两个Bean装配到SpringIoC容器中
第四步:@Autowired添加依赖注入
第五步:测试
接口
//创建电力工人接口
public interface Electrician {
public void use();
public void setUtiles(electricityPower_tools e);
}
//创建电力工具接口
public interface electricityPower_tools {
public void use();
}
实现类
//电力工人实现类
@Component
public class Electricianimpl implements com.itheima.pojo.Electrician {
@Autowired
private electricityPower_tools e = null;
@Override
public void use() {
System.out.println("电力工人");
e.use();
}
@Override
public void setUtiles(electricityPower_tools e) {
this.e = e;
}
}
//电力工具实现类
@Component
public class electricityPower_toolsimpl implements com.itheima.pojo.electricityPower_tools {
@Override
public void use() {
System.out.println("钳子工具被使用");
}
}
最后进行测试
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
Electrician electrician = (Electrician)applicationContext.getBean(Electricianimpl.class);
electrician.use();
}
那我们发现,我们成功获取Electrician,并调用其属性electricityPower_tools的use()方法
3.3.1注解@Autowired
@Autowired是比较常用的注解,所以我们要进一步探讨此注解,我们继续思考一个问题,工具不可能只有一个把,例如电笔、钳子等,这个时候就出现问题了,当SpringIoC依赖注入electricityPower_tools时,如果electricityPower_tools接口有两个实现类,且都在SpringIoC容器中,那Spring IoC将要注入谁呢,这个时候就会报错
报错演示
我们在创建一个electricityPower_tools接口的实现类
//电笔工具类
@Component
public class electricityPower_toolsimpl2 implements electricityPower_tools {
@Override
public void use() {
System.out.println("电笔工具被使用");
}
}
这个时候我们在运行就会报错
现在我们来解决一下这个问题
第一种方式:我们将依赖注入的属性名改为具体的实现类的名称,这里改为钳子实现类,
这种方式能解决问题的原因:@Autowired默认根据属性的类型进行注入,如果IoC容器中对应的类型不是唯一的,那么它会根据属性名进行注入
第二种和第三种我们看下一节
3.3.2消除歧义性-------@Primary和@Quelifier
我们继续上面的问题,现在是有两个工具实现类,导致SpringIoC不知道应该注入哪一个,其实我们可以使用@Primary注解为其中一个设置优先级,这也是一种解决方案
我们举个栗子,我们为电笔工具类设置优先级,也可以解决问题
这里记得改回来
如果我们为电笔工具类和钳子工具类都设置@Primary,依然会出现问题,这里大家可以自己尝试
接着再来说@Qualifier注解
上面两种方式都不是很灵活,不可♥啊,咋可咋办,使用@Qualifier注解呗,它的属性value需要一个字符串去定义,它将与@Autowired组合在一起使用,通过类型或者名称一起找到Bean
@Autowired
//根据名称注入
@Qualifier(“electricityPower_toolsimpl2”)
private electricityPower_tools electricityPower_tools= null;
@Autowired
//根据类型注入
@Qualifier(“electricityPower_tools.class”)
private electricityPower_tools electricityPower_tools= null;
3.3.3带有参数的构造方法类的装配
其实我们可以通过带参数的构造方法将依赖注入
我们将@Autowired删掉,在构造方法参数前加上
@Autowired @Qualifier("electricityPower_toolsimpl2")
//改成这样
@Component
public class Electricianimpl implements Electrician {
private electricityPower_tools electricityPower_tools= null;
public Electricianimpl(@Autowired @Qualifier("electricityPower_toolsimpl2") electricityPower_tools electricityPower_tools) {
this.electricityPower_tools = electricityPower_tools;
}
@Override
public void use() {
System.out.println("电力工人");
electricityPower_tools.use();
}
@Override
public void setUtiles(electricityPower_tools e) {
this.electricityPower_tools = e;
}
}
3.4生命周期
我们有必要了解Bean的生命周期,例如当我们通过SpringIoC容器获取数据源后,我们希望在Bean销毁时关闭数据源(执行close方法),以此达到释放资源的目的,这个时候就要了解Bean的生命周期
大致分为Bean的定义、Bean的初始化、Bean的生存期、Bean的销毁4个部分。
Bean定义大致过程:
1.资源定位:Spring通过我们的配置,例如@ComponentScan定义扫描指定的包,找到@Component修饰的类
2. Bean定义:一旦找到资源,会对他进行解析,并将定义信息保存起来。此时还没有实例化Bean,仅仅是Bean的定义
3.发布Bean定义:然后将Bean定义发布到SpringIoC容器中。此时IoC容器也只是定义,没有Bean实例
接下来会实例化Bean,完成依赖注入
如果我们不希望在IoC容器创建时就完成Bean的实例化和依赖注入,可以在@ComponnetScan注解中添加配置项lazyInit,默认为false,也就是默认不进行延迟初始化,如果将其改为true,在Spring发布Bean定义后,将不会立即进行Bean的初始化和依赖注入
来个实例
1.正常情况
我们先在Electricianimpl构造方法中加一句打印
public Electricianimpl(@Autowired @Qualifier("electricityPower_toolsimpl2") electricityPower_tools electricityPower_tools) {
System.out.println("完成依赖注入");
this.electricityPower_tools = electricityPower_tools;
}
在此处打断点
调试方式运行结果
2.lazyInit=true的情况
修改配置类
调试方式运行结果
Spring在完成依赖注入后,还提供一系列接口和配置来完成Bean的初始化过程,除了BeanPostProcess对所以Bean有效,其他接口只针对单个Bean有效
为了测试生命周期,我们对电力工人类进行改造
package com.POJO.impl;
import com.POJO.Electrician;
import com.POJO.electricityPower_tools;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class Electricianimpl implements Electrician, BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {
private electricityPower_tools electricityPower_tools= null;
@Override
public void use() {
System.out.println("电力工人");
electricityPower_tools.use();
}
@Override
@Autowired
@Qualifier("electricityPower_toolsimpl2")
public void setUtiles(electricityPower_tools electricityPower_tools) {
this.electricityPower_tools = electricityPower_tools;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("调用BeanNameAware");
}
@Override
public void setBeanName(String s) {
System.out.println("调用BeanFactoryAwar");
}
@Override
public void destroy() throws Exception {
System.out.println("调用DisposableBean");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("调用InitializingBean");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("调用ApplicationContextAware");
}
@PostConstruct
public void init(){
System.out.println("自定义初始化方法");
}
@PreDestroy
public void destroy1(){
System.out.println("自定义销毁方法");
}
}
如果出现一下问题 ,加入一下依赖即可
<dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency>
同时我们还要创建一个BeanPostProcessorExample类(Bean后置处理器)
package com.POJO.impl;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class BeanPostProcessorExample implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcess调用postProcessBeforeInitialization方法,参数【"+bean.getClass().getSimpleName()+"】【"+beanName+"】");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcess调用postProcessAfterInitialization方法,参数【"+bean.getClass().getSimpleName()+"】【"+beanName+"】");
return bean;
}
}
测试结果:我们可以看到Bean后置处理器对所以Bean都生效,其他接口只有实现并重写该接口的方法的Bean才会生效
有时候Bean的定义可能会使用第三方的类,此时使用@Bean注解来配置自定义初始化和销毁方法,如下所示:
@Bean(initMethod = "init" destroyMethod = "destroy")
DisposableBean接口中的destroy为什么没有执行?
看看上面的测试结果,没有destroy方法的执行语句,why?
对于Bean的作用域如果是多例-prototype,IOC容器并不会执行相关的清理工作,只有单例模式下才会进行Bean的销毁,因此示例代码中destroy方法根本不会被调用,多例Bean的生命周期不归IOC容器来管理,单例Bean的生命周期归IOC容器管理。
但是我们的是单例呀,我认为是还没等destroy方法执行,Bean就已经销毁,故而没有执行