系统学习Spring之Spring in action(五)

每日一叨:

    原本想这个星期六和星期天把依赖注入和面向切面编程写完的,结果小姨来南京玩,写博客的计划泡汤了.只能拖到今天写了,一定要找时间弥补上....

 

文章导读:

 

    1.通过XML实现Bean的自动装配

    2.通过注解装配bean

 

 

知识点:

    到现在为止,我们学习了如何用<constructor-arg>和<properties>向bean中注入值.在小一点的应用程序中,这种做法是个不错的选择.如果bean比较多,

注入的值也多,如果你还要用之前的方法配置会很繁琐。还好Spring提供了一种可以减少配置说是消除<constructor-arg>和<properties>的技术.

   1.通过XML实现Bean的自动装配

  Spring提供了5种类型的自动装配.分别是byName,byType,constructor,autodetect,default

  1)byName:匹配<beans>标签中所有bean的name或者id与当前自动装配bean中properties名字相同的bean,并且自动注入到自动装配的bean中,

  若没有匹配的name或者id,则匹配失败

  2)byType:匹配<beans>标签中所有bean的类型与当前自动装配bean中properties中值的类型相同的bean,并且自动注入到自动装配的bean中,

  若没有匹配的类型,则匹配失败.

  3)constructor:匹配<beans>标签中所有bean的构造方法的参数类型与当前自动装配相匹配的bean,若没有匹配类型,则匹配失败.

  4)autodetect:autodetect的匹配策略是先通过constroctor进行匹配,若匹配失败,则进行byType匹配,若仍然没有匹配成功,则匹配失败.

  5)default:dafault是根据Spring configuration配置而来,可以是上面的任意一种. 

  byName事例:

public interface Game {
    //获取游戏名称
    public void getGameName();
}
public class War3 implements Game {
    @Override
    public void getGameName() {
        // TODO Auto-generated method stub
        System.out.println("大家好,我的名字叫魔兽争霸,相信你们很多人都已经认识我了.");
    }
}
public class Dota implements Game {
    //Dota游戏作者
    private String author;
    //Dota玩家同样会玩War3
    private War3 war3;    

    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
    public War3 getWar3() {
        return war3;
    }
    public void setWar3(War3 war3) {
        this.war3 = war3;
    }

    @Override
    public void getGameName() {
        // TODO Auto-generated method stub
        System.out.println("大家好,我们名字叫Dota,来自 " + this.author + " 之手");
        this.war3.getGameName();
    }
}

 

  这是手动注入的配置:

    <bean id="war3" class="com.ricky.zero.pojo.War3"></bean>
    <bean id="dota" class="com.ricky.zero.pojo.Dota">
        <property name="author" value="冰蛙"></property>
        <property name="war3" ref="war3"></property>
    </bean>

  这是自动注入的配置:

    <bean id="war3" class="com.ricky.zero.pojo.War3"></bean>
    <bean id="dota" class="com.ricky.zero.pojo.Dota" autowire="byName">
    <property name="author" value="冰蛙"></property>
</bean>

data实例中有一个com.ricky.zero.pojo.War3类型的属性,该属性的变量名为war3,所以bean被加载的时候会通过autowire的byName策略自动寻找

id或者name为war3的bean,完成自动注入.

事例测试:

        ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
        Dota dota  = (Dota)ctx.getBean("dota");
        dota.getGameName();

测试结果:

大家好,我们名字叫Dota,来自 冰蛙 之手
大家好,我的名字叫魔兽争霸,相信你们很多人都已经认识我了.

不管有多少个properties通过ref属性去注入对象都不再去重新配置XML文件了,其优势不言而喻。

  byType事例:

只需要修改Spring配置文件即可.

    <bean id="war3" class="com.ricky.zero.pojo.War3"></bean>
    <bean id="dota" class="com.ricky.zero.pojo.Dota" autowire="byType">
        <property name="author" value="冰蛙"></property>
    </bean>

data实例中有一个com.ricky.zero.pojo.War3类型的属性,所以bean被加载的时候会通过autowire的byType策略自动寻找类型为com.ricky.zero.pojo.War3

类型的bean,完成自动注入

直接运行测试并得到运行结果为:

大家好,我们名字叫Dota,来自 冰蛙 之手
大家好,我的名字叫魔兽争霸,相信你们很多人都已经认识我了.

byType有一个问题就是同一类型的bean不能同时出现.如下示例:

    <bean id="war4" class="com.ricky.zero.pojo.War3"></bean>
    <bean id="war3" class="com.ricky.zero.pojo.War3"></bean>
    <bean id="dota" class="com.ricky.zero.pojo.Dota" autowire="byType">

若都是同一个类型com.ricky.zero.pojo.War3,只是id不同,在autowire的byType策略下会抛异常,异常如下:

 expected single matching bean but found 2: war4,war3

在autowire的byType策略下,出现两个相同类型Spring不会去猜你想要的是哪个,而是直接抛错,让你自己来解决,目前只能先确保一个类型在Spring context中

只有一个对应的bean.若同一类型必须要出现多个bean而且还要运用byType策略,Spring提供2种方法解决上述问题,一种是通过识别出重要的候选者,

另一种是消除byType搜索的候选者.

识别重要的候选者事例:

修改Spring configuration配置文件:

    <bean id="war4" class="com.ricky.zero.pojo.War3"></bean>
    <bean id="war3" class="com.ricky.zero.pojo.War3" primary="true"></bean>
    <bean id="dota" class="com.ricky.zero.pojo.Dota" autowire="byType">
        <property name="author" value="冰蛙"></property>
    </bean>

若在一个bean中加上parmary="true"属性,byType策略会首先注入当前bean.同类中,只能有一个primary="true"属性,否则也会抛异常.

还有一种方法是消除byType搜索的候选者:

    <bean id="war4" class="com.ricky.zero.pojo.War3" ></bean>
    <bean id="war3" class="com.ricky.zero.pojo.War3" autowire-candidate="false"></bean>
    <bean id="dota" class="com.ricky.zero.pojo.Dota" autowire="byType">
        <property name="author" value="冰蛙"></property>
    </bean>

若哪个bean后面有autowire-candidate="false"属性时,byType搜索的时候将会跳过有autowire-candidate="false"属性的bean.

两个事例的测试结果为:

大家好,我们名字叫Dota,来自 冰蛙 之手
大家好,我的名字叫魔兽争霸,相信你们很多人都已经认识我了.

constructor事例:

public class War3 implements Game {
    @Override
    public void getGameName() {
        // TODO Auto-generated method stub
        System.out.println("大家好,我的名字叫魔兽争霸,相信你们很多人都已经认识我了.");
    }
}
public class DotaPlayer implements Player {
    
    //已杀敌人总数
    private int currentKill = 0;
    private War3 war3;
    
    //没有参数的构造方法 
    public DotaPlayer(){}
    
    //以基本类型为参数的构造方法
    public DotaPlayer(int currentKill){
        this.currentKill = currentKill;
    };
    
    //以对象类型为参数的构造方法
    
    public DotaPlayer(War3 war3){
        this.war3 = war3;
    };    
    
    //重写Player类中的play()方法
    @Override
    public void play() {
        // TODO Auto-generated method stub
        System.out.println("DOTA玩家已经杀了"+ this.currentKill +"个敌人.");
        war3.getGameName();
    }
}

Spring Configuration配置文件如下:

    <bean id="war3" class="com.ricky.zero.pojo.War3"></bean>
    <bean id="dotaPlayer" class="com.ricky.zero.pojo.DotaPlayer"  autowire="constructor">
    </bean>

autowire="constructor"作用是搜寻与构造方法参数列表中参数变量的名称与bean的name相同的bean,然后注入构造方法中.

运行测试

ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
        DotaPlayer dotaPlayer = (DotaPlayer)ctx.getBean("dotaPlayer");
        dotaPlayer.play();


测试结果:

DOTA玩家已经杀了0个敌人.
大家好,我的名字叫魔兽争霸,相信你们很多人都已经认识我了.

default:默认的自动装配策略是空,可以自定义默认的策略.

在你使用自动装配策略的时候,仍然可以使用手动指定装配.手动指定装配将会overriding自动装配,这样的混合装配在constructor-autowiring中是不允许的.

Spring必须装配所有的arguments.

 

