1,@Autowired注解的使用
继续上文深入理解spring注解之@ComponentScan注解中的例子,现在我们需要在UserService中调用UserDao相关操作,那我们可以在UserService中增加如下代码:
import com.zhang.dao.UserDao; @Service public class UserService { @Autowired private UserDao userDao; /** * 增加一个tostring方法 方便测试 */ @Override public String toString() { return "UserService [userDao=" + userDao + "]"; } }
测试代码如下:
AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainScanConfig.class); UserService object = (UserService) applicationContext2.getBean("userService"); System.out.println("实例bean为 === "+object);
运行结果如下:
实例bean为 === UserService [userDao=com.zhang.dao.UserDao@51b279c9]
根据运行结果我们可以发现userDao已经成功注入到UserService中了
假设现在业务中有一种情况是UserDao是第三方提供的服务,我们也不能保证其是否可以成功加入到spring容器中,那我们也不能因为UserDao没能成功注入到spring容器而使我们整个UserService服务都不能使用,那这边我们就来演示一下这种情况,如下我们注释掉UserDao的@Repository注解:
import org.springframework.stereotype.Repository; //@Repository public class UserDao { }
这个时候你再启动测试类会报如下错误:
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userDao'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.zhang.dao.UserDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userDao'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.zhang.dao.UserDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1268) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84) at com.zhang.ApplicationTest.main(ApplicationTest.java:31)
其实很简单这个时候我们只需要在@Autowired注解中加上如下属性:
@Autowired(required=false) private UserDao userDao;
再次运行测试类你会发现错误已经消失只是这个时候userDao是null如下:
实例bean为 === UserService [userDao=null]
2,@Qualifier注解的使用
把UserDao代码修改为如下:
import org.springframework.stereotype.Repository; @Repository public class UserDao { // 给一个默认值 private Integer version = 0; /** * 增加tostring方便测试 */ @Override public String toString() { return "UserDao [version=" + version + "]"; } /** * @return the version */ public Integer getVersion() { return version; } /** * @param version the version to set */ public void setVersion(Integer version) { this.version = version; } }
同时在配置类中增加一个@Bean的UserDao配置如下:
@Bean(value = "userDao2") public UserDao getUserDao(){ UserDao userDao = new UserDao(); userDao.setVersion(2); return userDao; }
运行测试类结果如下:
实例bean为 === UserService [userDao=UserDao [version=0]]
我们可以发现这个时候用的是默认的扫描到的UserDao,这个时候我们把UserService中注入的UserDao改成如下:
@Autowired(required=false) private UserDao userDao2;
继续运行测试类结果如下:
实例bean为 === UserService [userDao=UserDao [version=2]]
从以上运行结果我们可以发现如果有多个同类型的bean默认是根据对应的bean名称注入的,那如果这个时候我们不想使用spring默认的注入方式而是希望根据自己业务需要指定固定的bean,那就是@Qualifier注解表现的时候了如下:
@Qualifier(value="userDao") @Autowired(required=false) private UserDao userDao2;
这个时候不管你UserDao定义什么名字永远只会注入userDao这个bean了
3,@Primary注解的使用
不管是因为洁癖还是什么也好可能有些同学不是特别喜欢使用@Qualifier注解,那么没关系,spring还为我们提供了另外一个注解@Primary同样可以实现以上功能
假设现在我们UserService中注入的UserDao是userDao2,这么这个时候我们可以在配置类中的UserDao上增加@Primary注解如下:
@Primary @Bean(value = "userDao2") public UserDao getUserDao(){ UserDao userDao = new UserDao(); userDao.setVersion(2); return userDao; }
这个时候运行测试类你会发现UserService注入的就是userDao2了
注意:这个时候UserService中的UserDao就不能再加@Qualifier对应的注解了