Spring学习(四)-Bean的三种装配方式

来源:互联网 发布:网络共享硬盘无法访问 编辑:程序博客网 时间:2024/05/18 01:51

本篇博客主要讲述Spring中Bean的三种主要装配:1、在XML中进行显示配置;2、自动装配方式;3、在Java中进行显示配置。下面分别来研究上述三种装配方式。

在Spring IOC容器中注入依赖资源主要有以下两种基本实现方式:(1)构造器注入:容器实例化Bean时注入一些依赖,通过在Bean定义中指定构造器参数进行注入依赖,包括实例工厂方法参数注入依赖,但静态工厂方法参数不允许注入依赖;(2)setter注入:通过setter方法进行注入依赖。

1、在XML中进行显示配置
定义接口:Animal

package com.wygu.spring.animal;public interface Animal {    public void walk();    public void call();}

实现类Cat和Dog

package com.wygu.spring.animal;public class Cat implements Animal{    private String catName;    public Cat(String catName) {        this.catName = catName;    }    @Override    public void walk() {        System.out.println("The Cat name: "+catName+" is walking");    }    @Override    public void call() {        System.out.println("The Cat name: "+catName+" is called");    }}package com.wygu.spring.animal;public class Dog implements Animal{    private String dogName;    public Dog(String dogName) {        this.dogName = dogName;    }    @Override    public void walk() {        System.out.println("The Dog name: "+dogName+" is walking");     }    @Override    public void call() {        System.out.println("The Dog name: "+dogName+" is walking");    }}

定义一个工厂AnimalFactory生产动物

package com.wygu.spring.animal;public class AnimalFactory {    private Animal animal;    public AnimalFactory(){    }    public AnimalFactory(Animal animal){        this.animal = animal;    }    public Animal getAnimal() {        return animal;    }    public void setAnimal(Animal animal) {        this.animal = animal;    }}

编写spring-application.xml

<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="cat" class="com.wygu.spring.animal.Cat">        <constructor-arg name="catName" value="加菲猫"/>      </bean>    <!-- 通过构造器参数索引进行注入 -->     <bean id="dog" class="com.wygu.spring.animal.Dog">        <constructor-arg index="0" value="欧巴"/>      </bean>    <!-- 通过构造器参数索引依赖注入 -->     <bean id="factory1" class="com.wygu.spring.animal.AnimalFactory">          <constructor-arg name="animal" ref="cat"/>      </bean>     <!-- 通过setter方式依赖注入 -->     <bean id="factory2" class="com.wygu.spring.animal.AnimalFactory">         <property name="animal" ref="dog" />    </bean></beans>

通过上述示例,我们知道,利用构造器注入时,常见的有两种注入方式:(1)根据参数索引注入,使用标签“”来指定注入的依赖,其中“index”表示索引,从0开始,即第一个参数索引为0,“value”来指定注入的常量值;(2)根据参数名进行注入,使用标签“”来指定注入的依赖,其中“name”表示需要匹配的参数名字,“value”来指定注入的常量值。
对于setter方法注入方式,只有一种依赖注入方式: 根据参数名进行注入,语法同上。
但是,当系统非常庞大时,如果Bean之间的依赖关系全部通过XML方式实现,会导致配置文件的可读性与可维护性变得很低。此外,在开发中,开发人员需要在.java文件和.xml文件之间来回的切换,从而降低开发效率。
为了解决上述问题,Spring引入了注解,通过”@XXX”的方式,让注解与Java Bean紧密结合,不仅精简了大量的配置文件,而且还增加了Java Bean的可读性与内聚性。下面给出通过注解方式实现Java Bean的装配。

2、通过注解技术实现Bean的自动装配
(1)@Autowired

 1)在AnimalFactory的成员域上标注注解 @Autowired
package com.wygu.spring.animal;import org.springframework.beans.factory.annotation.Autowired;public class AnimalFactory {    @Autowired    private Animal animal;....}

spring-application.xml

<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">    <!-- 该 BeanPostProcessor 将自动起作用,对标注 @Autowired 的 Bean 进行自动注入 -->    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>    <!-- 通过构造器参数索引进行注入 -->     <bean id="dog" class="com.wygu.spring.animal.Dog">        <constructor-arg index="0" value="欧巴"/>      </bean>    <bean id="factory1" class="com.wygu.spring.animal.AnimalFactory">      </bean></beans>

Spring 通过一个 BeanPostProcessor 对 @utowired 进行解析,所以要让 @Autowired 起作用必须事先在 Spring 容器中如spring-application.xml中声明AutowiredAnnotationBeanPostProcessor Bean。当 Spring 容器启动时,AutowiredAnnotationBeanPostProcessor 将扫描 Spring 容器中所有 Bean,当发现 Bean 中拥有 @Autowired 注释时就找到和其匹配(默认byType)的 Bean,并注入到对应的地方中去。

