第 2 章 Spring中的Bean

在第一章中,详细讲解了Spring的loC思想及原理,并通过案例演示了Spring框架的基本使用。本章将在第一章节的基础上,针对Spring中Bean的相关知识进行详细的讲解。

Bean的配置
  • Spring可以被看作是一个大型工厂,这个工厂的作用就是生产和管理Spring容器中的Bean。如果想要在项目中使用这个工厂,就需要开发者对Spring的配置文件进行配置。
  • Spring器支持XML和Properties两种格式的配置文件,在实际开发中,最常使用的就是XML格式的配置方式。这种配置方式通过XML文件来注册并管理Bean之间的依赖关系。接下来本小节将使用XML文件的形式对Bean的属性和定义进行详细的讲解。
  • 在Spring中,XML配置文件的根元素是<beans>,<beans>中包含了多个<bean>子元素,每一个<bean>子元素定义了一个Bean,并描述了该Bean如何被装配到Spring容器中。<bean>元素中同样包含了多个属性以及子元素,其常用属性及子元素如表所示。
属性或子元素名称 描述
id 一个Bean的唯一标识符,Spring窑器对Bean的配置、筐理通过该属性来完成
name Spring容器同样可以通过此属性对容器中的Bean进行配置和管理,name属性中可以为Bean指定多个名称,每个名称之间用逗号或分号隔开
class 该属性指定了Bean的具体实现类,它必须是一个完整的类名,使用类的全限定名
scope 用来设定Bean实例的作用域,其属性值有:singleton(单例)、prototype(原型)、request、sesslon、globalSession,application和websocket.真默认值为singleton
constructor-arg <bean>元素的子元素,可以使用此元素传入构造参数进行实例化。该元素的index属性指定构造参数的序号(从0开始),type属性指定构造参数的类型,参数值可以通过ref属性或value属性直接指定,也可以通过ref或value子元素指定
property <bean>元素的子元素,用于调用Bean实例中的setter方法完成属性赋值,从而完成依赖注入。该元素的nae属性指定Bean实例中的相应属性毡ref属性或value属性用于指定参数值
ref <property>、<constructor-arg>等元素的属性或子元素,可以用于指定对Bean工厂中某个Bean实例的引用
value <property>、<constructor-arg>等元素的属性或子元素,可以用于直接指定一个常量值
list 用于封装List或数组类型的依赖注入
set 用于封装Set类型的依赖注入
map 用于封装Map类型的依赖注入
entry <map>元素的子元素,用于设置一个键值对。真key属性指定字符串类型的键值,ref或value子元素指定真值,也可以通过value-ref或value属性指定真值

在配置文件中,通常一个普通的Bean只需要定义id(或name)和class两个属性即可,定义Bean的方式如下所示。

   <!-- 使用 id 属性定义 bean1 ,其对应的实现类为 com.neuedu.Bean1 -->
   <bean id="bean1" class="com.neuedu.Bean1"></bean>
   <!-- 使用 name 属性定义 bean2 ,其对应的实现类为 com.neuedu.Bean2 -->
   <bean name="bean2" class="com.neuedu.Bean2"></bean>

在上述代码中,分别使用id属性和name属性定义了两个Bean,并使用class元素指定其对应的实现类。
注意:在Bean中没有指定id和name,则Spring会将class当做id来使用。

Bean 的实例化

在面向对象的程序中,想要使用某个对象,就需要先实例化这个对象。同样,在Spring中,要想使用容器中的Bean,也需要实例化Bean。实例化Bean有三种方式,分别为构造器实例化静态工厂方式实例化实例工厂方式实例化(其中最常用的是构造器实例化)。接下来的几个小节中,将分别对这三种实例化Bean的方式进行详细讲解。

  • 构造器实例化

构造器实例化是指Spring容器通过Bean对应类中默认的无参构造方法来实例化Bean。下面通过一个案例来演示Spring容器是如何通过构造器来实例化Bean的。
(1)在Eclipse中,创建一个名为spring02的Web项目,在该项目的lib目录中加入Spring支持和依赖的JAR包。
(2)在spring02项目的src目录下,创建一个com.neuedu.instance.constructor包,在该包中创建Bean1类,如下面文件所示。

