再学习之Spring(依赖注入).

1、概述

    Spring框架是以 简化Java EE应用程序的开发 为目标而建立的。Spring能够实现不少功能,可是这些功能的底层都依赖于它的两个核心特性,也就是依赖注入和面向切面编程。几乎Spring所作的任何事情均可以追溯到下述的一条或多条策略:java

基于POJO的轻量级和最小侵入性编程;
经过依赖注入和面向接口实现松耦合;
基于切面和惯例进行声明式编程;
经过切面和模板减小样板式代码。 正则表达式

    Spring的三个基本愿景:spring

使用DI来实现低耦合
使用AOP切面实现高内聚
使用模板消除样板式代码,好比jdbcTemplate编程

2、Bean

    容器是Spring框架的核心。Spring容器使用DI管理构成应用的组件(Bean),它会建立相互协做的组件之间的关联。毫无疑问,这些对象更简单干净,更易于理解和重用,更易于单元测试。session

    Spring为每一个Bean定义了多种做用域,默认都是以单例的模式建立的:app

单例(Singleton):在整个应用中,只建立bean的一个实例。
原型(Prototype):每次注入或者经过Spring应用上下文获取的时候,都会建立一个新的bean实例。
会话(Session):在Web应用中,为每一个会话建立一个bean实例。
请求(Rquest):在Web应用中,为每一个请求建立一个bean实例。框架

    Bean的生命周期:ide

    Bean实例生命周期的执行过程以下:post

Spring对bean进行实例化,默认bean是单例;
Spring对bean进行依赖注入;
若是bean实现了BeanNameAware接口,spring将bean的id传给setBeanName()方法;
若是bean实现了BeanFactoryAware接口,spring将调用setBeanFactory方法,将BeanFactory实例传进来;
若是bean实现了ApplicationContextAware接口,它的setApplicationContext()方法将被调用,将应用上下文的引用传入到bean中;
若是bean实现了BeanPostProcessor接口,它的postProcessBeforeInitialization方法将被调用;
若是bean实现了InitializingBean接口,spring将调用它的afterPropertiesSet接口方法,相似的若是bean使用了init-method属性声明了初始化方法,该方法也会被调用;
若是bean实现了BeanPostProcessor接口,它的postProcessAfterInitialization接口方法将被调用;
此时bean已经准备就绪,能够被应用程序使用了,他们将一直驻留在应用上下文中,直到该应用上下文被销毁;
若bean实现了DisposableBean接口,spring将调用它的distroy()接口方法。一样的,若是bean使用了destroy-method属性声明了销毁方法,则该方法被调用;单元测试

    Spring自带了多种类型的上下文(适用于在普通类中利用Spring的上下文加载须要的Bean):

AnnotationConfigApplicationContext:从一个或者多个的Java配置类中加载Spring的应用上下文

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(JavaConfig.class);
View Code

AnnotationConfigWebApplicationContext:从一个或多个基于Java的配置类中加载Spring Web应用上下文。
ClassPathXmlApplicationContext:从类路径下的一个或多个XML配置文件中加载上下文定义,把应用上下文的定义文件做为类资源。

ApplicationContext applicationContext= new ClassPathXmlApplicationContext("applicationContext.xml");
View Code

FileSystemXmlapplicationcontext:从文件系统下的一个或多个XML配置文件中加载上下文定义。
XmlWebApplicationContext:从Web应用下的一个或多个XML配置文件中加载上下文定义。

3、依赖注入

一、@Component 代表该类会做为组件类,并告知Spring 要为这个类建立bean(这个bean的ID默认取名类名的首字母小写)。不过组件扫描默认是不启用的。咱们还须要显示配置一下Spring,从而命令他去寻找带有@Component(相似的还有@Repository @service @controller)注解的类,并为他建立bean。有三种方式来配置Spring建立Bena:

* 自动化装配的方式:一、建一个配置类。@Configuration 代表这个类是一个配置类 加上@ComponentScan(basePackages={"包名1","包名2"} 或者 basePackageClasses={包1的某个Class,包2的某个Class}) 会扫描和配置类相同的包,以及这个包下的全部子包。

                                  二、Spring XML配置方式: <context:component-scan base-package=""/>

