Spring 注入方式

Spring 通过 DI(依赖注入)实现 IOC(控制反转),常用的注入方式主要有三种:构造方法注入,setter 注入,基于注解的注入

构造方法注入

在 spring 的配置文件中注册 UserService,将 UserDao 通过 constructor-arg 标签注入到 UserService 的与之匹配(参数类型和参数个数匹配)的构造方法中

<!-- 注册 userService -->
<bean id="userService" class="com.spring.service.impl.UserService">
    <constructor-arg ref="userDao"></constructor-arg>
</bean>
<!-- 注册 dao -->
<bean id="userDao" class="com.spring.dao.impl.UserDao"></bean>
public class UserService implements IUserService {

    private IUserDao userDao;
    
    public UserService(IUserDao userDao) {
        this.userDao = userDao;
    }
    
    public void loginUser() {
        userDao.loginUser();
    }

}

如果想向有多个参数的构造方法中注入值,就在配置文件中通过 name 属性指定要注入的值,与构造方法参数列表参数的顺序无关
如果有多个构造方法,每个构造方法只有参数的顺序不同,这种情况下就与构造方法顺序有关

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 注解指定某个具体名称的 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 = descriptor.getDependencyType();
   // 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 = candidateBeans.entrySet().iterator();

           String candidateBeanName;
           Object beanInstance;
           do {
               if (!var6.hasNext()) {
                   return null;
               }

               // 3. 再找 bean 的名称匹配的
               Entry<String, Object> entry = (Entry)var6.next();
               candidateBeanName = (String)entry.getKey();
               beanInstance = entry.getValue();
           } while(!this.resolvableDependencies.values().contains(beanInstance) && !this.matchesBeanName(candidateBeanName, descriptor.getDependencyName()));

           return candidateBeanName;
       }
   }
}

determineAutowireCandidate 方法的逻辑是:

  • 先找 Bean 上有@Primary 注解的,有则直接返回 bean 的 name
  • 再找 Bean 上有 @Order,@PriorityOrder 注解的,有则返回 bean 的 name
  • 最后再以名称匹配(ByName)的方式去查找相匹配的 bean

可以简单的理解为先以 ByType 的方式去匹配,如果匹配到了多个再以 ByName 的方式去匹配,找到了对应的 bean 就去注入,没找到就抛出异常

还有一点要注意:如果使用了 @Qualifier 注解,那么当自动装配匹配到多个 bean 的时候就不会进入 determineAutowireCandidate 方法,而是直接查找与 @Qualifer 指定的 bean name 相同的 bean 去注入,找到了就直接注入,没有找到则抛出异常

ByName 的注入方式和 @Qualifier 有点类似,都是在自动装配匹配到多个 bean 的时候,指定一个具体的 bean,ByName 的方式需要遍历,@Qualifier 直接一次定位。在匹配到多个 bean 的情况下,使用 @Qualifier 来指明具体装配的 bean 效率会更高

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容