属性注入(注解方式)
有以下四种方式实现对属性的注入:
- @Autowired : 根据属性类型进行自动装配
- @Qualifier : 根据属性名称进行自动装配
- 注意: 该注解要与@Autowired注解联合使用
- @Resource : 可以根据类型注入, 可根据名称注入
- @Value : 注入普通类型属性(该注解不是自动装配)
自动装配就是让应用程序上下文为你找出依赖项的过程。说的通俗一点,就是Spring会在上下文中自动查找,并自动给bean装配与其关联的属性
1. @Autowired
- 根据属性类型进行自动装配, 如果有多个该属性类型的bean实例, 那么就会出现异常, spring也不知道到底给你装配哪个bean作为该属性值
- 这里我们以在Service层中调用Dao层进行一个举例:
- 创建Service类和Dao类
package com.ffyc.spring.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserDao userDao;
}
package com.ffyc.spring.service;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
}
- 注意: 这里有一点很重要, 我们在UserService类中并没有提供对应的userDao属性的setter方法, 并且也没有提供对应的userDao属性的有参构造器, 但是我们可以发现userDao属性还是被赋值了, 那么这里是如何进行赋值的?
- **这里其实执行过程如下: **
- 判断是否有该属性的setter方法, 如果有, 直接用无参构造创建对象, 然后用setter方法赋值
- 如果有setter方法, 但是没有无参构造:
1-1: 判断是否有对应的有参构造, 如果有, 则直接使用对应的有参构造创建对象并完成赋值 - 如果有无参构造, 没有setter()方法, 也没有对应的有参构造, 那么使用无参构造创建对象, 并直接为其成员变量赋值(即使是私有成员变量, 也会在底层开放权限之后为其赋值)
- 注意: 我们反射中对于私有属性在直接赋值时要先调用setAccessible()方法获取权限就是为了让不直接对私有属性进行操作, 保持封装性, 但是你这里直接在外界对私有属性操作, 这就是在破坏封装性, 所以我们不建议这么做
- 如果没有对应属性的setter方法, 没有对应的构造器, 也没有无参构造器, 那么这个时候就会报出异常, 因为如果是对属性直接赋值, 那我们也要使用反射来通过无参构造器创建对象呀(底层源码就是这么写的, 你总不能什么也没有吧?)
- 注意: 使用注解自动装配和使用XML方式自动装配有以下不同:
- 使用XML方式自动装配如果没有setter方法和没有对应构造器, 则会直接报出异常, 而注解方式则是直接用无参创建对象后直接为其属性赋值(甚至私有属性都可以直接赋值)
- 使用XML方式自动装配, 如果没有无参, 有对应setter方法和对应有参构造, 那么调用对应有参构造之后还会调用一次setter方法, 也就是只要有setter方法, 那么只要你能创建出这个对象, 最终都会调用一次setter方法, 而使用注解方式的自动装配则不是这样, 使用注解方式自动装配, 如果有setter方法, 但没有无参, 但是有对应属性的有参构造的时候使用对应属性的有参构造创建对象并初始化之后就不会调用对应的setter方法了
- 进行一个测试:
@Test
public void test7(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
//获取对应的bean实例并打印
UserService userService = applicationContext.getBean("userService", UserService.class);
System.out.println(userService);
}
2.@Qualifier:
- 根据属性名称进行自动装配, @Qualifier注解中的value属性值匹配bean标签的id属性值
- @Qualifier注解要和@Autowired注解一起配合才能使用, 我们要将@Autowired注解写在上面, 先定位到某种类型
- 如果这种类型只在SpringIOC容器中只有一个bean实例的时候, 这个时候我们直接就根据类型锁定并完成了自动装配
- 如果这种类型在SpringIOC容器中有多个bean实例的时候, 这个时候我们就再根据bean的id属性值来锁定到某个bean实例, 将该bean实例装配到对应的属性中
Spring底层就是这么个执行顺序, 就是先用@Autowired注解匹配bean类型, 然后是@Qualifier注解匹配bean的id值
eg:
package com.ffyc.spring.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
@Qualifier(value = "userDao")
UserDao userDao;
@Override
public String toString() {
return "UserService{" +
"userDao=" + userDao +
'}';
}
}
3. @Resource
- 可以根据属性类型注入, 也可以根据该注解的name属性值匹配bean标签id值注入
- 当直接在属性上声明一个@Resource注解, 而不给name成员变量赋值时, 就是根据类型注入
- 当@Resource注解指明了name成员变量值之后就是根据该name属性值对应bean标签id值进行注入
- 注意: @Resource是javax.annotation包下的一个注解, 所以Spring不推荐使用@Resource注解来对属性注入, 是Java扩展包下的, 而不是Spring自带的
eg:
package com.ffyc.spring.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class UserService {
@Resource(name = "userDao")
UserDao userDao;
@Override
public String toString() {
return "UserService{" +
"userDao=" + userDao +
'}';
}
}
4. @Value
- 注入普通类型属性(注意: 这类的普通属性可不是说就是基本类型属性, 比如String就算做了这里的普通类型属性之一)
- 直接在普通属性(例如: String类型属性)上加@Value注解, 在@Value注解中有一个成员变量value就是用来指定要注入的值的(我们之前就讲过: 如果我们只为注解中的value成员变量赋值, 那么我们可以直接写值, 不用加value键, 也就是@Value(value = “123”)变成了@Value(“123”))
- @Value注解中的value属性值是一个String类型, 在该value属性值中可以使用${}方式取出.properties属性文件中的值
eg:
package com.ffyc.spring.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired()
@Qualifier(value = "userDao")
UserDao userDao;
@Value("${p.username}")
String s;
public UserService(UserDao userDao){
System.out.println(111);
this.userDao = userDao;
}
@Override
public String toString() {
return "UserService{" +
"userDao=" + userDao +
", s='" + s + '\'' +
'}';
}
}
给出属性文件(放在text的resource目录之下, 因为我们这里是通过Junit测试的, 所以将该属性文件放到resource目录之下)
p.username=hahaha
- 注意: properties属性文件中就是一些键值对, 键和值都是String类型的