5. Bean
的依赖注入
如何将一个组件自动注入到另外一个组件中呢?
5.1 @Autowired
@Autowired
可以可以标注在构造器,方法,参数,字段上,默认按照Bean
的类型去IOC
容器中去寻找组件,如果有且一个组件被找到则成功注入,如果有多个组件被找到,则再使用默认名称去获取Bean
,Bean
的默认名称为类首字母缩写。也可以配合@Qualifier
注解明确指定注入哪个Bean
,还可以在注册Bean
的地方使用@Primary
,在不指定@Qualifier
的情况下,默认注入@Primary修饰的Bean。如果Bean
不一定存在,可以使用属性required=false
,则Bean
不存在也不会抛出异常。本文不演示构造注入、setter方法注入
- 编写一个用来注入到另外一个类中的
Bean AutowiredBean.java
并使用@Component
注入到容器中
package com.ddf.spring.annotation.bean;
import org.springframework.stereotype.Component;
/**
* @author DDf on 2018/8/6
*/
@Component
public class AutowiredBean {
public AutowiredBean() {
System.out.println("AutowiredBean创建完成。。。。。。。。。。。。");
}
}
- 修改
AnnotationConfiguration.java
,增加方法autowiredBean2()
,再次注入AutowiredBean
,并取名autowiredBean2
区分
package com.ddf.spring.annotation.configuration;
import com.ddf.spring.annotation.bean.*;
import com.ddf.spring.annotation.entity.User;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Controller;
/**
* @author DDf on 2018/7/19
* @Configuration 表明当前类是一个配置类
* @ComponentScan 指定扫描的包路径,并且配置了excludeFilters来排除注解类型为@Controller的不纳入容器中,
* 排除符合自定义ExcludeTypeFilter类中规则的类
* @Import 导入组件,可以直接导入普通类,或者通过ImportSelector接口或者ImportBeanDefinitionRegistrar接口来自定义导入
*/
@Configuration
@ComponentScan(value = "com.ddf.spring.annotation", excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class),
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = ExcludeTypeFilter.class)
})
@Import(value = {ImportBean.class, CustomImportSelector.class, CustomImportBeanDefinitionRegistrar.class})
public class AnnotationConfiguration {
/**
* 注入一个Type为User(方法返回值)的bean,bean的名称为user(方法名)
* initMethod 指定Bean创建后调用的初始化方法
* destroyMethod 指定Bean在销毁后会调用的方法
* @return
*/
@Bean(initMethod = "init", destroyMethod = "destory")
public User user() {
return new User();
}
/**
* 测试@Conditional 满足{@link DevelopmentProfileCondition} 这个类的条件返回true则当前Bean能够成功注入,反之不能
*
* @return
*/
@Bean
@Conditional({DevelopmentProfileCondition.class})
public DevelopmentBean developmentService() {
return new DevelopmentBean();
}
/**
* 满足{@link ProductionProfileCondition} 这个类的条件返回true则当前Bean能够成功注入,反之不能
*
* @return
*/
@Bean
@Conditional({ProductionProfileCondition.class})
public ProductionBean productionService() {
return new ProductionBean();
}
/**
* 使用FactoryBean工厂来注册组件
* @return
*/
@Bean
public FactoryPrototypeBeanConfiguration factoryPrototypeBeanConfiguration() {
return new FactoryPrototypeBeanConfiguration();
}
/**
* 使用FactoryBean工厂来注册组件
* @return
*/
@Bean
public FactorySingletonBeanConfiguration factorySingletonBeanConfiguration() {
return new FactorySingletonBeanConfiguration();
}
/**
* 注册一个实现InitializingBean, DisposableBean接口来指定Bean的初始化和销毁方法的Bean
* @return
*/
@Bean
public InitAndDisposableBean initAndDisposableBean() {
return new InitAndDisposableBean();
}
/**
* 创建一个通过JSR250 @PostConstruct指定初始化方法/@PreDestroy指定销毁方法的Bean
* @return
*/
@Bean
public PostConstructAndPreDestoryBean postConstructAndPreDestoryBean() {
return new PostConstructAndPreDestoryBean();
}
/**
* 注入AutowiredBean,名称为autowiredBean2,并将该bean作为默认依赖注入的首选
* @return
*/
@Bean
@Primary
public AutowiredBean autowiredBean2() {
return new AutowiredBean();
}
}
- 新建一个
AutowiredService.java
并使用@Service
注解交由容器管理,然后注入AutowiredBean
package com.ddf.spring.annotation.service;
import com.ddf.spring.annotation.bean.AutowiredBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
/**
* @author DDf on 2018/8/6
* @Autowired 默认使用Bean的类型去匹配注入,如果找到多个相同类型的Bean,则使用默认名称去获取Bean,Bean的默认名称为类首字母缩写
* * 也可以配合@Autowired配合@Qualifier注解明确指定注入哪个Bean,还可以在注入Bean的地方使用@Primary,在不指定@Qualifier的情况下,
* * 默认注入@Primary修饰的Bean,如果Bean不一定存在,可以使用属性required=false,则Bean不存在也不会抛出异常
* 可以标注在构造器,方法,参数,字段上
*
* @Resource 默认使用名称注入Bean,可以使用name属性指定具体要注入的Bean的名称,不支持@Primary,不支持required=false,不支持@Primary
* @Inject 使用类型去注入,支持@Primary,不支持required=false
*
*
*/
@Service
public class AutowiredService {
@Autowired
private AutowiredBean autowiredBean;
public AutowiredBean getAutowiredBean() {
return autowiredBean;
}
@Autowired
@Qualifier("autowiredBean")
private AutowiredBean qualifierAutowiredBean;
public AutowiredBean getQualifierAutowiredBean() {
return qualifierAutowiredBean;
}
public AutowiredService() {
System.out.println("AutowiredService创建完成。。。。。。。。。。。。");
}
}
- 测试见 5.3 综合演示测试结果
5.2 @Resource
和@Inject
这两个注解也可以完成依赖注入,并且是
java
规范提供的。区别为@Resource
默认是使用名称来注入组件的,并且不支持@Primary
,也不支持required=false
,有一个属性名为name可以指定要注入的Bean的名称;@Inject
和@Autowired
类似,按照类型注入,支持@Primary
,不支持required=false
- 修改
AutowiredService.java
,加入@Resource
和@Inject
的注入代码
package com.ddf.spring.annotation.service;
import com.ddf.spring.annotation.bean.AutowiredBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.inject.Inject;
/**
* @author DDf on 2018/8/6
* @Autowired 默认使用Bean的类型去匹配注入,如果找到多个相同类型的Bean,则使用默认名称去获取Bean,Bean的默认名称为类首字母缩写
* * 也可以配合@Autowired配合@Qualifier注解明确指定注入哪个Bean,还可以在注入Bean的地方使用@Primary,在不指定@Qualifier的情况下,
* * 默认注入@Primary修饰的Bean,如果Bean不一定存在,可以使用属性required=false,则Bean不存在也不会抛出异常
* 可以标注在构造器,方法,参数,字段上
*
* @Resource 默认使用名称注入Bean,可以使用name属性指定具体要注入的Bean的名称,不支持@Primary,不支持required=false,不支持@Primary
* @Inject 使用类型去注入,支持@Primary,不支持required=false
*
*
*/
@Service
public class AutowiredService {
@Autowired
private AutowiredBean autowiredBean;
public AutowiredBean getAutowiredBean() {
return autowiredBean;
}
@Autowired
@Qualifier("autowiredBean")
private AutowiredBean qualifierAutowiredBean;
public AutowiredBean getQualifierAutowiredBean() {
return qualifierAutowiredBean;
}
@Resource(name = "autowiredBean")
private AutowiredBean resourceAutowiredBean;
public AutowiredBean getResourceAutowiredBean() {
return resourceAutowiredBean;
}
@Resource(name = "autowiredBean2")
private AutowiredBean resourceAutowiredBean2;
public AutowiredBean getResourceAutowiredBean2() {
return resourceAutowiredBean2;
}
@Inject
private UserService userService;
public UserService getUserService() {
return userService;
}
public AutowiredService() {
System.out.println("AutowiredService创建完成。。。。。。。。。。。。");
}
}
- 测试见 5.3 综合演示测试结果
5.3 综合演示测试结果
- 修改主启动类
Application.java
,增加测试依赖注入的方法testAutowired()
package com.ddf.spring.annotation;
import com.ddf.spring.annotation.service.AutowiredService;
import com.ddf.spring.annotation.service.LazyBeanService;
import com.ddf.spring.annotation.service.PrototypeScopeService;
import com.ddf.spring.annotation.service.UserService;
import com.ddf.spring.annotation.bean.AutowiredBean;
import com.ddf.spring.annotation.bean.FactoryPrototypeBean;
import com.ddf.spring.annotation.bean.FactorySingletonBean;
import com.ddf.spring.annotation.configuration.AnnotationConfiguration;
import com.ddf.spring.annotation.entity.User;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Primary;
/**
* @author DDf on 2018/7/19
*/
public class Application {
public static void main(String[] args) {
System.out.println("-----------------------IOC容器初始化-------------------------");
// 创建一个基于配置类启动的IOC容器,如果主配置类扫描包的路径下包含其他配置类,则其他配置类可以被自动识别
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AnnotationConfiguration.class);
System.out.println("-----------------------IOC容器初始化完成-------------------------\n");
// 获取当前IOC中所有bean的名称,即使是懒加载类型的bean也会获取到
String[] definitionNames = applicationContext.getBeanDefinitionNames();
// 打印当前IOC中对应名称的bean和bean的类型
for (String name : definitionNames) {
// 这个会影响到测试懒加载的效果,如果需要测试懒加载,这行代码需要注释掉,因为getBean方法一旦调用则会初始化
Object bean = applicationContext.getBean(name);
System.out.println("bean name:" + name + ", type: " + bean.getClass());
}
// 测试@Scope bean的作用域
testPrototypeScopeService(applicationContext);
// 测试单实例bean的@Lazy懒加载
testLazyBeanService(applicationContext);
// 测试FactoryBean接口导入单实例与Prototype作用域的组件
testFactoryBeanPrototypeBean(applicationContext);
// 测试@PropertySource和@Value属性赋值
testPropertySourceValue(applicationContext);
// 测试@Autowired注入多个相同类型的Bean
testAutowired(applicationContext);
// 销毁容器
applicationContext.close();
}
/**
* 测试@Scope bean的作用域
*
* @param applicationContext
*/
public static void testPrototypeScopeService(ApplicationContext applicationContext) {
System.out.println("\n-----------------------测试@Scope开始-------------------------");
UserService userService = (UserService) applicationContext.getBean("userService");
UserService userService1 = applicationContext.getBean(UserService.class);
System.out.println("默认单实例bean UserService是否相等 " + (userService == userService1));
PrototypeScopeService prototypeScopeService = applicationContext.getBean(PrototypeScopeService.class);
PrototypeScopeService prototypeScopeService1 = applicationContext.getBean(PrototypeScopeService.class);
System.out.println("PrototypeScopeService prototype scope作用域是否相等: " + (prototypeScopeService == prototypeScopeService1));
System.out.println("-----------------------测试@Scope结束-------------------------\n");
}
/**
* 测试单实例bean的懒加载,只有等使用的时候再创建实例。
* IOC容器启动后不会创建该bean的实例,如果是在该方法中才创建这个bean的实例,并且获得的两个bean是同一个的话,则测试通过。
*/
public static void testLazyBeanService(ApplicationContext applicationContext) {
System.out.println("\n---------------测试单实例bean的@Lazy懒加载开始----------------------");
LazyBeanService lazyBeanService = applicationContext.getBean(LazyBeanService.class);
LazyBeanService lazyBeanService1 = applicationContext.getBean(LazyBeanService.class);
System.out.println("lazyBeanService==lazyBeanService1?: " + (lazyBeanService == lazyBeanService1));
System.out.println("---------------测试单实例bean的@Lazy懒加载结束----------------------\n");
}
/**
* 测试通过FactoryBean接口导入单实例与Prototype作用域的组件,根据打印可以看出FactoryBean创建的单实例Bean都是懒加载的
* @param applicationContext
*/
public static void testFactoryBeanPrototypeBean(ApplicationContext applicationContext) {
System.out.println("\n----------测试通过FactoryBean注册单实例和Prototype作用域的组件开始----------");
FactorySingletonBean factorySingletonBean = applicationContext.getBean(FactorySingletonBean.class);
FactorySingletonBean factorySingletonBean1 = applicationContext.getBean(FactorySingletonBean.class);
FactoryPrototypeBean factoryPrototypeBean = applicationContext.getBean(FactoryPrototypeBean.class);
FactoryPrototypeBean factoryPrototypeBean1 = applicationContext.getBean(FactoryPrototypeBean.class);
System.out.println("单实例factorySingletonBean==factorySingletonBean1?" + (factorySingletonBean==factorySingletonBean1));
System.out.println("Prototype作用域factoryPrototypeBean==factoryPrototypeBean1?" + (factoryPrototypeBean==factoryPrototypeBean1));
System.out.println("----------测试通过FactoryBean注册单实例和Prototype作用域的组件结束----------\n");
}
/**
* 测试通过@PropertySource和@Value注解来对属性进行赋值
* @param applicationContext
*/
public static void testPropertySourceValue(ApplicationContext applicationContext) {
System.out.println("\n---------------测试@PropertySource和@Value赋值开始----------------");
User user = applicationContext.getBean(User.class);
System.out.println("user属性为: " + user.toString());
System.out.println("---------------测试@PropertySource和@Value赋值结束----------------\n");
}
/**
* 测试在IOC容器中存在两个相同类型的Bean,但是Bean的名称不一致
* 在这种情况下,使用@Autowired将该Bean注入到另外一个容器中
* @Autowired 默认使用Bean的类型去匹配注入,如果找到多个相同类型的Bean,则使用默认名称去获取Bean,Bean的默认名称为类首字母缩写
* 也可以配合@Autowired配合@Qualifier注解明确指定注入哪个Bean,还可以在注入Bean的地方使用@Primary,在不指定@Qualifier的情况下,
* 默认注入哪个Bean {@link AutowiredService}
* @param applicationContext
*/
public static void testAutowired(ApplicationContext applicationContext) {
System.out.println("\n--------------测试autowired注入多个相同类型的类开始-----------------");
AutowiredBean autowiredBean = (AutowiredBean) applicationContext.getBean("autowiredBean");
AutowiredBean autowiredBean2 = (AutowiredBean) applicationContext.getBean("autowiredBean2");
System.out.println("autowiredBean: " + autowiredBean);
System.out.println("autowiredBean2: " + autowiredBean2);
System.out.println(autowiredBean == autowiredBean2);
/**
* 这里已做更改,修改了默认注入 {@link com.ddf.spring.annotation.configuration.AnnotationConfiguration.autowiredBean2}
*/
AutowiredService autowiredService = applicationContext.getBean(AutowiredService.class);
AutowiredBean autowiredServiceBean = autowiredService.getAutowiredBean();
System.out.println("使用@Primay后AutowiredService默认注入bean: " + autowiredServiceBean);
AutowiredBean autowiredServiceBean2 = autowiredService.getQualifierAutowiredBean();
System.out.println("使用@Qualifier明确注入Bean: " + autowiredServiceBean2);
// 使用@Resource注入
AutowiredBean resourceAutowiredBean = autowiredService.getResourceAutowiredBean();
System.out.println("使用@Resource注入autowiredBean: " + resourceAutowiredBean);
AutowiredBean resourceAutowiredBean2 = autowiredService.getResourceAutowiredBean2();
System.out.println("使用@Resource注入autowiredBean2: " + resourceAutowiredBean2);
// 使用@Inject注入
UserService userService = autowiredService.getUserService();
System.out.println("使用@Inject注入UserService: " + userService);
System.out.println("--------------测试autowired注入多个相同类型的类开始-----------------\n");
}
}
- 打印日志如下,不相关日志已过滤
// 过滤。。。
--------------测试autowired注入多个相同类型的类开始-----------------
autowiredBean: com.ddf.spring.annotation.bean.AutowiredBean@77e4c80f
autowiredBean2: com.ddf.spring.annotation.bean.AutowiredBean@1d119efb
false
使用@Primay后AutowiredService默认注入bean: com.ddf.spring.annotation.bean.AutowiredBean@1d119efb
使用@Qualifier明确注入Bean: com.ddf.spring.annotation.bean.AutowiredBean@77e4c80f
使用@Resource注入autowiredBean: com.ddf.spring.annotation.bean.AutowiredBean@77e4c80f
使用@Resource注入autowiredBean2: com.ddf.spring.annotation.bean.AutowiredBean@1d119efb
使用@Inject注入UserService: com.ddf.spring.annotation.service.UserService@4b5d6a01
--------------测试autowired注入多个相同类型的类开始-----------------