Spring实战 | 第一部分 Spring的核心(第二章 装配bean,第三章 高级装配)

第二章 装配bean java

声明beanweb

构造器注入和setter方法注入正则表达式

装配beanspring

控制bean的建立和销毁express

1、Spring配置的可选方案编程

主要的装配机制:安全

一、在xml中进行显式配置session

二、在Java中进行显式配置app

三、隐式的bean发现机制和自动装配框架

尽量的用自动配置的机制。

2、自动化装配bean

一、建立可被发现的bean

Spring从两个角度来实现自动化装配:

 组件扫描:spring会自动发现应用上下文中所建立的bean。

自动装配:spring自动知足bean之间的依赖。

组件扫描和自动装配组合在一块儿就能发挥出强大的威力,它们可以将你的显示装配降到最低。

① 由于使用了@Component注解,因此spring会为你把事情处理稳当。

② @ComponentScan注解启用了组件扫描

③ 经过xml启用组件扫描

二、为组件扫描的bean命名

强烈推荐@Component命名

三、设置组件扫描的基础包

@Configuration
@ComponentScan("soundsystem")
public class CDPlayConfig{
}

设置组件扫描的基础包

@Configuration
@ComponentScan(basePackageClasses={CDPlayer.class,DVDPlayer.class})
public class CDPlayerConfig{
}

四、经过为bean添加注解实现自动装配

@Autowired

将required属性设置为false时,Spring会尝试执行自动装配,可是若是没有匹配的bean的话,spring将会让这个bean处于未装配的状态。可是此时要进行null值检查,不然会出现空指针异常。

@Inject注解来源于Java依赖注入规范

spring同时支持@Autowired和@Inject。

五、验证自动装配

3、经过Java代码装配bean

当要将第三方库中的组件装配到程序中时,这种状况下是没办法加@Component和@Autowired注解的,所以不能自动化装配。

显式配置时,JavaConfig是更好的方案,JavaConfig是配置代码,JavaConfig要放到单独的包中。

一、建立配置类

建立JavaConfig类的关键在于为其添加@Configuration注解,@Configuration注解代表这个类是一个配置类,该类应该包含在Spring应用上下文中如何建立bean的细节。

二、声明简单的bean

三、借助JavaConfig实现注入

@Bean注解代表这个方法会建立一个bean实例并将其注册到Spring应用上下文中。

四、经过xml装配bean

① 建立XML配置规范

② 声明一个简单的bean

③ 借助构造器注入初始化bean

④ 设置属性

推荐构造器注入

五、导入和混合配置

① 在JavaConfig中引用XML配置

② 在XML配置中引用JavaConfig

六、小结

Spring框架的核心是Spring容器。容器负责管理应用中组件的生命周期,它会建立这些组件并保证他们的依赖可以获得知足,这样的话,组件才能完成预约的任务。在本章中,咱们看到了在Spring中装配bean的三种主要方式,这些技术描述了Spring应用中的组件以及这些组件之间的关系。

尽量使用自动化配置,以免显示配置所带来的维护成本。可是,若是你确实须要显示配置Spring的话,应该优先选择基于Java的配置,它比基于CML的配置更增强大、类型安全而且易于重构。

第三章 高级装配

Spring profile

条件化的bean声明

自动装配与歧义性

bean的做用域

Spring表达式语言

1、环境与profile

一、spring中的profile是什么?

profile能够理解为咱们在Spring容器中所定义的Bean的逻辑组名称,只有当这些Profile被激活的时候,才会将Profile中所对应的Bean注册到Spring容器中。举个更具体的例子,咱们以前定义bean的时候,当spring容器以启动的时候,就会一股脑的所有加载这些信息完成对bean的建立;而使用profile后,它会将bean的定义进行更细粒度的划分,将这些定义的bean划分为几个不一样的组,当spring容器加载信息的时候,首先查找激活的profile,而后只会去加载被激活的组中所定义的bean信息,而不被激活的profile中所定义的bean是不会加载用于建立bean的。

二、为何要是用profile

由于须要啥就加载啥,不须要的就不用了加载了

三、配置spring profile

在3.1版本中,spring引入了bean profile的功能。要使用profile,首先要将全部不一样的bean定义整理到一个或多个profile中,将应用部署到每一个环境中,确保对应的profile处于激活(active)的状态。

