spring-framework-reference阅读笔记(一)

Spring Framework Runtime

首先需要对Spring FrameWok框架有个直观的认识

Java日志框架的发展史

在读到Spring依赖JCL的时候,对Java的日志系统做点普及!

最 早出现的日志框架是apache提供的log4j,使用最为广泛,成为了Java日志的事实上的标准;然而当时Sun公司在jdk1.4中增加了 JUL(java.util.logging),企图对抗log4j,于是造成了混乱,当然此时也有其它的一些日志框架的出现,如simplelog等, 简直是乱上加乱。

解决这种混乱的方案出现了:抽象出一个接口层:于是开源社区提供了commons-logging,被称为JCL。抽象时参考了log4j、JUL、simplelog,对它们进行了适配或转接,这样就一统江湖了。

看 上去现在已经非常完美了,但好景不长,log4j的作者(Ceki Gülcü)觉得JCL不够优秀,他要搞出一套更优雅的出来,于是slf4j就出现了,并且亲自实现了一个亲子——logback(有点,老子又回来了的 感觉^_^)。好吧,确实更优雅了,但混乱局面又出现了,之前使用JCL的怎么办呢,于是Ceki Gülcü在slf4j又对JCL作了桥接转换,然而事情还没完,Ceki Gülcü又回来拯救自己的“大阿哥”——log4j,于是log4j2就诞生了,同时log4j2也加进了slf4j体系中。

PS:SLF4J是在Compile绑定实现的,而JCL是Runtime时绑定的。


Spring中如何使用日志系统

  • 使用JCL
    由于Spring-core依赖JCL,所以可以直接配置JCL的实现,比如log4j:
    <dependency>
    	<groupId>log4j</groupId>
    	<artifactId>log4j</artifactId>
    	<version>1.2.14</version>
    </dependency>
  • 使用SLF4J
    如果你想使用SLF4J需要三步来做:先排除JCL
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-core</artifactId>
    	<version>4.1.2.RELEASE</version>
    	<exclusions>
    		<exclusion>
    			<groupId>commons-logging</groupId>
    			<artifactId>commons-logging</artifactId>
    		</exclusion>
    	</exclusions>
    </dependency>
    使用SLF4J来桥接JCL,因此首先需要引用jcl-over-slf4j
    <dependency>
    	<groupId>org.slf4j</groupId>
    	<artifactId>jcl-over-slf4j</artifactId>
    	<version>1.5.8</version>
    </dependency>
    这时,spring-core调用的JCL API将桥接到了SLF4J。
    最后再引入SLF4J的组合,这个组合有比较多,你参考官网,如使用SLF4J-LOG4J
    <dependency>
    	<groupId>org.slf4j</groupId>
    	<artifactId>slf4j-log4j12</artifactId>
    	<version>1.7.7</version>
    </dependency>
    建议使用SLF4J,因为JCL的Runtime绑定再与别的框架一起使用时可能出现不兼容的情况。

