今天來整理一下Spring的自動裝配 autowire一節,在這里我們要解決以下問題:
- 什么是自動裝配?
- 自動裝配的意義?
- 自動裝配有幾種類型?
- 如何啟用自動裝配?
- 自動裝配將引發的問題?
一、什么是自動裝配?
The Spring container is able to autowire relationships between collaborating beans. This means that it is possible to automatically let Spring resolve collaborators (other beans) for your bean by inspecting the contents of the BeanFactory.
Spring IoC容器可以自動裝配(autowire)相互協作bean之間的關聯關系。因此,如果可能的話,可以自動讓Spring通過檢查BeanFactory中的內容,來替我們指定bean的協作者(其他被依賴的bean)。
簡而言之,就是對於bean當中引用的其他bean不需要我們自己去配置它改使用哪個類,Spring 的自動裝配可以幫助我們完成這些工作。
二、自動裝配的意義?
It is important to understand the various advantages and disadvantages of autowiring. Some advantages of autowiring include:
- Autowiring can significantly reduce the volume of configuration required. However, mechanisms such as the use of a bean template (discussed elsewhere in this chapter) are also valuable in this regard.
- Autowiring can cause configuration to keep itself up to date as your objects evolve. For example, if you need to add an additional dependency to a class, that dependency can be satisfied automatically without the need to modify configuration. Thus there may be a strong case for autowiring during development, without ruling out the option of switching to explicit wiring when the code base becomes more stable.
理解自動裝配的優缺點是很重要的。其中優點包括:
- 自動裝配能顯著減少配置的數量。不過,采用bean模板(見這里)也可以達到同樣的目的。
- 自動裝配可以使配置與java代碼同步更新。例如,如果你需要給一個java類增加一個依賴,那么該依賴將被自動實現而不需要修改配置。因此強烈推薦在開發過程中采用自動裝配,而在系統趨於穩定的時候改為顯式裝配的方式。
三、 自動裝配有幾種類型?
- no
No autowiring at all. Bean references must be defined via a refelement. This is thedefault,and changing this is discouraged for larger deployments, since explicitly specifying collaborators gives greater control and clarity. To some extent, it is a form of documentation about the structure of a system.
- byName
Autowiring by property name. This option will inspect the container and look for a bean named exactly the same as the property which needs to be autowired. For example, if you have a bean definition which is set to autowire by name, and it contains a master property(that is, it has a setMaster(..) method), Spring will look for a bean definition named master, and use it to set the property.
- byType
Allows a property to be autowired if there is exactly one bean of the property type in the container. If there is more than one, a fatal exception is thrown, and this indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens; the property is not set. If this is not desirable, setting the dependency-check="objects"
attribute value specifies that an error should be thrown in this case.
- constructor
This is analogous to byType, but applies to constructor arguments. If there isn't exactly one bean of the constructor argument type in the container, a fatal error is raised.
- autodetect
Chooses constructor or byType through introspection of the bean class. If a default constructor is found, the byType mode will be applied.
- no
默認不使用autowiring。 必須顯示的使用""標簽明確地指定bean合作者,對於部署給予更大的控制和明了。
- 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方式。
四、 如何啟用自動裝配?
你可以參照以下的配置去啟用自動裝配
<xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
default-autowire="byType" >
當然,這里的byType你可以更改為其他你想要的裝配類型。
五、自動裝配將引發的問題?
Some disadvantages of autowiring:
• Autowiring is more magical than explicit wiring. Although, as noted in the above table, Spring is careful to avoid guessing in case of ambiguity which might have unexpected results, the relationships between your Spring-managed objects are no longer documented explicitly.
• Wiring information may not be available to tools that may generate documentation from a Spring container.
自動裝配的一些缺點:
- 盡管自動裝配比顯式裝配更神奇,但是,正如上面所提到的,Spring會盡量避免在裝配不明確的時候進行猜測,因為裝配不明確可能出現難以預料的結果,而且Spring所管理的對象之間的關聯關系也不再能清晰的進行文檔化。
- 對於那些根據Spring配置文件生成文檔的工具來說,自動裝配將會使這些工具沒法生成依賴信息。
- 自動裝配可以減輕配置的工作量,但同時使得配置文件的可讀性變得很差,因為你不可能從配置文件中獲知這些對象之間得依賴關系,從而維護困難!
注意:
當根據類型進行自動裝配的時候,容器中可能存在多個bean定義跟自動裝配的setter方法和構造器參數類型匹配。這樣就會存在模棱兩可的問題。如果bean定義不唯一,裝配時就會拋出異常。
解決方案(任選其一):
- 放棄使用自動裝配,使用顯示裝配。
- 將bean排除在自動裝配之外,
兩個功能:
1 通過設定bean定義中的'autowire-candidate'屬性顯式的設置為'true' 或 'false'來設置其是否為被自動裝配對象。
2 使用對bean名字進行模式匹配來對自動裝配進行限制,其做法是在元素的 'default-autowire-candidates' 屬性中進行設置。可以使用通配符,如以'Repository'結尾的bean,
那么可以設置為"*Repository“。
3 通過在bean定義中設置'primary'屬性為'true'來將該bean設置為首選自動裝配bean。
AnnotationConfigBeanDefinitionParser AnnotationConfigUtils AutowiredAnnotationBeanPostProcessor public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, Object source) { Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4); if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor. if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor. if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); try { ClassLoader cl = AnnotationConfigUtils.class.getClassLoader(); def.setBeanClass(cl.loadClass(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME)); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex); } def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); } return beanDefs; } 這里:有Autowired和Value AutowiredAnnotationBeanPostProcessor: public AutowiredAnnotationBeanPostProcessor() { this.autowiredAnnotationTypes.add(Autowired.class); this.autowiredAnnotationTypes.add(Value.class); ClassLoader cl = AutowiredAnnotationBeanPostProcessor.class.getClassLoader(); try { this.autowiredAnnotationTypes.add((Class<? extends Annotation>) cl.loadClass("javax.inject.Inject")); logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } }