[学习笔记]Spring依赖注入

来源:互联网 发布:中日 知乎 编辑:程序博客网 时间:2024/04/19 22:16

依赖:

典型的企业应用程序不可能由单个对象(在spring中,也可称之bean)组成,再简单的应用也是由几个对象相互配合工作的,这一章主要介绍bean的定义以及bean之间的相互协作。

依赖注入:

spring中的依赖注入(Dependency injection (DI))主要有两种形式:构造器注入和setter方法注入。

构造器注入:

基于构造函数的方式有其自己的优势,它能够明确地创建出带有特定构造参数的对象,另外它对于创建一些在类中不需要经常变化的域有明显的优势。如果用setter方法来做这种事情会显得很不协调,但通常可以采用init的方法在创建时就将其初始化。当然对于某些类可能有很多的域,构造函数不可能包含所有的情况,而且其中能够包含的构造参数也是有限的,此时Setter方法注入即可以发挥其余地。

以下示例是一个只能通过构造器注入的类:

[java] view plain copy
  1. public class SimpleMovieLister {  
  2.   
  3.   // the SimpleMovieLister has a dependency on a MovieFinder  
  4.   private MovieFinder movieFinder;  
  5.   
  6.   // a constructor so that the Spring container can 'inject' a MovieFinder  
  7.   public SimpleMovieLister(MovieFinder movieFinder) {  
  8.       this.movieFinder = movieFinder;  
  9.   }  
  10.   
  11.   // business logic that actually 'uses' the injected MovieFinder is omitted...  
  12. }  

构造函数参数匹配时依据的是构造器参数类型,为了不产生歧义,一般构造参数给出的顺序按照构造函数中参数给定的顺序。如下:

[java] view plain copy
  1. package x.y;  
  2.   
  3. public class Foo {  
  4.   
  5.   public Foo(Bar bar, Baz baz) {  
  6.       // ...  
  7.   }  
  8. }  

没有潜在的歧义存在,假设Bar和Baz两个类不想关

[html] view plain copy
  1. <span style="color:#333333;"><beans>  
  2.   <bean id="foo" class="x.y.Foo">  
  3.       <</span><span style="color:#ff0000;">constructor-arg</span><span style="color:#333333;"> ref="bar"/>  
  4.       <constructor-arg ref="baz"/>  
  5.   </bean>  
  6.   
  7.   <bean id="bar" class="x.y.Bar"/>  
  8.   <bean id="baz" class="x.y.Baz"/>  
  9.   
  10. </beans></span>  

当另一个bean被引用时,如果类型一直,匹配就可以发生。当一个简单类型使用时,例如<value>true<value>,Spring不能决定value的类型,就不能进行匹配。看下面一个示例:
[java] view plain copy
  1. package examples;  
  2.   
  3. public class ExampleBean {  
  4.   
  5.   // No. of years to the calculate the Ultimate Answer  
  6.   private int years;  
  7.   
  8.   // The Answer to Life, the Universe, and Everything  
  9.   private String ultimateAnswer;  
  10.   
  11.   public ExampleBean(int years, String ultimateAnswer) {  
  12.       this.years = years;  
  13.       this.ultimateAnswer = ultimateAnswer;  
  14.   }  
  15. }  

在前面这个示例中,使用type属性,容器可以进行简单的类型匹配:


[html] view plain copy
  1. <bean id="exampleBean" class="examples.ExampleBean">  
  2. <constructor-arg type="int" value="7500000"/>  
  3. <constructor-arg type="java.lang.String" value="42"/>  
  4. </bean>  

同样,我们也可以使用index属性来指定参数顺序(注意index从0开始):

[html] view plain copy
  1. <bean id="exampleBean" class="examples.ExampleBean">  
  2. <constructor-arg index="0" value="7500000"/>  
  3. <constructor-arg index="1" value="42"/>  
  4. </bean>  

在spring3.0中,我们也可以使用构造器参数名字来制定对应的参数值:

[html] view plain copy
  1. <bean id="exampleBean" class="examples.ExampleBean">  
  2. <constructor-arg name="years" value="7500000"/>  
  3. <constructor-arg name="ultimateanswer" value="42"/>  
  4. </bean>  