Spring资源

  1. Resource
    Spring的Resource接口以及父接口,如下
    public interface Resource extends InputStreamSource {
    
        boolean exists();
    
        boolean isOpen();
    
        URL getURL() throws IOException;
    
        File getFile() throws IOException;
    
        Resource createRelative(String relativePath) throws IOException;
    
        String getFilename();
    
        String getDescription();
    
    }
    public interface InputStreamSource {
    
        InputStream getInputStream() throws IOException;
    
    }
    Resource接口的实现由以下几类:
    • ClassPathResource可用来获取类路径下的资源文件。假设我们有一个资源文件test.txt在类路径下,我们就可以通过给定对应资源文件在类路径下的路径path来获取它,new ClassPathResource(“test.txt”)。
    • FileSystemResource 可用来获取文件系统里面的资源。我们可以通过对应资源文件的文件路径来构建一个FileSystemResource。 FileSystemResource还可以往对应的资源文件里面写内容,当然前提是当前资源文件是可写的,这可以通过其isWritable()方法来 判断。FileSystemResource对外开放了对应资源文件的输出流,可以通过getOutputStream()方法获取到。
    • UrlResource可用来代表URL对应的资源,它对URL做了一个简单的封装。通过给定一个URL地址,我们就能构建一个UrlResource。
    • ByteArrayResource是针对于字节数组封装的资源,它的构建需要一个字节数组。
    • ServletContextResource 是针对于ServletContext封装的资源,用于访问ServletContext环境下的资源。ServletContextResource持 有一个ServletContext的引用,其底层是通过ServletContext的getResource()方法和 getResourceAsStream()方法来获取资源的。
    • InputStreamResource是针对于输入流封装的资源,它的构建需要一个输入流。
  2. ResourceLoader
    Spring定义了ResourceLoader接口来加载资源
    public interface ResourceLoader {
    
        Resource getResource(String location);
    
    }
    所有的“application context”都实现了ResourceLoader接口,但他们的getResource方法返回的都是相应的Resource,如 ClassPathXmlApplicationContext返回的是 ClassPathResource,FileSystemXmlApplicationContext返回的是 FileSystemResource,WebApplicationContext返回的是ServletContextResource等等,如:
    Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
    但如果你想使用FileSystemXmlApplicationContext返回ClassPathResource怎么办呢?那就加前缀了,如:
    Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
    常用的前缀有classpath、file、http、ftp等,如:

    classpath:com/myapp/config.xml
    file:///data/config.xml
    http://myserver/logo.png

    这 里我们先挑一个DefaultResourceLoader来讲。DefaultResourceLoader在获取Resource时采用的是这样的策 略:首先判断指定的location是否含有“classpath:”前缀,如果有则把location去掉“classpath:”前缀返回对应的 ClassPathResource;否则就把它当做一个URL来处理,封装成一个UrlResource进行返回;如果当成URL处理也失败的话就把 location对应的资源当成是一个ClassPathResource进行返回。
    ResourceLoader resourceLoader=new DefaultResourceLoader();
    Resource resource=resourceLoader.getResource("/a.xml");
    System.out.println(resource.exists());
  3. ApplicationContext
    但是ApplicationContext不会因为Resource的不同而相互转换,如
    ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
    Ctx仍然是FileSystemXmlApplicationContext,而不会是ClassPathXmlApplicationContext。

PS:classpath*是加载多个资源,而且可以使用通配符。


