Spring IOC的三种主要注入方式?

摘要:
有三种常见的注入方法:构造方法注入、setter注入和基于注释的注入——注册由jdbc实现的dao--˃。如果只有一个带有参数的构造函数,并且参数类型与注入的bean的类型匹配,那么它将被注入到构造函数中。构造函数:通过构造方法自动注入。Spring将使用构造方法的相同参数类型来匹配bean以进行注入。如果有一个多参数构造方法和一个只有一个参数的构造方法,并且在容器中找到了多个与多参数构造方式匹配的bean,那么spring将优先将bean注入到多参数构造法中。

常用的注入方式主要有三种:构造方法注入,setter注入,基于注解的注入。

构造方法注入

先简单了解一下测试项目的结构,用maven构建的,四个包:

    entity:存储实体,里面只有一个User类
    dao:数据访问,一个接口,两个实现类
    service:服务层,一个接口,一个实现类,实现类依赖于IUserDao
    test:测试包

在spring的配置文件中注册UserService,将UserDaoJdbc通过constructor-arg标签注入到UserService的某个有参数的构造方法

<!-- 注册userService --> 
<bean class="com.lyu.spring.service.impl.UserService">
  <constructor-arg ref="userDaoJdbc"></constructor-arg>
</bean>
<!-- 注册jdbc实现的dao -->
<bean 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("applicationContext.xml");
   // 获取bean对象
    UserService userService = ac.getBean(UserService.class, "userService"); 
    // 模拟用户登录
     userService.loginUser(); 
}

 测试打印结果:jdbc-登录成功

注:模拟用户登录的loginUser方法其实只是打印了一条输出语句,jdbc实现的类输出的是:jdbc-登录成功,mybatis实现的类输出的是:mybatis-登录成功。

问题一:如果有多个有参数的构造方法并且每个构造方法的参数列表里面都有要注入的属性,那userDaoJdbc会注入到哪里呢?

public class UserService implements IUserService {
     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(); 
    } 
}

 结果:会注入到只有一个参数的构造方法中,并且经过测试注入哪一个构造方法与构造方法的顺序无关

Spring IOC的三种主要注入方式?第1张

接来来几个问题详情见:https://blog.csdn.net/a909301740/article/details/78379720

setter注入

配置文件如下:

<!-- 注册userService -->
 <bean   class="com.lyu.spring.service.impl.UserService"> 
    <!-- 写法一 --> 
    <!-- <property name="UserDao" ref="userDaoMyBatis"></property> -->
     <!-- 写法二 --> 
    <property name="userDao" ref="userDaoMyBatis"></property> </bean>
     <!-- 注册mybatis实现的dao --> 
<bean   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没有办法实例化对象,导致报错。

Spring IOC的三种主要注入方式?第2张

基于注解的注入
在介绍注解注入的方式前,先简单了解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 = 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 的情况下,使用 @Qualifier 来指明具体装配的 bean 效率会更高一下。

博主个人觉得:@Qualifer 注解出现的意义或许就是 Spring 为了解决 JDK 自带的 ByName 遍历匹配效率低下的问题。要不然也不会出现两个容易混淆的匹配方式。
写在最后:虽然有这么多的注入方式,但是实际上开发的时候自己编写的类一般用注解的方式注册类,用@Autowired描述依赖进行注入,一般实现类也只有一种(jdbc or hibernate or mybatis),除非项目有大的变动,所以@Qualifier标签用的也较少;但是在使用其他组件的API的时候用的是通过xml配置文件来注册类,描述依赖,因为你不能去改人家源码嘛。
---------------------
作者:曲健磊
来源:CSDN
原文:https://blog.csdn.net/a909301740/article/details/78379720

免责声明:文章转载自《Spring IOC的三种主要注入方式?》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇ubuntu下使用mutt+msmtp发送邮件Nginx错误提示:504 Gateway Time-out解决方法下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

shiro框架 4种授权方式 说明

1. shiro的配置文件(applicationContext-shiro.xml)中使用filterChain过滤url的方式 详细配置看注释 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/bean...

Java各种反射性能对比

对各种方法实现get方法的性能进行了一个测试。 总共有5个测试,,每个测试都是执行1亿次 1. 直接通过Java的get方法 2.通过高性能的ReflectAsm库进行测试 3.通过Java Class类自带的反射获得Method测试 4.使用Java自带的Property类获取Method测试 5.BeanUtils的getProperty测试 1 测试...

springboot集成下,mybatis的mapper代理对象究竟是如何生成的

前言 开心一刻    中韩两学生辩论。中:端午节是属于谁的?韩:韩国人!中:汉字是谁发明的?韩:韩国人!中:中医是属于谁的?韩:韩国人!中:那中国人到底发明过什么?韩:韩国人! 前情回顾 Mybatis源码解析 - mapper代理对象的生成,你有想过吗,我们讲到了mybatis操作数据库的流程:先创建SqlSessionFactory,然后创建SqlS...

spring中@value注解需要注意

首先,@value需要参数,这里参数可以是两种形式:@Value("#{configProperties['t1.msgname']}")或者@Value("${t1.msgname}");其次,下面我们来看看如何使用这两形式,在配置上有什么区别:1、@Value("#{configProperties['t1.msgname']}")这种形式的配置中有“...

Spring Boot之@ImportResource、配置类、占位符表达式

一、@ImportResource    spring boot自动装配/自动配置     Spring 扥配置文件 默认会被spring boot自动给配置好。     如果要自己编写spring等配置文件,spring boot能否识别?     当然是可以的。 在resources目录下创建spring.xml文件。 <bean class...

SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例(转)

1.前言 本文主要介绍使用SpringBoot与shiro实现基于数据库的细粒度动态权限管理系统实例。 使用技术:SpringBoot、mybatis、shiro、thymeleaf、pagehelper、Mapper插件、druid、dataTables、ztree、jQuery 开发工具:intellij idea 数据库:mysql、red...