注意:Keep in mind that to make this work out of the box your code must be compiled with the debug flag enabled so that Spring can look up the parameter name from the constructor. If you can't compile your code with debug flag (or don't want to) you can use @ConstructorProperties JDK annotation to explicitly name your constructor arguments. The sample class would then have to look as follows:

[java] view plain copy
  1. package examples;  
  2.   
  3. public class ExampleBean {  
  4.   
  5.   // Fields omitted  
  6.   
  7.   @ConstructorProperties({"years""ultimateAnswer"})  
  8.   public ExampleBean(int years, String ultimateAnswer) {  
  9.       this.years = years;  
  10.       this.ultimateAnswer = ultimateAnswer;  
  11.   }  
  12. }  

setter方法注入:

以下是一个简单的只能用setter方法进行注入的例子:

[java] view plain copy
  1. public class SimpleMovieLister {  
  2.   
  3.   // the SimpleMovieLister has a dependency on the MovieFinder  
  4.   private MovieFinder movieFinder;  
  5.   
  6.   // a setter method so that the Spring container can 'inject' a MovieFinder  
  7.   public void setMovieFinder(MovieFinder movieFinder) {  
  8.       this.movieFinder = movieFinder;  
  9.   }  
  10.   
  11.   // business logic that actually 'uses' the injected MovieFinder is omitted...  
  12. }  

ApplicationContext 支持setter注入和构造器注入,也支持在已存在构造器注入的情况下继续进行setter注入。


依赖注入实例:

以下例子为setter注入的xml配置文件:

[html] view plain copy
  1. <bean id="exampleBean" class="examples.ExampleBean">  
  2.   
  3. <!-- setter injection using the nested <ref/> element -->  
  4. <property name="beanOne"><ref bean="anotherExampleBean"/></property>  
  5.   
  6. <!-- setter injection using the neater 'ref' attribute -->  
  7. <property name="beanTwo" ref="yetAnotherBean"/>  
  8. <property name="integerProperty" value="1"/>  
  9. </bean>  
  10.   
  11. <bean id="anotherExampleBean" class="examples.AnotherBean"/>  
  12. <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>  

[java] view plain copy
  1. public class ExampleBean {  
  2.   
  3.   private AnotherBean beanOne;  
  4.   private YetAnotherBean beanTwo;  
  5.   private int i;  
  6.   
  7.   public void setBeanOne(AnotherBean beanOne) {  
  8.       this.beanOne = beanOne;  
  9.   }  
  10.   
  11.   public void setBeanTwo(YetAnotherBean beanTwo) {  
  12.       this.beanTwo = beanTwo;  
  13.   }  
  14.   
  15.   public void setIntegerProperty(int i) {  
  16.       this.i = i;  
  17.   }  
  18. }  

上面这个例子使用的是setter注入。下面是构造器注入的一个例子:

[html] view plain copy
  1. <bean id="exampleBean" class="examples.ExampleBean">  
  2.   
  3. <!-- constructor injection using the nested <ref/> element -->  
  4. <constructor-arg>  
  5.   <ref bean="anotherExampleBean"/>  
  6. </constructor-arg>  
  7.   
  8. <!-- constructor injection using the neater 'ref' attribute -->  
  9. <constructor-arg ref="yetAnotherBean"/>  
  10.   
  11. <constructor-arg type="int" value="1"/>  
  12. </bean>  
  13.   
  14. <bean id="anotherExampleBean" class="examples.AnotherBean"/>  
  15. <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>  