在JavaConfig中进行显示配置(适用于第三方的类库组件装配到本身应用中)  

@Configuration
public class JavaConfig {



    @Bean(name = "base64Util")
    public Base64Util getBase64Util(){
        return new Base64Util();
    }

    @Bean(name = "base64UtilExpand") //这种显示配置默认的Bean名是方法名。因此最好显示配置一下
    public Base64UtilExpand getBase64UtilExpand(){
        //对于这种建立的bean须要引用其余的bean。Spring是这样处理的:当引用到还没建立的bean的时候,Spring会拦截下这个引用,等到引用的bean的建立完成。已保证Spring bean的单例模式.
        return new Base64UtilExpand(getBase64Util());
    }
    
    @Bean(name = "base64UtilExpand")
    public Base64UtilExpand getBase64UtilExpand(Base64Util base64Util){
        return new Base64UtilExpand(base64Util);
    }
}
View Code

在XML中进行显示配置,最古老的方式,通常不多用,有两种方式注入,构造器注入和set注入:

    <!--构造器注入-->
    <bean id="cDPlayer" class="com.CDPlayer">
        <constructor-arg ref="compactDisc">
    </bean>
    <bean id="cDPlayer" class="com.CDPlayer">
        <constructor-arg value="compactDisc">
    </bean>
    <bean id="cDPlayer" class="com.CDPlayer">
        <constructor-arg>
            <list>
                <value></value>
            </list>
        </constructor-arg>
    </bean>

    <!--setter注入-->
    <bean id="cDPlayer" class="com.CDPlayer">
        <property name="compactDisc" ref="compactDisc">
    </bean>
    <bean id="cDPlayer" class="com.CDPlayer">
        <property name="compactDisc" value="compactDisc"><!--装配字面量:-->
    </bean>
    装配集合
    <bean id="cDPlayer" class="com.CDPlayer">
        <property name="compactDisc" value="compactDisc">
        <property name="">
            <list>
                <value></value><!--装配集合-->
            </list>
        </property>
    </bean>
View Code

二、@scope 指定bean建立时的做用域,Spring默认建立单例模式的bean。但也有特殊的状况,这里须要说明的是若是某个类注入了Session 和 Request 做用域的Bean,由于这个Bean是在用户请求的时候产生的,在Spring 运行起来的时候并不存在。这里Spring是这样处理的:注入给对应Bean的一个代理,而当用户请求产生session 或者 request 做用域Bean的时候,由这个代理链接到相应的Bean处理请求...

 

单例(一个应用建立一个实例):@Scope(value=ConfigurableBeanFactory.SCOPE_SINGLETON)

请求(一个请求建立一个实例):@Scope(value=WebApplicationContext.SCOPE_REQUEST,proxyMode = ScopedProxyMode.INTERFACES)

原型(一次注入建立一个实例):@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

会话(一次会话建立一个实例):

    (接口)@Scope(value = WebApplicationContext.SCOPE_SESSION,proxyMode = ScopedProxyMode.INTERFACES)

          (具体类)@Scope(value = WebApplicationContext.SCOPE_SESSION,proxyMode = ScopedProxyMode.TARGET_CLASS)

全局会话(全局会话建立一个实例):@Scope(value=WebApplicationContext.SCOPE_GLOBAL_SESSION)

               固然也能够在XML中配置:

    <bean id="shoppingCart2" class="com.entity.ShoppingCart2" scope="session">
        <aop:scoped-proxy proxy-target-class="false"/>
    </bean>
View Code

三、限定符(用的较少)

 @Profile 决定哪些bean能够被激活。须要注意的是没有指定profile的bean始终都会被建立,与激活哪一个profile没有关系。

 @Conditional中给定了一个Class,这个Class 实现了Codition 接口的matches 方法,该方法返回true 则生成bean,不然不生成。

 @primary 常见的状况是一个接口仅有一个实现类,因此使用@Autowire的后,Spring能够走到对应的实现类。若是一个接口有多个实现类呢?@Component 和 @primary 同时使用,标注哪一个实现类优先被使用。

 @Qualifier 使用@primary 仍然没法保证哪一个bean被选择,由于能够在多个实现类上使用@primary。因此能够在实现类用@Qualifter("bean")名指定bean的名字。并在@Autowire 注入接口的时候是使用Qualifier 指定实现类的bean名。固然,也能够用@Resource(name=" ")指定类的名称。

