Spring详解3.Bean的装配


点击进入个人博客

1 Spring容器与Bean配置信息

Bean配置信息

Bean配置信息是Bean的元数据信息,它由一下4个方面组成:java

  • Bean的实现类
  • Bean的属性信息,如数据库的链接数、用户名、密码。
  • Bean的依赖关系,Spring根据依赖关系配置完成Bean之间的装配。
  • Bean的行为信息,如生命周期范围及生命周期各过程的回调函数。
Bean元数据信息

Bean元数据信息在Spring容器中的内部对应物是一个个BeanDefinition造成的Bean注册表,Spring实现了Bean元数据信息内部表示和外部定义之间的解耦。正则表达式

Spring支持的配置方式

Spring1.0仅支持基于XML的配置,Spring2.0新增基于注解配置的支持,Spring3.0新增基于Java类配置的支持,Spring4.0则新增给予Groovy动态语言配置的支持。
Spring容器内部协做解构spring

2 基于XML的配置

2.1 理解XML与Schema

<?xml version="1.0" encoding="utf-8" ?>
<beans (1)xmlns="http://www.springframework.org/schema/beans"
       (2)xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:(3)context=(4)"http://www.springframework.org/schema/context"
       xsi:(5)schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
  • (1)处是默认命名空间,无命名空间前缀的元素属于默认命名空间。
  • (2)xsi标准命名空间,用于指定自定义命名空间的Schema文件
  • (3)自定义命名空间的简称,能够任意命名
  • (4)自定义命名空间的全称,必须在xsi命名空间为其指定空间对应的Schema文件,能够任意命名,习惯上用文档发布机构的相关网站目录。
  • (5)为每一个命名空间指定Schema文件位置,
详解xmlns
  • 定义:xml namespace的缩写,可译为“XML命名空间”。
  • 做用:防止XML文档含有相同的元素命名冲突,如<table>既能够表示表格,又能够表示桌子。若是增长了命名空间如<table>和<t:table>就可使二者区分开来。
  • 使用:xmlns:namespace-prefix="namespaceURI",其中namespace-prefix为自定义前缀,只要在这个XML文档中保证前缀不重复便可;namespaceURI是这个前缀对应的XML Namespace的定义。
理解xsi:schemaLocation

xsi:schemaLocation定义了XML Namespace和对应的 XSD(Xml Schema Definition)文档的位置的关系。它的值由一个或多个URI引用对组成,两个URI之间以空白符分隔(空格和换行都可)。第一个URI是定义的 XML Namespace的值,第二个URI给出Schema文档的位置,Schema处理器将从这个位置读取Schema文档,该文档的targetNamespace必须与第一个URI相匹配。例如:数据库

<beans 
       xsi:schemaLocation="http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">
</beans>

这里表示Namespace为http://www.springframework.or...://www.springframework.org/schema/context/spring-context.xsd。express

2.2 使用XML装配Bean

直接装配Bean
<!--id不能够配置多个: context.getBean("myBean1, myBean2")-->
<bean class="com.ankeetc.spring.MyBean" id="myBean1, myBean2"/>
    
<!--context.getBean("myBean1") == context.getBean("myBean2")-->
<bean class="com.ankeetc.spring.MyBean" name="myBean1, myBean2"/>
  • id:用于表示一个Bean的名称,在容器内不能重复;不能够配置多个id。
  • name:用于表示一个Bean的名称,在容器内不能重复;能够配置多个名称,用,分割;id和name能够都为空,此时则能够经过获取全限定类名来获取Bean。
  • class:全限定类名
静态工厂方法装配
  • 静态工厂无需建立工厂类示例就能够调用工厂类方法。
  • factory-method:工厂方法
public class MyBeanFactory {
    public static MyBean createMyBean() {
        return new MyBean();
    }
}
<bean id="myBean" class="com.ankeetc.spring.MyBeanFactory" factory-method="createMyBean"/>
非静态工厂方法装配
  • 非静态工厂方法必须首先定义一个工厂类的Bean,而后经过factory-bean引用工厂类实例。
  • factory-bean:指向定义好的工厂Bean
