Spring Bean的装配

来源:互联网 发布:ssd主控测试软件 编辑:程序博客网 时间:2024/06/07 10:49

依赖注入的3种方式

在实际环境中实现Ioc容器的方式主要分为两大类,一类是依赖查找,依赖查找是通过资源定位,把对应的资源查找回来;另一类则是依赖注入,而Spring主要使用的是依赖注入,同事依赖注入分为3种方式:
(1)构造器注入
(2)setter注入
(3)接口注入
构造器注入和setter注入是主要的方式,而接口注入是从别的地方注入的方式,比如Web工程中使用JNDI的方式配置数据源,就是通过接口将它注入Spring Ioc容器中来。

构造器注入
构造器注入依赖于构造器方法实现,而构造器方法可以是有参数的或者是无参数的。大部分通过类的构造方法创建类,Spring也可以采用反射的方法,通过使用构造方法来完成注入,这就是构造器注入的原理。

public class Role {    private Long id;    private String roleName;    private String note;    //setter and getter    public Role(String roleName,String note){        this.roleName=roleName;        this.note=note;    }}
<bean id="role1" class="com.bob.analyst.model.Role">   <constructor-arg index="0" value="总经理"></constructor-arg>   <constructor-arg index="1" value="公司管理者"></constructor-arg></bean>

备注:constructor-arg元素用于定义类构造器方法的参数,其中index用于定义参数的位置,value则是设置值。

使用setter注入
setter注入是Spring中最主流的注入方式,它利用Java Bean规范所定义的setter方法来完成注入,灵活且可读性高。它消除了使用构造器注入时出现多个参数的可能性,首先可以把构造器方法声明为无参数的,然后使用setter注入为其设置对应的值,其实也是通过Java反射技术得以实现的。

同上面的构造器注入,把有参改成无参即可,同时xml配置:

<bean id="role1" class="com.bob.analyst.model.Role">       <property name="roleName" value="高级工程师"></property>        <property name="note" value="重要人员"></property>    </bean>

接口注入
有时候资源来自外界,如配置在Tomcat下,然后通过JNDI的形式去获取它,这样数据库连接资源是属于开发工程外的资源。
例如:在tomcat的context.xml文件中配置了数据库的相关信息,然后在Spring中通过接口注入实现
context.xml配置文件

<!--    name JNDI名称    url 数据库的jdbc连接    username用户名    password 数据库密码    -->    <Resource name="jdbc/ssm"     auth="Container"      type="javax.sql.DataSource"       driverClassName="com.mysql.jdbc.Driver"       url="jdbc:mysql://localhost:3306/ssm?zeroDateTimeBehavior=converToNull"       username="root"       password="123456"></Resource>

spring配置文件:

<!-- 通过JNDI获取数据源,通过Spring的接口注入实现 --><bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">        <property name="jndiName">           <value>java:comp/env/jdbc/ssm</value>        </property>    </bean>

装配Bean
Spring提供了3种方式进行装配