在java配置中,可使用@profile注解指定某个bean属于哪个profile。

四、激活profile

spring在肯定哪一个profile处于激活状态时,须要依赖两个独立的属性:spring.profiles.active和spring.profiles.default。若是配置了spring.profiles.active属性的话,那么它的值就会用来肯定哪一个profile是激活的。可是若是没有配置spring.profiles.active,那spring将会查找spring.profiles.default的值。

有两种方式来设置这两个属性:

  • 做为DispatcherServlet的初始化参数
  • 做为web应用的上下文参数
  • 做为环境变量
  • 做为JVM的系统属性
  • 在继承测试类上,使用@ActiveProfiles注解配置。

我所喜欢的一种方式是使用DispatcherServlet的参数将spring.profiles.default设置为开发环境的profile,我会在servlet上下文中进行设置(为了兼顾到ContextLoaderListener)。例如,在web应用中,设置spring.profiles.default的web.xml文件会以下所示:

在web应用的web.xml文件中设置默认的profile

<?xml version="1.0" encoding="UTF-8"?>
<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.xsd">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/root-context.xml</param-value>
    </context-param>
    <context-param>
        <param-name>spring.profiles.default</param-name><!--为上下文设置默认的profile-->
        <param-value>dev</param-value>
    </context-param>
    <listen>
        <listen-class>
            org.springframework.web.context.ContextLoaderListenr
        </listen-class>
    </listen>
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
            <param-name>spring.profiles.default</param-name><!--为servlet设置默认的profile-->
            <param-value>dev</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</beans>

按照这种方式设置spring.profiles.default,全部开发人员都能从版本控制软件中得到应用程序源码,并使用开发环境的设置运行代码,而不须要额外的配置。

系统会优先使用spring.profiles.active中所设置的profile。

spring提供了@ActiveProfiles注解,咱们可使用它来指定运行测试时要激活哪一个profile。在集成测试时,一般想要激活的是开发环境的profile。

在条件化建立bean的时候,spring的profile机制是一种很棒的方法,这里的条件要基于哪一个profile处于激活状态来判断。

2、条件化的bean

spring4引入了一个新的@Conditional注解,它能够用到带有@Bean注解的方法上。若是给定的条件计算结果为true,就会建立这个bean,否者,这个bean就会被忽略。

例如,假设有一个名为MagicBean的类,咱们但愿只有设置了magic环境属性的时候,Spring才会实例化这个类。若是环境中没有这个属性,那么MagicBean将会被忽略。

条件化的建立bean:

@Bean
@Conditional(MagicExistsCondition.class)//条件化地建立bean
public MagicBean magicBean(){
    return new MagicBean();
}

能够看到,@Conditional中给定了一个Class,它指明了条件,在本例中,也就是MagicExistsCondition。@Conditional将会经过Condition接口进行条件对比:

public interface Condition{
    boolean matches(ConditionContext ctxt,AnnoatedTypeMetadata metadata);
}

设置给@Conditional的类能够是任意实现了Condition接口的类。能够看出,这个接口实现起来很简单直接,只需提供matches()方法的实现便可。若是matches()方法返回true,那么就会建立带有@Conditional注解的bean。若是matches()方法返回false,将不会建立bean。

在condition中检查是否存在magic属性

public class MagicExistsCondition implements Condition{
    public boolean matches(ConditionContext ctxt,AnnoatedTypeMetadata metadata){
        Environment env = context.getEnvironment();
        return env.containsProperty("magic");//检查magic属性
    }
}

在上面的程序中,matches()方法很简单但功能很强大。它经过给定的ConditionContext对象进而获得Environment对象,并使用这个对象检查环境中是否存在名为magic的环境属性。若是返回true,就表示@Conditional注解上引用MagicExistsCondition的bean都会被建立。

ConditionContext是一个接口,大体以下所示:

public interface ConditionContext{
    //检查bean定义
    BeanDefinitionRegistry getRegistry();
    //检查bean是否存在,检查bean的属性
    ConfigurableListableBeanFactory getBeanFactory();
    //检查环境变量是否存在以及它的值是什么
    Environment getEnvironment();
    //读取ResourceLoader加载的资源
    ResourceLoader getResourceLoader();
    //加载并检查类是否存在
    ClassLoader getClassLoader();
}