public class MyBeanFactory {
    public MyBean createMyBean() {
        return new MyBean();
    }
}
<bean id="myBeanFactory" class="com.ankeetc.spring.MyBeanFactory"/>
<bean id="myBean" factory-bean="myBeanFactory" factory-method="createMyBean"/>
Bean的继承和依赖
  • parent:经过设置<bean>标签的parent属性,子<bean>将自动继承父<bean>的配置信息。
  • depends-on:经过设置<bean>标签的depends-on属性,Spring容许显示的设置当前Bean前置依赖的Bean,确保前置依赖的Bean在当前Bean实例化以前已经建立好。
自动装配autowire

<beans>元素提供了一个default-autowire属性能够全局自动匹配,默认为no。<bean>元素提供了一个指定自动装配类型的autowire属性,能够覆盖<beans>元素的default-autowire属性,该属性有以下选项:缓存

自动装配类型 说明
no 显式指定不使用自动装配。
byName 若是存在一个和当前属性名字一致的 Bean,则使用该 Bean 进行注入。若是名称匹配可是类型不匹配,则抛出异常。若是没有匹配的类型,则什么也不作。
byType 若是存在一个和当前属性类型一致的 Bean ( 相同类型或者子类型 ),则使用该 Bean 进行注入。byType 可以识别工厂方法,即可以识别 factory-method 的返回类型。若是存在多个类型一致的 Bean,则抛出异常。若是没有匹配的类型,则什么也不作。
constructor 与 byType 相似,只不过它是针对构造函数注入而言的。若是当前没有与构造函数的参数类型匹配的 Bean,则抛出异常。使用该种装配模式时,优先匹配参数最多的构造函数。
default 根据 Bean 的自省机制决定采用 byType 仍是 constructor 进行自动装配。若是 Bean 提供了默认的构造函数,则采用 byType;不然采用 constructor 进行自动装配。
经过util命名空间配置集合类型的Bean
<util:list></util:list>
<util:set></util:set>
<util:map></util:map>

2.3 使用XML依赖注入

属性配置
  • Bean有一个无参数的构造器
  • 属性有对应的Setter函数
  • 属性命名知足JavaBean的属性命名规范
<bean class="com.ankeetc.spring.MyBean" id="myBean">
    <property name="prop" value="prop"/>
</bean>
构造方法
  • constructor-arg中的type和index能够没有,只要能保证能够惟一的肯定对应的构造方法便可
  • type中基本数据类型和对应的包装类不能通用
  • 循环依赖:若是两个Bean都采用构造方法注入,并且都经过构造方法入参引用对方,就会形成循环依赖致使死锁。
<bean class="com.ankeetc.spring.MyBean" id="myBean">
    <constructor-arg type="java.lang.String" index="0" value="abc"/>
    <constructor-arg type="int" index="1" value="10"/>
</bean>

2.4 注入参数

字面值
  • 基本数据类型及其封装类、String均可以采起字面值注入。
  • 特殊字符可使用<![CDATA[]]>节或者转义序列
引用其余Bean

<ref>元素能够经过如下三个属性引用容器中的其余Bean:session

  • bean:经过该属性能够引用同一容器或父容器的Bean,这是最多见的形式。
  • local:经过该属性只能引用同一配置文件中定义的Bean,它能够利用XML解析器自动检验引用的合法性,以便在开发编写配置时可以及时发现并纠正配置的错误。
  • parent:引用父容器中的Bean,如<ref parent="car">的配置说明car的Bean是父容器中的Bean。
内部Bean
  • 内部Bean只会被当前Bean引用,不会被容器中其余的Bean引用
  • 内部Bean即便提供了id、name、scope也会被忽略,Scope默认为prototype类型。
