spring注解驱动开发-4 Spring 自动装配

前言

spring自动装配是指利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值,本文介绍了通过@Autowired、@Resource和@Inject进行自动装配的方式。文章课程链接:尚硅谷spring注解驱动教程(雷神)

Spring 自动装配的几种方式

1.@Autowired

@Autowired是spring中的一个注解,可以放在类、接口以及方法上。实现对类成员变量、方法和构造函数的自动装配工作。其通过AutowiredAnnotationBeanPostProcessor类实现,有兴趣的小伙伴可自行学习。

在未使用@Autowired等注解进行装配时,我们是使用xml配置的方式,如下

<bean id="now" class="java.util.Date"/>
<bean id="person" class="com.atgugui.bean.Person">
 	<property name="age" value="22"></property>
 	<property name="name" value="zx"></property>
 	<property name="birthday" value="now"></property>
</bean>

@Autowired注入,这个大家都用的很多,举个简单的例子

@Controller
public class UserController {

    @Autowired
    private UserService userService;    
}
----------------分隔线-------------------
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;    
}

@Qualifier(“组件id”)

@Autowired优先按照类型(Class)去容器中找相应的组件,如有多个相同类型的组件,则按照属性的名称作为id去容器中查找,如 private UserService userService; 首先按照类型UserService类型进行查找。如果有多个,则按userService作为id去容器中进行查找。下面是实验过程,我们用@Bean方式再注册一个UserService,取名userService2。

@Configuration
@ComponentScan({"配置扫描标注@Controller、@Service等注解的包"})
public class MainConfig {
	
	@Bean("userService2")
	public UserService userService() {
		UserService userService = new UserService();
		return userService ;
	}
}

@Autowired
private UserService userService;
结果:我们在如上代码中自动装配的组件为@Service注册的,如果将userService替换为userService2,则注入的是@Bean方式注册的,那我们能不能指定需要的组件呐?当然可以,在@Autowired上再加上 @Qualifier(“组件id”) 则可注入指定的该类型组件,实验过程略。

重点:自动装配时,容器中必须存在该组件,不然会报错! 因为@Autowired的属性 required 默认为true ,修改其属性为@Autowired(required=false) 则可以为null,不会报错。

@Primary

如果IOC容器中相同类型的组件有多个,那么我们不可避免地就要来回用@Qualifier注解来指定要装配哪个组件,这还是比较麻烦的,Spring正是帮我们考虑到了这样一种情况,就提供了这样一个比较强大的注解,即@Primary。我们可以利用这个注解让Spring进行自动装配的时候,默认使用首选的bean。

2.@Resource方式

JSR-250规范中的注解,属于java规范的注解,默认按照属性名称来进行装配,可以通过@Resource(name = “组件id”) 来指定注入的组件,没有@Autowired中的@Primary、@Qualifier(“组件id”)等自定义注解,因此,还是@Autowired使用起来方便一些。

3.@Inject方式

JSR-330规范中的注解,属于java规范的注解,需要导入javax.inject架包,可以指定required = false。

补充-@Autowired标注在方法上、参数上

标注在方法上,一般我们注入一个对象时通过属性的方式,例如
@Autowired
private *Service *service
其实也可以标注在set方法上或有参构造器上,标在方法上时,Spring容器创建当前对象时,就会调用方法,完成赋值,方法上使用的参数,自定义类型的值从IOC容器中拿,例如

@Component
public class Boss {
	
	private Car car;

	@Autowired
	public void setCar(Car car) {
		this.car = car;
	}
	// 或有参构造器,如果只有一个有参构造器,可以不加此注解,也可完成注入
	@Autowired
	public Boss(Car car) {
		this.car = car;
	}
	
	// 前面两种也可这样写
	public void setCar(@Autowired Car car) {
		this.car = car;
	}
}

其次,还可标在@Bean注解的方法上,参数从IOC容器中获取,例如

@Configuration
public class MainConfigOfLifeCycle {

	@Bean
	public Car car(@Autowired Color color) { // @Autowired可不标注
		Car car = new Car();
		car.setColor(color);
		return car;
	}
}

注入Spring底层组件

当我们自定义的组件中需要用到Spring底层的一些组件,比如ApplicationContext(IOC容器)、BeanFactory等等,需要如何操作呐?Spring给我们提供了许多的***Aware接口,我们需要使用时,直接实现该接口即可。Spring会在创建对象的时候,调用***Aware接口中定义的方法注入相关的组件。例如:

public class Color implements ApplicationContextAware {
	
	private ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		System.out.println("传入的IOC容器:" + applicationContext);
		this.applicationContext = applicationContext;
	}
}

源码分析

这些***Aware都继承Aware接口,如图:

在这里插入图片描述在这里插入图片描述

***Aware接口的底层是由***AwareProcessor实现类实现的,每一个***Aware接口都有与之对应的***AwareProcessor实现类,以ApplicationContextAware为例,如图:

在这里插入图片描述

Spring是怎么将ApplicationContext对象注入到自定义类中的,源于调用了ApplicationContextAwareProcessor的invokeAwareInterfaces(Object bean)方法,其方法依次判断自定义类是否实现了***Aware,如果实现,强转为该***Aware,并将值设置进去,源码如图,最后一个判断为设置applicationContext。

如何想了解更多,请通过打断点方式进行源码分析。

@Profile多环境配置和切换

spring给我们提供的强大注解,可根据当前环境,动态的激活或切换一系列组件。
@Profile注解可以标注在方法上和类上。如果标注在配置类上,如 @Profile(“test”) 那么只有在test环境下,整个配置类里面的配置才会生效。如果标注在方法上,则是对应的方法受环境限制。@Profile(“default”) 为默认环境,未设置环境时,加载的就是它所标注的。
以数据源为例,一般我们通过加载配置文件方式,可以更方便的配置数据源。

配置如下

db.user=root
db.password=123456
db.driverClass=com.mysql.jdbc.Driver

可以通过前面学习的各种方式将值设置到数据源对象中

@PropertySource("classpath:/dbconfig.properties") // 加载外部的配置文件
@Configuration
public class DbConfig implements EmbeddedValueResolverAware {
	
	@Value("${db.user}")
	private String user;
	
	private StringValueResolver valueResolver;
	
	private String dirverClass;
	// 模拟测试环境
	@Profile("test")
	@Bean("testDataSource")
	public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws Exception {
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setUser(user);
		dataSource.setPassword(pwd);
		dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
		dataSource.setDriverClass(dirverClass);
		return dataSource;
	}
	// 模拟开发环境
	@Profile("dev")
	@Bean("devDataSource")
	public DataSource dataSourceDev(@Value("${db.password}") String pwd) throws Exception {
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setUser(user);
		dataSource.setPassword(pwd);
		dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/dev");
		dataSource.setDriverClass(dirverClass);
		return dataSource;
	}
	// 模拟生产环境
	@Profile("prod")
	@Bean("prodDataSource")
	public DataSource dataSourceProd(@Value("${db.password}") String pwd) throws Exception {
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setUser(user);
		dataSource.setPassword(pwd);
		dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/prod");
		dataSource.setDriverClass(dirverClass);
		return dataSource;
	}

	@Override
	public void setEmbeddedValueResolver(StringValueResolver resolver) {
		this.valueResolver = resolver;
		dirverClass = valueResolver.resolveStringValue("${db.driverClass}");
	}
}

下一步,我们设置环境属性,验证@Profile是否生效,设置的方式为

@Test
public void test02() {	
	// 1. 使用无参构造器创建一个IOC容器
	AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
	// 2. 在我们容器还没启动创建其他bean之前,先设置需要激活的环境(可以设置激活多个环境,用,号隔开)
	applicationContext.getEnvironment().setActiveProfiles("test");
	// 3. 注册主配置类
	applicationContext.register(MainConfigOfProfile.class);
	// 4. 启动刷新容器
	applicationContext.refresh();
	
	String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
	for (String name : namesForType) {
		System.out.println("组件名称为" + name);
	}

	Yellow yellow = applicationContext.getBean(Yellow.class);
	System.out.println(yellow);
	
	// 关闭容器
	applicationContext.close();
}

结论

配置为 test时,加载的数据源为test下的数据源,且如果将 @Profile(“test”)标注在类上时,将会作用于整个类,只有是test环境时,才会去注册该配置类的组件。

end…

如果总结的还行,就点个赞呗 @_@ 如有错误,欢迎指点,下一篇spring注解驱动开发-5 Spring AOP实现···

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值