zoukankan      html  css  js  c++  java
  • 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等,如有兴趣可自行学习。
  • 相关阅读:
    Android系统在新进程中启动自定义服务过程(startService)的原理分析
    Thread和Service应用场合的区别
    Android数据格式解析对象JSON用法
    数据交换格式XML和JSON对比
    Android Handler的使用
    Android之Handler用法总结
    Handler的另外一种用法(HandlerThread)
    solr原理
    mysql主从:主键冲突问题
    修改mysql数据库存储目录
  • 原文地址:https://www.cnblogs.com/zhaiqianfeng/p/4622587.html
Copyright © 2011-2022 走看看