2) 此外,还可以在AnimalFactory的构造器上标注注解 @Autowired:

package com.wygu.spring.animal;import org.springframework.beans.factory.annotation.Autowired;public class AnimalFactory {    private Animal animal;    @Autowired    public AnimalFactory(Animal animal){        this.animal = animal;    }    .....}

3)或者在AnimalFactory的setter方法上标注注解 @Autowired:

package com.wygu.spring.animal;import org.springframework.beans.factory.annotation.Autowired;public class AnimalFactory {    private Animal animal;    public Animal getAnimal() {        return animal;    }    @Autowired    public void setAnimal(Animal animal) {        this.animal = animal;    }    ......}

但是,当我们在 Spring 容器中配置了两个类型为Animal类型的 Bean时,在对AnimalFactory中成员域animal自动注入时,Spring 容器将无法确定注入哪一个,就会抛出如下的异常:

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'factory1': Unsatisfied dependency expressed through method 'setAnimal' parameter 0: No qualifying bean of type [com.wygu.spring.animal.Animal] is defined: expected single matching bean but found 2: cat,dog; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.wygu.spring.animal.Animal] is defined: expected single matching bean but found 2: cat,dog    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:651)    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:350)    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:775)    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861)    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541)    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)    at com.wygu.spring.main.Main.main(Main.java:12)Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.wygu.spring.animal.Animal] is defined: expected single matching bean but found 2: cat,dog    at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:172)    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1059)    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1018)    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:643)    ... 15 more

通过上述异常,很容易看出是由于同时存在两个满足注入条件的Bean(同一种类型Animal),从而使得IOC容器抛出异常。
Spring中 允许我们通过 @Qualifier 注解指定注入 Bean 的名称,这样歧义就消除了。

(2)@Qualifier

    @Autowired    @Qualifier("cat")     public void setAnimal(Animal animal) {        this.animal = animal;    }

仍然报上述错误,不知道为什么,网上各种方法都试了,仍然没有消除二义性?????
然后在spring-application.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">   <context:annotation-config/>    <bean id="factory1" class="com.wygu.spring.animal.AnimalFactory"/>     <!-- 通过构造器参数索引进行注入 -->     <bean id="cat" class="com.wygu.spring.animal.Cat">         <constructor-arg index="0" value="加菲猫"/>    </bean>    <!-- 通过构造器参数索引进行注入 -->     <bean id="dog" class="com.wygu.spring.animal.Dog">        <constructor-arg index="0" value="欧巴"/>      </bean></beans>

@Autowired 和@Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。@Autowired 和@Qualifier可以结合对构造器和成员域进行标注。

(3)如果不存在Bean匹配,可以在自动注入的地方加上 @Autowired(required = false)

 @Autowired(required = false)    public void setAnimal(Animal animal) {        this.animal = animal;    }

但是上述方式不建议使用,因为很有可能出现空指针异常。

3、在Java中进行显示配置

尽管在很多场景下通过自动装配实现实现Spring的自动化配置是更为推荐的,但有时候自动化配置的方案是行不通的,因此我们需要明确配置Spring。比如想将第三方第三方库中的组件装配到应用中,此时是没有办法自动装配的,因而我们需要采用显示装配方式,比如XML,或者JavaConfig方式。由于JavaConfig是类型安全的而且重构友好,我们更推荐使用JavaConfig方式。

package com.wygu.spring.hello;public interface HelloWorld {    public void printHelloWorld(String str);}package com.wygu.spring.hello;public class HelloWorldImp implements HelloWorld{    @Override    public void printHelloWorld(String str){        System.out.println("Hello:"+str);    }}

创建HelloConfig

package com.wygu.spring.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import com.wygu.spring.hello.HelloWorld;import com.wygu.spring.hello.HelloWorldImp;@Configurationpublic class HelloConfig {     @Bean(name="helloBean")    public HelloWorld getHelloWorld(){        return new HelloWorldImp();    }}
package com.wygu.spring.main;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import com.wygu.spring.config.HelloConfig;import com.wygu.spring.hello.HelloWorld;public class Main {    public static void main(String[] args) {         @SuppressWarnings("resource")        ApplicationContext context = new AnnotationConfigApplicationContext(HelloConfig.class);            HelloWorld helloWorld = (HelloWorld) context.getBean("helloBean");            helloWorld.printHelloWorld("Spring Java Config");    }}

执行结果为:Hello:Spring Java Config
显然,上述代码示例中,没有使用自动装配和XMl实现了装配Bean的功能。

原创粉丝点击