package com.neuedu.instance.constructor;
public class Bean1 {
  
}

(3)在com.neuedu.instance.constructor包中,创建Spring的配置文件beans1.xml,在配置文件中定义一个id为bean1的Bean,并通过class属性指定其对应的实现类为Bean1,如下面文件所示。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
   <bean id="bean1" class="com.neuedu.instance.constructor.Bean1">></bean>

</beans>

(4)在com.neuedu.instance.constructor包中,创建测试类InstanceTest1,来测试构造器是否能实例化Bean,编辑后文件如下所示。

package com.neuedu.instance.constructor;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class InstanceTest1 {

  public static void main(String[] args) {
      //定义配置文件路径  
      String xmlPath = "com/neuedu/instance/constructor/beans1.xml"; 
      //ApplicationContext 在加载配置文件时,对 Bean 进行实例化 
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath) ; 
      Bean1 bean = (Bean1) applicationContext .getBean("bean1"); 
      System.out.println(bean) ; 

  }

}

在上述文件中,首先定义了配置文件的路径,然后Spring容器ApplicationContext会加载配置文件。在加载时,Spring容器会通过id为bean1的实现类Bean1中默认的无参构造方法对Bean进行实例化。执行程序后,控制台的输出结果如图所示。



从图中可以看出,Spring容器已经成功实例化了Bean1,并输出了结果。
为了方便大家的学习,本章中的所有配置文件和类文件(包括测试类)都根据知识点放置在同一个包中。在实际开发中,为了方便管理和维护,建议将这些文件根据类别放置在不同目录中。

  • 静态工厂方式实例化

使用静态工厂是实例化Bean的另一种方式。该方式要求开发者创建一个静态工厂的方法来创建Bean的实例,其Bean配中的class属性所指定的不再是Bean实例的实现类,而是静态工厂类,同时还需要使用factory-method属性来指定所创建的静态工厂方法。下面通过一个案例来演示如何使用静态工厂方式实例化Bean。
(1)在spring02项目的src目录下,创建一个com.neuedu.instance.static_factory包,在该包中创建一个Bean2类,该类与Bean1一样,不需添加任何方法。
(2)在com.neuedu.instance.static_factory包中,创建一个MyBean2Factory类,并在类中创建一个静态方法createBean()来返回Bean2实例,文件如下所示。

package com.neuedu.instance.static_factory;
public class MyBean2Factory {
  public static Bean2 createBean(){
      return new Bean2();
  }
}

(3)在com.neuedu.instance.static_factory包中,创建Spring配置文件beans2.xml,编辑后文件如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
   <bean id="bean2" >class="com.neuedu.instance.static_factory.MyBean2Factory" 
   factory-method="createBean"></bean>

</beans>

在上述配置文件中,首先通过<bean>元素的id属性定义了一个名称为bean2的Bean,然后由于使用的是静态工厂方法,所以需要通过class属性指定其对应的工厂实现类为MyBean2Factory。由于这种方式配置Bean后,Spring容器不知道哪个是所需要的工厂方法,所以增加了factory-method属性来告诉Spring容器,其方法名称为createBean。
(4)在com.neuedu.instance.static_factory包中,创建一个测试类InstanceTest2,来测试使用静态工厂方式是否能实例化Bean,编辑后文件如下所示。

package com.neuedu.instance.static_factory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.neuedu.instance.constructor.Bean1;

public class InstanceTest2 {

  public static void main(String[] args) {
      // 定义配置文件路径
      String xmlPath = "com/neuedu/instance/static_factory/beans2.xml";       
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);        
      System.out.println(applicationContext.getBean("bean2"));

  }
}

在执行程序后,控制台的输出结果如图下图所示。从图中可以看到,使用自定义的静态工厂方法,已成功实例化了Bean2。


  • 实例工厂方式实例化

