spring-framework-reference閱讀筆記(一)

Spring Framework Runtimehtml

首先須要對Spring FrameWok框架有個直觀的認識java

Java日誌框架的發展史spring

在讀到Spring依賴JCL的時候,對Java的日誌系統作點普及!sql

最 早出現的日誌框架是apache提供的log4j,使用最爲普遍,成爲了Java日誌的事實上的標準;然而當時Sun公司在jdk1.4中增長了 JUL(java.util.logging),企圖對抗log4j,因而形成了混亂,固然此時也有其它的一些日誌框架的出現,如simplelog等, 簡直是亂上加亂。apache

解決這種混亂的方案出現了:抽象出一個接口層:因而開源社區提供了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時綁定的。session


Spring中如何使用日誌系統
app

  • 使用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等,若有興趣可自行學習。
相關文章
相關標籤/搜索