Spring框架学习 (二) 依赖注入的两类形式

总体而言,依赖注入有两种注册形式,一种在xml中配置申明注册,另一种是用注解的方式注入。
①xml配置申明注册。然后setter、构造器、工厂方法注入;
②注解方式申明注册,然后是注解方式注入
但是在开发中用的较多的是注解的方式。本文主要介绍setter、构造器、接口注入以及注解注入的方式,工厂方法注入

1、Set注入

采用属性的set方法进行初始化,就成为set注入。
1)给普通字符类型赋值。

public class User{
   privateString username;
 
   publicString getUsername() {
       returnusername;
   }
   publicvoid setUsername(String username) {
      this.username= username;
   }
}

我们只需要提供属性的set方法,然后去属性文件中去配置好让框架能够找到applicationContext.xml文件的beans标签。标签beans中添加bean标签, 指定id,class值,id值不做要求,class值为对象所在的完整路径。bean标签再添加property 标签,要求,name值与User类中对应的属性名称一致。value值就是我们要给User类中的username属性赋的值。

<bean id="userAction"class="com.lsz.spring.action.User" >
	<property name="username" value="admin"></property>
</bean>

2)给对象赋值

同样提供对象的set方法

public class User{
     private UserService userservice;
     public UserServicegetUserservice() {
          returnuser;
     }
     public void setUserservice(UserService userservice){
         this.userservice= userservice;
     }
}

配置文件中要增加UserService的bean标签声明及User对象对UserService引用。

<!--对象的声明-->
<bean id="userService" class="com.lsz.spring.service.UserService"></bean>
 
<bean id="userAction"class="com.lsz.spring.action.User" >
   <property name="userservice" ref="userService"></property>
</bean>

这样配置,框架就会将UserService对象注入到User类中。

3)给list集合赋值

同样提供set方法

public class User{
    privateList<String> username;
    publicList<String> getUsername() {
        returnusername;
    }
    publicvoid setUsername(List<String> username) {
        this.username= username;
    }
}
<bean id="userAction"class="com.lsz.spring.action.User" >
     <propertyname="username">
           <list>
               <value>zhang,san</value>
               <value>lisi</value>
               <value>wangwu</value>                                
               </list>
    </property>
</bean>

4)给属性文件中的字段赋值

public class User{
    privateProperties props ;
    publicProperties getProps() {
        returnprops;
    }
    publicvoid setProps(Properties props) {
        this.props= props;
    }
}
<bean>
    <propertyname="props">
        <props>
           <propkey="url">jdbc:oracle:thin:@localhost:orl</prop>
           <propkey="driverName">oracle.jdbc.driver.OracleDriver</prop>
           <propkey="username">scott</prop>
           <propkey="password">tiger</prop>
        </props>
    </property>
</bean>

标签中的key值是.properties属性文件中的名称

注意:

无论给什么赋值,配置文件中标签的name属性值一定是和对象中名称一致。

2构造方法注入

1)构造方法一个参数

public class User{
    privateString usercode;
    publicUser(String usercode) {
        this.usercode=usercode;
    }
}
<bean id="userAction"class="com.lsz.spring.action.User">                        
    <constructor-argvalue="admin"></constructor-arg>                        
</bean>

2)构造函数有两个参数时

当参数为非字符串类型时,在配置文件中需要制定类型,如果不指定类型一律按照字符串类型赋值。

当参数类型不一致时,框架是按照字符串的类型进行查找的,因此需要在配置文件中制定是参数的位置

<constructor-argvalue="admin"index="0"></constructor-arg>                
<constructor-argvalue="23" type="int"index="1"></constructor-arg>

这样制定,就是构造函数中,第一个参数为string类型,第二个参数为int类型

3、接口注入

接口注入模式因为历史较为悠久,在很多容器中都已经得到应用。但由于其在灵活性、易用性上不如其他两种注入模式,因而在 IOC的专题世界内并不被看好。
接口注入有点复杂,被注入对象如果想要IOC容器为其注入依赖对象,就必须实现某个接口,这个接口提供一个方法,用来为被注入对象注入依赖对象,IOC容器通过接口方法将依赖对象注入到被注入对象中去。相对于前两种注入方式,接口注入比繁琐和死板,被注入对象就必须专声明和实现另外的接口。在接口注入的举例中,我们不再列出配置文件XML中的代码。只是通过如下代码让大家了解接口注入概念的思想。 Logic是一个接口,LogicImpl是接口的一个实现类,LogicAction是接口注入实现代码。示例如下所示

接口Logic的代码如下

public interface Logic {
 
public String getName();
}

接口Logic的实现类LogicImpl

public class LogicImpl implements Logic{
 
public String getName(){
 
  return "fengyun";
 
}
}

LogicAction.java 实现的例子

public class LoginAction {
 
private Logic logic;
 
 
 
public void execute() {
 
  try {
 
    Object obj = Class.forName("com.spring.test.di.LogicImpl")
 
       .newInstance();
 
    logic = (Logic) obj;
 
    String name = logic.getName();
 
    System.out.print("My Name Is " + name);
 
  } catch (Exception e) {
 
    e.printStackTrace();
 
  }
 
}
}

4、基于注解的注入

在介绍注解注入的方式前,先简单了解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 方法的逻辑是:

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

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

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

tips:大家如果认真思考可能会发现 ByName 的注入方式和 @Qualifier 有点类似,都是在自动装配匹配到多个 bean 的时候,指定一个具体的 bean,那它们有什么不同呢?

ByName 的方式需要遍历,@Qualifier 直接一次定位。在匹配到多个 bean 的情况下,使用 @Qualifier 来指明具体装配的 bean 效率会更高一下

写在最后:虽然有这么多的注入方式,但是实际上开发的时候自己编写的类一般用注解的方式注册类,用@Autowired描述依赖进行注入,一般实现类也只有一种(jdbc or hibernate or mybatis),除非项目有大的变动,所以@Qualifier标签用的也较少;但是在使用其他组件的API的时候用的是通过xml配置文件来注册类,描述依赖,因为你不能去改人家源码。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值