@(SPRING FRAMEWORK)java
聲明:mysql
- 斜體字:《官檔》原文
- 斜體加粗字:《官檔》原文的重點字、詞、句
- 正體字+前置〔〕:我的表述行爲,如:〔總結〕、〔分析〕等
- <samp style="color:#CCCCCC">灰體字:生詞</samp>
- <samp style="color:#FF0066">粉體字:疑問</samp>
[TOC]spring
A typical enterprise application does not consist of a single object
〔總結〕一個典型的程序,是須要多個對象相互協做sql
how you go from defining a number of bean definitions that stand alone to a fully realized application where objects collaborate to achieve a goal.
〔總結〕定義一羣具有相互協做能力的對象,以實現目標程序數據庫
Dependency injection (DI):is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse, hence the name Inversion of Control (IoC), of the bean itself controlling the instantiation or location of its dependencies on its own by using direct construction of classes, or the Service Locator pattern.apache
<samp style="color:blue">關鍵詞:</samp>
<constructor-arg>
架構
Constructor-based DI is accomplished by the container invoking a constructor with a number of arguments, each <samp style="color:#CCCCCC">representing</samp> a dependency. app
Calling a static
factory method with specific arguments to construct the bean is nearly <samp style="color:#CCCCCC">equivalent</samp>, and this discussion treats arguments to a constructor and to a static
factory method similarly. less
示例代碼ide
public class SimpleMovieLister { // the SimpleMovieLister has a dependency on a MovieFinder private MovieFinder movieFinder; // a constructor so that the Spring container can inject a MovieFinder public SimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // business logic that actually uses the injected MovieFinder is omitted... }
〔總結〕基於構造函數的依賴注入,其核心在構造函數上,即重點須要放在一個疑問點上:<samp style="color: #FF0066">構造函數如何影響依賴注入(依賴注入的成功標準:一個bean對象已完成實例化)?</samp>
示例代碼
package x.y; public class Foo { public Foo(Bar bar, Baz baz) { // ... } }
第1種bean引用方式:引用
引用類型
〔注意〕bean引用方式是指:容器在實例化化一個bean時,須要讀取配置元數據。由於配置元數據中包含了可實例化一個POJO對象的全部必要數據
<!-- --> <beans> <!-- 順序一致 --> <bean id="foo" class="x.y.Foo"> <constructor-arg ref="bar"/><!-- Foo(Bar bar,***) --> <constructor-arg ref="baz"/><!-- Foo(***,Baz baz) --> </bean> <bean id="bar" class="x.y.Bar"/> <bean id="baz" class="x.y.Baz"/> </beans>
第2種bean引用方式:引用
基本數據類型
package examples; public class ExampleBean { // Number of years to calculate the Ultimate Answer private int years; // The Answer to Life, the Universe, and Everything private String ultimateAnswer; public ExampleBean(int years, String ultimateAnswer) { this.years = years; this.ultimateAnswer = ultimateAnswer; } }
基於type
屬性
<!-- --> <bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg type="int" value="7500000"/> <constructor-arg type="java.lang.String" value="42"/> </bean>
基於name
屬性
<!-- --> <bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg name="years" value="7500000"/> <constructor-arg name="ultimateAnswer" value="42"/> </bean>
基於index
屬性
<!-- --> <bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg index="0" value="7500000"/> <constructor-arg index="1" value="42"/> </bean>
<samp style="color:blue">關鍵詞:</samp>
no-argument
〔總結〕符合如下任一點,可選擇基於setter
的依賴注入
public class SimpleMovieLister { // the SimpleMovieLister has a dependency on the MovieFinder private MovieFinder movieFinder; // a setter method so that the Spring container can inject a MovieFinder public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // business logic that actually uses the injected MovieFinder is omitted... }
〔總結〕對比基於帶參構造函數
依賴注入和基於setter
依賴注入,能夠發現:基於構造函數
的不須要提供,基於setter/getter
setter
的不須要提供帶參構造函數
<samp style="color:#FF0066">Q: Constructor-based or setter-based DI?</samp>
Since you can <samp style="color:#CCCCCC">mix</samp> constructor-based and setter-based DI, it is a good rule of <samp style="color:#CCCCCC">thumb</samp> to use constructors for <samp style="color:#CCCCCC">mandatory</samp> dependencies and setter methods or configuration methods for <samp style="color:#cccccc">optional</samp> dependencies. Note that use of the @Required annotation on a setter method can be used to make the property a required dependency.
能夠混搭一塊兒用。根據實際開發經驗,以
Constructor-based DI
爲主,以setter-based DI
爲輔。主是強制,輔是可選。
The Spring team generally <samp style="color:#CCCCCC">advocates</samp> constructor injection as it enables one to implement application <samp style="color:#CCCCCC">components</samp> as <samp style="color:#CCCCCC">immutable</samp> objects and to ensure that required dependencies are not null. <samp style="color:#CCCCCC">Furthermore</samp> constructor-injected <samp style="color:#CCCCCC">components</samp> are always returned to client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be <samp style="color:#CCCCCC">refactored</samp> to better address <samp style="color:#CCCCCC">proper separation</samp> of concerns.
Constructor-based DI
能確保
Setter injection should <samp style="color:#cccccc">primarily</samp> only be used for optional dependencies that can be <samp style="color:#cccccc">assigned</samp> reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class <samp style="color:#cccccc">amenable</samp> to reconfiguration or re-injection later. Management through JMX MBeans is therefore a <samp style="color:#cccccc">compelling</samp> use case for setter injection.
Use the DI style that makes the most sense for a particular class. Sometimes, when dealing with third-party classes for which you do not have the source, the choice is made for you. For example, if a third-party class does not expose any setter methods, then constructor injection may be the only available form of DI.
〔總結〕懵逼¬_¬
〔總結〕執行注入的步驟(詳見《官檔》):
ApplicationContext
根據配置元數據裝配全部bean對象當依賴的具體表現形式爲如下時:
ApplicationContext
會根據配置元數據的信息爲POJO對象引用到對應的bean對象上。<samp style="color:#FF0066">如何解決Circular dependencies ?</samp>
Circular dependencies場景:
BeanCurrentlyInCreationException
</samp>.Circular dependencies異常解決方案:
you can configure circular dependencies with setter injection
.
代碼案例:參見《官檔》
觀察如下三個代碼片斷:
代碼片斷 1 使用最原始的方式
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- results in a setDriverClassName(String) call --> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mydb"/> <property name="username" value="root"/> <property name="password" value="masterkaoli"/> </bean>
代碼片斷 2 : 使用
p-namespace
配置元數據
<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 id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost:3306/mydb" p:username="root" p:password="masterkaoli"/> </beans>
代碼片斷 3:使用
java.util.Properties
<bean id="mappings" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <!-- typed as a java.util.Properties --> <property name="properties"> <value> jdbc.driver.className=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mydb </value> </property> </bean>
<samp style="color:#ff0066">〔總結〕</samp>
<samp style="color:#ff0066">1. 閱讀對應《官檔》後,依舊對小標題#Straight values#的具體含義模糊不清,不清楚其牽引下文的意圖是什麼。</samp>
<samp style="color:#ff0066">2. 對比上文給出的3個代碼片斷,可發現:皆在用不一樣的方法進行元數據配置,而且被配置的具體對象是數據庫驅動。</samp>
關鍵詞:
ref
The ref
element is the final element inside a ① <constructor-arg/>
or ② <property/>
definition element.Here you set the value of the specified property of ③ a bean to be a reference to another bean (a collaborator) managed by the container. The referenced bean is a dependency of the bean whose property will be set, and it is initialized on demand as needed before the property is set. (If the collaborator is a singleton bean, it may be initialized already by the container.) All references are ultimately a reference to another object. Scoping and validation depend on whether you specify the id/name
of the other object through the bean
, local
, or parent
attributes.
〔總結〕從上文可得
ref
屬性在哪用,有什麼用示例代碼
<ref bean="someBean"/>
<!-- in the parent context --> <bean id="accountService" class="com.foo.SimpleAccountService"> <!-- insert dependencies as required as here --> </bean>
<!-- in the child (descendant) context --> <bean id="accountService" <!-- bean name is the same as the parent bean --> class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <ref parent="accountService"/> <!-- notice how we refer to the parent bean --> </property> <!-- insert other configuration and dependencies as required here --> </bean>
<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>
〔總結〕inner bean相似於Java的內部類
Spring Collections | 映射 | Java Collections |
---|---|---|
<list> |
—— | List |
<set> |
—— | Set |
<map> |
—— | Map |
<props> |
—— | Properties |
示例代碼
<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>
〔總結〕根據示例代碼,得如下結論:<map>
的key
、value
;<set>
的value
也能夠是用如下屬性做爲key或value值:
bean | ref | idref | list | set | map | props | value | null
示例代碼:2種方式爲字符串設置NULL或空值
設空值時, 第1種方式:
<bean class="ExampleBean"> <property name="email" value=""/> </bean>
設空值時, 第2種方式:
exampleBean.setEmail("")
設NULL時, 第1種方式:
<bean class="ExampleBean"> <property name="email"> <null/> </property> </bean>
設NULL時, 第2種方式:
exampleBean.setEmail(null)
代碼片斷
<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"> <!--use standard XML format--> <bean name="classic" class="com.example.ExampleBean"> <property name="email" value="foo@bar.com"/> </bean> <!--use the p_namespace--> <bean name="p-namespace" class="com.example.ExampleBean" p:email="foo@bar.com"/> </beans>
代碼片斷 :使用
p:spouse-ref=""
<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>
代碼片斷
<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"/> </beans>
代碼片斷:確保
fred
,bob
屬性的值不爲空,即確保不拋NullPointerException
異常
<bean id="foo" class="foo.Bar"> <property name="fred.bob.sammy" value="123" /> </bean>
depends-on
的做用:
explicitly force one or more beans to be initialized before the bean using this element is initialized.
使用depends-on
的場景:
代碼片斷 1
<bean id="beanOne" class="ExampleBean" depends-on="manager"/> <bean id="manager" class="ManagerBean" />
代碼片斷 2
<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" />
〔總結〕參閱現有博客提供的博主,及具體應用場景
延遲加載(初始化)bean
對象。
By default, ApplicationContext implementations <samp style="color:#CCCCCC">eagerly</samp> create and configure all singleton beans as part of the initialization process. Generally, this <samp style="color:#CCCCCC">pre-instantiation</samp> is desirable, because errors in the configuration or surrounding environment are discovered immediately, as opposed to hours or even days later. When this behavior is not desirable, you can prevent pre-instantiation of a singleton bean by marking the bean definition as lazy-initialized. A lazy-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at startup.
代碼示例
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/> <bean name="not.lazy" class="com.foo.AnotherBean"/>
When the <samp style="color:#CCCCCC">preceding</samp> configuration is <samp style="color:#CCCCCC">consumed</samp> by an ApplicationContext
, the bean named lazy
is not <samp style="color:#CCCCCC">eagerly</samp> pre-instantiated when the ApplicationContext
is starting up, <samp style="color:#CCCCCC">whereas</samp> the not.lazy
bean is eagerly pre-instantiated.
However, when a lazy-initialized bean is a dependency of a singleton bean that is not lazy-initialized, the ApplicationContext
creates the lazy-initialized bean at startup, because it must satisfy the singleton’s dependencies. The lazy-initialized bean is injected into a singleton bean elsewhere that is not lazy-initialized.
代碼示例: 控制懶加載級別
<beans default-lazy-init="true"> <!-- no beans will be pre-instantiated... --> </beans>
〔總結〕參閱《官檔》,屢次閱讀原文旨意。
〔總結〕Spring有能力自動裝配協做者(即對象之間存在依賴關係)。
自動裝配的優勢:
〔理論總結〕使用自動裝配後,配置文件具有自更新能力。主流開發的趨勢之一。
autowire //基於XML
property
and constructor-arg
settings always override autowiring. You cannot autowire <samp style="color:#cccccc">so-called</samp> simple properties such as <samp style="color:#cccccc">primitives</samp>, Strings, and Classes (and arrays of such simple properties). This limitation is by-design.禁用某特定bean的自動裝配功能
基於XML
autowire-candidate=false
基於註解
@Autowired
unavailable to the autowiring infrastructure.
only affect type-based autowiring.
主要用於解決如下場景:
在大多數應用場景中,bean
對象通常都是單例對象。但在bean
對象的依賴狀況下:
古老的作法:是將一個bean
定義爲另外一個bean
的屬性。以此創建依賴關係。
如:A-bean 依賴 B-bean,那麼POJO可定義爲:
public class A{ public B b; } public class B{}
bean之間的依賴關係創建後,會產生一個問題: <samp style="color:#FF0066">兩個bean的生命週期不同,怎麼辦?</samp>
古老的作法是這樣子的,見代碼示例:
【代碼示例說明】:
CommandManager obejct
在系統架構中屬於業務邏輯層(SERVICE)。CommadManger object
的開放式方法process(Map commandState)
的調用依賴Command obejct
。根據【1.】和【2.】可得,兩個類之間存在密切的依賴關係。因此Spring提供一種方式:當依賴方(CommandManager)的行爲受制於被依賴方(Command)時,能夠在<u>依賴方</u>內部實現<u>被依賴方</u>的初始化。完成上述行爲的前提的是:依賴方(CommandManager)須要實現接口ApplicationContextAware
。
// a class that uses a stateful Command-style class to perform some processing package fiona.apple; // Spring-API imports import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; //依賴方(CommanderManager)現了ApplicationContextAware public class CommandManager implements ApplicationContextAware { private ApplicationContext applicationContext; //process()的執行受制於Command對象 public Object process(Map commandState) { Command command = createCommand(); command.setState(commandState); return command.execute(); } protected Command createCommand() { // notice the Spring API dependency! return this.applicationContext.getBean("command", Command.class); } public void setApplicationContext( ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
這種作法須要你放棄Spring framework的IoC特性。以上述代碼來看,bean object
有兩個:CommandManager
和Command
。須要分析如下幾個問題
bean object
中,<u>依賴方</u>是誰? <u>被依賴方</u>又是誰?CommandManager
; 被依賴方:Command
。bean obejct
的生命週期如何獲得保證?ApplicationContextAware
,目的是爲了調用Spring framework的方法這種在業務層耦合Spring framework代碼已破化IoC原則,由於在實現接口的那一刻起,IoC原則就已經被破壞了。即這種解決方案不可取。Spring給出了2種可靠的方案:
〔經驗之道〕業務層的全部代碼應該知足如下注釋要求
// no more Spring imports!
參閱《官檔》,思考代碼示例邏輯
參閱《官檔》,思考代碼示例邏輯