SpringIOC的三种主要注⼊⽅式?
常⽤的注⼊⽅式主要有三种:构造⽅法注⼊,setter注⼊,基于注解的注⼊。
构造⽅法注⼊
先简单了解⼀下测试项⽬的结构,⽤maven构建的,四个包:
entity:存储实体,⾥⾯只有⼀个User类
dao:数据访问,⼀个接⼝,两个实现类
service:服务层,⼀个接⼝,⼀个实现类,实现类依赖于IUserDao
test:测试包
在spring的配置⽂件中注册UserService,将UserDaoJdbc通过constructor-arg标签注⼊到UserService的某个有参数的构造⽅法
<!-- 注册userService -->
<bean id="userService" class="com.lyu.spring.service.impl.UserService">
  <constructor-arg ref="userDaoJdbc"></constructor-arg>
</bean>
<!-- 注册jdbc实现的dao -->
<bean id="userDaoJdbc" class="com.lyu.spring.dao.impl.UserDaoJdbc"></bean>
如果只有⼀个有参数的构造⽅法并且参数类型与注⼊的bean的类型匹配,那就会注⼊到该构造⽅法中。
public class UserService implements IUserService {
private IUserDao userDao;
public UserService(IUserDao userDao) {
this.userDao = userDao;
}
public void loginUser() {
userDao.loginUser();
}
}
@Test
public void testDI() {
ApplicationContext ac = new ClassPathXmlApplicationContext("l");
// 获取bean对象
UserService userService = ac.getBean(UserService.class, "userService");
// 模拟⽤户登录
userService.loginUser();
}
测试打印结果:jdbc-登录成功
注:模拟⽤户登录的loginUser⽅法其实只是打印了⼀条输出语句,jdbc实现的类输出的是:jdbc-登录成功,mybatis实现的类输出的是:mybatis-登录成功。
问题⼀:如果有多个有参数的构造⽅法并且每个构造⽅法的参数列表⾥⾯都有要注⼊的属性,那userDaoJdbc会注⼊到哪⾥呢?
public class UserService implements IUserService {实例化bean的三种方式
private IUserDao userDao;
private User user;
public UserService(IUserDao userDao) {
System.out.println("这是有⼀个参数的构造⽅法");
this.userDao = userDao;
}
public UserService(IUserDao userDao, User user) {
System.out.println("这是有两个参数的构造⽅法");
this.userDao = userDao; this.user = user;
}
public void loginUser() {
userDao.loginUser();
}
}
结果:会注⼊到只有⼀个参数的构造⽅法中,并且经过测试注⼊哪⼀个构造⽅法与构造⽅法的顺序⽆关
setter注⼊
配置⽂件如下:
<!-- 注册userService -->
<bean id="userService" class="com.lyu.spring.service.impl.UserService">
<!-- 写法⼀ -->
<!-- <property name="UserDao" ref="userDaoMyBatis"></property> -->
<!-- 写法⼆ -->
<property name="userDao" ref="userDaoMyBatis"></property> </bean>
<!-- 注册mybatis实现的dao -->
<bean id="userDaoMyBatis" class="com.lyu.spring.dao.impl.UserDaoMyBatis"></bean>
注:上⾯这两种写法都可以,spring会将name值的每个单词⾸字母转换成⼤写,然后再在前⾯拼接上"set"构成⼀个⽅法名,然后去对应的类中查该⽅法,通过反射调⽤,实现
注⼊。
切记:name属性值与类中的成员变量名以及set⽅法的参数名都⽆关,只与对应的set⽅法名有关,下⾯的这种写法是可以运⾏成功的
public class UserService implements IUserService {
private IUserDao userDao1;
public void setUserDao(IUserDao userDao1) {
this.userDao1 = userDao1;
}
public void loginUser() {
userDao1.loginUser();
}
}
还有⼀点需要注意:如果通过set⽅法注⼊属性,那么spring会通过默认的空参构造⽅法来实例化对象,所以如果在类中写了⼀个带有参数的构造⽅法,⼀定要把空参数的构造⽅法写上,否则spring没有办法实例化对象,导致报错。
基于注解的注⼊
在介绍注解注⼊的⽅式前,先简单了解bean的⼀个属性autowire,autowire主要有三个属性值:constructor,byName,byType。
constructor:通过构造⽅法进⾏⾃动注⼊,spring会匹配与构造⽅法参数类型⼀致的bean进⾏注⼊,如果有⼀个多参数的构造⽅法,⼀个只有⼀个参数的构造⽅法,在容器中查到多个匹配多参数构造⽅法的bean,那么spring会优先将bean注⼊到多参数的构造⽅法中。
byName:被注⼊bean的id名必须与set⽅法后半截匹配,并且id名称的第⼀个单词⾸字母必须⼩写,这⼀点与⼿动set注⼊有点不同。
byType:查所有的set⽅法,将符合符合参数类型的bean注⼊。
下⾯进⼊正题:注解⽅式注册bean,注⼊依赖
主要有四种注解可以注册bean,每种注解可以任意使⽤,只是语义上有所差异:
@Component:可以⽤于注册所有bean
@Repository:主要⽤于注册dao层的bean
@Controller:主要⽤于注册控制层的bean
@Service:主要⽤于注册服务层的bean
描述依赖关系主要有两种:
@Resource:java的注解,默认以byName的⽅式去匹配与属性名相同的bean的id,如果没有到就会以byType的⽅式查,如果byType查到多个的话,使⽤
@Qualifier注解(spring注解)指定某个具体名称的bean。
@Resource
@Qualifier("userDaoMyBatis")
private IUserDao userDao;
public UserService(){
}
@Autowired:spring注解,默认是以byType的⽅式去匹配类型相同的bean,如果只匹配到⼀个,那么就直接注⼊该bean,⽆论要注⼊的 bean 的 name 是什么;如果匹配到多个,就会调⽤ DefaultListableBeanFactory 的 determineAutowireCandidate ⽅法来决定具体注⼊哪个bean。determineAutowireCandidate ⽅法的内容如下:
// candidateBeans 为上⼀步通过类型匹配到的多个bean,该 Map 中⾄少有两个元素。 protected String determineAutowireCandidate(Map<String, Object> candidateBeans, DependencyDescriptor descriptor) { //  requiredType 为匹配到的接⼝的类型
Class<?> requiredType = DependencyType();
// 1. 先 Bean 上有@Primary 注解的,有则直接返回
String primaryCandidate = this.determinePrimaryCandidate(candidateBeans, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
} else {
// 2.再 Bean 上有 @Order,@PriorityOrder 注解的,有则返回 String              priorityCandidate = this.determineHighestPriorityCandidate(candidateBeans, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
} else {
Iterator var6 = Set().iterator();
String candidateBeanName;
Object beanInstance;
do {
if (!var6.hasNext()) {
return null;
}
// 3. 再 bean 的名称匹配的
Entry<String, Object> entry = (();
candidateBeanName = (Key();
beanInstance = Value();
}          while(!solvableDependencies.values().contains(beanInstance) && !this.matchesBeanName(candidateBeanName,        DependencyName()));
return candidateBeanName; }
}
}