还有一种实例化Bean的方式就是采用实例工厂。此种方式的工厂类中,不再使用静态方法创建Bean例,而是用直接创建Bean实例的方式。同时,在配置文件中,需要实例化的Bean也不是通过class属性直接指向的实例化类,而是通过factory-bean属性指向配置的实例工厂,然后使用factory-method属性确定使用工厂中的哪个方法。下面通过一个案例来演示实例工厂方式的使用。
(1)在spring02项目的src目录下,创建一个com.neuedu.instance.factory包,在该包中创建Bean3类,该类与Bean1一样,不需添加任何方法。
(2)在com.neuedu.instance.factory包中,创建工厂类MyBean3Factory,在类中使用默认无参构造方法输出"bean3工厂实例化中"语句,并使用createBean()方法创建Bean3对象,文件如下所示。

package com.neuedu.instance.factory;

public class MyBean3Factory {

  public MyBean3Factory() {
      System.out.println("bean3工厂实例化中");
  }
  //创建 Bean3 实例的方法 
  private Bean3 createBean() {
      return new Bean3();
  }
}

(3)在com.neuedu.instance.factory包中,创建Spring配置文件beans3.xml,设置相关配置后,文件如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
   <!-- 配配置工 -->
   <bean id="myBean3Factory" >class="com.neuedu.instance.factory.MyBean3Factory"></bean>
   <!-- 使用 factory-bean 属性指向配置的实例工厂, 
         使用 factory-method 属性确定使用工厂中的哪个方法  -->
   <bean id="bean3" factory-bean="myBean3Factory" factory->method="createBean"></bean>

</beans>

在上述配置文件中,首先配置了一个工厂Bean,然后配置了需要实例化的Bean。id为bean3的Bean中,使用factory-bean属性指向配置的实例工厂,该属性值就是工厂Bean的id。使用factory-method属性来确定使用工厂中的createBean()方法。
(4)在com.neuedu.instance.factory的包中,创建测试类InstanceTest3,来测试实例工厂方式能否实例化Bean,编辑后文件如下所示。

package com.neuedu.instance.factory;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class InstanceTest3 {

  public static void main(String[] args) {
      // 定义配置文件路径
      String xmlPath = "com/neuedu/instance/factory/beans3.xml";
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
      System.out.println(applicationContext.getBean("bean3"));

  }
}

在执行程序后,控制台的输出结果如下图所示。



从图中可以看到,使用实例工厂的方式,同样成功实例化了Bean3。

Bean 的作用域

通过Spring容器创建一个Bean的实例时,不仅可以完成Bean的实例化,还可以为Bean指定特定的作用域。本节将主要围绕Bean的作用域知识进行讲解。

  • 作用域的种类

Spring4.3中为Bean的实例定义了7种作用域,这7种作用域及其说明如表所示。

作用域名称 描述
singleton 使用singleton定义的Bean在Spring容器中将只有一个实例,也就是说,无论有多少个Bean引用它,始终将指向同一个对象。这也是Spring容器默认的作用域。
prototype 每次通过Spring容器获取的prototype定义的Bean时,容器都将创建一个新的Bean实例
request 在一次HTTP请求中,容器会返回该Bean的同一个实例。不同的HTTP请求则会产生一个新的Bean,而且该Bean在当前HTTPRequest内有效
sesslon 在一次HTTPSession中,容器会返回该Bean的同一个实例。对不同的HTTP请求则会产生一个新的Bean,而且该Bean仅在当前HTTPSession内有效
globalSession 在一个全局的HTTPSession中,容器会返回该Bean的同一个实例。仅在使用portlet上下文时有效
application 在每个ServletContext对象创建一个实例。仅在Web相关的ApplicationContext中生效
websocket 在每个websocket对象创建一个实例。仅在Web相关的ApplicationContext中生效

在表的7种作用域中,singleton和prototype是最常用的两种,在接下来的两个小节中,将会对这两种作用域进行详细的讲解。

  • singleton 作用域

singleton是Spring容器默认的作用域,当Bean的作用域为singleton时,Spring容器就只会存在一个共享的Bean实例,并且所有对Bean的请求,只要id与该Bean的id属性相匹配,就会返回同一个Bean实例。singleton作用域对于无会话状态的Bean(如Dao组件、Service组件)来说,是最理想的选择。
在Spring配置文件中,Bean的作用域是通过<bean>元素的scope属性来指定的,该属性值可以设置为singleton、prototype、request、session,globalSession、application和websocket七个值,分别表示七种作用域。要将作用域定义成singleton,只需将scope的属性值设置为singleton即可,其示例代码如下。