Spring IOC

  1. BeanFactory 是获取bean的接口,而ApplicationContext是BeanFactory的子接口,它增加了更多企业级操作。 ApplicationConext可以看做是IOC的container,它装载配置资源,并根据配置的逻辑来装配各部件,实现用户的业务。
  2. 配置有三种方式:Annotation-based(Spring2.5支持)、Java-based(Spring3.0)、XML-based配置。
  3. XML-based方式,XML可以使用import引用别的XML
    <beans>
        <import resource="services.xml"/>
        <import resource="resources/messageSource.xml"/>
        <import resource="/resources/themeSource.xml"/>
    
        <bean id="bean1" class="..."/>
        <bean id="bean2" class="..."/>
    </beans>
    import时注意路径,而且引用的xml必须是合法的Spring Schema,因此必须包含<beans/>。同时,也可以通过ApplicationContext的构造函数传入多个资源文件达到一样的效果。
  4. bean 的定义设计到的字段:class、name、scope、constructor arguments、properties、autowiring mode、lazy-initialization mode、initialization method、destruction method。
  5. Bean 可以使用name、id来唯一标示,name和id也可以同时使用,如果bean definition只有一个class,也可以不用任何标示。但有时只有name和id仍然不够个性化命名,而且name和id必须遵循Java的命名 规范,因此别名就营运而生了。
    <alias name="fromName" alias="toName"/>
  6. bean的定义示例
    • 构造函数bean
      <bean id="exampleBean" class="examples.ExampleBean"/>
      <bean name="anotherExample" class="examples.ExampleBeanTwo"/>
      这是使用默认构造函数,如果是自己定义函数,需要使用constructor-arg指定入参。PS:如果内部静态类需要用“$”符号,如com.example.Foo$Bar。
    • 静态工厂bean
      <bean id="clientService"
          class="examples.ClientService"
          factory-method="createInstance"/>
      public class ClientService {
          private static ClientService clientService = new ClientService();
          private ClientService() {}
      
          public static ClientService createInstance() {
              return clientService;
          }
      }
      如果静态工厂需要参数呢?后面在DI时再细说。
    • 实例工厂bean
      <!-- the factory bean, which contains a method called createInstance() -->
      <bean id="serviceLocator" class="examples.DefaultServiceLocator">
          <!-- inject any dependencies required by this locator bean -->
      </bean>
      
      <!-- the bean to be created via the factory bean -->
      <bean id="clientService"
          factory-bean="serviceLocator"
          factory-method="createClientServiceInstance"/>
      public class DefaultServiceLocator {
      
          private static ClientService clientService = new ClientServiceImpl();
          private DefaultServiceLocator() {}
      
          public ClientService createClientServiceInstance() {
              return clientService;
          }
      }
      实例工厂不像静态工厂,它必须需要一个实例,因此必须定义一个bean,这不难理解的。如上面实例的serviceLocator,在clientService的bean中通过使用factory-bean来设置,而删除了class属性。
      PS:理论上静态工厂的bean也可以像实例工厂那样配置,因为我认为static既属于类也属于实例,但我的DEMO没通过,其实在JAVA代码中也不建议使用实例对象去访问类的静态方法,因此Spring更规范化了。
  7. 依赖注入(DI)
    DI分为两类:基于构造器的注入和基于Setter的注入,静态工厂和实例工厂都归属于构造器注入。
    • 构造器注入
      package x.y;
      
      public class Foo {
      
          public Foo(Bar bar, Baz baz) {
              // ...
          }
      
      }
      <beans>
          <bean id="foo" class="x.y.Foo">
              <constructor-arg ref="bar"/>
              <constructor-arg ref="baz"/>
          </bean>
      
          <bean id="bar" class="x.y.Bar"/>
      
          <bean id="baz" class="x.y.Baz"/>
      </beans>
      PS:这里需要对bean的参数 (arg)做个说明:它先按照配置出现的顺序来注入,因为默认的value是当String来处理的,但当发现类型不匹配,它会按照类型类匹配,如果还不行就需要手动指定index,或者使用参数名,但 如果使用参数,为了防止被编译器优化需要使用 @ConstructorProperties来annotate。如
      package examples;
      
      public class ExampleBean {
      
          // Fields omitted
      
          @ConstructorProperties({"years", "ultimateAnswer"})
          public ExampleBean(int years, String ultimateAnswer) {
              this.years = years;
              this.ultimateAnswer = ultimateAnswer;
          }
      
      }
      order定义
      <bean id="exampleBean" class="examples.ExampleBean">
          <constructor-arg value="7500000"/>
          <constructor-arg value="42"/>
      </bean>
      type定义
      <bean id="exampleBean" class="examples.ExampleBean">
          <constructor-arg type="int" value="7500000"/>
          <constructor-arg type="java.lang.String" value="42"/>
      </bean>
      index定义
      <bean id="exampleBean" class="examples.ExampleBean">
          <constructor-arg index="0" value="7500000"/>
          <constructor-arg index="1" value="42"/>
      </bean>
      name定义
      <bean id="exampleBean" class="examples.ExampleBean">
          <constructor-arg name="years" value="7500000"/>
          <constructor-arg name="ultimateAnswer" value="42"/>
      </bean>
      静态工厂注入
      public class ExampleBean {
      
          // a private constructor
          private ExampleBean(...) {
              ...
          }
      
          // a static factory method; the arguments to this method can be
          // considered the dependencies of the bean that is returned,
          // regardless of how those arguments are actually used.
          public static ExampleBean createInstance (
              AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
      
              ExampleBean eb = new ExampleBean (...);
              // some other operations...
              return eb;
          }
      
      }
      <bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
          <constructor-arg ref="anotherExampleBean"/>
          <constructor-arg ref="yetAnotherBean"/>
          <constructor-arg value="1"/>
      </bean>
      
      <bean id="anotherExampleBean" class="examples.AnotherBean"/>
      <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
    • Setter注入
      public class ExampleBean {
      
          private AnotherBean beanOne;
          private YetAnotherBean beanTwo;
          private int i;
      
          public void setBeanOne(AnotherBean beanOne) {
              this.beanOne = beanOne;
          }
      
          public void setBeanTwo(YetAnotherBean beanTwo) {
              this.beanTwo = beanTwo;
          }
      
          public void setIntegerProperty(int i) {
              this.i = i;
          }
      
      }
      <bean id="exampleBean" class="examples.ExampleBean">
          <!-- setter injection using the nested ref element -->
          <property name="beanOne">
              <ref bean="anotherExampleBean"/>
          </property>
      
          <!-- setter injection using the neater ref attribute -->
          <property name="beanTwo" ref="yetAnotherBean"/>
          <property name="integerProperty" value="1"/>
      </bean>
      
      <bean id="anotherExampleBean" class="examples.AnotherBean"/>
      <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
  8. idref
    用于<constructor-arg/> 或者 <property/>,使用方式同value,只是增加了校验idref的bean存不存在,如
    <bean id="theTargetBean" class="..."/>
    
    <bean id="theClientBean" class="...">
        <property name="targetName">
            <idref bean="theTargetBean" />
        </property>
    </bean>
    等同于
    <bean id="theTargetBean" class="..." />
    
    <bean id="client" class="...">
        <property name="targetName" value="theTargetBean" />
    </bean>
    两者注入的都是“theTargetBean”字符串,但前者会校验theTargetBean标识的bean是否存在,而后者不校验。
  9. 内部类注入
    <bean id="outer" class="...">
        <!-- instead of using a reference to a target bean, simply define the target bean inline -->
        <property name="target">
            <bean class="com.example.Person"> <!-- this is the inner bean -->
                <property name="name" value="Fiona Apple"/>
                <property name="age" value="25"/>
            </bean>
        </property>
    </bean>
  10. 集合类注入
    <bean id="moreComplexObject" class="example.ComplexObject">
        <!-- results in a setAdminEmails(java.util.Properties) call -->
        <property name="adminEmails">
            <props>
                <prop key="administrator">administrator@example.org</prop>
                <prop key="support">support@example.org</prop>
                <prop key="development">development@example.org</prop>
            </props>
        </property>
        <!-- results in a setSomeList(java.util.List) call -->
        <property name="someList">
            <list>
                <value>a list element followed by a reference</value>
                <ref bean="myDataSource" />
            </list>
        </property>
        <!-- results in a setSomeMap(java.util.Map) call -->
        <property name="someMap">
            <map>
                <entry key="an entry" value="just some string"/>
                <entry key ="a ref" value-ref="myDataSource"/>
            </map>
        </property>
        <!-- results in a setSomeSet(java.util.Set) call -->
        <property name="someSet">
            <set>
                <value>just some string</value>
                <ref bean="myDataSource" />
            </set>
        </property>
    </bean>
  11. null和空值的注入
    空值
    <bean class="ExampleBean">
        <property name="email" value=""/>
    </bean>
    null
    <bean class="ExampleBean">
        <property name="email">
            <null/>
        </property>
    </bean>
  12. 用p-namespace可以简化对setter注入的XML配置,Spring2.0及之后的版本支持,p-namespace并不是被定义在xsd文件,而是存在于Spring的core。因此不能添加一个对应的p:schemaLocation,因为p命名空间在Spring的XSD中是没有定义的,仅仅存在于Spring Core中。换个角度想,如果指定了schemaLocation,那么就要有一个真实存在的XSD但必须引入
    xmlns:p="http://www.springframework.org/schema/p
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:p="http://www.springframework.org/schema/p"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean name="john-classic" class="com.example.Person">
            <property name="name" value="John Doe"/>
            <property name="spouse" ref="jane"/>
        </bean>
    
        <bean name="john-modern"
            class="com.example.Person"
            p:name="John Doe"
            p:spouse-ref="jane"/>
    
        <bean name="jane" class="com.example.Person">
            <property name="name" value="Jane Doe"/>
        </bean>
    </beans>
    加“-ref”表示对另一个bean的引用。也可以用c-namespace简化对构造器的XML配置,但需要引如
    xmlns:c="http://www.springframework.org/schema/c"
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:c="http://www.springframework.org/schema/c"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="bar" class="x.y.Bar"/>
        <bean id="baz" class="x.y.Baz"/>
    
        <!-- traditional declaration -->
        <bean id="foo" class="x.y.Foo">
            <constructor-arg ref="bar"/>
            <constructor-arg ref="baz"/>
            <constructor-arg value="foo@bar.com"/>
        </bean>
    
        <!-- c-namespace declaration -->
        <bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/>
        <!-- c-namespace index declaration  -->
        <bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz" c:_2="foo@bar.com" />
    </beans>
  13. 复合注入
    <bean id="foo" class="foo.Bar">
        <property name="fred.bob.sammy" value="123" />
    </bean>
    foo的bean有一个fred属性,fred对象有一个bob属性,bob对象有一个sammy属性。要让这个配置有效,在foo被创建之后fred、bob、sammy属性不可以为null。否则报NullPointerException异常,因此按照上面的配置最终sammy的值为123。
  14. depends-on
    当前的bean实例化前,它depend-on的bean必须先实例化,而且可以depend-on多个bean,以空格、“,”、“;”隔开,如:
    <bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
        <property name="manager" ref="manager" />
    </bean>
    
    <bean id="manager" class="ManagerBean" />
    <bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
  15. 延迟初始化bean
    当ApplicationContext在startup时就会初始化已经定义的bean,这叫预初始化,但你可以通过添加lazy-init属性来设置是否预初始化,如果设置为true,只有当bean在第一次使用的时候才会初始化。
    <bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>
  16. 自动装配(AutoWire)
    自动装配是使用bean的autowire的属性来设置的,autowire共有4个值no(默认值)、byName、byType、constructor。其中no不自动装配,需要自己手动来装配;byName和byType都是对setter注入来说的;constructor内部也是使用byType方式来自动装配的,只是用于构造器注入。当根据byType(constructor通用)类型装配时,当在容器内找到多个匹配的类型时会抛出异常信息的。因此此时可以使用autowire-candidate="false"来设置此bean不允许被自动装配到其它bean或设置primary="true"来设置此bean作为首要被自动装配到其它bean。