2.通过注解装配bean

  从Spring的2.5版本,Spring就支持用注解的方式去自动装载bean的properties.用注解的方式和用XML配置文件的方式大同小异.但是用注解可以更细粒度的

装配bean.注解在Spring configuration中默认是关闭的,如果要想使用注解,首先要打开注解.最简单的方法是在<beans>标签下添加如下语句.

<context:annotation-config/>

这个标签告诉Spring你打算使用注解去装配bean,当你使用注解的时候,注解可以装配值到properties,method,constructors中.

Spring支持3种自动装配注解:@Autowired,@Inject,@Resource.

@Autowired事例:

当你想用@Autowired去装配bean的id为dota的properties为wa3的bean的时候.(感觉不好理解就看下面的例子,一看就懂了,可能我的表达能力不有,慢慢提高哈.)

@Autowire注解自动装配的策略是byType

可以在properties的setter方法上加这个注解:

    @Autowired
    public void setWar3(War3 war3) {
        this.war3 = war3;
    }

Spring configuration配置文件:

 

    //用于打开Spring注解
        <context:annotation-config />
    <bean id="war3" class="com.ricky.zero.pojo.War3"></bean>
    <bean id="dota" class="com.ricky.zero.pojo.Dota">
        <property name="author" value="冰蛙"></property>
    </bean>

在setter方法上加@Autowired相当于<properties name="变量名" ref="已经实例的对象"></properties>  这个标签的功能.@Autowired比XML配置

要简单,精巧,

@Autowired也可以用在构造方法上.

    @Autowired
    public Dota(War3 war3) {
        this.war3 = war3;
    }

运行两次使用@Autowired的测试,并得到结果如下:

大家好,我们名字叫Dota,来自 冰蛙 之手
大家好,我的名字叫魔兽争霸,相信你们很多人都已经认识我了.

@Autowired也可以直接在properties上使用,而不用在setter方法上就可以完成注入.用注解以后就废除setter方法,直接在属性上注解.

例:

    @Autowired
    private War3 war3;

运行测试结果如上.

当@Autowired没有搜索到匹配的bean,或者多个bean同时被注入的时候,Spring将会抛异常.

当没有搜索到匹配的bean的时候会抛异常:

expected at least 1 bean which qualifies as autowire candidate for this dependency

因为@Autowired有一个缺省值@Autowired(required=true),当required为true的时候,若没有匹配到bean则会抛异常.若把required设置成false,

则不会抛出上述异常,但是当你用到该属性的时候,会抛NullPointerException异常,因为你注入失败,属性值为null.

当有多个bean同时匹配时,将会抛出异常如下:

 

    <bean id="war3" class="com.ricky.zero.pojo.War3"></bean>
    <bean id="war4" class="com.ricky.zero.pojo.War3"></bean>
    <bean id="dota" class="com.ricky.zero.pojo.Dota">
        <property name="author" value="冰蛙"></property>
    </bean>

expected single matching bean but found 2: war3,war4

为了找这个异常的时候,发现一个有趣的事情,本来书上写@Autowired是按byType策略去注入的.当我在

    //Dota玩家同样会玩War3
    @Autowired
    private War3 war3;    

当我的就是名为war3 ,与Spring configuration中的一个bean的id名称相同时,运行测试不会抛异常.若把变量名改成与两个bean不一样,则会抛上述异常.

该问题大家也考虑下,现在没有时间研究,有时间来探究下.

这种异常可以通过Spring的又一个注解来解决@Qualifier.

@Qualifier是用于通过ID来选择你想要的bean.简单来说就是@Autowired搜索出所有相同类型的候选bean,@Qualifier(value="要选择bean的id")

在这些候选bean中选择id与@Qualifier的value相同的bean.

    //Dota玩家同样会玩War3
    @Autowired
    @Qualifier(value="war3")
    private War3 war3;   

这样就解决上述的异常问题了.

 

--------------------------------申明----------------------------       

                  本文可以免费阅读以及转载,转载时请注明出处.      

                  本人邮箱:Ricky_LS@163.com

                  Thank you for your corporation.

 

posted @ 2013-03-26 00:59  养家糊口  阅读(1684)  评论(0编辑  收藏  举报