<bean id="prop" class="com.ankeetc.spring.Prop">
        <property name="value" value="1314"/>
    </bean>

    <bean id="myBean" class="com.ankeetc.spring.MyBean">
        <property name="prop">
            <!--内部Bean即便提供了id、name、scope也会被忽略-->
            <bean id="prop" class="com.ankeetc.spring.Prop">
                <property name="value" value="520"/>
            </bean>
        </property>
    </bean>
null值
  • 使用<null/>表明null值
级联属性
  • Spring支持级联属性如prop.value,并且支持多层级联属性
  • 级联属性必须有初始值,不然会抛出NullValueInNestedPathException
public class MyBean {
    // 必须初始化
    private Prop prop = new Prop();

    public Prop getProp() {
        return prop;
    }

    public void setProp(Prop prop) {
        this.prop = prop;
    }
}
<bean id="myBean" class="com.ankeetc.spring.MyBean">
        <property name="prop.value" value="1314"/>
    </bean>
集合类型属性
  • List、Set、Map:经过<list><set><map><entry>等标签能够设置List、Set、Map的属性
  • Properties:能够经过<props><prop>等标签设置Properties的属性,Properties属性的键值都只能是字符串。
  • 集合合并:子Bean能够继承父Bean的同名属性集合元素,而且使用merge属性选择是否合并,默认不合并。
<bean id="parentBean" class="com.ankeetc.spring.MyBean">
        <property name="list">
            <list>
                <value>1314</value>
            </list>
        </property>
    </bean>

    <bean id="myBean" class="com.ankeetc.spring.MyBean" parent="parentBean">
        <property name="list">
            <list merge="true">
                <value>520</value>
            </list>
        </property>
    </bean>

2.5 多配置文件整合

  1. 能够经过ApplicationContext加载多个配置文件,此时多个配置文件中的<bean>是能够互相访问的。
  2. 能够经过XML中的<import>将多个配置文件引入到一个文件中,这样只须要加载一个配置文件便可。

2.6 Bean的做用域

类型 说明
singleton 在Spring IoC容器中仅存在一个Bean实例,Bean以单实例的方式存在
prototype 每次从容器中调用Bean时,都返回一个新的实例
request 每次HTTP请求都会建立一个新的Bean,该做用域仅适用于WebApplicationContext环境
session 同一个HTTP session共享一个Bean,不一样的HTTP session使用不一样的Bean,该做用域仅适用于WebApplicationContext环境
globalSession 同一个全局Session共享一个Bean,通常用于Portlet环境,该做用域仅适用于WebApplicationContext环境
singleton做用域
  • 无状态或者状态不可变的类适合使用单例模式
  • 若是用户不但愿在容器启动时提早实例化singleton的Bean,可使用lazy-init属性进行控制
  • 若是该Bean被其余须要提早实例化的Bean所引用,那么Spring将会忽略lazy-init的设置
prototype做用域
  • 设置为scope="prototype"以后,每次调用getBean()都会返回一个新的实例
  • 默认状况下,容器在启动时不会实例化prototype的Bean
  • Spring容器将prototype的Bean交给调用者后就再也不管理它的生命周期
Web应用环境相关的Bean做用域

见后续章节app

做用域依赖的问题

见后续章节ide

3 FactoryBean

因为实例化Bean的过程比较负责,可能须要大量的配置,这是采用编码的方式多是更好的选择。Spring提供了FactoryBean工厂类接口,用户能够实现该接口定制实例化Bean的逻辑。当配置文件中<bean>的class属性配置的是FactoryBean的子类时,经过getBean()返回的不是FactoryBean自己,而是getObject()方法所返回的对象,至关因而FactoryBean#getObject()代理了getBean()方法函数

  • T getObject() throws Exception;:返回由FactoryBean建立的Bean实例,若是isSingleton()返回的是true,该实例会放到Spring容器的实例缓存池中。
  • Class<?> getObjectType();:返回该FactoryBean建立的Bean的类型
  • boolean isSingleton();:建立的Bean是singleton的仍是prototype
/**
 * 实现,分割的方式配置 KFCCombo 属性
 */
public class KFCFactoryBean implements FactoryBean<KFCCombo> {
    private String prop;

    public String getProp() {
        return prop;
    }

    // 接受,分割的属性设置信息
    public void setProp(String prop) {
        this.prop = prop;
    }

    // 实例化KFCCombo
    public KFCCombo getObject() throws Exception {
        KFCCombo combo = new KFCCombo();
        String[] props = prop.split(",");
        combo.setBurger(props[0]);
        combo.setDrink(props[1]);
        return combo;
    }

    public Class<?> getObjectType() {
        return KFCCombo.class;
    }

    // true则放进容器缓存池,false则每次都调用getObject()方法返回新的对象
    public boolean isSingleton() {
        return false;
    }
}
<bean id="combo" class="com.ankeetc.spring.KFCFactoryBean">
        <property name="prop" value="ZingerBurger, PepsiCola"/>
    </bean>

4 基于注解的配置

4.1 支持的注解

@Component:在Bean的实现类上直接标注,能够被Spring容器识别
@Repository:用于对DAO实现类进行标柱
@Service:用于对Service实现类进行标注
@Controller:用于对Controller实现类进行标注

4.2 扫描注解定义对Bean

Spring提供了一个context命名空间,用于扫描以注解定义Bean的类。

<!--生命context命名空间-->
<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.0.xsd 
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <context:component-scan base-package="com.ankeetc.spring"/>
</beans>
base-package属性
  • 指定一个须要扫描的基类包,Spring容器会扫描这个包下的全部类,并提取标注了相关注解的Bean。