Bean scopes

在Spring文档中明明列出6种开箱即用的scope,但不知道为什么却只说有5种?

6种分别如下:

  • singleton:单例模式,当spring创建applicationContext容器的时候,spring会欲初始化所有的该作用域实例,加上lazy-init就可以避免预处理;只要applicationContext容器在,该实例一直存在;
  • prototype:原型模式,每次通过getBean获取该bean就会新产生一个实例,创建后spring将不再对其管理;
  • request:请求模式,只用于Web,每个http请求都产生一个新的实例,创建后spring将不再对其管理;
  • session:会话模式,只用于Web,每个session产生一个新的实例,创建后spring将不再对其管理;
  • global session:全局会话模式,只用于Web,整个session产生一个新的实例,创建后spring将不再对其管理;
  • application:全局会话模式,只用于Web,每个ServletContext产生一个新的实例,创建后spring将不再对其管理;

示例

<bean id="user" class="com.byd.springdemo.User" scope="prototype">
	<property name="name" value="kevin"></property>
</bean>

PS:当需要把一个http级别的scope的对象注入到其他bean中的时候,需要在声明的http级别的scope的对象中加入,如下面的userPreferences对象

<!-- an HTTP Session-scoped bean exposed as a proxy -->
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
      <!-- this next element effects the proxying of the surrounding bean -->
      <aop:scoped-proxy/>
