Spring 学习笔记

 

把Spring 2.0 reference文档下载下来,有几天了,前面简单的看了一下新特性什么的,感觉没什么大的变化,今天接着看“核心技术”这一章的3.3节“依赖”

依赖注入是我很了解的技术了,看到3.3.1.2“构造器注入”,我发现了以前使用比较少的这个构造器注入,下面将代码贴上,已备以后参考

<bean id="exampleBean" class="examples.ExampleBean">
  <constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
 
引用:使用index属性除了可以解决多个简单类型构造参数造成的模棱两可的问题之外,还可以用来解决两个构造参数类型相同造成的麻烦。注意:index属性值从0开始
 
<bean id="exampleBean" class="examples.ExampleBean">
  <constructor-arg index="0" value="7500000"/>
  <constructor-arg index="1" value="42"/>
</bean>
以上的两种构造器注入一种用的类型,一种用的索引     

引用其他Bean

这里有三种引用的方法,<ref bean="someBean"/>
                      <ref local="someBean"/>
                <ref parent="accountService"/>很少使用
经过试验证明<ref local="someBean"/> 这种方法只有在当前的Xml文件中才能起到作用,使用引用标记
<import resource="teama-applicationContext.xml"/>
不能起作用,会抛出异常。

3.3.3.4.1集合合并

<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
    <property name="adminEmails">
        <props>
            <prop key="administrator">[email]administrator@somecompany.com[/email]</prop>
            <prop key="support">[email]support@somecompany.com[/email]</prop>
        </props>
    </property>
</bean>
<bean id="child" parent="parent">
    <property name="adminEmails">
        <!-- the merge is specified on the *child* collection definition -->
        <props merge="true">
            <prop key="sales">[email]sales@somecompany.com[/email]</prop>
            <prop key="support">[email]support@somecompany.co.uk[/email]</prop>
        </props>
    </property>
</bean>
<beans>
在上面的例子中,childbean的adminEmails属性的<props/>元素上使用了merge=true属性。当child bean被容器实际解析及实例化时,其 adminEmails将与父集合的adminEmails属性进行合并。
最终:administrator=administrator@somecompany.com
      sales=sales@somecompany.com
      support=support@somecompany.co.uk
 
 

3.3.3.4.2. 强类型集合(仅适用于Java5+)

public class Foo {
    private Map<String, Float> accounts;
    public void setAccounts(Map<String, Float> accounts) {
        this.accounts = accounts;
    }
}
<beans>
    <bean id="foo" class="x.y.Foo">
        <property name="accounts">
            <map>
                <entry key="one" value="9.99"/>
                <entry key="two" value="2.75"/>
                <entry key="six" value="3.99"/>
            </map>
        </property>
    </bean>
</beans>
foobean的accounts属性被注入之前,通过反射,利用强类型Map<String, Float>的泛型信息,Spring的底层类型转换机制将会把各种value元素值转换为Float类型,因此字符串9.99、2.753.99就会被转换为实际的Float类型。

3.3.3.5. Nulls

<bean class="ExampleBean">
  <property name="email"><null/></property>
</bean>
上述的配置等同于Java代码:exampleBean.setEmail(null)

3.3.3.6. XML-based configuration metadata shortcuts(Xml的简写)

<property name="myProperty" value="hello"/>
<constructor-arg value="hello"/>
<entry key="myKey" value="hello"/>
<property name="myProperty" ref="myBean"/>
<constructor-arg ref="myBean"/>

注意,尽管存在等同于<ref bean="xxx"> 元素的简写形式,但并没有<ref local="xxx">的简写形式,为了对当前xml中bean的引用,你只能使用完整的形式。

<entry>
  <key>
    <ref bean="myKeyBean" />
  </key>
  <ref bean="myValueBean" />
</entry>
等同于:
<entry key-ref="myKeyBean" value-ref="myValueBean"/>

3.3.3.7. 组合属性名称

当设置bean的组合属性时,除了最后一个属性外,只要其他属性值不为null,组合或嵌套属性名是完全合法的。例如,下面bean的定义:
<bean id="foo" class="foo.Bar">
  <property name="fred.bob.sammy" value="123" />
</bean>
foo bean有个fred属性,此属性有个 bob属性,而bob属性又有个sammy属性,最后把sammy属性设置为123。为了让此定义能工作, foofred属性及fredbob属性在bean被构造后都必须非空,否则将抛出NullPointerException异常。

3.3.4. 使用depends-on(强制初始化)

若需要表达对多个bean的依赖,可以在'depends-on'中将指定的多个bean名字用分隔符进行分隔,分隔符可以是逗号、空格及分号等。下面的例子中使用了'depends-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" />

3.3.5. 延迟初始化bean

ApplicationContext实现的默认行为就是在启动时将所有singleton bean提前进行实例化。提前实例化意味着作为初始化过程的一部分,ApplicationContext实例会创建并配置所有的singleton bean。通常情况下这是件好事,因为这样在配置中的任何错误就会即刻被发现(否则的话可能要花几个小时甚至几天)。
有时候这种默认处理可能并不是你想要的。如果你不想让一个singleton bean在ApplicationContext实现在初始化时被提前实例化,那么可以将bean设置为延迟实例化。一个延迟初始化bean将告诉IoC 容器是在启动时还是在第一次被用到时实例化。
在XML配置文件中,延迟初始化将通过<bean/>元素中的lazy-init属性来进行控制。
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true">
</bean>
  <bean name="not.lazy" class="com.foo.AnotherBean"/>

