Spring自動(dòng)裝配之方法、構(gòu)造器位置的自動(dòng)注入操作
Spring自動(dòng)裝配之方法、構(gòu)造器位置的自動(dòng)注入
1. 注解定義
@Autowired的定義信息如下:
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { /** * Declares whether the annotated dependency is required. * <p>Defaults to {@code true}. */ boolean required() default true; }
可以看出,@Autowired注解可以定義在構(gòu)造器上,方法上,方法參數(shù)上,字段上,還有自定義注解上;
2. 注解使用
2.1 定義在造器上
@Autowired //定義在構(gòu)造器方法上 public UserService(UserDao userDao) { this.userDao = userDao; }
或者
// 定義在構(gòu)造器參數(shù)上 public UserService(@Autowired UserDao userDao) { this.userDao = userDao; }
特別注意,當(dāng)一個(gè)類只有一個(gè)有參構(gòu)造器,且該構(gòu)造器不一定需要是public修飾的, 組件注入的時(shí)候不需要指定在構(gòu)造器方法上或者構(gòu)造器參數(shù)上指定@Autowired,只需要聲明構(gòu)造器即可;
2.2 定義在方法和參數(shù)上
定義在Set方法上
@Autowired //定義在set方法上 public void setUserDao(UserDao userDao) { this.userDao = userDao; }
定義在配置Bean的方法上
package com.yibai.spring.annotation.main.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Service; import com.yibai.spring.annotation.service.UserDao; import com.yibai.spring.annotation.service.UserService; //@ComponentScan("com.yibai.spring.annotation.service") @Service public class MainConfigForAutowired { // Spring自動(dòng)從IOC容器中找出UserDao作為方法參數(shù)傳入,這里@Autowired可有可無 @Bean public UserService userService(@Autowired UserDao userDao) { UserService userService = new UserService(); userService.setUserDao(userDao); return userService; } @Bean public UserDao userDao() { return new UserDao(); } }
2.3 定義在字段上
package com.yibai.spring.annotation.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import lombok.Getter; @Getter @Service public class UserService { @Qualifier(value = "userDao") @Autowired(required = true) private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } }
3. 注入位置推薦
一般推薦注入位置放在構(gòu)造器上,因?yàn)椴还茏侄芜€是方法的方式注入,都是先創(chuàng)建組件,再注入依賴的組件,如果在構(gòu)造方法上就需要使用依賴的組件,那么只有在構(gòu)造器上注入才是可以實(shí)現(xiàn)的,因?yàn)閳?zhí)行順序的問題;
package com.yibai.spring.annotation.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import lombok.Getter; @Getter @Service public class UserService { @Qualifier(value = "userDao") @Autowired(required = true) private UserDao userDao; public UserService() { //@Autowired定義在字段或者set方式上,在構(gòu)造器上無法獲取到依賴的組件 System.out.println(userDao); // null } public void setUserDao(UserDao userDao) { this.userDao = userDao; } }
Spring 自動(dòng)注入的幾種方式
Spring 中實(shí)現(xiàn)自動(dòng)裝配的注解有以下幾個(gè):
@Autowired
@Qualifier
@Primary
@Resource
@Inject
一、@Autowired
Spring 中最常用的一個(gè)注解,當(dāng)一個(gè)組件需要另一個(gè)組件作為屬性的時(shí)候,我們可以通過兩種方式對屬性進(jìn)行賦值,一種是通過構(gòu)造方法,一種是通過 set 方法(類比),而這個(gè)注解使用的方法就是后者。
下面介紹該注解的特點(diǎn):
首先是 按照類型 自動(dòng)注入,適用于容器中只有一種該類型的組件;
如果存在多個(gè)相同類型的組件,則將屬性名作為 id 查詢?nèi)萜髦械慕M件并注入;
默認(rèn)屬性對應(yīng)的組件在容器中必須是存在的,如果想無論存在與否都注入可以令屬性 required = false;
可以在該注解的基礎(chǔ)之上使用 @Qualifier("bookDao") 注解指定要注入組件的 id,這時(shí)屬性名的 id 已失效;
如果不使用上述注解指定 id ,存在多個(gè)相同類型的組件時(shí)也可以使用 @Primary 注解設(shè)置 Bean 的優(yōu)先級為最優(yōu)。
@Autowired(required = false) @Qualifier("bookDao2") private BookDao bookDao;
上面注入的組件的 id 值為 bookDao2;
二、@Resource
與 @Autowired 不同,@Resource 注解是 按照屬性名 自動(dòng)注入,它屬于 JSR250 規(guī)范;
該注解不支持 @Qualifier、@Primary 的使用,但是可以使用它的 name 屬性指定要注入組件的 id 值。
@Resource(name = "bookDao3") private BookDao bookDao;
上面注入的組件的 id 值為 bookDao3;
三、@Inject
要使用 @Inject 注解必須要先導(dǎo)包,它屬于 JSR330 規(guī)范 :
<!-- inject 注解 --> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>
導(dǎo)包之后就可以使用了,該注解的效果和 @Autowired 的效果一樣,只不過沒有任何屬性,所以功能有些欠缺,但是可以和另外兩個(gè)注解配合使用。
@Inject private BookDao bookDao;
上面注入的組件的 id 值為 bookDao;
四、@Autowired 的使用方式
該注解主要是通過 BeanPostProcessor 的實(shí)現(xiàn)類 AutowiredAnnotationBeanPostProcessor 實(shí)現(xiàn)的。
該類及其父類重寫了 postProcessBeforeInitialization 方法,在初始化 Bean 之前,先對屬性進(jìn)行賦值,從而實(shí)現(xiàn)自動(dòng)注入。
1、Set 方法
該注解除了可以放在屬性上面,還可以放在方法上面:
@Component public class Boss { private Car car; public Car getCar() { return car; } @Autowired public void setCar(Car car) { this.car = car; } }
可以放在 set 方法上面,他會(huì)自動(dòng)的去 IOC 容器中找方法中的參數(shù),這里的參數(shù)是 car ,所以他會(huì)去容器中找 car 這個(gè)類,然后創(chuàng)建一個(gè)對象完成賦值。
官方 3.X 建議使用該方式注入。
2、構(gòu)造器
對于加在 IOC 容器中的組件,容器啟動(dòng)后會(huì)調(diào)用 無參構(gòu)造器 創(chuàng)建對象進(jìn)行初始化賦值操作。
我們也可以不使用默認(rèn)的,我們提供一個(gè)有參構(gòu)造器:
@Autowired public Boss(Car car) { this.car = car; }
構(gòu)造器要使用的組件,也都是從容器中獲取。
所以也可以這么寫:
public Boss(@Autowired Car car) { this.car = car; }
同時(shí)如果該類只有一個(gè)有參構(gòu)造器,那么 @Autowired 注解 可以省略。
官方 4.X 開始建議使用該方式注入。
3、@Bean + 方法參數(shù)
我們可以不改變 Boss 這個(gè)類,即不在 Boss 中注入 Car,而是在將 Boss 放入容器的時(shí)候注入它需要的參數(shù) Car。
@Bean public Boss boss(@Autowired Car car) { return new Boss(); }
這里的 @Autowired 可以省略,也是用的最多的一種方式。
五、使用 Spring 底層的組件
如果自己寫的組件想要使用 Spring 底層的組件可以使用另一種方式 :比如想要使用 Spring 的 ApplicationContext。
Spring 為我們提供了相關(guān)的接口,他們都是 xxxAware,比如 ApplicationContextAware。
每一個(gè)接口都對應(yīng)一個(gè)方法,我們可以在方法中獲取 Spring 底層的組件,然后給成員變量賦值以獲取相關(guān)組件。
public class Red implements ApplicationContextAware { private ApplicationContext context; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } }
關(guān)于 Aware 的原理:
其實(shí)它還是使用了我們之前說過的 BeanPostProcessor ,每一個(gè) Aware 都對應(yīng)一個(gè) AwareProcessor,這個(gè) processor 正是BeanPostProcessor 的實(shí)現(xiàn)類,所以肯定會(huì)有一個(gè) postProcessBeforeInitialization 方法,我們重點(diǎn)來看一下這個(gè)方法。
@Override @Nullable public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException { AccessControlContext acc = null; if (System.getSecurityManager() != null && (bean instanceof ApplicationContextAware)) { acc = this.applicationContext.getBeanFactory().getAccessControlContext(); } if (acc != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareInterfaces(bean); return null; }, acc); } else { invokeAwareInterfaces(bean); } return bean; }
在 invokeAwareInterfaces(bean); 方法中主要是下面的邏輯:
if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); }
其實(shí)就是先判斷是不是那幾個(gè) Aware 中的一個(gè),如果是就賦值,我們能看到的就是在 初始化 的時(shí)候利用 后置處理器 完成賦值。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
一文帶你徹底了解Java8中的Lambda,函數(shù)式接口和Stream
這篇文章主要為大家詳細(xì)介紹了解Java8中的Lambda,函數(shù)式接口和Stream的用法和原理,文中的示例代碼簡潔易懂,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-08-08java8中NIO緩沖區(qū)(Buffer)的數(shù)據(jù)存儲(chǔ)詳解
在本篇文章中小編給大家分享了關(guān)于java8中NIO緩沖區(qū)(Buffer)的數(shù)據(jù)存儲(chǔ)的相關(guān)知識(shí)點(diǎn),需要的朋友們參考下。2019-04-04Java7和Java8中的ConcurrentHashMap原理解析
這篇文章主要介紹了Java7和Java8中的ConcurrentHashMap原理解析,對ConcurrentHashMap感興趣的讀者,一定要好好看一下2021-04-04Java 運(yùn)算符 動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了Java 運(yùn)算符 動(dòng)力節(jié)點(diǎn)Java學(xué)院整理,需要的朋友可以參考下2017-04-04Java畢業(yè)設(shè)計(jì)實(shí)戰(zhàn)之圖片展覽館管理系統(tǒng)的實(shí)現(xiàn)
這是一個(gè)使用了java+Springboot+SpringMVC+JPA+Jsp+maven+mysql開發(fā)的圖片展覽館管理系統(tǒng),是一個(gè)畢業(yè)設(shè)計(jì)的實(shí)戰(zhàn)練習(xí),具有圖片展覽管理該有的所有功能,感興趣的朋友快來看看吧2022-01-01SpringBoot整合JDBC的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot整合JDBC的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01Mybatis Mapper接口和xml綁定的多種方式、內(nèi)部實(shí)現(xiàn)原理和過程解析
在Mybatis中,我們需要?jiǎng)?chuàng)建一個(gè)與實(shí)體類對應(yīng)的Mapper接口,然后在該接口上添加方法,這些方法對應(yīng)著SQL語句,這篇文章主要介紹了Mybatis Mapper接口和xml綁定的多種方式、內(nèi)部實(shí)現(xiàn)原理和過程,需要的朋友可以參考下2023-11-11