</bean>

<!-- a singleton-scoped bean injected with a proxy to the above bean -->
<bean id="userService" class="com.foo.SimpleUserService">
    <!-- a reference to the proxied userPreferences bean -->
    <property name="userPreferences" ref="userPreferences"/>
</bean>

这样做的原因 是正常情况下singleton的userService中有一个session级别的对象,这样singleton的userService只初始化一次,而其所依赖的session级别的userPreferences也只初始化一次。这就与我们所定义的每个session对应一个对象的初衷相违背了,而使用<aop:scoped-proxy/>的时候,就会在实际调用的时候每次使用代理去代理userPreferences调用其对应的方法,代理访问的是对应的session中的对象,这样就可以实现每个session对应一个对象。而在代理的时候有两种方式,一种是基于JDK的interface的,一种是CGLIB形式的,如果要代理的类是面向对象的,就可以直接使用JDK的代理,否则就需要开启对CGLIB代理的支持,同时要引入CGLIB的jar包。

<bean id="userPreferences" class="com.foo.DefaultUserPreferences" scope="session">
	<aop:scoped-proxy proxy-target-class="false"/><!-- 为true则为开启对CGLIB的支持  -->
</bean>

Scope也自己自定义的,但需要实现“org.springframework.beans.factory.config.Scope”接口,具体可参考Spring官方文档。