AnnotatedTypeMetadata则可以让咱们检查带有@bean注解的方法上是否还有其余的注解。像ConditionContext同样,AnnotatedTypeMetadata也是一个接口。

public interface AnnotatedTypeMetadata{
    //借助isAnnotated()方法,判断带有@bean注解的方法是否还有其余特定的注解。
    boolean isAnnotated(String annotationType);
    //检查@bean注解的方法上其余注解的属性
    Map<String,Object> getAnnotationAttributes(String annotationType);
    Map<String,Object> getAnnotationAttributes(String annotationType,boolean classValuesAsString);
    MultiValueMap<String,Object> getAllAnnotationAttributes(String annotationType);
    MultiValueMap<String,Object> getAllAnnotationAttributes(String annotationType,boolean classValuesAsString);
}

从spring4开始,@profile注解进行了重构,使其基于@Conditional和Condition实现。来看一下,spring4中@profile是如何实现的。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile{
    String[] value();
}

@Profile自己也使用了@Condition注解,而且引用ProfileCondition做为Condition实现。以下所示,ProfileCondition实现了Condition接口,而且考虑到了ConditionContext和AnnotatedTypeMatadata中的多个元素。

ProfileCondition检查某个bean profile是否可用:

class ProfileCondition implements Condition{
    public boolean matches(ConditionContext context,AnnotatedTypeMetadata){
        if(context.getEnvironment()!=null){
            MultiValueMap<String,Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
            if(attrs!=null){
                for(Object value:attrs.get("value")){
                    if(context.getEnvironment().acceptsProfiles(String[] value)){
                        return true;
                    }
                }
                 return false;
            }
        }
        return true;
    }
}

咱们能够看到,ProfileCondition经过AnnotatedTypeMetadata获得了用于@Profile注解的全部属性,借助这些信息,它明确的检查value属性,该属性包含了bean的Profile名称,而后,它经过ConditionContext获得的Environment来检查[借助accpetsProfiles()方法]该profile是否处于激活状态。

3、处理自动装配的歧义性

若是不只有一个bean可以匹配结果的话,这种歧义性会阻碍spring自动装配属性、构造器参数或方法参数。

若是三个实现类均使用了@Component注解,在组件扫描的时候,可以发现他们并将其建立为Spring应用上下文里面的bean,而后,当spring自动装配的时候,它并无惟1、无歧义的可选值。此时,spring会抛出异常。

就算歧义性是个问题,但实际开发中不多碰见,给定的类型只有一个实现类就行了嘛,自动装配可以很好地运行。

可是,当歧义性确实发生时,spring提供了多种可选方案来解决这样的问题。你能够将可选bean中的某一个设为首选(primary)的bean,或者使用限定符(qualifier)来帮助spring将可选的bean的范围缩小到只有一个bean。

一、标识首选的bean

在声明bean的时候,经过将其中一个可选的bean设置为首选(primary)bean可以避免自动装配时的歧义性。当遇到歧义性的时候,Spring将会使用首选的bean,而不是其余可选的bean。

@Bean
@Primary
public class IceCrean implements Dessert{
}

若是XML配置bean的话,一样能够实现此功能。<bean>元素有一个primary属性来指定首选的bean。

二、限定自动装配的bean

@Autowired
@Qualifier("iceCream")
public void setDessert(Dessert dessert){
    this.dessert = dessert;
}

@Qualifier注解所设置的参数就是想要注入对的bean的ID。全部使用@Component注解声明的类都会建立bean,而且bean的ID为首字母变为小写的类名。所以,@Qualifier(“iceCream”)指向的是组件扫描时锁建立的bean,而且这个bean时IceCream类的实例。

@Component--把这些类归入进spring容器中管理。

建立自定义的限定符

咱们能够为bean设置本身的限定符,而不是依赖将beanID做为限定符。在这里须要加上@Qualifier,与@Component配合使用

@Component
@Qualifier("sexy")
public class IceCream implements Dessert{...}

在这种状况下,sexy限定符分配给了IceCreambean。由于它没有耦合类名,所以你能够随意重构IceCream的类名,而没必要担忧会破坏自动装配。在注入的地方,引用sexy限定符便可:

@Component
@Qualifier("sexy")
public class setDessert(Dessert dessert){
    this.dessert = dessert;
}

 

当配置显式bean的时候,@Qualifier也能够与@bean注解一块儿使用:

@Bean
@Qualifier("sexy")
public Dessert iceCream{
    return new IceCream();
}

当使用@Qualifier值时,最佳实践是为bean选择特征性或描述性的术语,而不是使用随便的名字。

经过声明自定义的限定符注解,咱们能够同时使用多个限定符,不会再有Java编译器的限制或错误。与此同时,相对于使用原始的@Qualifier并借助string类型来指定限定符,自定义的注解更为安全。

为了建立自定义的条件化注解,咱们建立了一个新的注解并在这个注解上添加了@Conditional。为了建立自定义的限定符注解,咱们建立了一个新的注解并在这个注解上添加了@Qualifier。这种技术能够用到不少spring注解中,从而可以将它们组合在一块儿造成特定目标的自定义注解。

如今咱们来看一下如何在不一样的做用域中声明bean。

4、bean的做用域

默认状况下,spring应用上下文中全部bean都是做为以单例(singleton)的形式建立的。也就是说,无论给定的一个bean被注入到其它bean多少次,每次所注入的都是同一个实例。

spring定义了多种做用域,能够基于这些做用域建立bean,包括:

  • 单例(singleton):在整个应用中,只建立bean的一个实例。
  • 原型(prototype):每次注入或者经过spring应用上下文获取的时候,都会建立一个新的bean实例。
  • 会话(session):在web应用中,为每一个会话建立一个bean实例。
  • 请求(request):在web应用中,为每一个请求建立一个bean实例。

单例是默认的做用域,对于易变的类型,这并不合适。若是选择其余的做用域,要使用@Scope注解,它能够与@Component或@bean一块儿使用。

@Component
@Scope(ConfigurableBeanFacory.SCOPE_PROTOTYPE)
public class Notepad{
}

使用ConfigurableBeanFacory类的SCOPE_PROTOTYPE常量设置了原型做用域。你固然也可使用@Scope(“prototype”),可是使用SCOPE_PROTOTYPE常量更加安全而且不易出错。

一样也可使用XML来配置bean:

<bean id="notepad" class="com.oschina.Notepad" scope="prototype"></bean>

无论你使用哪一种方式来声明原型做用域,每次注入或从spring应用上下文中检索该bean的时候,都会建立新的实例。这样所致使的结果就是每次操做都能获得本身的notepad实例。

一、使用会话和请求做用域

在web应用中,若是可以实例化在会话和请求范围内共享的bean,那将时很是有价值的事情。例如,在典型的电子商务应用中,可能会有一个bean表明用户的购物车。若是购物车是单例的,那么将会致使全部的用户都会向同一个购物车中添加商品。另外一方面,若是购物车是原型做用域的,那么在应用中某一个地方网购物车中添加物品,在应用的另一个地方可能就不可用了,由于这里注入的是另外一个原型做用域的购物车。

就购物车bean来讲,会话做用域是最为合适的,由于它与给定的用户关联性最大。要指定会话做用域,咱们可使用@Scope注解,它的使用方式与指定原型做用域是相同的:

@Component
@Scope(value=WebApplicationContext.SCOPE_SESSION,
       proxyMode=ScopeProxyMode.INTERFACES)
public ShoppingCart cart(){
}

咱们将value设置成了WebApplicationContext中的SCOPE_SESSION常量,这回告诉spring为web应用中每个会话建立一个ShoppingCart。这会建立多个ShoppingCart的bean实例,可是对于给定的会话只是建立了一个实例,在当前会话中,这个bean至关于单例的。

要注意的是,@Scope同时还有一个proxyMode属性,它被设置成了ScopeProxyMode.INTERFACES。这个属性解决了将会话或请求做用域的bean注入到单例bean中所遇到的问题。在描述proxyMode属性以前,咱们先来看一下ProxyMode所解决的场景。

假设咱们将shoppingCart bean注入到单例StoreService bean的setter方法中,以下所示:

@Component
public class StoreService{
    @Autowired
    public void setShoppingCart(ShoppingCart shoppingCart){
        this.shoppingCart=shoppingCart;
    }
    ...
}

@Autowired是用在Javabean中的注解,经过byType形式,用来给指定的字段或方法注入所需的外部资源。

先看一下bean实例化和@Autowired装配过程:

一、一切都是从bean工厂的getBean方法开始的,一旦该方法调用总会返回一个bean实例 。

