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等,如有兴趣可自行学习。

spring-framework-reference阅读笔记(一)的更多相关文章

  1. Spring源码阅读笔记

    前言 作为一个Java开发者,工作了几年后,越发觉力有点不从心了,技术的世界实在是太过于辽阔了,接触的东西越多,越感到前所未有的恐慌. 每天捣鼓这个捣鼓那个,结果回过头来,才发现这个也不通,那个也不精 ...

  2. 《深入实践Spring Boot》阅读笔记之三:核心技术源代码分析

    刚关注的朋友,可以回顾前两篇文章: 基础应用开发 分布式应用开发 上篇文章总结了<深入实践Spring Boot>的第二部分,本篇文章总结第三部分,也是最后一部分.这部分主要讲解核心技术的 ...

  3. Spring源码阅读笔记02:IOC基本概念

    上篇文章中我们介绍了准备Spring源码阅读环境的两种姿势,接下来,我们就要开始探寻这个著名框架背后的原理.Spring提供的最基本最底层的功能是bean容器,这其实是对IoC思想的应用,在学习Spr ...

  4. Spring源码阅读笔记01:源码阅读环境准备

    1. 写在前面 对于做Java开发的同学来说,Spring就像是一条绕不过去的路,但是大多数也只是停留在对Spring的简单使用层面上,对于其背后的原理所知不多也不愿深究,关于这个问题,我在平时的生活 ...

  5. 《深入实践Spring Boot》阅读笔记之一:基础应用开发

    上上篇「1718总结与计划」中提到,18年要对部分项目拆分,进行服务化,并对代码进行重构.公司技术委员会也推荐使用spring boot,之前在各个技术网站中也了解过,它可以大大简化spring配置和 ...

  6. spring源码阅读笔记06:bean加载之准备创建bean

    上文中我们学习了bean加载的整个过程,我们知道从spring容器中获取单例bean时会先从缓存尝试获取,如果缓存中不存在已经加载的单例bean就需要从头开始bean的创建,而bean的创建过程是非常 ...

  7. Spring Framework Reference,Documentation,spring英文文档.pdf 官方文档

    直接上链接:http://files.cnblogs.com/files/kongkaikai/spring-framework-reference.pdf 官网链接:http://docs.spri ...

  8. 《深入实践Spring Boot》阅读笔记之二:分布式应用开发

    上篇文章总结了<深入实践Spring Boot>的第一部分,这篇文章介绍第二部分:分布式应用开发,以及怎么构建一个高性能的服务平台. 主要从以下几个方面总结: Spring Boot SS ...

  9. Spring技术内幕阅读笔记(一)

    1.BeanFactory:实现ioc容器的最基本形式.String FACTORY_BEAN_PREFIX = "&";Object getBean(String var ...

  10. Spring源码阅读笔记03:xml配置读取

    前面的文章介绍了IOC的概念,Spring提供的bean容器即是对这一思想的具体实现,在接下来的几篇文章会侧重于探究这一bean容器是如何实现的.在此之前,先用一段话概括一下bean容器的基本工作原理 ...

随机推荐

  1. gdb命令整理

    Microsoft Windows XP [版本 ] (C) 版权所有 - Microsoft Corp. C:\Documents and Settings\Administrator>e: ...

  2. XML文档的PHP程序查询代码

    PHP文档: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" " http://www ...

  3. thinkPHP 模板中的语法

    一.导入CSS和JS文件   1.css link       js  scr        <link rel='stylesheet' type='text/css' href='__PUB ...

  4. 学习笔记: JavaScript/JQuery 的cookie操作

    转自:http://blog.csdn.net/barryhappy/archive/2011/04/27/6367994.aspx cookie是网页存储到用户硬盘上的一小段信息.最常见的作用是判断 ...

  5. CSS继承性和层叠性

    一. 继承性    1. 含义:从自己开始直到所包裹的最小的元素,都可以继承一些特有的属性.    2. 作用范围:  a. color.text-开头的.line-开头的.font-开头的,均可以继 ...

  6. 采用FirePHP调试PHP程序

    采用FirePHP调试PHP程序 FirePHP是什么? FirePHP是一个利用Firebug console栏输出调试信息方便程序调试.这一切只需要调用几个简单的函数. 他看起来是怎么个样子? 1 ...

  7. jQuery 判断checkbox是否被选中 4种方法

    下午写JS验证,有一个需求需要判断 checkbox是否被选择,查阅相关资料后,总结以下4种方法,分享给大家. <!DOCTYPE html> <html lang="en ...

  8. 微信小程序开发系列(二)小程序的全局文件

    其实你已经知道了小程序的文件结构 上一节讲到,小程序的页面由三部分组成: 视图(.wxml).逻辑(.js).样式(.wxss). 我们这次重新展开文件结构: 小程序用到的文件类型只有四种,正如你所看 ...

  9. Redis系列四(keepalived+lvs搭建负载均衡)

    1.安装Keepalived(主备服务器都要安装) 10.8.80.218  主服务器 10.8.80.217  备服务器 10.8.80.200  虚拟IP $ wget http://www.ke ...

  10. 从零开始学习C#——HelloWorld(一)

    从零开始学习C# 老规矩Hello World 您的第一个程序 visual studio 如何使用就不说了 //编程的开始,Hello World! program in C# using Syst ...