  1. 在XML中显示配置
  2. 在Java的接口和类中实现配置
  3. 隐式Bean的发现及时和自动装配原则

(1)其中基于预定优于配置的原则,最优先的应该是隐式Bean的发现机制和自动装配的原则。这样的好处是减少程序开发者的决定权,简单又灵活。
(2)在没有办法使用自动装配原则的情况下应该优先考虑Java接口和类中实现配置,这样的好处是避免XML配置的泛滥。
(3)最后考虑使用XML去配置Spring容器。

当配置的类是你自身正在开发的工程,那么应该考虑Java配置为主,而Java配置又分为自动装配和Bean名称配置。在没有歧义的基础上,优先使用自动装配,这样就可以减少大量的XML配置。如果所需配置的类并不是你的工程开发的,那么建议使用XML的方式。

简单方式:

<bean id="role1" class="com.bob.analyst.model.Role">       <property name="id" value="1"></property>        <property name="roleName" value="高级工程师"></property>        <property name="note" value="重要人员"></property>    </bean>

装配集合:
bean:

public class Role {    private Long id;    private List<String> list;    private Map<String,String> map;    private Properties props;    private Set<String> set;    private String[] array;    //忽略getter setter}

xml:

<bean id="role1" class="com.bob.analyst.model.Role">       <property name="id" value="1"></property>        <property name="list" >            <list>              <value>value-list-1</value>              <value>value-list-2</value>              <value>value-list-3</value>            </list>        </property>        <property name="map" >            <map>               <entry key="key1" value="value-key-1"></entry>               <entry key="key2" value="value-key-2"></entry>               <entry key="key3" value="value-key-3"></entry>                       </map>        </property>        <property name="props" >            <props>               <prop key="prop1">value-prop1</prop>               <prop key="prop2">value-prop2</prop>               <prop key="prop3">value-prop3</prop>            </props>        </property>        <property name="set" >            <set>               <value>value-set-1</value>               <value>value-set-2</value>               <value>value-set-3</value>            </set>        </property>        <property name="array" >            <set>               <value>value-array-1</value>               <value>value-array-2</value>               <value>value-array-3</value>            </set>        </property>    </bean>

命名空间方式:略

通过注解装配Bean
使用注解的方式可以减少XML的配置,注解功能更为强大,它既能实现XML的功能,也提供了自动装配的功能,采用了自动装配后,程序员所需要做的决断就少了,更加有利于对程序的开发,这就是“约定优于配置”的开发原则。
Spring中提供了两种方式来让Spring Ioc容器发现Bean。

  1. 组件扫描:通过定义资源的方式,让Spring Ioc容器扫描对应的包,从而把Bean装配起来。
  2. 自动配置:通过注解定义,使得一些依赖关系可以通过注解完成。

使用@Component装配Bean

@Component(value="role")public class Role {    @Value("1")    private Long id;    @Value("高级工程师")    private String roleName;    @Value("role_note_1")    private String note;}

解释:

  1. 注解@Component代表Spring Ioc会把这个类扫描成Bean实例,而其中的value属性代表这个类在Spring 中的id,也可以写成@Component(“role”)或者@Component,对于不用写的,Spring Ioc容器就默认类名,但是以首字母小写的形式作为id,为其生成对象,配置到容器中。
  2. 注解@Value代表的是值的注入,其中Long注入的时候Spring会自动转换类型。

这个时候Spring还需要到哪里去扫描它,所以需要在同级包下新建一个类

package com.bob.analyst.model;import org.springframework.context.annotation.ComponentScan;@ComponentScanpublic class PojoConfig {}

@ComponentScan代表进行扫描,默认是扫描当前包的路径,POJO的包名和它保持一致才能扫描,否则是没有的。

@ComponentScan存在这两个配置项:

  1. basePackages,可以配置一个java包的数组,Spring会根据它的配置扫描对应的包和子包,将配置好的Bean装配进来。
  2. basePackageClasses,可以配置多个类,Spring会根据配置的类所在的包,为包和子包进行扫描装配对应配置的Bean。
//@ComponentScan(basePackageClasses={User.class,UserService.class,UserServiceImpl.class})@ComponentScan(basePackages={"com.bob.analyst.model","com.bob.analyst.service"})public class PojoConfig {}

(1)如果采用多个@ComponentScan去定义对应的包,但是每定义一个@ComponentScan,Spring就会为所定义的类生成一个新的对象,也就是所配置的Bean将会生成多个实例,这往往不是我们的需要。

(2)对于已经定义了backPackages和basePackageClasses的@ComponentScan,Spring会进行专门的区分,也就是说在同一个@ComponentScan中即使重复定义相同的包或者存在其子包定义,也不会造成因一个Bean的多次扫描,而导致一次配置生成多个对象。
备注:不建议采用配置多个@ComponentScan,因为一旦有重复的包和子包就会产生重复的对应,这往往不是真实的需求。
尽量不要实用哦个basePackages,因为很多时候重构修改包名需要反复的配置,而IDE不会给你任何的提示。

自动装配 @Autowired

