@(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!
参阅《官档》,思考代码示例逻辑
参阅《官档》,思考代码示例逻辑