spring中給bean的屬性賦值
- xml文件properties標簽設置
<bean id="student" class="com.enjoy.study.cap10.Student" > <property name="id" value="18"/> <property name="name" value="wxf"/> </bean>
- 注解
- @Autowired
- @Value
- @Resource JSR250
- @Inject JSR330
本章博客重點介紹注解賦值的使用
@Autowired
自動裝配:Spring利用依賴注入完成對IOC容器中各個組件的依賴關系賦值
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
可以在構造方法、方法、參數、屬性以及注解上使用,最常用的就是在屬性上使用
不管使在什么地方使用,都是從IOC容器中獲取bean
基本使用
@Configuration @ComponentScan("com.enjoy.study.cap11") public class CapMainConfig { }
@Repository
public class TestDao {
public void test(){
System.out.println("---------TestDao test()--------");
}
}
@Service
public class TestService {
@Autowired
private TestDao testDao;
public void test() {
System.out.println("---------TestService test()--------"+testDao);
}
public TestDao getTestDao() {
return testDao;
}
public void setTestDao(TestDao testDao) {
this.testDao = testDao;
}
}
測試
public class TestCap {
@Test
public void testMethod(){
ApplicationContext context = new AnnotationConfigApplicationContext(CapMainConfig.class);
TestDao testDao = (TestDao) context.getBean("testDao");
TestService service = (TestService) context.getBean("testService");
TestDao testDao1 = service.getTestDao();
System.out.println(testDao==testDao1);
}
}
結果
true
結論:
- @Autowired注解先按照類型去IOC容器中找到相應組件,再將id為testDao的bean取出並注入TestService的testDao屬性中
- 如果沒有找到該id對應的bean,就將相同類型的bean注入,不會報錯
優先級
當容器中有多個相同類型的bean,使用@Autowired注解注入哪一個?
@Configuration @ComponentScan("com.enjoy.study.cap11") public class CapMainConfig { @Bean("testDao2") public TestDao testDao(){ TestDao testDao = new TestDao(); testDao.setFlag(2); return testDao; } }
@Repository
public class TestDao {
private int flag = 1;
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
this.flag = flag;
}
@Override
public String toString() {
return "TestDao{" +
"flag=" + flag +
'}';
}
}
@Service
public class TestService {
@Autowired
private TestDao testDao2;
public TestDao getTestDao() {
return testDao2;
}
public void setTestDao(TestDao testDao) {
this.testDao2 = testDao;
}
}
測試類 public class TestCap { @Test public void testMethod(){ ApplicationContext context = new AnnotationConfigApplicationContext(CapMainConfig.class); TestService service = context.getBean(TestService.class); TestDao testDao1 = service.getTestDao(); System.out.println(testDao1); } }
結果
TestDao{flag=2}
結論:
TestService中注入的是testDao2;另外,如果TestService中定義private TestDao testDao;那么結果是TestDao{flag=1},也就是TestService中注入的是testDao
指定注入哪個bean
如果TestService中定義private TestDao testDao;還是想要注入testDao,那么可以使用@Qualifier+@Autowired
@Service
public class TestService {
@Qualifier("testDao") @Autowired
private TestDao testDao2;
public TestDao getTestDao() {
return testDao2;
}
public void setTestDao(TestDao testDao) {
this.testDao2 = testDao;
}
}
結果
TestDao{flag=1}
required屬性
當容器中不存在該類型的bean時:
將配置類中的@Bean和TestDao類的@Repository注釋掉
@Configuration @ComponentScan("com.enjoy.study.cap11") public class CapMainConfig { //@Bean("testDao") public TestDao testDao(){ TestDao testDao = new TestDao(); testDao.setFlag(2); return testDao; } }
//@Repository public class TestDao { private int flag = 1; public int getFlag() { return flag; } public void setFlag(int flag) { this.flag = flag; } @Override public String toString() { return "TestDao{" + "flag=" + flag + '}'; } } @Service public class TestService { @Autowired private TestDao testDao2; public TestDao getTestDao() { return testDao2; } public void setTestDao(TestDao testDao) { this.testDao2 = testDao; } }
public class TestCap { @Test public void testMethod(){ ApplicationContext context = new AnnotationConfigApplicationContext(CapMainConfig.class); TestService service = context.getBean(TestService.class); TestDao testDao1 = service.getTestDao(); System.out.println(testDao1); } }
結果 org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.enjoy.study.cap11.dao.TestDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
結論:
啟動容器,兩個TestDao類型的bean都不會被加載,並且提示異常報錯;原因是@Autowired注解的required屬性默認為true,即要求容器中必須存在bean,否則拋出異常
使用@Autowired(required=false)即可解決
@Service public class TestService { @Autowired(required = false) private TestDao testDao2; public TestDao getTestDao() { return testDao2; } public void setTestDao(TestDao testDao) { this.testDao2 = testDao; } }
結果
null
結論:
因為容器中沒有TestDao類型的bean,所以打印結果為null
驗證@Qualifier和@Primary加載順序
@Primary的作用是使得該bean默認優先被使用
應用場景:多個類都需要注入一個bean時,如果每個類都使用@Qualifier指定注入相同的bean會導致代碼冗余,這時可以使用@Primary直接在該bean上作用即可
@Configuration
@ComponentScan("com.enjoy.study.cap11")
public class CapMainConfig {
@Primary
@Bean("testDao2")
public TestDao testDao(){
TestDao testDao = new TestDao();
testDao.setFlag(2);
return testDao;
}
}
@Repository
public class TestDao {
private int flag = 1;
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
this.flag = flag;
}
@Override
public String toString() {
return "TestDao{" +
"flag=" + flag +
'}';
}
}
@Service
public class TestService {
@Qualifier("testDao") @Autowired(required = false)
private TestDao testDao2;
public TestDao getTestDao() {
return testDao2;
}
public void setTestDao(TestDao testDao) {
this.testDao2 = testDao;
}
}
public class TestCap { @Test public void testMethod(){ ApplicationContext context = new AnnotationConfigApplicationContext(CapMainConfig.class); TestDao testDao = context.getBean(TestDao.class); System.out.println("context.getBean(TestDao.class) = " + testDao); TestService service = context.getBean(TestService.class); TestDao testDao1 = service.getTestDao(); System.out.println("service.getTestDao() = " + testDao1); } }
結果
context.getBean(TestDao.class) = TestDao{flag=2}
service.getTestDao() = TestDao{flag=1}
結論1:
- TestService中注入的是testDao,直接從容器中獲取時取到的是testDao2
- 說明@Qualifier是根據bean的id獲取的bean,不受@Primary影響
修改TestService類
@Service
public class TestService {
// @Qualifier("testDao")
@Autowired(required = false)
private TestDao testDao;
public TestDao getTestDao() {
return testDao;
}
public void setTestDao(TestDao testDao) {
this.testDao = testDao;
}
}
結果
context.getBean(TestDao.class) = TestDao{flag=2} service.getTestDao() = TestDao{flag=2}
結論2:
- 注入的都是@Primary指定的testDao2
- 通過@Primary標記的bean,默認首先被使用
@Value
@Configuration public class CapMainConfig { @Bean public Student student(){ return new Student(); } }
public class TestCap { @Test public void testMethod(){ ApplicationContext context = new AnnotationConfigApplicationContext(CapMainConfig.class); Student student = (Student) context.getBean("student"); System.out.println("student = " + student); } }
字符形式@Value("")
public class Student { @Value("12") private Integer id; @Value("wxf") private String name; @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + '}'; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
結果
student = Student{id=12, name='wxf'}
@Value("#{}")
@Value("#{}") 表示SpEl表達式,通常用來獲取bean的屬性,或者調用bean的某個方法
容器中bean類:
public class TestBean { @Value("qf") private String name; @Value("12") private Integer id; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
public class Student { @Autowired private TestBean testBean; //表達式,或者字符形式#{1} @Value("#{12-1}") private String sex; //bean屬性 @Value("#{testBean.id}") private Integer id; //bean方法 @Value("#{testBean.getName()}") private String name; public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student{" + "sex='" + sex + '\'' + ", id=" + id + ", name='" + name + '\'' + '}'; } }
配置類
@Configuration public class CapMainConfig { @Bean public TestBean testBean(){ return new TestBean(); } @Bean public Student student(){ return new Student(); } }
測試類
public class TestCap { @Test public void testMethod(){ ApplicationContext context = new AnnotationConfigApplicationContext(CapMainConfig.class); Student student = (Student) context.getBean("student"); System.out.println("student = " + student); } }
結果
student = Student{sex='11', id=12, name='qf'}
@Value("${}")
通過@Value("${}") 可以獲取對應屬性文件中定義的屬性值
想要使用${}方式獲取值
首先通過@PropertySource注解將properties配置文件中的值存儲到Spring的 Environment中,Environment接口提供方法去讀取配置文件中的值,參數是properties文件中定義的key值
@Configuration @PropertySource(value={"classpath:test.properties","classpath:test2.properties"}) public class MainConfig { @Bean public Person person(){ return new Person(); } }
測試
配置文件 test.properties文件 TestStr=12345 test2.properties文件 TestStr1=123456789
bean類
public class Person {
@Value("${TestStr}")
private Integer id;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
測試 public class TestCap { @Test public void testM(){ ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class); Person person = context.getBean(Person.class); System.out.println("person.getId() = " + person.getId()); } }
結果
person.getId() = 12345
@Resource
基本使用
@Configuration
@ComponentScan("com.enjoy.study.cap11")
public class CapMainConfig {
@Primary
@Bean("testDao2")
public TestDao testDao(){
TestDao testDao = new TestDao();
testDao.setFlag(2);
return testDao;
}
}
@Repository
public class TestDao {
private int flag = 1;
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
this.flag = flag;
}
@Override
public String toString() {
return "TestDao{" +
"flag=" + flag +
'}';
}
}
@Service
public class TestService {
@Resource
private TestDao testDao;
public TestDao getTestDao() {
return testDao;
}
public void setTestDao(TestDao testDao) {
this.testDao = testDao;
}
}
public class TestCap { @Test public void testMethod(){ ApplicationContext context = new AnnotationConfigApplicationContext(CapMainConfig.class); TestDao testDao = context.getBean(TestDao.class); System.out.println("context.getBean(TestDao.class) = " + testDao); TestService service = context.getBean(TestService.class); TestDao testDao1 = service.getTestDao(); System.out.println("service.getTestDao() = " + testDao1); } }
結果
context.getBean(TestDao.class) = TestDao{flag=2} service.getTestDao() = TestDao{flag=1}
結論:
- @Primary對@Resource並不生效
- @Resource和@Autowired一樣可以裝配bean
name屬性
可以指定注入哪個bean
@Service
public class TestService {
@Resource(name = "testDao2")
private TestDao testDao;
public TestDao getTestDao() {
return testDao;
}
public void setTestDao(TestDao testDao) {
this.testDao = testDao;
}
}
結果
context.getBean(TestDao.class) = TestDao{flag=2} service.getTestDao() = TestDao{flag=2}
bean為null的情況
將TestDao上的@Repository和配置類中的@Bean都注釋掉,再測試會拋出異常
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'testDao2' available
所以@Resource並不支持類似@Autowired(required=false)的功能
@Inject
基本使用
需要導入javax.inject的包
<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>
測試
@Configuration @ComponentScan("com.enjoy.study.cap11") public class CapMainConfig { @Primary @Bean("testDao2") public TestDao testDao(){ TestDao testDao = new TestDao(); testDao.setFlag(2); return testDao; } } @Repository public class TestDao { private int flag = 1; public int getFlag() { return flag; } public void setFlag(int flag) { this.flag = flag; } @Override public String toString() { return "TestDao{" + "flag=" + flag + '}'; } } @Service public class TestService { @Inject private TestDao testDao; public TestDao getTestDao() { return testDao; } public void setTestDao(TestDao testDao) { this.testDao = testDao; } } public class TestCap { @Test public void testMethod(){ ApplicationContext context = new AnnotationConfigApplicationContext(CapMainConfig.class); TestDao testDao = context.getBean(TestDao.class); System.out.println("context.getBean(TestDao.class) = " + testDao); TestService service = context.getBean(TestService.class); TestDao testDao1 = service.getTestDao(); System.out.println("service.getTestDao() = " + testDao1); } } 結果 context.getBean(TestDao.class) = TestDao{flag=2} service.getTestDao() = TestDao{flag=2}
結論:
@Inject支持@Primary
bean為null的情況
將TestDao上的@Repository和配置類中的@Bean都注釋掉,再測試會拋出異常
org.springframework.beans.factory.NoSuchBeanDefinitionException
@Inject並不支持類似@Autowired(required=false)的功能