四、读取 properties 文件

(1) @PropertySource 会引用一个类路径上的properties的文件,并利用Environment类获取properties的变量值。例如:@PropertySource("classpath:mongo.properties")

@Configuration
@PropertySource("classpath:mongo.properties")
public class JavaConfig {
    
    @Bean(name = "mongoUtil")
    public MongoUtil getMongoUtil(Environment env){
        return new MongoUtil(env.getProperty("mongo.host"),
                env.getProperty("mongo.port"),
                env.getProperty("mongo.database"),
                env.getProperty("mongo.username"),
                env.getProperty("mongo.password"));
    }
}
View Code

(2) 占位符

     Spring 中占位符的形式是使用${}的方式。在代码文件中咱们可使用@Value注解将配置文件的值注入到变量中。为了使用占位符,咱们必须配置一个PropertySourcesPlaceholderConfigurer 的类,已生成相关的bean,或者经过XML配置让Spring为咱们自动生成:

@Configuration
@PropertySource("classpath:mongo.properties")
public class JavaConfig {
    
    @Bean(name = "propertySourcesPlaceholderConfigurer")
    public PropertySourcesPlaceholderConfigurer getPropertySourcesPlaceholderConfigurer(){
        return new PropertySourcesPlaceholderConfigurer();
    }
}
View Code

或者:

    <!--提供读取配置文件可使用Spring占位符${}-->
    <context:property-placeholder location="classpath:mongo.properties" file-encoding="utf-8" />
View Code

用法以下:

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(classes = JavaConfig.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Test06 {

    @Value("${mongo.host}")
    private String host;

    @Test
    public void test06(){
        System.out.println(host);
    }
}
View Code

4、Expression Language

 Spring Expression Language,简称SpEL,是一种很是灵活的表达式语言,拥有不少特性,包括:

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

 SpEL 采用#{}的形式:

一、表明字面值:#{3.14} #{'Hello'} #{false}
二、引用bean、属性、方法 #{bean} #{bean.artist} #{bean.toUpperCase()} #{bean?.toUpperCase()}(表示若是bean为null 就返回null,不调用方法)
三、引用某个类 #{T{java.lang.Math}.PI}
四、三元表达式 #{bean.score > 1000 ? "win":"los"} 判空 #{bean.score ?: "win"}
五、正则表达式 #{bean.email matches '表达式'}
六、计算集合 #{bean.song[4].title}
     查询运算符(.?)  #{bean.songs.?[artist eq 'hello']}
     匹配第一个 (.^)  #{bean.songs.^[artist eq 'hello']}
     匹配最后一个 (.$) : #{bean.songs.$[artist eq 'hello']}                          

     投影运算符 (.!)  #{bean.songs.![title]}

<bean id="carl" class="com.springinaction.springidol.Instrumentalist">
    <property name="song" value="#{kenny.song}" />
</bean>
View Code
public static class FieldValueTestBean {
    @Value("#{ systemProperties['user.region'] }")
    private String defaultLocale;
    public void setDefaultLocale(String defaultLocale) {
        this.defaultLocale = defaultLocale;
    }
    public String getDefaultLocale() {
        return this.defaultLocale;
    }
}
View Code

Spring为SpEL创造了两种特殊的选择属性方式:systemEnvironment和systemProperties. 
systemEnvironment包含了应用程序所在机器上的全部环境变量。 
systemProperties包含了java应用程序启动时所设置的全部属性。

5、JUnit 测试

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(classes = JavaConfig.class) 加载配置类
@ContextConfiguration(locations = "classpath:applicationContext.xml") //加载配置文件
public class Test02 {

    @Resource(name = "iceCream")
    private Dessert dessert;

    @Test
    public void test02(){
        dessert.sys();
    }

}