<bean id="scope" class="com.neuedu.scope.Scope" scope="singleton"></bean>

在项目spring02中,创建一个com.neuedu.scope包,在包中创建Scope类,该类不需要写任何方法。然后在该包中创建一个配置文件beans4.xml,将上述示例代码写入配置文件中。最后在包中创建测试类ScopeTest,来测试singleton作用域,编辑后文件如下所示。

package com.neuedu.scope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ScopeTest {

  public static void main(String[] args) {
      //定义配置文件路径
      String xmlPath = "com/neuedu/scope/beans4.xml";
      //加载配置文件
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
      //输出获得实例
      System.out.println(applicationContext.getBean("scope"));
      System.out.println(applicationContext.getBean("scope"));
  }
}

在行程序后,控制台的输出结果如图所示。



从图中可以看出,两次输出的结果相同,这说明Spring容器只创建了一个Scope类的实例。需要注意的是,如果不设置scope="singleton",其输出结果也是一个实例,因为Spring容器默认的作用域就是singleton。

  • prototype 作用域

对需要保持会话状态的Bean(如Struts2的Action类)应该使用prototype作用域。在使用prototype作用域时,Spring容器会为每个对该Bean的请求都创建一个新的实例。
要将Bean定义为prototype作用域,只需在配置文件中将<bean>元素的scope属性值设置prototype即可其示例代码如下。

<bean id="scope" class="com.neuedu.scope.Scope" scope="prototype"></bean>

将上一个小节中的配置文件更改成上述代码形式后,再次运行测试类ScopeTest,控制台的输出结果如图所示。



从图中可以看到,两次输出的Bean实例并不相同,这说明在prototype作用域下,创建了两个不同的Scope实例。

Bean 的生命周期

Spring容器可以管理singleton作用域的Bean的生命周期,在此作用域下,Spring能精确地知道该Bean何时被创建,何时初始化完成以及何时被销毁。对于prototype作用域的Bean,Spring只负责创建,当容器创建了Bean实例后,Bean的实例就交给客户端代码来管理,Spring容器将不再跟踪其生命周期。每次客户端请求prototype作用域的Bean时,Spring容器都会创建一个新的实例,并且不会管那些被配置成prototype作用域的Bean的生命周期。
了解Bean的生命周期的意义就在于,可以在某个Bean生命周期的某些指定时刻完成一些相关操作。这种时刻可能有很多,但在一般情况下,常会在Bean的postinitiation(初始化后)和predestruction(销毁前)执行一些相关操作。
在Spring中,Bean生命周期的执行是一个很复杂的过程,读者可以利用Spring提供的方法来定制Bean创建过程。当一个Bean被加载到Spring容器时,它就具有了生命,而Spring容器在保证一个Bean能够使用之前,会做很多工作。Spring容器中,Bean的生命周期流程如下图所示。



在上图中,Bean的生命周期的整个执行过程描述如下。

  • (1)根据配置情况调用Bean构造方法或工厂方法实例化Bean。
  • (2)利用依赖注入完成Bean中所有属性值的配置注入。
  • (3)如果Bean实现BeanNameAware接口,则Spring调用Bean的setBeanName()方法传入当前Bean的id值。
  • (4)如果Bean实现了BeanFactoryAware接口,则Spring调用setBeanFactory()方法传入当前工厂实例的引用。
  • (5)如果Bean实现了ApplicationContextAware接口,贝IJSpring调用setApplicationContext()方法传入当前ApplicationContext实例的引用。
  • (6)如果BeanPostProcessor和Bean关联,则Spring将调用该接口的预初始化方法postProcessBeforelnitialzation()对Bean进行加工操作,这个非常重要,Spring的AOP就是用它实现的。
  • (7)如果Bean实现了InitializingBean接口,则Spring将调用afterPropertiesSet()方法。
  • (8)如果在配置文件中通过init-method属性指定了初始化方法,则调用该初始化方法。
  • (9)如果有BeanPostProcessor和Bean关联,贝IJSpring将调用该接口的初始化方法postProcessAfterlnitialization()。此时,Bean已经可以被应用系统使用了。
  • (10)如果在<bean>中指定了该Bean的作用范围为scope="singleton",则将该Bean放入SpringloC的缓存池中,将触发Spring对该Bean的生命周期管理;如果在<bean>中指定了该Bean的作用范围为scope="prototype",则将该Bean交给调用者,调用者管理该Bean的生命周期,Spring不再管理该Bean。
  • (11)如果Bean实现DisposableBean接口,则Spring会调用destory()方法将Spring中的Bean销毁;如果在配置文件中通过destory-method属性指定了Bean的销毁方法,则Spring将调用该方法进行销毁。
    Spring为Bean提供了细致全面的生命周期过程,通过实现特定的接口或通过<bean>的属性设置,都可以对Bean的生命周期过程产生影响。我们可以随意地配置<bean>的属性,但是在这里建议不要过多地使用Bean实现接口,因为这样会使代码和Spring聚合比较紧密。