  • Spring是先完成Bean的定义和生成,然后寻找需要注入的资源。也就是当Spring生成所有的Bean后,如果发现了这个注解,它就会在Bean中查找,然后找到对应的类型,将其注入进来,这样就完成了依赖注入了。所谓自动装配技术是一种由Spring自己发现对应的Bean,自动完成装配工作的方式。
  • @Autowired表示在Spring Ioc定位所有的Bean后,这个字段需要按类型注入,这样Ioc容器就会寻找资源,然后将其注入。Ioc容器有时候会寻找失败,在默认的情况下失败就会抛出异常,可以通过required来改变它,如入@Autowired(required=false)
    配置false时,就会告诉Spring Ioc容器,假如找不到资源允许不注入,这样也就没有异常抛出。

自动装配的歧义性(@Primary和@Qualifier)

背景:
例如,一个RoleService接口有两个实现类,分别是RoleServiceImpl和RoleServiceImpl3,这个时候Spring Ioc容器就会犯糊涂了,它无法判断把哪个对象注入进来,于是就抛出异常,这样@Autowired注入就会失败。
Spring Ioc最底层接口 BeanFactory中有一个按照类型注入引起的。

<T> T getBean(Class<T> requiredType) throws BeansException;

1、注解@Primary代表首要的,当Spring Ioc通过一个接口或者抽象类注入对象的时候,由于存在多个实现类或者具体类,@Primary则会告诉Spring Ioc容器优先使用该类。
@Primary放在类名上面即可。
备注:同时出现多个@Primary来代表优先时,也会抛出异常。

2、注解@Qualifier
出现歧义的原因是因为Spring在寻找类的时候采用按类型注入引起的。
@Qualifier就是采用名称查找的方法。

@Autowired    @Qualifier("analystService2")    private AnalystService analystService;

使用这个注解后就可以使用这个方法通过名称从Ioc容器中获取对象进行注入。

使用@Bean装配Bean

  • 背景:
    @Component只能注解在类上,不能注解到方法上。对于java而言,大部分的开发都需要引入第三方的包,而且往往并没有这些包的资源,这时候将无法为这些包的类加入@Component注解,让它们变为开发环境的Bean。可以使用新类扩展其包内的类,然后使用@Component,但是这样显得比较不伦不类。
  • 注解
    @Bean 可以注解到方法上,并且将方法返回的对象作为Spring的Bean,存放在Ioc容器中。它不能使用在类的标注上,它主要使用在方法上。其中有4个配置项:
    (1)name:是一个字符串,允许配置多个BeanName。
    (2)autowire:标识是否是一个引用Bean对象,默认值是Autowire.NO。
    (3)initMethod:自定义初始化方法。
    (4)destroyMethod:自定义销毁方法。

Bean的作用域

  • 默认情况下Spring Ioc只会为配置的Bean生成一个实例,而不是多个。
  • 有时候我们希望通过Spring Ioc容器获取多个实例,例如控制层如果是一个实例那么从头到尾就是一个而不是多个,这个不满足互联网的并发需求。
  • 为了解决这个问题,有时候希望控制层是多个实例,每当我们请求的时候就产生一个独立的对象,而不是默认的一个,这样多个实例就可以在不同的线程运行了,就没有并发问题了。

Spring提供了4种作用域,它会根据情况来决定是否生成新的对象。

  1. 单例(singleton):它是默认的选项,在整个应用中,Spring只为其生成一个Bean的实例。
  2. 原型(prototype):当每次注入,或者通过Spring Ioc容器获取Bean时,Spring都会为它创建一个新的实例。
  3. 会话(session):在Web应用中使用,就是在会话过程中Spring只创建一个实例。
  4. 请求(request):在Web应用中使用的,就是在一次请求中Spring会创建一个实例,但是不同的请求会创建不同的实例。