1. 程式人生 > 其它 >9-註解驅動開發-自動裝配@Autowired,@Resource,@Inject

9-註解驅動開發-自動裝配@Autowired,@Resource,@Inject

技術標籤:Springspringjavaiocbean容器

本文知識點來源於尚矽谷,感謝尚矽谷為廣大學子提供的優質教育資源,感謝各位老師熱情指導,本文僅作為學習筆記使用,記錄學習心得,如有不適,請聯絡作者。

自動裝配:
Spring利用依賴注入(DI),完成對IOC容器中各個元件的依賴關係賦值。

一、@Autowired 自動注入

  1. spring框架定義的
  2. 預設按照型別去容器中找,找到就賦值。
  3. 如果找到多個相同型別元件,再將屬性的名稱作為元件的id到容器中去匹配。
  4. 可以使用 @Qualifier(“userDao”)指定需要裝配的元件的id而不是使用屬性名
  5. 自動注入時,容器中沒有這個被注入元件的話會報錯 ,解決方案:@Autowired(required=false) 不必須的
  6. @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 構造器,方法,屬性都能標註

  1. 標註在方法上:
    Spring建立當前物件,就會呼叫方法,完成賦值,方法使用的引數,自定義型別的值從ioc容器中獲取

  2. 標註在構造器:
    預設加在ioc容器中的元件,容器啟動會呼叫無參構造器建立物件再進行初始化賦值的操作構造器使用的引數,自定義型別的值從ioc容器中獲取 如果元件只有一個有參構造器,@Autowired可以省略

  3. 放在引數位置:
    引數為自定義型別的值從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 自動注入

  1. @Resource() JSR250規範,是java註解規範
  2. @Resource:預設按id裝配 不支援@Primary 與@Autowired中的(required=false)這個功能

三、@Inject 自動注入

  1. @Inject(JSR330)java規範註解加粗樣式
  2. @Inject 需要匯入javax的包 和@Autowired支援功能一樣(支援@Primary 指定那個Bean優先),只不過沒有@Autowired(required=false)這個功能。