自定义Bean的特性

在Bean的生命周期中可以定义各种回调函数,方便来定制Bean,下面我们来一一列举。

  1. 初始化回调
    可以通过实现org.springframework.beans.factory.InitializingBean接口来定制,InitializingBean接口只有一个方法
    void afterPropertiesSet() throws Exception;
    但并不推荐使用InitializingBean接口,因为这种方式和Spring耦合性太强,一般采用@PostConstruct的注解或在BeanDefine的XML中使用init-method。如
    <bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
    init是Bean的自定义无参函数。
  2. 同上,销毁时回调也可以使用org.springframework.beans.factory.DisposableBean接口、@PreDestroy注解和XML中destroy-method来实现。Bean是单例模式,而且只有在容器关闭时才会调用destroy-method。可以使用ConfigurableApplicationContext,它扩展于ApplicationContext,它新增加了两个主要的方法:refresh()和close(),让ApplicationContext具有启动、刷新和关闭应用上下文的能力。在应用上下文关闭的情况下调用refresh()即可启动应用上下文,在已经启动的状态下,调用refresh()则清除缓存并重新装载配置信息,而调用close()则可关闭应用上下文,这些接口方法为容器的控制管理带来了便利。

PS:

  1. 如果初始化回调和销毁时回调在各个Bean中的方法都一样的,还可以在beans使用default-init-method和default-destroy-method统一设置,如果某个Bean没有回调,则忽略。
  2. 当使用注解PostConstruct和PreDestroy时,由于Spring不能默认地感知到注解,因此需要注册”CommonAnnotationBeanPostProcessor“ 或配置 “<context:annotation-config />”如
    <?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">
    	
    	<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
    	
    	<bean id="user" class="com.byd.springdemo.User">
    		<property name="name" value="kevin"></property>
    	</bean>
    </beans>
    <?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:context="http://www.springframework.org/schema/context"
    	xsi: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">
    	
    	<context:annotation-config />
    	
    	<bean id="user" class="com.byd.springdemo.User">
    		<property name="name" value="kevin"></property>
    	</bean>
    </beans>
    注意第二种方式,必须添加
    xmlns:context="http://www.springframework.org/schema/context"
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    如果同时使用注解(PostConstruct、PreDestroy)、接口(InitializingBean、DisposableBean)和XML配置的方法,他们的执行顺序是:注解、接口和XML配置。
  3. 还有一些其他的回调,分别实现相应的接口,如Lifecycle、LifecycleProcessor 、SmartLifecycle等。

Bean的继承

Bean可以继承父的配置,比如

<bean id="parentUser" abstract="true">
	<property name="name" value="kevin"></property>
</bean>

<bean id="user" class="com.byd.springdemo.User" parent="parentUser">
	<property name="age" value="20"></property>
</bean>

如果父bean不使用class,则需要使用abstract来生命为抽象Bean,abstract的Bean不能实例化。

