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实现···