Spring(二)---装配Bean

来源:互联网 发布:销售清单软件免费 编辑:程序博客网 时间:2024/06/06 02:03

(一)Spring容器
(1)概述
在基于Spring的应用中,应用对象生存于Spring容器(container)中,Spring容器负责创建对象,装配它们,配置它们并管理它们的整个生命周期,从生存到死亡.容器是Spring框架的核心.Spring容器使用DI(即IOC)管理构成应用的组件,它会创建相互协作的组件之间的关联.
这里写图片描述
(2)Spring容器的种类
Spring容器并不是只有一个.Spring自带了多个容器的实现,可以归为两种不同的类型.

  • BeanFactory(org.springframework.beans.factory.BeanFactory接口定义)是最简单的容器,提供基本的DI支持.
  • 应用上下文(由org.springframework.context.ApplicationContext接口定义),是BeanFactory的子接口,并提供应用框架级别的服务,例如从属性文件解析文本信息以及发布应用事件给感兴趣的事件监听者.

(3)使用应用上下文

Spring自带了多种类型的应用上下文。罗列几个常用的:

  • AnnotationConfigApplicationContext:从一个或多个基于Java的配置类中加载Spring应用上下文.
  • AnnotationConfigWebApplicationContext:从一个或多个基于Java的配置类中加载Spring Web应用上下wen.
  • ClassPathXmlApplicationContext:从类路径下的一个或多个XML配置文件中加载上下文定义,把应用上下文的定义文件作为类资源.
  • FileSystemXmlApplicationContext:从文件系统下的一个或多个XML配置文件中加载上下文定义.
  • XmlWebApplication:从Web应用下的一个或多个Xml配置文件中加载上下文定义.
    使用ClassPathXmlApplicationContext和FileSystemXmlApplicationContext装配Bean
//使用FileSystemXmlApplicationContextApplicationCOntext context = new FileSystemXmlApplicationContext("D://test.xml")//使用ClassPathXmlApplicationContextApplicationContext context = new ClassPathXmlApplicationContext("test.xml")//使用FileSystemXmlApplicationContext和ClassPathXmlApplicationContext的区别:FileSystemXmlApplicationContext在指定的文件系统路径下查找test.xml文件;而ClassPathXmlApplicationContext是在所有的类路径(包括JAR文件)下查找test.xml文件//使用AnnotationConfigApplicationContext//在这里没有指定加载Spring应用上下文所需的XML文件,AnnotationConfigApplicationContext通过一个配置类加载BeanApplicationContext context = new AnnotationConfigApplicationContext(com.test.config.testConfig.class);

(二)bean的生命周期

  • 在传统的Java应用中,bean的生命周期很简单。使用Java关键字new进行bean实例化,然后该bean就可以使用了。一旦该bean不在被使用,则由Java自动进行垃圾回收.
  • 相比之下,Spring容器的bean的生命周期就显得相对复杂多了,如下图展示了Spring上下文中一个典型的生命周期过程.
    这里写图片描述
    在bean准备就绪之前,bean工厂执行了若干启动步骤:
    ①Spring对bean进行实例化;
    ②Spring将值和bean的引用注入到bean对应的属性中;
    ③如果bean 实现了BeanNameAware接口,Spring将bean的ID传递给setBean-Name()方法;
    ④如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将Beanfactory容器实例传入;
    ⑤如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传入进来;
    ⑥如果bean实习了BeanPaostProcessor接口,Spring将调用它们的post-ProcessorBeforeInitialization()方法;
    ⑦如果bean实现了InitializingBean接口,Spring将调用它们的after-PropertiesSet()方法.类似地,如果bean使用init-method声明了初始化方法,该方法也会调用;
    ⑧如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessorAfterInitialization()方法;
    ⑨此时bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;
    ⑩如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法.同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用.
    (三)装配Bean
    · 创建应用对象之间协作关系的行为通常称为装配(wiring),这也是依赖注入(DI)的本质。
    · Spring提供了三种装配机制:
  • 在XML中进行显示配置.
  • 在Java中进行显示配置.
  • 隐式的bean发现机制和自动装配.
    (1)隐式的bean发现机制和自动装配.
    Spring从两个角度来实现自动化装配:
  • 组件扫描(cpmponent scanning):Spring会自动发现应用上下文中所创建的Bean.
    特定组件包括:
    @Component:基本注解,标识了一个受Spring管理的组件
    @Repository:标识持久层组件
    @Service:标识服务层(业务层)组件
    @Controller:标识表现层组件