二、实例化和装配过程当中会屡次递归调用getBean方法来解决类之间的依赖。

三、@Autowired是根据类型来查找和装配的,可是咱们设置了<beans default-autowired="byName"/>后会影响最终的类型匹配查找。由于在前面有根据BeanDefinition的autowire类型设置PropertyValue值的一步,其中会有新实例的建立和注册。就是那个autowireByName方法

由于StoreService是一个单例bean,会在spring应用上下文加载的时候建立,当它建立的时候,spring会试图将ShoppingCart bean注入到setShoppingCart()方法中。 但ShoppingCart bean是会话做用域的,此时并不存在。直到某个用户进入系统,建立了会话以后,才会出现ShoppingCart实例。

另外,系统中将会有多个ShoppingCart实例,每一个用户一个,咱们并不想让spring注入某个固定的ShoppingCart实例到storeService中。咱们但愿的是当StoreService处理购物车功能时,它所使用的ShoppingCart实例刚好是当前会话所对应的那一个。

二、在xml中声明做用域代理

若是你须要使用XML来声明会话或请求做用域的bean,那么就不能使用@Scope注解及其ProxyMode属性了。<bean>元素的scope属性可以设置bean的做用域,可是该怎样指定代理模式呢?

要设置代理模式,咱们须要使用Spring aop命名空间的一个新元素:

<bean id="cart" class="com.oschina.ShoppingCart" scope="session">
    <aop:scoped-proxy/>
</bean>

<aop:scoped-proxy/>是与@Scope注解的ProxyMode属性功能相同的Spring XML配置元素。它会告诉spring为bean建立一个做用域代理。默认状况下,它会使用CGLib建立目标类的代理。可是咱们也能够将proxy-target-class属性设置为false,进而要求它生成基于接口的代理:

<bean id="cart" class="com.oschina.ShoppingCart" scope="session">
    <aop:scoped-proxy proxy-target-class="false" />
</bean>

为了使用<aop:scoped-proxy>元素,咱们必须在XML配置中声明spring的aop命名空间:

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
...
</beans>

在第4章中,咱们使用spring和切面编程的时候,会讨论spring aop命名空间的更多知识,如今咱们来看一下spring高级配置的另一个可选方案:spring表达式语言(spring expression language)。

5、运行时值注入

一、注入外部的值

在Spring中,处理外部值最贱的方式就是声明属性源并经过spring的environment来检索属性。

package com.oschina
@Configuration
//声明属性源
@PropertySource("classpath:/com/oschina/app.properties")
public class ExpressiveConfig{
    @Autowired
    Environment env;
    @Bean
    public BlankDisc disc(){
        //检索属性值
        return new BlankDisc(env.getProperty("disc.title"),env.getProperty("disc.artist"));
    }
}

深刻学习spring的environment

直接从environment中检索属性是很是方便的,尤为是在Java配置中装配bean的时候。可是spring也提供了经过占位符装配属性的方法,这些占位符的值会来源于一个属性源。

解析占位符

在spring装配中,占位符的形式为使用“${...}”包装的属性名称。

<bean id="sgtPeppers" class="soundsystem.BlankDisc" c:_title="${disc.title}" c:_artist="${disc.artist} />"

二、使用spring表达式语言进行装配

spring3引入了spring表达式语言(spring expression language ,SpEL),它可以以一种强大和简洁的方式将值装配到bean属性和构造器参数中,在这个过程当中使用的表达式会在运行时计算的获得值,使用SpEL,你能够实现超乎想象的装配效果,这是使用其余的装配技术难以作到的。

SpEL拥有不少特性,包括:

  • 使用bean的ID来引用bean
  • 调用方法和访问对象的属性
  • 对值进行算术、关系和逻辑运算
  • 正则表达式匹配
  • 集合操做

SpEL可以用在依赖注入之外的其余地方,例如,spring Security支持使用SpEL表达式定义安全限制规则。另外,若是你在spring MVC应用中使用Thymeleaf模板做为视图的话,那么这些模板可使用SpEL表达式引用模板数据。

SpEL样例

SpEL是一种很是灵活的表达式语言,因此本书中不可能面面俱到的介绍它的各类用法。可是咱们能够展现几个基本的例子,这些例子会激发你的灵感,有助于编写本身的表达式。

SpEL表达式使用"#{...}",这与属性占位符有些相似,属性占位符是"${...}"。