resource-pattern属性
  • 若是不但愿扫描base-package下的全部类,可使用该属性提供过滤
  • 该属性默认是**/*.class,即基包下的全部类
<context:exclude-filter>与<context:include-filter>
  • <context:exclude-filter>:表示要排除的目标类
  • <context:include-filter>:表示要包含的目标类
  • <context:component-scan>能够有多个上述两个子元素;首先根据exclude-filter列出须要排除的黑名单,而后再根据include-filter流出须要包含的白名单。
类别 示例 说明
annotation com.ankeetc.XxxAnnotation 全部标注了XxxAnnotation的类。该类型采用目标类是否标志了某个注解进行过滤。
assignable com.ankeetc.XxService 全部继承或扩展XXXService的类。该类型采用目标类是否继承或者扩展了某个特定类进行过滤
aspectj com.ankeetc..*Service+ 全部类名以Service结束的类及继承或者扩展他们的类。该类型采用AspectJ表达式进行过滤
regex com.ankeetc.auto..* 全部com.ankeetc.auto类包下的类。该类型采用正则表达式根据目标类的类名进行过滤
custom com.ankeetc.XxxTypeFilter 采用XxxTypeFilter代码方式实现过滤规则,该类必须实现org.springframework.core.type.TypeFilter接口
use-default-filters属性
  • use-default-filters属性默认值为true,表示会对标注@Component、@Controller、@Service、@Reposity的Bean进行扫描。
  • 若是想仅扫描一部分的注解,须要将该属性设置为false。
<!-- 仅扫描标注了@Controller注解的类-->
<context:component-scan base-package="com.ankeetc.spring" use-default-filters="false">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

4.3 自动装配

@Component
public class KFCCombo {
    @Autowired
    private PepsiCola cola;

    @Autowired
    private Map<String, Cola> colaMap;

    @Autowired
    private List<ZingerBurger> burgerList;

    private ZingerBurger burger;
    @Autowired
    public void setBurger(@Qualifier(value = "zingerBurger") ZingerBurger burger) {
        this.burger = burger;
    }

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath:/beans.xml"});

        KFCCombo combo = context.getBean(KFCCombo.class);
    }
}

interface Cola {}

@Order(value = 1)
@Component
class CocaCola implements Cola {}

@Order(value = 2)
@Component
class PepsiCola implements Cola {}

@Component(value = "zingerBurger")
class ZingerBurger {
}
@Autowired注解
  • 使用该注解能够按类型自动装配对应的Bean
  • 没有找到对应的Bean则会抛出NoSuchBeanDefinitionException异常
  • 使用required=false属性能够设置即便找不到对应的Bean(即为null)也不会抛出异常
  • @Autowired能够对类成员变量及方法入参进行标注
@Quaifiler
  • 若是容器中有一个以上匹配的Bean时,能够按照Bean名字查找对应的Bean
  • @Quaifiler须要与@Autowired配合使用
对集合类进行标注
  • 可使用@Autowired对集合类进行标注,Spring会讲容器中按类型匹配对全部Bean注入进来
  • 可使用@Order指定加载顺序,值越小的越先加载
@Lazy延迟加载
  • 可使用@Lazy实现延迟加载,不会当即注入属性值,而是延迟到调用此属性对时候才会注入属性值。
@Resource和@Inject
  • Spring支持JSR-250中@Resource注解和JSR-330的@Inject注解
  • @Resource采用的是按照名称加载的方式,它要求提供一个Bean名称的属性,若是属性为空,则自动采用标注处的变量名或方法名做为Bean的名称。
  • @Inject是按照类型匹配注入Bean的。
  • 因为这两个注解功能没有@Autowired功能强大,通常不须要使用。

4.4 Bean做用范围及生命周期

  • 注解配置的Bean默认做用范围为singleton,可使用@Scope显示指定做用范围
  • 可使用@PostConstruct和@PreDestroy注解来达到init-method和destroy-method属性的功能。
  • @PostConstruct和@PreDestroy注解能够有多个

5 基于Java类的配置

5.1 @Configuration注解

  • JavaConfig是Spring的一个子项目,旨在经过Java类的方式提供Bean的定义信息。
  • 普通的POJO标注了@Configuration注解,就能够被Spring容器提供Bean定义信息。
  • @Configuration注解自己已经标注了@Component注解,因此任何标注了@Configuration的类均可以做为普通的Bean。

5.2 @Bean注解

  • @Bean标注在方法上,用于产生一个Bean
  • Bean的类型由方法的返回值的类型肯定,Bean名称默认与方法名相同,也能够显示指定Bean的名称。
  • 可使用@Scope来控制Bean的做用范围。

5.3 启动Spring容器

经过@Configuration类启动Spring容器
  1. 能够直接设置容器启动要注册的类
  2. 能够向容器中注册新的类,注册了新的类要记得refresh
  3. 能够经过@Import将多个配置类组装称一个配置类
public class Main {
    public static void main(String[] args) {
        // (1)能够直接设置容器启动要加载的类
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(DaoConfig.class);
        // (2)能够向容器中注册新的类
        ((AnnotationConfigApplicationContext) applicationContext).register(ServiceConfig.class);
        // (3)注册了新的类要记得refresh
        ((AnnotationConfigApplicationContext) applicationContext).refresh();
    }
}

@Configuration
class DaoConfig {
    @Bean
    public String getStr() {
        return "1314";
    }
}

@Configuration
@Import(DaoConfig.class)
// (4)能够经过@Import将多个配置类组装称一个配置类
class ServiceConfig {
}
经过XML配置文件引用@Configuration的配置

标注了@Configureation的配置类自己也是一个bean,它能够被Spring的<context:component-scan>扫描到。若是但愿将此配置类组装到XML配置文件中,经过XML配置文件启动Spring容器,仅在XML文件中经过<context:component-scan>扫描到相应的配置类便可。

<context:component-scan base-package="com.ankeetc.spring" resource-pattern="Config.class"/>
经过@Configuration配置类引用XML配置信息

在标注了@Configuration的配置类中,能够经过@ImportResource引入XML配置文件。

@Configuration
@ImportResource("classpath:beans.xml")
public class Config {
}
相关文章
相关标签/搜索