Spring容器扩展

  1. 通过使用BeanPostProcessor接口来定制bean
    public class MyProceesor implements BeanPostProcessor {
    
    	public Object postProcessBeforeInitialization(Object bean, String beanName)
    			throws BeansException {
    		System.out.println("beanName Before:"+beanName+" bean:"+bean);
    		return bean;
    	}
    
    	public Object postProcessAfterInitialization(Object bean, String beanName)
    			throws BeansException {
    		System.out.println("beanName After:"+beanName+" bean:"+bean);
    		return bean;
    	}
    }
    然后在beans里配置bean
    <bean class="net.oseye.springdemo.MyProceesor"></bean>
  2. 使用PropertyPlaceholderConfigurer获取property的配置
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations" value="classpath:com/foo/jdbc.properties"/>
    </bean>
    
    <bean id="dataSource" destroy-method="close"
            class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    jdbc.properties的配置如下
    jdbc.driverClassName=org.hsqldb.jdbcDriver
    jdbc.url=jdbc:hsqldb:hsql://production:9002
    jdbc.username=sa
    jdbc.password=root
    在运行时${jdbc.username}被替换成sa。

基于Java标注配置

虽然Spring提供了基于Java标注的配置,但一般不建议这样使用,因为这样造成可读性不强、代码入侵、不能部署时灵活配置等缺点。

  1. @Required
    该标注常用于setter,用于必须提供属性,否则报“NullPointerException”异常
    public class User {
    	private String name;
    
    	public String getName() {
    		return name;
    	}
    	
    	@Required
    	public void setName(String name) {
    		this.name = name;
    	}
    }
  2. @Autowired
    你可以使用该标注来配置传统的setter,如
    public class User {
    	private String name;
    	
    	@Autowired
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public String getName() {
    		return name;
    	}
    }
    配置文件
    <?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:context="http://www.springframework.org/schema/context"
    	xsi: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">
    	<context:annotation-config />
    	
    	<bean id="firstuser" class="net.oseye.springdemo.User"></bean>
    	
    	<bean class="java.lang.String">
    		<constructor-arg value="kevin"></constructor-arg>
    	</bean>
    </beans>
    @Autowired是byType来自动注入的,如果需要byName则可以使用下文的@Resource标注。也可以使用简化版,直接在name上使用@Autowired,如
    public class User {
    	@Autowired
    	private String name;
    
    	public String getName() {
    		return name;
    	}
    }
    你也可以配置构造函数
    public class User {
    	private String name;
    
    	public String getName() {
    		return name;
    	}
    	
    	@Autowired
    	public User(String name){
    		this.name=name;
    	}
    }
    你也可以对方法进行标注配置,如
    public class MovieRecommender {
    
        private MovieCatalog movieCatalog;
    
        private CustomerPreferenceDao customerPreferenceDao;
    
        @Autowired
        public void prepare(MovieCatalog movieCatalog,
                CustomerPreferenceDao customerPreferenceDao) {
            this.movieCatalog = movieCatalog;
            this.customerPreferenceDao = customerPreferenceDao;
        }
    
        // ...
    
    }
    @Autowired有可设置的required选项,默认为true。
    @Autowired默认是使用byType,但如果有多个则自动根据byName,如果没有name则报异常。但可以使用@Qualifier来配置使用哪个
    public class User {
    	@Autowired
    	@Qualifier("username")
    	private String name;
    
    	public String getName() {
    		return name;
    	}
    }
    public class User {
    	private String name;
    
    	public String getName() {
    		return name;
    	}
    	
    	@Autowired
    	public void setName(@Qualifier("username")String name) {
    		this.name = name;
    	}
    }
  3. @Resource
    上面@Autowired和@Qualifier的组合也可以使用@Resource
    public class User {
    	private String name;
    
    	public String getName() {
    		return name;
    	}
    	
    	@Resource(name="username")
    	public void setName(String name) {
    		this.name = name;
    	}
    }
  4. @PostConstruct和@PreDestroy参考前文。
  5. 由于不推荐使用Java标注配置,Spring文档5.9 - 5.16不作为重点学习,因此@Service、@Repository、@Configuration、@ComponentScan、@Bean、@Component、@Scope、@Inject、@Named、@Import、@ImportResource、 @Value、@Profile、@PropertySource、@EnableLoadTimeWeaving等,如有兴趣可自行学习。
posted @ 2014-12-07 15:14  码农神说  阅读(298)  评论(0编辑  收藏  举报