//基于Java代码的方式配置组件自动扫描@Configuration//@ComponentScan注解启用了组件扫描,如果没有其他配置的话,@ComponentScan会自动扫描与配置类相同的包及其这个包下的所有子包,查找带有@Component注解(@Repository,@Service,@Controller)的类@ComponentScanpublic class CDPlayerConfig{}//若想设置组件扫描的基础包,可以通过basePackages属性进行设置,若想扫描多个包,只需要将basePackages属性设置为一个数组即可,如下所示:@Configuration@ComponentScan(basePackages={"com.ty.test1","com.ty.test2"})public class CDPlayerConfig{}//除了将包设置为简单的String类型之外,@ComponentScan还提供了另外一种方法:指定为包中所包含的类或接口,所指定的类所在的包将会作为组件扫描的基础包@Configuration@ComponentScan(basePackageClasses={Test1.class,Test2.class})public class CDPlayerConfig{}//为组件扫描的bean命名,Spring应用上下文中所有的bean都会给定一个ID,Spring会根据类名为其指定一个ID(一般式将类名的第一个字母变成小写),若想设置该bean的ID,那么需要如下操作:@Component("loneId")public class Test{...};
  • 自动装配(autowiring):Spring自动满足Bean之间的依赖.
    简单来说,自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean,我们可以借助Spring的@Autowired注解:
@Componentpublic class CDPlayer implements MediaPlayer{  public CompactDisc cd;  @Autowired  public CDPlayer(CompactDisc cd){  this.cd = cd;}public void play(){cd.play();}}//@Autowired注解不仅能够用在构造器上,还能用在属性的Setter方法上@Autowiredpublic void setCompactDisc(CompactDisc cd){  this.cd = cd;}//注:不管构造器,Setter方法还是其他的方法,Spring都会尝试满足方法参数上所声明的依赖.假如有且只有一个bean匹配依赖需求的话,那么这个bean就会被装配进来.如果没有匹配的bean,那么在创建的时候,Spring会抛出一个异常,为了避免异常的出现,可以将@Autowired的required属性设置为false(即认为该属性允许不被设置),若有多个bean都能满足依赖关系的话,Spring将会抛出一个异常,表明没有明确指定要选择哪个bean进行自动装配.//@Autowired是Spring特有的注解,还可以使用@Inject注解(来源于Java依赖注入规范)

(2)在Java中进行显示配置.
- 尽管很多场景下通过组件扫描和自动装配实现Spring的自动化配置是更为推荐的方式,但有时候自动化配置的方案行不通(如:将第三方库中的组件装配到应用中),只能通过显示的方式去装配.
- 在进行显示配置的方式中,JavaCobfig是更好的方案,因为它更为强大,类型安全并且对重构友好.
- 如何通过JavaConfig显示配置Spring

//①创建配置类,创建JavaConfig类的关键在于为其添加@Configuration注解,@Configuration注解表明这个类是一个配置类,该类应该包含在Spring应用上下文中如何创建bean的细节.@Configurationpublic class JavaConfig{//②声明简单的bean,要在JavaConfig中声明bean,我们需要编写一个方法,这个方法会创建所需类的实例,然后给这个方法添加@Bean注解,@Bean注解会告诉Spring这个方法将返回一个对象,该对象要注册Spring应用上下文中的bean.//默认情况下bean的ID与带有@Bean注解的方法名是一样的.如果想为其设置成一个不同的名字的话,那么可以重命名该方法,也可以通过name属性指定一个不同的名字   @Bean(name="test")   public TestClass testClass(){     return new TestClass();   }//③借助JavaConfig实现注入(第一种方式),在JavaConfig中装配bean的最简单的方式就是引用创建bean的方法,如下所示:testClassOne()的方法体与testClass()稍有些区别,在这里并没有使用默认的构造器构建实例,而是调用了需要传入TestClass对象的构造器来创建TestClassOne实例@Beanpublic TestClassOne testClassOne(){  return new TestClassOne(testClass());}//注:看起来TestClass是通过调用testClass()得到的,但情况并非如此.因为testClass()方法上添加了@Bean注解,Spring将会拦截所有对它的调用,并确保直接返回该方法所创建的Bean,而不是每次都对其进行实际的调用.(即创建新的实例)//如下所显示的示例中,testClassOne1()和testClassOne2()中的testClass()所创建的是同一个TestClass实例,默认情况下,Spring中的bean都是单例的。所以Spring会拦截对testClass()的调用并确保返回的是Spring所创建的bean,也就是Spring本身在调用testClass()时所创建的TestClass,因此两个TestClassOne会得到相同的TestClass实例//例@Beanpublic TestClassOne testClassOne1(){ return new TestClassOne(testClass());}@Beanpublic TestClassOne testClassOne2(){ return new TestClassOne(testClass());} //③借助JavaConfig实现注入(第二种方式),testClassOne()方法请求一个TestClass作为参数.它会自动装配一个TestClass到配置方法之中@Beanpublic TestClassOne testClassOne(TestClass testClass){    new TestClassOne(testClass);}

