本文知识点来源于尚硅谷,感谢尚硅谷为广大学子提供的优质教育资源,感谢各位老师热情指导,本文仅作为学习笔记使用,记录学习心得,如有不适,请联系作者。
自动装配:
Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值。
一、@Autowired 自动注入
- spring框架定义的
- 默认按照类型去容器中找,找到就赋值。
- 如果找到多个相同类型组件,再将属性的名称作为组件的id到容器中去匹配。
- 可以使用 @Qualifier(“userDao”)指定需要装配的组件的id而不是使用属性名
- 自动注入时,容器中没有这个被注入组件的话会报错 ,解决方案:@Autowired(required=false) 不必须的
- @Primary 让spring自动装配时默认使用优选的bean,指定那个Bean优先 与 @Qualifier不能同时用(@Primary会失效)
测试如下:
@Controller
public class UserController {
@Autowired
private UserService userService;
public void print() {
userService.print();
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void print() {
System.out.println("============" + userDao);
System.out.println("============" + userDao.getName());
}
}
@Repository
public class UserDao {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
配置类如下:扫描以上三个Bean
@Configuration
@ComponentScan({"com.tedu.controller","com.tedu.service","com.tedu.dao"})
public class MainConfigOfAutowired {
}
测试类:
public class MainTest_Autowired {
@Test
public void test() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
UserController userController = applicationContext.getBean(UserController.class);
//输出自动装配的userDao
userController.print();
//输出IOC管理的userDao
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
System.out.println(userDao);
System.out.println(userDao.getName());
}
}
由输出看我们发现两个userDao的地址是一样的,也从侧面印证了 @Autowired 自动注入默认按照类型去容器中找,找到就赋值。
============com.tedu.dao.UserDao@6dbb137d
============null
com.tedu.dao.UserDao@6dbb137d
null
接下来我们修改配置类,王荣琦中新增一个UserDao类型的组件
@Configuration
@ComponentScan({"com.tedu.controller","com.tedu.service","com.tedu.dao"})
public class MainConfigOfAutowired {
@Bean("userDao2")
public UserDao userDao() {
UserDao userDao = new UserDao();
userDao.setName("userDao2");
return userDao;
}
}
运行测试方法输出:
可以看到这句输出是name属性没有赋值的
修改userService
@Service
public class UserService {
@Autowired
private UserDao userDao2;
public void print() {
System.out.println("============" + userDao2);
System.out.println("============" + userDao2.getName());
}
}
执行测试类输出:
该处名字被赋值,说明注入的是我们通过@Bean添加到容器中的组件,也证明了使用@Autowired,如果找到多个相同的类型组件,则会将属性的名称作为组件的id到容器中去找对应匹配的。
我们也可以使用 @Qualifier(“userDao”)指定需要装配的组件的id而不是使用属性名,在上边基础上继续测试:
修改userService,在属性上新增@Qualifier(“userDao”),属性名继续使用userDao2。
@Service
public class UserService {
@Qualifier("userDao")
@Autowired
private UserDao userDao2;
public void print() {
System.out.println("============" + userDao2);
System.out.println("============" + userDao2.getName());
}
}
执行测试方法输出:
我们发现name属性并没有赋值,由此可以证明,可以使用 @Qualifier(“userDao”)指定需要装配的组件的id而不是使用属性名。
继续测试,如果我们需要自动装配的组件并没有添加到容器中管理会怎么样呢?
修改配置类,注释掉@Bean相关内容
@Configuration
@ComponentScan({"com.tedu.controller","com.tedu.service","com.tedu.dao"})
public class MainConfigOfAutowired {
/*@Bean("userDao2")
public UserDao userDao() {
UserDao userDao = new UserDao();
userDao.setName("userDao2");
return userDao;
}*/
}
修改UserDao类注释@Repository注解
//@Repository
public class UserDao {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
执行测试代码报错
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userController': Unsatisfied dependency expressed through field 'userService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userDao2'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.tedu.dao.UserDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=userDao), @org.springframework.beans.factory.annotation.Autowired(required=true)}
那么我们能不能忽略这一报错呢?看上边报错信息我们发现**Autowired(required=true)**这个提示
那么我们来试一下,修改userService,将@Autowired的required属性赋值false
@Service
public class UserService {
@Qualifier("userDao")
@Autowired(required = false)
private UserDao userDao2;
public void print() {
System.out.println("============" + userDao2);
//System.out.println("============" + userDao2.getName());
}
}
执行测试注释如下内容:
@Test
public void test() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
UserController userController = applicationContext.getBean(UserController.class);
//输出自动装配的userDao
userController.print();
//输出IOC管理的userDao
/*UserDao userDao = (UserDao) applicationContext.getBean("userDao");
System.out.println(userDao);
System.out.println(userDao.getName());*/
}
运行并无报错输出:
============null
还原以上注释,继续测试
配置类修改如下添加@Primary注解表示该组件优先注入:
@Primary
@Bean("userDao2")
public UserDao userDao() {
UserDao userDao = new UserDao();
userDao.setName("userDao2");
return userDao;
}
修改UserService注释掉@Qualifier,修改属性名为userDao:
@Service
public class UserService {
//@Qualifier("userDao")
@Autowired(required = false)
private UserDao userDao;
public void print() {
System.out.println("============" + userDao);
System.out.println("============" + userDao.getName());
}
}
执行测试类发现添加了@Primary的Bean优先被注入:
============com.tedu.dao.UserDao@3aefe5e5
============userDao2
com.tedu.dao.UserDao@149e0f5d
null
继续测试:
修改UserService放开@Qualifier(“userDao”)注释
@Qualifier("userDao")
@Autowired(required = false)
private UserDao userDao;
执行测试类,发现此时虽然userDao2添加了@Primary注解,但是由于@Qualifier(“userDao”)的指定并没有生效,所以@Primary不能与@Qualifier同用,会失效:
============com.tedu.dao.UserDao@7880cdf3
============null
com.tedu.dao.UserDao@7880cdf3
null
@Autowired 构造器,方法,属性都能标注
-
标注在方法上:
Spring创建当前对象,就会调用方法,完成赋值,方法使用的参数,自定义类型的值从ioc容器中获取 -
标注在构造器:
默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象再进行初始化赋值的操作构造器使用的参数,自定义类型的值从ioc容器中获取 如果组件只有一个有参构造器,@Autowired可以省略 -
放在参数位置:
参数为自定义类型的值从ioc容器中获取
开始验证:
首先看测试Bean:
public class Yellow {
public Yellow() {
System.out.println("Yellow....constructor.....");
}
}
public class Color {
private Yellow yellow;
public Color() {
System.out.println("Color....constructor.....");
}
public Yellow getYellow() {
return yellow;
}
@Autowired
public void setYellow(Yellow yellow) {
System.out.println("Color....setYellow...." + yellow);
this.yellow = yellow;
}
}
配置类用@Bean的方式将Color,Yellow注入到IOC容器中
测试代码如下:
@Test
public void test2() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
System.out.println("容器创建完成....");
Yellow bean = applicationContext.getBean(Yellow.class);
System.out.println(bean);
}
我们看输出发现,默认组件注册的是单例Bean,会在容器创建的过程中执行构造函数,然后我们看加了@Autowired注解的set方法会被执行,同时我们输出set方法的参数yellow,与测试方法中输出的从容器中获取的yellow比较发现是同一个地址,代表是同一个对象
Color....constructor.....
Yellow....constructor.....
Color....setYellow....com.tedu.bean.Yellow@79b06cab
容器创建完成....
com.tedu.bean.Yellow@79b06cab
作为比较我们将Color类中set方法上标注的@Autowired注解注释掉
//@Autowired
public void setYellow(Yellow yellow) {
System.out.println("Color....setYellow...." + yellow);
this.yellow = yellow;
}
输出中并没有执行set方法:
Color....constructor.....
Yellow....constructor.....
容器创建完成....
com.tedu.bean.Yellow@49b0b76
继续测试:
改造Color类:
@Component
public class Color {
private Yellow yellow;
public Color() {
System.out.println("Color....无参constructor.....");
}
@Autowired
public Color(Yellow yellow) {
System.out.println("Color....有参constructor....." + yellow);
}
public Yellow getYellow() {
return yellow;
}
public void setYellow(Yellow yellow) {
System.out.println("Color....setYellow...." + yellow);
this.yellow = yellow;
}
}
修改配置类使用组件扫描的方式注入组件,注释@Bean方式注入的Color
运行测试方法通过输出我们可以看到,单例Yellow先执行构造函数,然后是单例Color的有参构造执行,其中有参构造的参数yellow与我们从测试方法中直接从容器中获取的yellow对象地址一样,是同一对象:
Yellow....constructor.....
Color....有参constructor.....com.tedu.bean.Yellow@794cb805
容器创建完成....
com.tedu.bean.Yellow@794cb805
注意:如果组件只有一个有参构造器,@Autowired可以省略此处不做赘述,请自行验证,反正我验证完了,哈哈!
也可以放在参数位置如:
public Color(@Autowired Yellow yellow) {
System.out.println("Color....有参constructor....." + yellow);
}
public void setYellow(@Autowired Yellow yellow) {
System.out.println("Color....setYellow...." + yellow);
this.yellow = yellow;
}
效果是一样的,此处也不再赘述!
继续验证:
注释Color中所有的@AutoWired注解,及@Component注解,
配置类中用@Bean注册Color组件
//注意此处@Autowired可以省略
@Bean()
public Color color(@Autowired Yellow yellow) {
Color color = new Color();
color.setYellow(yellow);
System.out.println(yellow);
return color;
}
执行测试方法我们发现,@Bean标注的方法参数中yellow被自动注入且与容器中直接获取的yellow对象为同一个,注意此处的@Autowired可以省略
Yellow....constructor.....
Color....无参constructor.....
Color....setYellow....com.tedu.bean.Yellow@778d1062
com.tedu.bean.Yellow@778d1062
容器创建完成....
com.tedu.bean.Yellow@778d1062
二、@Resource 自动注入
- @Resource() JSR250规范,是java注解规范
- @Resource:默认按id装配 不支持@Primary 与@Autowired中的(required=false)这个功能
三、@Inject 自动注入
- @Inject(JSR330)java规范注解加粗样式
- @Inject 需要导入javax的包 和@Autowired支持功能一样(支持@Primary 指定那个Bean优先),只不过没有@Autowired(required=false)这个功能。