Bean 的装配方式

Bean的装配可以理解为依赖关系注入,Bean的装配方式即Bean依赖注入的方式。Spring容器支持多种形式的Bean的装配方式,如基于XML的装配基于注解(Annotation)的装配自动装配等(其中最常用的是基于注解的装配)。本节将主要讲解这三种装配方式的使用。

  • 基于 XML 的装配

Spring提供了两种基于XML的装配方式:设值注入(SetterInjection)和构造注入(ConstructorInjection)。下面就讲解下如何在XML配置文件中使用这两种注入方式来实现基于XML的装配。
在Spring实例化Bean的过程中,Spring首先会调用Bean的默认构造方法来实例化Bean对象,然后通过反射的方式调用setter方法来注入属性值。因此,设值注入要求一个Bean必须满足以下两点要求。

  • Bean类必须提供一个默认的无参构造方法。
  • Bean类必须为需要注入的属性提供对应的setter方法。

使用设值注入时,在Spring配置文件中,需要使用<bean>元素的子元素<property>来为每个属性注入值;而使用构造注入时,在配置文件里,需要使用<bean>元素的子元素<constructor-arg>来定义构造方法的参数,可以使用其value属性(或子元素)来设置该参数的值。下面通过一个案例来演示基于XML方式的Bean的装配。
(1)在项目spring02的src目录下,创建一个com.neuedu.assemble包,在该包中创建User类,并在类中定义username、password和list集合三个属性及其对应的setter方法,文件如下所示。

package com.neuedu.assemble;
import java.util.List;

public class User {
  private String username;
  private Integer password;
  private List<String> list;
  
  
  /**
   * 1.使用构造注入 
   * 1.1 提供带所有参数的有参构造方法。 
   */ 
  public User(String username, Integer password, List<String> list) {
      super();
      this.username = username;
      this.password = password;
      this.list = list;
  }
  
  /**
   *  2.使用设值注入 
   *  2.1 提供默认空参构造方法; 
   *  2.2 为所有属性提供 setter 方法。 
   */
  public User() {
      super();
      // TODO Auto-generated constructor stub
  }
  public void setUsername(String username) {
      this.username = username;
  }
  public void setPassword(Integer password) {
      this.password = password;
  }
  public void setList(List<String> list) {
      this.list = list;
  }
  
  @Override
  public String toString() {
      return "User [username=" + username + ", password=" + password >+ ", list=" + list + "]";
  }
}

在上述文件中,由于要使用构造注入,所以需要其有参和无参的构造方法。同时,为了输出时能够看到结果,还重写了其属性的toString()方法。
(2)在com.neuedu.assemble包中,创建配置文件beans5.xml,在配置文件中通过构造注入和设值注入的方式装配User类的实例,文件如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
   <!-- 1.使用构造注入方式装配 User 实例 -->
   <bean id="user1" class="com.neuedu.assemble.User">
      <constructor-arg index="0" value="zhaosi"></constructor-arg>
      <constructor-arg index="1" value="456"></constructor-arg>
      <constructor-arg index="2">
          <list>
              <value>"constructorvalue1"</value>
              <value>"constructorvalue2"</value>
          </list>
      </constructor-arg>
   </bean>
   <!-- 2.使用设值注入方式装配 User 实例  -->
   <bean id="user2" class="com.neuedu.assemble.User">
      <property name="username" value="liuneng"></property>
      <property name="password" value="123"></property>
      <!-- 注入list集合  -->
      <property name="list">
          <list>
              <value>"setlistvalue1"</value>
              <value>"setlistvalue2"</value>
          </list>
      </property>
   </bean>