(3)在XML中进行显示配置.

//①通过Spring Tool Suite创建XML配置文件//②声明一个<bean>,通过Id属性,来为Bean附上ID值<bean id="testClass" class="com.test.TestClass"/>//③借助构造器注入初始化bean//具体到构造器注入有两种方式:  //<1><constructor-arg>元素,注:TestClassOne必须有注入TestClass类的参数  <bean id="testClassOne" class="com.ty.TestClassOne">    <constructor-arg ref="testClass"/>  //以字面量的形式注入到构造器中  <bean id="testClassOne" class="com.ty.TestClassOne">    <constructor-arg value="test"/>  </bean>  //若在构造器中存在List集合形式或Set的参数,类似也可以用<ref>代替<value>   <bean id="testClassOne" class="com.ty.TestClassOne">    <constructor-arg>       <list>         <value>test1</value>         <value>test2</value>         ....       </list>    </constructor-arg>  </bean>  //<set><list>的元素区别不大,其中最重要的不同在于当Spring创建要装配集合时,所创建的是java.util.Set还是java.util.List.如果是Set的话,所有重复的值都会被忽略掉.  <bean id="testClassOne" class="com.ty.TestClassOne">    <constructor-arg>       <set>         <value>test1</value>         <value>test2</value>         ....       </set>    </constructor-arg>  </bean>  //<2>使用Spring3.0所引入的c-命名空间  <bean id="testClassOne" class="com.ty.TestClassOne">    <c:cd-ref="testClass"/>  </bean>  //注:两者的区别在很大程度就是是否冗长繁琐.可以看到,<constructor-arg>元素比使用c-命名空间会更加冗长,从而导致XML更加难以读懂.另外,有些事情<constructor-arg>可以做到,但是使用c命名空间却无法实现  //④借助属性注入初始化bean <bean id="testClassOne" class="com.ty.TestClassOne">    <property name="testClass" ref="testClass"/>  </bean>  //使用p-命名空间来代替<property> <bean id="testClassOne" class="com.ty.TestClassOne"  p:testClass-ref ="testClass"/>//同样也可以使用字面量来给属性赋值    <bean id="testClassOne" class="com.ty.TestClassOne">    <property name="testClass" value="test"/>  </bean> //注:不能使用p命名空间来装配集合 <bean id="testClassOne" class="com.ty.TestClassOne"  p:testClass ="test"/> 

这里写图片描述
使用Spring util-命名空间

//例:<!--使用utility scheme  -->  <util:list id="cars">    <ref bean="car1"/>    <ref bean="car2"/>    <bean  class="com.ty.helloword.Car">      <constructor-arg value="唐" index="0"></constructor-arg>      <constructor-arg value="874123" index="1"></constructor-arg>      <constructor-arg value="比亚迪" index="2"></constructor-arg>      <constructor-arg value="185" index="3"></constructor-arg>    </bean>   </util:list>  <util:map id="map">    <entry key="AAA" value-ref="car1"></entry>    <entry key="BBB">      <ref bean="car2"/>    </entry>    <entry key="CCC">      <bean class="com.ty.helloword.Car">        <constructor-arg value="秦" index="0"></constructor-arg>        <constructor-arg value="100001" index="1"></constructor-arg>        <constructor-arg value="比亚迪" index="2"></constructor-arg>        <constructor-arg value="250" index="3"></constructor-arg>      </bean>    </entry>  </util:map>  <!-- 使用p命名空间 先导入p的命名空间,可以使用上述的util-命名空间的bean到相应的属性中 -->  <bean id="person2" class="com.ty.helloword.Person" p:age="30" p:name="Wang" p:cars-ref="cars">  </bean>

这里写图片描述
(四)导入和混合配置
自动装配的时候会考虑到Spring容器中所有的bean,不管它是在JavaConfig或XML中声明的还是通过组件扫描获取到的.
(1)在JavaConfig中引用XML配置或另外的JavaConfig类

//①在JavaConfig引入另外的JavaConfig类@Configuration@Import({Test1.class,Test2.class})public class TestConfig{}//②在JavaConfig引入另外的XML文件@Configuration@Import({Test1.class,Test2.class})@ImportResource("classpath:test1.xml")public class TestConfig{}

(2)在XML配置文件中引用XML配置或另外的JavaConfig类

//在XML文件中引用JavaConfig类<beans><bean class="com.ty.JavaConfig">//在XML文件中引用XML文件<import resource="test.xml"></beans>
原创粉丝点击