除去"#{...}"标记以后,剩下的就是SpEL表达式体了,也就是一个数字常量。这个表达式的计算结果就是数字1,这恐怕不会让你感到丝毫惊讶。

#{T(System).currentTimeMillis()}

它的最终结果是计算表达式的那一刻的当前时间的毫秒数。T()表达式会将java.lang.System视为Java中对应的类型,所以能够调用其static修饰的currentTimeMillis()方法。

SpEL表达式也能够应用其余的bean或其余bean的属性。例如,以下的表达式会计算获得ID为sgtPeppers的bean的artist属性:

#{sgtPeppers.artist}

咱们还能够经过systemProperties对象引用系统属性:

#{systemProperties['disc.title']}

这只是SpEL的几个基础样例。

若是经过组件扫描建立bean的话,在注入属性和构造器参数时,咱们可使用@Value注解,这与以前看到的属性占位符很是类似。不过,在这里咱们所使用的不是占位符表达式,而是SpEL表达式。例如,下面的样例展现了BlankDisc,它会从系统属性中获取专辑名称和艺术家的名字:

public BlankDisc(@Value{'#systemProperties['disc.title']'} string title,
    @Value{'#systemProperties['disc.artist']'} string artist){
    this.title = title;
    this.artist=artist;
}

在XML配置中,你能够将SpEL表达式传入<property>或<constructor-arg>的value属性中,或者将其做为p-命名空间或c-命名空间条目的值。例如,在以下BlankDisc bean的XML声明中,构造器参数就是经过SpEL表达式设置的:

<bean id="sgtPeppers" class="soundsystem.BlankDisc" c:title="#systemProperties['disc.title']"
c:_artist="#[systemProperties['disc.artist']]">

如今咱们来学一下SpEL所支持的基础表达式:

表示字面值

实际上能够用来表示浮点值、string值以及Boolean值。

下面的SpEL表达式样例所表示的就是浮点数:

#{3.141592657}

....

SpEL所能作到的另一件基础的事情就是经过ID引用其余的bean。例如,你可使用SpEL将一个bean装配到另外一个bean的属性中,此时要是用bean ID做为SpEL表达式

SpEL运算符

例子:#{2*T(java.lang.Math).PI*circle.radius}

这不只是使用SpEL中乘法运算符的绝佳样例,它也为你展现了如何将简单的表达式组合为更为复杂得表达式。
SpEL还提供了三元运算符

6、小结

咱们在本章介绍了许多背景知识,在第二章所介绍的基本bean装配基础之上,又学习了一些强大的高级装配技巧。

首先,咱们学习了spring profile,它解决了spring bean要跨各类部署环境的通用问题。在运行时,经过将环境相关的bean与当前激活的profile进行匹配,spring可以让相同的部署单元跨多种环境运行,而不须要进行从新构建。

Profile bean是在运行时条件化建立bean的一种方式,可是spring4提供了一种更为通用的方式,经过这种方式能可以声明某些bean的建立与否要依赖于给定条件的输出结果。结合使用@Conditional注解和Spring Condition接口的实现,可以为开发人员提供一种强大和灵活的机制,实现条件化的建立bean。

咱们还看了两种解决自动装配歧义性的方法:首选bean以及限定符。尽可能将某个bean设置为首选bean是很简单的,单这种方式也有其局限性,因此咱们讨论了如何将一组可选的自动装配bean,借助限定符将其范围缩小到只有一个符合条件的bean。除吃以外,咱们还看到了如何建立自定义的限定符注解,这些限定符描述了bean的特性。

尽管大多数的spring bean都是单例的方式建立的,但有的时候其它的建立策略更为合适。spring可以让bean以单例、原型、请求做用域或会话做用域的方式来建立。在声明请求做用域或会话做用域的bean的时候,咱们还学习了如何建立做用域代理,它分为基于类的代理和基于接口的代理两种方式。

最后咱们还学习了spring表达式语言,他可以在运行时计算要注入到bean属性中的值。

对于bean装配,咱们已经掌握了扎实的基础知识,如今咱们将注意力转向面向切面编程(AOP)。依赖注入可以将组建及其协做的其余组件解耦,与之相似,AOP有助于将应用组件与跨多个组件的任务进行解耦。在下一章,咱们将学习在spring中如何建立和使用切面。

相关文章
相关标签/搜索