这样当一个bean需要使用到这个bean的时候才会初始化这个bean

3.3.6. 自动装配(autowire)协作者

表 3.2. Autowiring modes
模式
说明
no
不使用自动装配。必须通过ref元素指定依赖,这是默认设置。由于显式指定协作者可以使配置更灵活、更清晰,因此对于较大的部署配置,推荐采用该设置。而且在某种程度上,它也是系统架构的一种文档形式。
byName
根据属性名自动装配。此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配。例如,在bean定义中将autowire设置为by name,而该bean包含master属性(同时提供setMaster(..)方法),Spring就会查找名为master的bean定义,并用它来装配给master属性。
byType
如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配。如果存在多个该类型的bean,那么将会抛出异常,并指出不能使用byType方式进行自动装配。若没有找到相匹配的bean,则什么事都不发生,属性也不会被设置。如果你不希望这样,那么可以通过设置dependency-check="objects"让Spring抛出异常。
constructor
byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。
autodetect
通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式。

引用开发手册原文:

理解自动装配的优缺点是很重要的。其中优点包括:
·         自动装配能显著减少配置的数量。不过,采用bean模板(见这里)也可以达到同样的目的。
·         自动装配可以使配置与java代码同步更新。例如,如果你需要给一个java类增加一个依赖,那么该依赖将被自动实现而不需要修改配置。因此强烈推荐在开发过程中采用自动装配,而在系统趋于稳定的时候改为显式装配的方式。
自动装配的一些缺点:
·         尽管自动装配比显式装配更神奇,但是,正如上面所提到的,Spring会尽量避免在装配不明确的时候进行猜测,因为装配不明确可能出现难以预料的结果,而且Spring所管理的对象之间的关联关系也不再能清晰的进行文档化。
·         对于那些根据Spring配置文件生成文档的工具来说,自动装配将会使这些工具没法生成依赖信息。
·         如果采用by type方式自动装配,那么容器中类型与自动装配bean的属性或者构造函数参数类型一致的bean只能有一个,如果配置可能存在多个这样的bean,那么就要考虑采用显式装配了。
尽管使用autowire没有对错之分,但是能在一个项目中保持一定程度的一致性是最好的做法。例如,通常情况下如果没有使用自动装配,那么仅自动装配一个或两个bean定义可能会引起开发者的混淆。

3.4. bean的作用域

3.4.1. Singleton作用域

当把一个bean定义设置为singlton作用域时,Spring IoC容器只会创建该bean定义的唯一实例。这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求和引用都将返回被缓存的对象实例。
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>
<bean id="accountService" class="com.foo.DefaultAccountService" singleton="true"/>

3.4.2. Prototype作用域

Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。根据经验,对所有有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>
简单地说,如果你用"singleton"属性那么就必须在那个文件里引用'spring-beans.dtd' DTD。 如果你用"scope"属性那么必须 在那个文件里引用'spring-beans-2.0.dtd' DTD 或'spring-beans-2.0.xsd' XSD。

今天就看到这里,也许在将来的几天中我会将这个学习笔记作为我的工作日志,里面会写上一些关于Hibernate的学习笔记

<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>
针对每次HTTP请求,Spring容器会根据loginAction bean定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,你可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。

3.4.3.5. 作用域bean与依赖

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
    <aop:scoped-proxy/>
</bean>
 
<bean id="userManager" class="com.foo.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>
当注入某种类型对象时,该对象实现了和UserPreferences类一样的公共接口(即UserPreferences实例)。并且不论我们底层选择了何种作用域机制(HTTP request、Session等等),容器都会足够智能的获取到真正的UserPreferences对象,因此我们需要将该对象的代理注入到userManager bean中, 而userManager bean并不会意识到它所持有的是一个指向UserPreferences引用的代理。在本例中,当UserManager实例调用了一个使用UserPreferences对象的方法时,实际调用的是代理对象的方法。随后代理对象会从HTTP Session获取真正的UserPreferences对象,并将方法调用委派给获取到的实际的UserPreferences对象。

今天工作日志(2007.2.14)

今天下午写了一个基于SpringMVC的架构,内部包含JSTL,明天会加上displaytag,今天又遇到了“监听器异常”的问题,经过检查发现我的jdbc.properties文件用的classpath,问题解决,还遇到了两个字母敲错了的错误,解决,明天的任务是使用displaytag把数据库的数据取出来导出为各种格式的(pdf、excel)
(2007.2.27)Spring的org.springframework.web.portlet.mvc.ParameterizableViewController
(我们一般只用他的跳转功能)类的方法,
Method Summary
getViewName()
          Return the name of the view to delegate to.
protected  ModelAndView
handleRenderRequestInternal(RenderRequest request, RenderResponse response)
          Return a ModelAndView object with the specified view name.
protected  void
initApplicationContext()
          Subclasses can override this for custom initialization behavior.
 void
setViewName(String viewName)
          Set the name of the view to delegate to.
子类可以继承并实现initApplicationContext这个方法,用于初始化一些属性