[java] view plain copy
  1. public class ExampleBean {  
  2.   
  3.   private AnotherBean beanOne;  
  4.   private YetAnotherBean beanTwo;  
  5.   private int i;  
  6.   
  7.   public ExampleBean(  
  8.       AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {  
  9.       this.beanOne = anotherBean;  
  10.       this.beanTwo = yetAnotherBean;  
  11.       this.i = i;  
  12.   }  
  13. }  

现在来看看以上例子的变形,我们不直接使用构造器,而是调用静态工厂的方法来实例化一个对象:

[html] view plain copy
  1. <bean id="exampleBean" class="examples.ExampleBean"  
  2.     <span style="color:#ff0000;">factory-method</span>="createInstance">  
  3. <constructor-arg ref="anotherExampleBean"/>  
  4. <constructor-arg ref="yetAnotherBean"/>  
  5. <constructor-arg value="1"/>  
  6. </bean>  
  7.   
  8. <bean id="anotherExampleBean" class="examples.AnotherBean"/>  
  9. <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>  

[java] view plain copy
  1. public class ExampleBean {  
  2.   
  3.   // a private constructor  
  4.   private ExampleBean(...) {  
  5.     ...  
  6.   }  
  7.     
  8.   // a static factory method; the arguments to this method can be  
  9.   // considered the dependencies of the bean that is returned,  
  10.   // regardless of how those arguments are actually used.  
  11.   public static ExampleBean createInstance (  
  12.           AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {  
  13.   
  14.       ExampleBean eb = new ExampleBean (...);  
  15.       // some other operations...  
  16.       return eb;  
  17.   }  
  18. }  
创建具体Bean实例的是静态方法,将其纳入Spring容器来管理,需要通过factory-method方法指定静态方法名称。

在增加一个,使用工厂方法实例化一个bean。

与静态方法不同之处在于,工厂方法不需要是静态的,但是在spring的配置文件中需要配置更多的内容。如下:

[html] view plain copy
  1. <bean id = "personServiceBeanFactory" class = "factory.PersonServiceBeanFactory"></bean>  
  2. <bean id = "factory" factory-bean="personServiceBeanFactory" factory-method="createPersonServiceBean"></bean>  

[java] view plain copy
  1. package factory;  
  2.   
  3. public interface PersonService {  
  4.     public void save();  
  5. }  

[java] view plain copy
  1. package factory;  
  2.   
  3. public class PersonServiceBean implements PersonService{  
  4.     public void save() {  
  5.         System.out.println("my name is zfq");  
  6.     }  
  7. }  

[java] view plain copy
  1. package factory;  
  2.   
  3. public class PersonServiceBeanFactory {  
  4.     public PersonServiceBean createPersonServiceBean() {  
  5.         return new PersonServiceBean();  
  6.     }  
  7. }  

实现:

[java] view plain copy
  1. package factory;  
  2.   
  3. import org.springframework.context.ApplicationContext;  
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  5.   
  6. public class Realize {  
  7.   
  8.     public static void main(String[] args) {  
  9.         ApplicationContext context = new ClassPathXmlApplicationContext("com/springinaction/springidol/factory.xml");  
  10.         PersonServiceBean factory = (PersonServiceBean) context.getBean("factory");  
  11.         System.out.println(factory);  
  12.     }  
  13. }  

createServiceBeanFactory方法不要求是静态的。

依赖和配置细节:

在上面的内容中,我们可以通过引用其他的bean来定义bean的properties和constructor参数,或直接使用values。

Straight values (primitives, Strings, and so on)

在<property/>中的value属性作为properties或constructor的具体参数值,使人较易读懂,如下:

[html] view plain copy
  1. <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  
  2.   
  3. <!-- results in a setDriverClassName(String) call -->  
  4. <property name="driverClassName" value="com.mysql.jdbc.Driver"/>  
  5. <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>  
  6. <property name="username" value="root"/>  
  7. <property name="password" value="masterkaoli"/>  
  8. </bean>  
在xml文件中使用p-namespace使得配置更加简洁明了:

[html] view plain copy
  1. <beans xmlns="http://www.springframework.org/schema/beans"  
  2.      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.      xmlns:p="http://www.springframework.org/schema/p"  
  4.      xsi:schemaLocation="http://www.springframework.org/schema/beans  
  5.      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  6.   
  7. <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"  
  8.       destroy-method="close"  
  9.       p:driverClassName="com.mysql.jdbc.Driver"  
  10.       p:url="jdbc:mysql://localhost:3306/mydb"  
  11.       p:username="root"  
  12.       p:password="masterkaoli"/>  
  13.   
  14. </beans>  
选择<property> 还是命名空间p取决与你自己,两者是等价的。然而,错误会在运行时被发现而不是在编译时,除非使用类似intelliJ IDEA或SpringSource Tool Suite这写工具。


idref元素:

idref元素用来讲容器中其他bean的id传给<constructor-arg/> 或<property/>元素,同时提供错误验证功能。

[html] view plain copy
  1. <bean id="theTargetBean" class="..."/>  
  2.   
  3. <bean id="theClientBean" class="...">  
  4.   <property name="targetName">  
  5.       <idref bean="theTargetBean" />  
  6.   </property>  
  7. </bean>  

上述bean定义与以下bean定义等同

[html] view plain copy
  1. <bean id="theTargetBean" class="..." />  
  2.   
  3. <bean id="client" class="...">  
  4.   <property name="targetName" value="theTargetBean" />  
  5. </bean>  

但是,第一种方法比第二种方法更好。idref允许容器在部署时就对参数进行验证,保证这个bean是存在的,在第二种方法中,一开始不会对targetName进行验证,错误要在真正实例化bean时才会被发现,如果这个bean又是个prototype bean(查看bean的周期),则错误可能会更迟被发现。


对其他bean的引用:

在<constructor-arg>或<property>中,ref属性可以设置当前的bean使用引用的bean,当所有的参数最终都是其他对象的引用,范围和验证取决于你应用其他对象时是使用bean,regardless或是parent属性。

bean属性的值可能与目标bean的id属性值相同,或者与目标bean中的某个name属性值相同

[html] view plain copy
  1. <ref bean="someBean"/>  

local属性只能与当前配置的对象在同一个配置文件的对象定义的名称
[html] view plain copy
  1. <ref local="someBean"/>  
parent只能定位于当前容器的父容器中定义的对象的引用。parent属性的值可能与目标bean的id属性的值相同,或者与目标bean的name属性中的某个值相同

[html] view plain copy
  1. <!-- in the parent context -->  
  2. <bean id="accountService" class="com.foo.SimpleAccountService">  
  3.   <!-- insert dependencies as required as here -->  
  4. </bean>  

[html] view plain copy
  1. <!-- in the child (descendant) context -->  
  2. <bean id="accountService"  <-- bean name is the same as the parent bean -->  
  3.     class="org.springframework.aop.framework.ProxyFactoryBean">  
  4.     <property name="target">  
  5.         <ref parent="accountService"/>  <!-- notice how we refer to the parent bean -->  
  6.     </property>  
  7.   <!-- insert other configuration and dependencies as required here -->  
  8. </bean>  

内部bean:

内部bean是把<bean/>直接写在<constructor-arg/>或<property/>下。

[html] view plain copy
  1. <bean id="outer" class="...">  
  2. <!-- instead of using a reference to a target bean, simply define the target bean inline -->  
  3. <property name="target">  
  4.   <bean class="com.example.Person"> <!-- this is the inner bean -->  
  5.     <property name="name" value="Fiona Apple"/>  
  6.     <property name="age" value="25"/>  
  7.   </bean>  
  8. </property>  
  9. </bean>  
一个内部bean的定义不需要定义id或name属性,容器会自动忽略这些值,他也会忽略scope标记,内部bean通常是匿名的,且他们的scope通常是prototypes类型, It is notpossible to inject inner beans into collaborating beans other than into the enclosing bean.(??)

集合装配:

java自带了很多集合类,spring也提供了相应的集合装配元素:<list/>,<map/>,<set/>,<props>

[html] view plain copy
  1. <bean id="moreComplexObject" class="example.ComplexObject">  
  2. <!-- results in a setAdminEmails(java.util.Properties) call -->  
  3. <property name="adminEmails">  
  4.   <props>  
  5.       <prop key="administrator">administrator@example.org</prop>  
  6.       <prop key="support">support@example.org</prop>  
  7.       <prop key="development">development@example.org</prop>  
  8.   </props>  
  9. </property>  
  10. <!-- results in a setSomeList(java.util.List) call -->  
  11. <property name="someList">  
  12.   <list>  
  13.       <value>a list element followed by a reference</value>  
  14.       <ref bean="myDataSource" />  
  15.   </list>  
  16. </property>  
  17. <!-- results in a setSomeMap(java.util.Map) call -->  
  18. <property name="someMap">  
  19.   <map>  
  20.       <entry key="an entry" value="just some string"/>  
  21.       <entry key ="a ref" value-ref="myDataSource"/>  
  22.   </map>  
  23. </property>  
  24. <!-- results in a setSomeSet(java.util.Set) call -->  
  25. <property name="someSet">  
  26.   <set>  
  27.       <value>just some string</value>  
  28.       <ref bean="myDataSource" />  
  29.   </set>  
  30. </property>  
  31. </bean>  

map 的key或value值,或者set value,都可以是一下元素类型:

bean | ref | idref | list | set | map | props | value | null

集合的合并:以下例子实现的集合的合并:

[html] view plain copy
  1. <beans>  
  2. <bean id="parent" abstract="true" class="example.ComplexObject">  
  3.   <property name="adminEmails">  
  4.       <props>  
  5.           <prop key="administrator">administrator@example.com</prop>  
  6.           <prop key="support">support@example.com</prop>  
  7.       </props>  
  8.   </property>  
  9. </bean>  
  10. <bean id="child" parent="parent">  
  11.   <property name="adminEmails">  
  12.       <!-- the merge is specified on the *child* collection definition -->  
  13.       <props <span style="color:#ff0000;">merge="true"</span>>  
  14.           <prop key="sales">sales@example.com</prop>  
  15.           <prop key="support">support@example.co.uk</prop>  
  16.       </props>  
  17.   </property>  
  18. </bean>  
  19. <beans>  
注意到在adminEmails中的<props/>元素中,我们使用了merge= true,最终的adminEmails将会合并父类的adminEmails中的值,结果为:


administrator=administrator@example.comsales=sales@example.comsupport=support@example.co.uk

合并的限制:不能合并不同类型集合(例如map和list则不能相互合并),合并的属性必须是底层的,继承的,子类的定义,,想象一个在父类中定义merge是多余的


强类型集合:(Strongly-typed collection (Java 5+ only)

[java] view plain copy
  1. public class Foo {  
  2.   
  3.   private Map<String, Float> accounts;  
  4.   
  5.   public void setAccounts(Map<String, Float> accounts) {  
  6.       this.accounts = accounts;  
  7.   }  
  8. }  
[html] view plain copy
  1. <beans>  
  2.   <bean id="foo" class="x.y.Foo">  
  3.       <property name="accounts">  
  4.           <map>  
  5.               <entry key="one" value="9.99"/>  
  6.               <entry key="two" value="2.75"/>  
  7.               <entry key="six" value="3.99"/>  
  8.           </map>  
  9.       </property>  
  10.   </bean>  
  11. </beans>  

Null或empty的string值:

spring将属性empty参数类型类似Stirngs的empty类型输出,以下xml配置中的emal属性为空

[html] view plain copy
  1. <bean class="ExampleBean">  
  2. <property name="email" value=""/>  
  3. </bean>  

以上写法与下面的写法等价

[html] view plain copy
  1. <bean class="ExampleBean">  
  2. <property name="email"><null/></property>  
  3. </bean>  

在java中我们可以写成:exampleBean.setEmail(null).


xml中的p-namespace:


在bean元素属性中,p命名空间可以替代<property/>元素。以下例子说明了p命名空间的用法,第一个bean用的是property,第二个用的是p-namespace

[html] view plain copy
  1. <beans xmlns="http://www.springframework.org/schema/beans"  
  2.   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.   xmlns:p="http://www.springframework.org/schema/p"  
  4.   xsi:schemaLocation="http://www.springframework.org/schema/beans  
  5.       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  6.   
  7.   <bean name="classic" class="com.example.ExampleBean">  
  8.       <property name="email" value="foo@bar.com"/>  
  9.   </bean>  
  10.   
  11.   <bean name="p-namespace" class="com.example.ExampleBean"  
  12.         p:email="foo@bar.com"/>  
  13. </beans>  

下面例子中也有两个bean,每一个bean都有对其他bean的引用,分别是用property
[html] view plain copy
  1. <beans xmlns="http://www.springframework.org/schema/beans"  
  2.   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.   xmlns:p="http://www.springframework.org/schema/p"  
  4.   xsi:schemaLocation="http://www.springframework.org/schema/beans  
  5.       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  6.   
  7.   <bean name="john-classic" class="com.example.Person">  
  8.       <property name="name" value="John Doe"/>  
  9.       <property name="spouse" <span style="color:#ff0000;">ref="jane"</span>/>  
  10.   </bean>  
  11.   
  12.   <bean name="john-modern"  
  13.       class="com.example.Person"  
  14.       p:name="John Doe"  
  15.       <span style="color:#ff0000;">p:spouse-ref</span>="jane"/>  
  16.   
  17.   <bean name="jane" class="com.example.Person">  
  18.       <property name="name" value="Jane Doe"/>  
  19.   </bean>  
  20. </beans>  
值得注意的是,p-namespace没有标准的xml形式来的灵活,在定义一个property引用时,p-namespace在ref处结束,而标准的xml格式并没有这个限制,根据具体情况选择合适的写法。

混合属性命名:

当设置bean的properties属性时你也可以混合或嵌套属性命名,只要使得路径中除了最后一个属性外其他属性名都不为null。如下所示:

[html] view plain copy
  1. <bean id="foo" class="foo.Bar">  
  2. <property name="fred.bob.sammy" value="123" />  
  3. </bean>  
foo bean有一个fred 属性,fred又有bob属性,bob又有sammy属性,最终sammy属性被设置为123,为了使这个bean可以工作,fred和bob都不能为空。


使用depends-on:

如果一个bean是另一个bean的依赖,一般来说,一个bean会被设置成另一个bean的引用,典型的就是使用<ref> 元素,但是,有时候两个bean之间并没有直接的关系,例如在某些bean实例化之前需要数据库启动,则应当设置depends-on属性使相应的bean先启动。

[html] view plain copy
  1. <bean id="beanOne" class="ExampleBean" depends-on="manager"/>  
  2.   
  3. <bean id="manager" class="ManagerBean" />  
如果是depends-on多个bean
[html] view plain copy
  1. <bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">  
  2. <property name="manager" ref="manager" />  
  3. </bean>  
  4.   
  5. <bean id="manager" class="ManagerBean" />  
  6. <bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />  

要注意的是,在销毁时,depends-on指定的bean会先销毁。


延迟初始化:(lazy-initialized beans)

ApplicationContext默认会实例化singleton的bean,如果想延迟初始化它则在bean标签中用lazy-init=‘true’,同时也可以再beans标签中指定default-lazy-init=”treu“来讲所有的延迟初始化:

[html] view plain copy
  1. <bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>  
  2.   
  3. <bean name="not.lazy" class="com.foo.AnotherBean"/>  


[html] view plain copy
  1. <beans default-lazy-init="true">  
  2.   <!-- no beans will be pre-instantiated... -->  
  3. </beans>  

但是,如果将一个已经设置为lazy-init="true'"的bean注入到另一个没有设置延迟的bean中,那么前者的lazy-init="true'"是无效的,它还是会被初始化。


自动装配:(Autowiring collaborator)

当涉及自动装配bean的依赖关系时,spring有多种处理方式:

Table 3.2. Autowiring modes

ModeExplanationno

(Default) No autowiring. Bean references must be defined via a ref element. Changing the default setting is not recommended for larger deployments, because specifying collaborators(合作者)explicitly gives greater control and clarity. To some extent, it documents the structure of a system.

byName

Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name, and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master, and uses it to set the property.

byType

Allows a property to be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not usebyType autowiring for that bean. If there are no matching beans, nothing happens; the property is not set.

constructor

Analogous to byType, but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised.



byName——把与bean的属性具有相同名字或ID的其他bean自动装配到bean对应的属性中。如果没有跟属性的名字相匹配的bean,该属性不进行装配。

byType——把与bean的属性具有相同类型的其他bean自动装配到bean对应的属性中。如果没有跟属性类型相匹配的bean,则该属性不进行装配。(应用只允许存在一个Bean与需要自动装配的属性类型相匹配,为自动识别一个普通bean,可以使用bean元素的primary属性,如果只有一个自动装配的候选Bean的primary属性设置为true,那么该bean优先选择。)

constructor——把与bean的构造器入参具有相同类型的其他bean自动装配到bean构造器的对应入参中。

autowiring的限制和不利之处:

1.在property和constructor-arg中的明确指定的依赖会覆盖掉autowiring,你也不能自动装配简单属性,例如Strings,Classes等,这是设计的不足引起的。

2.Autowiring比明确的注入写法正确率来的低,spring不会去猜测不明确的关系,可能不能拿到想要的结果。

3.对已setter或构造器注入的具体参数中,可能存在多个bean匹配参数,这样是不允许的,因为不明确结果。


方法注入:(method injection)

在大部分情况下,容器中的bean都是singleton类型的。如果一个singleton bean要引用另外一个singleton bean,或者一个非singleton bean要引用另外一个非singleton bean时,通常情况下将一个bean定义为另一个bean的property值就可以了。不过对于具有不同生命周期的bean来说这样做就会有问题了,比如在调用一个singleton类型bean A的某个方法时,需要引用另一个非singleton(prototype)类型的bean B,对于bean A来说,容器只会创建一次,这样就没法在需要的时候每次让容器为bean A提供一个新的的bean B实例。

对于上面的问题,spring提供了三种解决方案:

  • 放弃控制反转。通过实现ApplicationContextAware接口让bean A能够感知bean 容器,并且在需要的时候通过使用getBean("B")方式向容器请求一个新的bean B实例。
  • Lookup方法注入。Lookup方法注入利用了容器的覆盖受容器管理的bean方法的能力,从而返回指定名字的bean实例。
  • 自定义方法的替代方案。该注入能使用bean的另一个方法实现去替换自定义的方法。

放弃控制反转:

ApplicationContextAware和BeanFactoryAware差不多,用法也差不多,实现了ApplicationContextAware接口的对象会拥有一个ApplicationContext的引用,这样我们就可以已编程的方式操作ApplicationContext。看下面的例子。 

[html] view plain copy
  1. // a class that uses a stateful Command-style class to perform some processing  
  2. package fiona.apple;  
  3.   
  4. // Spring-API imports  
  5. import org.springframework.beans.BeansException;  
  6. import org.springframework.context.Applicationcontext;  
  7. import org.springframework.context.ApplicationContextAware;  
  8.   
  9. public class CommandManager implements ApplicationContextAware {  
  10.   
  11.  private ApplicationContext applicationContext;  
  12.   
  13.  public Object process() {  
  14.     // grab a new instance of the appropriate Command  
  15.     Command command = createCommand();  
  16.     return command.execute();  
  17.  }  
  18.   
  19.  protected Command createCommand() {  
  20.     // notice the Spring API dependency!  
  21.     return this.applicationContext.getBean("command", Command.class);  
  22.  }  
  23.   
  24.  public void setApplicationContext(ApplicationContext applicationContext)  
  25.                                                                   throws BeansException {  
  26.     this.applicationContext = applicationContext;  
  27.  }  
  28. }  
下面定义Command接口和其实现类AsyncCommand

[java] view plain copy
  1. package example;  
  2.   
  3. public interface Command {  
  4.     public Object execute();  
  5. }  

[java] view plain copy
  1. package example;  
  2.   
  3. public class AsyncCommand implements Command{  
  4.     public Object execute() {  
  5.         return this;  
  6.     }  
  7. }  
xml配置:

[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2.  <beans xmlns="http://www.springframework.org/schema/beans"  
  3.  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.  xmlns:util="http://www.springframework.org/schema/util"  
  5.  xmlns:context="http://www.springframework.org/schema/context"   
  6.  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
  7.         http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd  
  8.         http://www.springframework.org/schema/context  
  9.         http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
  10.   
  11.     <bean id = "asyncCommand" class = "example.AsyncCommand" scope = "prototype"></bean>  
  12.     <bean id = "commandManager" class = "example.CommandManager"></bean>  
  13. </beans>  
以上主要是单例bean commandManager的process()方法需要引用一个prototype(非单例)的bean,所以在调用process的时候先通过createCommand方法从容易中取出一个Command,然后再执行业务计算。

[java] view plain copy
  1. public class Realize {  
  2.   
  3.     public static void main(String[] args) {  
  4.         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/springinaction/springidol/example.xml");  
  5.         CommandManager commandManager = (CommandManager) applicationContext.getBean("commandManager");  
  6.         System.out.println("first = " + commandManager.process());  
  7.         System.out.println("second = " + commandManager.process());  
  8.       
  9.     }  
  10. }  
可以通过控制台输出看到两次的输出借中的Command的地址是不一样的,因为我们为asyncCommand配置了scope="prototype"属性,这种方式就是使得每次从容器中取得的bean实例都不一样。通过这样方式我们实现了单例bean(commandManager)中的方法(process方法)引用非单例的bean(asyncCommand)。虽然我们实现了,但是这不是一种好的方法,因为我们的业务代码和Spring Framework产生了耦合。下面介绍Spring提供的另外一种干净的实现方式,就是Lookup方法注入。 

lookup方法注入:(Lookup method injection)

使用这种方式很简单,因为Spring已经为我们做了很大一部分工作,我们要做的就是bean配置和业务类。 

  • 首先修改CommandManager类为abstract的,修改createCommand方法也为abstract的。
  • 去掉ApplicationContextAware的实现及相关set方法和applicationContext变量定义
  • 修改bean配置文件,在commandManager Bean中增加<lookup-method name="createCommand" bean="asyncCommand"/>。
  • 其他保持不变
修改后的CommandManager和bean配置文件如下: 
[java] view plain copy
  1. package example;  
  2.   
  3. public abstract class CommandManager {  
  4.       //模拟业务处理的方法    
  5.     public Object process(){    
  6.         Command command=createCommand();    
  7.         return command.execute();    
  8.     }    
  9.     //获取一个命令    
  10.     protected abstract Command createCommand();  
  11. }  
xml文件

[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4. xmlns:util="http://www.springframework.org/schema/util"  
  5. xmlns:context="http://www.springframework.org/schema/context"   
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
  7.         http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd  
  8.         http://www.springframework.org/schema/context  
  9.         http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
  10.   
  11.     <bean id = "asyncCommand" class = "example.AsyncCommand" scope = "prototype"></bean>  
  12.     <bean id = "commandManager" class = "example.CommandManager">   
  13.         <lookup-method name="createCommand" bean="asyncCommand"/>    
  14.     </bean>   
  15. </beans>  



运行测试,控制台打印出的两个Command的地址不一样,说明我们实现了。 
<lookup-method>标签中的name属性就是commandManager Bean的获取Command实例(AsyncCommand)的方法,也就createCommand方法,bean属性是要返回哪种类型的Command的,这里是AsyncCommand。 
这里的createCommand方法就成为被注入方法,他的定义形式必须为: 
<<span class="hl-tag" style="margin: 0px; padding: 0px; color: rgb(63, 127, 127);">public|protected</span>> [abstract] <<span class="hl-tag" style="margin: 0px; padding: 0px; color: rgb(63, 127, 127);">return-type</span>> theMethodName(no-arguments);
被注入方法不一定是抽象的,如果被注入方法是抽象的,动态生成的子类(这里就是动态生成的CommandManager的子类)会实现该方法。否则,动态生成的子类会覆盖类里的具体方法。为了让这个动态子类得以正常工作,需要把CGLIB的jar文件放在classpath里,这就是我们引用cglib包的原因。还有,Spring容器要子类化的类(CommandManager)不能是final的,要覆盖的方法(createCommand)也不能是final的。 
如果没有把CGLIB的jar包引入,会报错,这个一定要注意,引入的方式可以再pom.xml中加入如下代码:
[html] view plain copy
  1. <dependency>  
  2.         <groupId>cglib</groupId>  
  3.         <artifactId>cglib</artifactId>  
  4.         <version>2.2</version>  
  5.     </dependency> 
0 0