</beans>

在上述配置文件中,<constructor-arg>元素用于定义构造方法的参数,其属性index表示其索引(从0开始),value属性用于设置注入的值,其子元素<Iist>来为User类中对应的list集合属性注入值。然后又使用了设值注入方式装配User类的实例,其中<property>元素用于调用Bean实例中的setter方法完成属性赋值,从而完成依赖注入,而其子元素<Iist>同样是为User类中对应的list集属性注入值。
(3)在com.neuedu.assemble包中,创建测试类XmlBeanAssembleTest,在类中分别获取并输出配置文件中的user1和user2实例,文件如下所示。

package com.neuedu.assemble;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class XmlBeanAssembleTest {

  public static void main(String[] args) {
      // 定义配置文件路径
      String xmlPath = "com/neuedu/assemble/beans5.xml";
      // 加载配置文件
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
      // 构造方式输出结果 
      System.out.println(applicationContext.getBean("user1"));
      // 设值方式输出结果 
      System.out.println(applicationContext.getBean("user2"));

  }
}

在行程序后,控制台的输出结果如图所示。



从图可以看出,已经成功地使用基于XML装配的构造注入和设值注入两种方式装配了User实例。

  • 基于 Annotation 的装配

Spring中,尽管使用XML配置文件可以实现Bean的装配工作,但如果应用中有很多Bean时,会导致XML配置文件过于臃肿,给后续的维护和升级工作带来一定的困难。为此,Spring提供了对Annotation(注解)技术的全面支持。
Spring中定义了一系列的注解,常用的注解如下所示。

  • @Component:可以使用此注解描述Spring中的Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。
  • @Repository:用于将数据访问层(DAO层)的类标识为Spring中的Bean,其功能与@Component相同。
  • @Service:通常作用在业务层(Service层),用于将业务层的类标识为Spring中的Bean,其功能与@Component相同。
  • @Controller:通常作用在控制层(如SpringMVC的Controller),用于将控制层的类标识为Spring中的Bean,其功能与@Component相同。
  • @Autowired:用于对Bean的属性变量、属性的setter方法及构造方法进行标注,配合对应的注解处理器完成Bean的自动配置工作。默认按照Bean的类型进行装配。
  • @Resource:其作用与Autowired一样。其区别在于@Autowired默认按照Bean类型装配,而@Resource默认按照Bean实例名称进行装配。@Resource中有两个重要属性:name和typeoSpring将name属性解析为Bean实例名称,type属性解析为Bean实例类型。如果指定name属性,则按实例名称进行装配;如果指定type属性,则按Bean类型进行装配;如果都不指定,则先按Bean实例名称装配,如果不能匹配,再按照Bean类型进行装自己;如果都无法匹配,则抛出NoSuchBeanDefinitionException异常。
  • @Qualifier:与@Autowired注解配合使用,会将默认的按Bean类型装配修改为接Bean的实例名称装配,Bean的实例名称由@Qualifier注解的参数指定。

在上面几个注解中,虽然@Repository、@Service与@Controller功能与@Component注解的功能相同,但为了使标注类本身用途更加清晰,建议在实际开发中使用@Repository、@Service与@Controller分别对实现类进行标注。面,通过一个案例来演示如何通过这些注解来装配Bean。
(1)在spring02项目的src目录下,创建一个com.neuedu.annotation包,在该包中创建接口UserDao,并在接口中定义一个save()方法,文件如下所示。

package com.neuedu.annotation;
public interface UserDao {
  public void save();
}

(2)在com.neuedu.annotation包中,创建UserDao接口的实现类UserDaolmpl,该类需要实现接口中的save()方法,文件如下所示。

package com.neuedu.annotation;
@Repository("userDao")
public class UserDaolmpl implements UserDao {

  @Override
  public void save() {
      System.out.println("userdao.....save......");

  }
}

