通過構(gòu)造方法注入
public class UserServiceImpl implements UserService {
? ? private UserDao userDao;
? ? public UserServiceImpl(UserDao userDao) {
? ? ? ? this.userDao = userDao;
? ? }
? ? /**繼承自UserService的方法**/
}
首先定義一個服務(wù)層UserServiceImpl,然后在其內(nèi)部增加對dao層的引用userDao。
接下來就是添加一個構(gòu)造方法public UserServiceImpl(UserDao userDao)以待Spring通過這個方法為userDao注入實例。
<!--注冊userDao-->
<bean id="userDao" class="com.klasdq.sb.c1.di.dao.impl.UserDaoImpl"></bean>
<!--注冊userService 并注入userDao-->
<bean id="userService" class="com.klasdq.sb.c1.di.service.impl.UserServiceImpl">
? ? ? ? <constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
最后在Spring XML配置文件中注入相應(yīng)的bean實例。
通過構(gòu)造方法的注入,必須要注入類中具有對應(yīng)的構(gòu)造方法,若沒有對應(yīng)的構(gòu)造方法,會出現(xiàn)報錯。
通過setter方法注入
修改UserServiceImpl.java為:
public class UserServiceImpl implements UserService {
? ? private UserDao userDao;
? ? public void setUserDao(UserDao userDao) {
? ? ? ? this.userDao = userDao;
? ? }
? ? /**繼承自UserService的方法**/
}
再修改XML文件內(nèi)容為:
<!--注冊userDao-->
<bean id="userDao" class="com.klasdq.sb.c1.di.dao.impl.UserDaoImpl"></bean>
<!--注冊userService 并注入userDao-->
<bean id="userService" class="com.klasdq.sb.c1.di.service.impl.UserServiceImpl">
? ? ? ? <property name="userDao" ref="userDao"></property>
</bean>
這兩種方式的區(qū)別在于,一、UserServiceImpl.java可以不用添加構(gòu)造方法,但是必須存在一個無參構(gòu)造方法(如public UserServiceImpl(),示例里面沒寫,是因為java默認會提供一個無參構(gòu)造方法)以供Spring 容器注冊生成Bean(如userService)。二、XML文件中,采用構(gòu)造方法注入時,需要使用<constructor-arg ></constructor-arg>這對標簽;而在setter方法注入時,使用<property ></property>標簽。
在XML注入過程中,除了使用ref=""引用之外,還可以使用value=""設(shè)定具體的值,其效果和使用注解@Value差不多。
基于注解的依賴注入
@Autowired
源碼
@Target({ElementType.CONSTRUCTOR,
? ? ? ? ElementType.METHOD,
? ? ? ? ElementType.PARAMETER,
? ? ? ? ElementType.FIELD,
? ? ? ? ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
? ? boolean required() default true;
}
@Autowired是基于注解的依賴注入的關(guān)鍵點,它的源碼非常簡單,只有一個參數(shù)request(),這個參數(shù)的作用是標識注入Bean是否一定要注入,也就是說,在Spring容器沒有找到相應(yīng)Bean時,如果其值為true,就會報出異常;如果其值為false,就不會出現(xiàn)異常,但在使用過程中,如果容器一直不對Bean進行注入,那么有可能出現(xiàn)空指針異常。
另外一點就是,源碼當中的@Target所包含的參數(shù)正好就是基于注解的依賴注入的注入方式種類,@Target決定了@Autowired能夠標注在哪些類型上面。
通過構(gòu)造方法注入
@Service("userService")
public class UserServiceImpl implements UserService {
? ? private UserDao userDao;
? ? @Autowired
? ? public UserServiceImpl(UserDao userDao) {
? ? ? ? this.userDao = userDao;
? ? }
? ? /**繼承自UserService的方法**/
}
根據(jù)開發(fā)文檔的說法,這種只有一個構(gòu)造方法的情況,自Spring4.3以后,就不再需要添加@Autowired標注,也可以。但是,如果有多個構(gòu)造方法時,是必須要對其中一個方法標注@Autowired,不然Spring會報出異常。
通過setter方法注入
@Service("userService")
public class UserServiceImpl implements UserService {
? ? private UserDao userDao;
@Autowired
? ? public void setUserDao(UserDao userDao) {
? ? ? ? this.userDao = userDao;
? ? }
? ? /**繼承自UserService的方法**/
}
通過字段注入
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
? ? private UserDao userDao;
? ? /**繼承自UserService的方法**/
}
通過方法入?yún)⒆⑷?/p>
上面三種注入方式,都是比較熟悉的就不再多做闡述了。重點說一下參數(shù)注入,其實方法入?yún)⒆⑷敕绞礁杏X上是和構(gòu)造方法、setter方法注入形式差不多,相當于將構(gòu)造方法、setter方法上的注解@Autowired放到入?yún)⒌奈恢?。說起來可能有些抽象,直接看例子:
@Component
public class UserDaoImpl implements UserDao {
? ? //簡單返回一個User,模擬數(shù)據(jù)庫查找過程
? ? @Override
? ? public User getUser(Long id, String name){
? ? ? ? User user = new User();
? ? ? ? user.setId(id);
? ? ? ? user.setName(name);
? ? ? ? user.setAccount("12345678911");
? ? ? ? user.setPassword("******");
? ? ? ? user.setOtherInfo("this is a test account");
? ? ? ? return user;
? ? }
}
//UserService類
@Service("userService")
public class UserServiceImpl implements UserService {
? ? private UserDao userDao;
? public UserServiceImpl(@Autowired UserDao userDao,
? ? ? ? ? ? ? ? ? ? ? ? ? @Autowired User user) {
? ? ? System.out.println("UserServiceImpl: "+user);
? ? ? this.userDao = userDao;
? }
? ? @Override
? ? public User getUser(Long id, String name){
? ? ? ? return userDao.getUser(id,name);
? ? }
}
//簡單的配置類
//作用就是為標有@Componet(@Service也算)注解的類 生成Bean
//同時 為@Autowired標識下的Bean(對象) 注入實例
@Configuration
@ComponentScan
public class DIConfig {
? ? //用于Service類中入?yún)ser的注入
? ? @Bean
? ? public User getUser(){
? ? ? ? User u = new User();
? ? ? ? u.setName("user inject into service");
? ? ? ? return u;
? ? }
}
//測試類
//注意:使用JUnit4測試時,如果需要使用@Autowired注入那么必須添加
//@RunWith 標注使用Spring方式啟動(或者SpringBootRunner)
//@ContextConfiguration? 掃描配置類
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = DIConfig.class)
public class DITest {
? ? //如果不添加測試類上兩個注解,會注入失敗
? ? @Autowired
? ? private UserService userService;
? ? @Test
? ? public void testAutowired(){ System.out.println(userService.getUser(1L,"name"));
? ? }
}
運行測試方法之后就得到以下結(jié)果:
public UserServiceImpl(@Autowired UserDao userDao,@Autowired User user)中的輸出結(jié)果:
di-test1.png
public void testAutowired()測試方法中的輸出結(jié)果:
di-test2.png
注意這里public UserServiceImpl(@Autowired UserDao userDao,@Autowired User user)的入?yún)ⅲ?/p>
userDao是UserServiceImpl的字段,但user不是。也就是說,我們可以在構(gòu)造方法中添加任意參數(shù),只要是我們需要的,不一定要求該參數(shù)是類中屬性字段。
此外還有需要注意的是,這里所說的方法,不是任意的方法,而是構(gòu)造方法或setter方法,這種public void initService(@Autowired UserDao userDao)自定義的方法是無法完成注入的。
@Primary 和 @Qualifier
在上面的例子中,我們注入使用到的bean,都只是容器中只有一個Bean實例的情況。那么當容器當中出現(xiàn)多個同類型的Bean時,如何處理呢?
修改配置類代碼如下:
@Configuration
@ComponentScan
public class DIConfig {
? ? @Bean
? ? public User getUser(){
? ? ? ? User u = new User();
? ? ? ? u.setName("this is user");
? ? ? ? return u;
? ? }
? ? @Bean
? ? public User getUser2(){
? ? ? ? User u = new User();
? ? ? ? u.setName("this is user2");
? ? ? ? return u;
? ? }
}
修改測試類:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = DIConfig.class)
public class DITest {
? ? @Autowired
? ? private User user;
? ? @Test
? ? public void testAutowiredPriamry(){
? ? ? ? System.out.println(user);
? ? }
}
當不做其他處理時,結(jié)果為:
di-test-priority.png
因為有兩個User Bean(getUser , getUser2 ,@Bean未注明的情況下,默認方法名為Bean Name)的存在,所以Spring無法確定使用那個進行注入。
修改方式:
在@Bean中設(shè)置name,如@Bean(name="user"),當名字能夠匹配上private User user;時,也能完成注入。
將private User user改寫成getUser或getUser2任意一個,也能完成注入。道理和上面一樣,Spring首先會按照type進行匹配,如果無法匹配,再按照名字匹配,都匹配不上時,自然拋出異常。
除此之外呢,Spring為我們提供了兩個注解來消除依賴注入時的歧義問題。
@Primary
@Target({ElementType.TYPE, // 類、接口、枚舉類型
? ? ? ? ElementType.METHOD})// 方法
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Primary {
}
@Primary是一個設(shè)定相同類型Bean優(yōu)先級的注解,也就是說,一旦在某個類型上添加@Priamry,當注入時,沒有明確指定Bean時,就會注入被@Priamry標識的Bean。
@Configuration
@ComponentScan
public class DIConfig {
? ? @Primary
? ? @Bean
? ? public User getUser(){
? ? ? ? User u = new User();
? ? ? ? u.setName("this is user");
? ? ? ? return u;
? ? }
? ? @Bean
? ? public User getUser2(){
? ? ? ? User u = new User();
? ? ? ? u.setName("this is user2");
? ? ? ? return u;
? ? }
}
比如上面這樣,在getUser()上添加相應(yīng)注解,測試方法也能正常運行。
但是這種方法的問題就在于@Priamry可以用在很多類上,如果同一類型有多個Bean被標注了@Primary,那么@Priamry就失去了應(yīng)有的效果。
@Qualifier
因此,Spring又提供了@Qualifier這個注解,直接標注在@Autowired注入的Bean上,為其明確指定注入某個Bean。
@Target({ElementType.FIELD,
? ? ? ? ElementType.METHOD,
? ? ? ? ElementType.PARAMETER,
? ? ? ? ElementType.TYPE,
? ? ? ? ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
? ? String value() default "";
}
@Qualifier可以出現(xiàn)任何@Autowired能夠出現(xiàn)的地方,與之配套使用。比如下面這樣:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = DIConfig.class)
public class DITest {
? ? //直接指定使用getUser2進行注入
? ? @Autowired
? ? @Qualifier("getUser2")
? ? private User user;
? ? @Test
? ? public void testAutowiredPriamry(){
? ? ? ? System.out.println(user);
? ? }
}
亞馬遜測評www.yisuping.com