在上述文件中,首先使用@Repository注解将UserDaolmpl类标识为Spring中的Bean,其写法相当于配置文件中<bean id="userDao"class="com.neuedu.annotation.UserDaolmpl"/>的编写。然后在save()法中输出打印一句话,用于验证是否成功调用了该方法。
(3)在com.neuedu.annotation包中,创建接口UserService,在接口中同样定义一个save()方法,文件如下所示。

package com.neuedu.annotation;
public interface UserService {
  public void save();
}

(4)在com.neuedu.annotation包中,创建UserService接口的实现类UserServicelmpl,该类需要实现接口中的save()方法如文件所示。

package com.neuedu.annotation;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserServiceImpl implements UserService{
  @Resource(name="userDao")
  private UserDao userDao;
  @Override
  public void save() {
      //调用UserDao中的save()方法
      this.userDao.save();
      System.out.println("userservice......save......");  
  }
}

在上述文件中,首先使用@Service注解将UserServicelmpl类标识为Spring中的Bean,这相当于配置文件中<bean id="userService"class="com.neuedu.annotation.UserServicelmpl"/>的编写;然后使用@Resource注解标注在属性userDao上,这相当于配置文件中<propertyname="userDao"ref="userDao"/>的写法;最后在该类的save()方法中调用userDao中的save()方法,并输出一句话。
(5)在com.neuedu.annotation包中,创建控制器类UserController,编辑后文件如下所示。

package com.neuedu.annotation;
import javax.annotation.Resource;
import org.springframework.stereotype.Controller;

@Controller("userController")
public class UserController {
  @Resource(name="userService")
  private UserService userService;
  public void save(){
      this.userService.save();
      System.out.println("usercontroller......save......");
  }

}

在上述文件中,首先使@Controller注解标注了UserController类,这相当于在配置文件中编写<bean id="userController"class="com.neuedu.annotation.UserController"/>;然后使用了@Resource注解标注在userService属性上,这相当于在配置文件中编写<propertyname="userService"ref="userService"/>;最后在其save()方法中调用了userService中的save()方法,并输出一句话。
(6)在com.neuedu.annotation包中,创建配置文件beans6.xml,在配置文件中编写基于Annotation装配的代码,文件如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd 
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context-4.3.xsd"> 
   <!-- 使用 context 命名空间,在配置文件中开启相应的注解处理器 -->
   <context:annotation-config />
   <!-- 分别定义 3 个 Bean 实例  -->
   <bean id="userDao" class="com.neuedu.annotation.UserDaolmpl">></bean>
   <bean id="userService" >class="com.neuedu.annotation.UserServiceImpl"></bean>
   <bean id="userController" >class="com.neuedu.annotation.UserController"></bean>
   
</beans>

从上述代码可以看出,文件与之前的配置文件有很大不同。首先,在<beans>元素中,增加了4行,第7行和第8行中包含有context的约束信息;然后通过配置<context:annotation­config/>来开启注解处理器;最后分别定义了3个Bean对应所编写的3个实例。与XML装备方式有所不同的是,这里不再需要配置子元素<property>。
上述Spring配置文件中的注解方式虽然较大程度简化了XML文件中Bean的配置,但仍需要在Spring配置文件中一一配置相应的Bean,为此Spring注解提供了另外一种高效的注解配置方式(对包路径下的所有Bean文件进行扫描),其配置方式如下。

<context :cornponent-scan base-package="Bean 所在的包路栓" />

以可以将上述文件中代码进行如下替换(推荐)。

<context :cornponent-scan base-package="com.neuedu.annotation" /> 

注意:
Spring4.0以上版本使用上面的代码对指定包中的注解进行扫描前,需要先向项目中导入SpringAOP的JARspring-aop-4.3.6.RELEASE.jar,否则程序在运行时会报出
"java.lang.NoClassDefFoundError:orglspringframeworklaop/TargetSource"错误。

(7)在com.neuedu.annotation包中,创建测试类AnnotationAssembleTest,在类中编写测试方法并定义配置文件的路径,然后通过Spring容器加载配置文件并获取UserController实例,最后调用实例中的save()方法,文件如下所示。

package com.neuedu.annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AnnotationAssembleTest {

  public static void main(String[] args) {
      // 定义配置文件路径
      String xmlPath = "com/neuedu/annotation/beans6.xml";
      // 加载配置文件
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
      // 获取UserController实例
      UserController userController = (UserController) applicationContext.getBean("userController");
      // 调用 UserController中的save()方法
      userController.save();
  }
}

在执行程序后,控制台的输出结果如图所示。


从上图可以看到,Spring容器已成功获取了UserController的实例,并通过调用实例中的方法执行了各层中的输出语句,这说明已成功实现了基于Annotation装配Bean。
小提示:上述案例中如果使用@Autowired注解替换@Resource注解,也可以达到同样的效果。

  • 自动装配

虽然使用注解的方式装配 Bean ,在一定程度上减少了配置文件中的代码量,但是也有企业项目中, 是没有使用注解方式开发的,那么有没有什么办法既可以减少代码量,又能够实现 Bean 的装配呢?
答案是肯定的, Spring 的 <bean>元素中包含一个 autowire 属性,我们可以通过设置 autowire 的属性值来自动装配 Bean。 所谓自动装配,就是将一个 Bean 自动地注入到其他 Bean 的 Property 中。
autowíre 属性有 5 个值,其值及说明如下表所示。

属性值 说明
default 由 <bean> 的上级标签 <beans> 的 default-autowire 属 性值确定 。 例如 <beans defaultautowire="byName"> ,则该<bean>元素中的 autowire 属性对应的属性值就为 byName
byName 根据属性的名称自动装配。 窑器将根据名称查找与属性完全一致的 Bean ,并将真属性自动装配
byType 根据属性的数据类型 (Type) 自动装配,如果一个 Bean 的数据类型,兼容另一个 Bean 中属性的 数据类型,则自动装配
constructor 根据何造函数参数的数据类型,进行 byType 模式的自动装配
no 在默认情况下,不使用自动装配, Bean 依赖必须通过 ref 元素定义

下面通过修改上节中的案例来演示如何使用自动装配。
( 1 )修改上中的文件UserServicelmpl 和文件UserController,分别在文 件中增加类属性的 setter 方法。
(2 )修改上节中的配置文件 beans6.xml,将配置文件修改成自动装配形式,文件如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context" 
  xsi:schemaLocation="http://www.springframework.org/schema/beans 
  http://www.springframework.org/schema/beans/spring-beans-4.3.xsd 
  http://www.springframework.org/schema/context 
  http://www.springframework.org/schema/context/spring-context-4.3.xsd"> 
   <!-- 使用 bean 元素的 autowire 属性完成自动装配 -->
   <bean id="userDao" class="com.neuedu.annotation.UserDaolmpl"></bean>
   <bean id="userService" class="com.neuedu.annotation.UserServiceImpl" autowire="byName"></bean>
   <bean id="userController" class="com.neuedu.annotation.UserController" autowire="byName"></bean>
   
</beans>

上述配置文件中,用于配置 userService 和 userControl1凹的<bean>元素中除了 id 和 class 属性外,还增加了 autowire 属性,并将其属性值设置为 by队lame。 在默认情况下,配置文件中 需要通过 ref 来装配 Bean ,但设置了 autowire=" byName"后, Spring 会自动寻找 userService Bean 中的属性,并将其属性名称与配置文件中定义的 Bean 做匹配。 由于 UserServicelmpl 中 定义了 userDao 属'性及其 setter 方法,这与配置文件中 id 为 userDao 的 Bean 相匹配,所以 Spring 会自动地将 id 为 userDao 的 Bean 装配到 id 为 userService 的 Bean 中。
执行程序后,控制台的输出结果如图所示。



从图中可以看出,使用自动装配同样完成了依赖注入。

本章小结

本章主要对 Spring 中的 Bean 进行了详细讲解。 首先介绍了 Bean 的配置,然后通过案例讲 解了 Bean 实例化的三种方式;接下来介绍了 Bean 的作用域和生命周期;最后讲解了 Bean 的 三种装配方式。 通过本章的学习,读者可以了解 Bean 的常用属性及其作用,可以掌握实例化 Bean 的三种方式,熟悉 Bean 作用域的种类及其生命周期,掌握 Bean 的三种装配方式。

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

推荐阅读更多精彩内容