Spring MVC是Spring框架中用于Web应用开发的一个模块。Spring MVC的MVC是Model-View-Controller的缩写。它是一个广泛应用于图像化用户交互开发中的设计模式,不仅常见于Web开发,也广泛应用于如Swing和JavaFX等桌面开发。

Spring MVC基于Spring框架、Servlet和JSP(JavaServer Page),在掌握这3门技术的基础上学习Spring MVC将非常容易。

Spring框架是一个开源的企业应用开发框架,作为一个轻量级的解决方案,它包含20多个不同的解决方法。我们主要关注Core、Spring Bean、Spring MVC和Spring MVC Test模块。

一 下载Spring

1、Spring下载

Spring框架下载官网:http://spring.io/projects,具体可以参考博客Spring框架下载方法

也可以直接下载:URL为最Spring最新5.1.6版本地址,获取其他版本只需修改下面链接的5.1.6的版本号信息成想要的版本即可:https://repo.spring.io/webapp/#/artifacts/browse/tree/General/libs-release-local/org/springframework/spring/5.1.6.RELEASE

将下载好的zip解压到任意目录,在解压的目录中,包含相应的文档和Java源代码,其中libs文件下为基于Spring框架开发应用所需要的jar文件。

2、Spring依赖包下载

关于spring的spring-framework-3.0.2.RELEASE-dependencies.zip包的下载:http://s3.amazonaws.com/dist.springframework.org/release/SPR/spring-framework-3.0.2.RELEASE-dependencies.zip

3、下载Spring源码

Spring框架是一个开源项目,如果你想要尚未发布的最新版本的Spring,可以使用在github上下载源代码:https://github.com/spring-projects/spring-framework

二 IoC和DI

Spring框架有两个重要的概念:控制翻转、依赖注入。 

1、IoC是什么

Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:

  • 谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等);
  • 为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

用图例说明一下,传统程序设计如图,都是主动去创建相关对象然后再组合起来:

当有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了:

2  IoC能做什么

IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。

IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

3、IoC和DI

DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:

  • 谁依赖于谁:当然是应用程序依赖于IoC容器;
  • 为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
  • 谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
  • 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。

注:如果想要更加深入的了解IoC和DI,请参考大师级人物Martin Fowler的一篇经典文章《Inversion of Control Containers and the Dependency Injection pattern》,原文地址:http://www.martinfowler.com/articles/injection.html

4、依赖注入具体案例

有两个组件A和B,A依赖于B。假设A是一个类,且A有一个方法importantMethod用到了B,如下:

class B{
public void usefulMethod() {}
} public class A{
public void importantMethod() {
//get an instance of B
B b = new B();
b.usefulMethod();
}
}

要使用B,类A必须先获得组件B的实例引用。若B是一个具体类,则可通过new 关键字之间创建组件B的实例。但是,如果B是接口,且有多个实现,则问题就变得复杂了。我们固然可以任意选择接口B的一个实现类,但是这意味着A的可重用性大大降低了,因为无法采用B的其他实现。

依赖注入是这样处理此类情景的:接管对象的创建工作,并将该对象的引用注入到需要该对象的组件中。以上述情况为例,依赖注入框架会分别创建对象A和对象B,然后将对象B注入到对象A中。

为了能让框架进行依赖注入,程序员需要编写特定的set方法或者构造方法。例如,为了能将B注入到A中,类A会被修改成如下形式:

public class A{
private B b;
public void importantMethod() {
b.usefulMethod();
} public void setB(B b) {
this.b = b;
}
}

修改后的类A新增了一个set方法,该方法将会被框架调用,以注入B的一个实例。由于对象依赖由依赖注入,类A的importantMethod()方法不再需要在调用B的usefulMethod()方法前去创建B的一个实例。

当然,也可以采用构造器方式注入,如下所示:

public class A{
private B b; public A(B b) {
this.b = b;
} public void importantMethod() {
b.usefulMethod();
}
}

本例中,Spring会先创建B的实例,再创建A的实例,然后把B注入到实例中。

注:Spring管理的对象称为beans。

通过提供一个Ioc容器(或者说DI容器),Spring为我们提供一种可以“聪明”的管理Java对象依赖关系的方法。其优雅之处在于,程序员无需了解Spring框架的存在,更不需要引入任何Spring类型。

5、ApplicationContext接口

使用Spring,程序几乎将所有重要对象的创建工作移交给Spring,并配置如何注入依赖。Spring支持XML或注解两种配置方式。此外,还需要创建一个ApplicationContext对象,代表一个Spring IoC容器,org.springframework.context.ApplicationContext接口有很多实现类,包括ClassPathXmlApplicationContext和FileSystemXmlApplicationContext。这两个实现都需要至少一个包含beans信息的XML文件。

  • ClassPathXmlApplicationContext:尝试在类加载路径中加载配置文件;
  • FileSystemXmlApplicationContext:从文件系统中加载配置路径。
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package org.springframework.context; import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.lang.Nullable; /**
* Central interface to provide configuration for an application.
* This is read-only while the application is running, but may be
* reloaded if the implementation supports this.
*
* <p>An ApplicationContext provides:
* <ul>
* <li>Bean factory methods for accessing application components.
* Inherited from {@link org.springframework.beans.factory.ListableBeanFactory}.
* <li>The ability to load file resources in a generic fashion.
* Inherited from the {@link org.springframework.core.io.ResourceLoader} interface.
* <li>The ability to publish events to registered listeners.
* Inherited from the {@link ApplicationEventPublisher} interface.
* <li>The ability to resolve messages, supporting internationalization.
* Inherited from the {@link MessageSource} interface.
* <li>Inheritance from a parent context. Definitions in a descendant context
* will always take priority. This means, for example, that a single parent
* context can be used by an entire web application, while each servlet has
* its own child context that is independent of that of any other servlet.
* </ul>
*
* <p>In addition to standard {@link org.springframework.beans.factory.BeanFactory}
* lifecycle capabilities, ApplicationContext implementations detect and invoke
* {@link ApplicationContextAware} beans as well as {@link ResourceLoaderAware},
* {@link ApplicationEventPublisherAware} and {@link MessageSourceAware} beans.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see ConfigurableApplicationContext
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.core.io.ResourceLoader
*/
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver { /**
* Return the unique id of this application context.
* @return the unique id of the context, or {@code null} if none
*/
@Nullable
String getId(); /**
* Return a name for the deployed application that this context belongs to.
* @return a name for the deployed application, or the empty String by default
*/
String getApplicationName(); /**
* Return a friendly name for this context.
* @return a display name for this context (never {@code null})
*/
String getDisplayName(); /**
* Return the timestamp when this context was first loaded.
* @return the timestamp (ms) when this context was first loaded
*/
long getStartupDate(); /**
* Return the parent context, or {@code null} if there is no parent
* and this is the root of the context hierarchy.
* @return the parent context, or {@code null} if there is no parent
*/
@Nullable
ApplicationContext getParent(); /**
* Expose AutowireCapableBeanFactory functionality for this context.
* <p>This is not typically used by application code, except for the purpose of
* initializing bean instances that live outside of the application context,
* applying the Spring bean lifecycle (fully or partly) to them.
* <p>Alternatively, the internal BeanFactory exposed by the
* {@link ConfigurableApplicationContext} interface offers access to the
* {@link AutowireCapableBeanFactory} interface too. The present method mainly
* serves as a convenient, specific facility on the ApplicationContext interface.
* <p><b>NOTE: As of 4.2, this method will consistently throw IllegalStateException
* after the application context has been closed.</b> In current Spring Framework
* versions, only refreshable application contexts behave that way; as of 4.2,
* all application context implementations will be required to comply.
* @return the AutowireCapableBeanFactory for this context
* @throws IllegalStateException if the context does not support the
* {@link AutowireCapableBeanFactory} interface, or does not hold an
* autowire-capable bean factory yet (e.g. if {@code refresh()} has
* never been called), or if the context has been closed already
* @see ConfigurableApplicationContext#refresh()
* @see ConfigurableApplicationContext#getBeanFactory()
*/
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException; }

下面是从类加载路径中加载config1.xml和config2.xml的ApplicationContext创建的一个代码示例:

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"config1.xml","config2.xml"});

可以通过调用ApplicationContext的getBean()方法从IoC容器中获得对象:

Product product = context.getBean("product",Product.class);

genBean()方法会在xml配置文件中查询name(或id)为product且类型为Product的bean对象。

注:理想情况下,我们只需在测试代码中创建一个ApplicationContext,应用程序本身无需处理。对于Spring MVC应用,可以通过一个Spring Servlet来处理ApplicationContext,而无需直接处理。

三 XML配置文件

从1.0版本开始,Spring就支持基于XML的配置;从2.5版本开始,增加了通过注解的配置文件。下面介绍如何配置XML文件,配置文件的根元素通常为beans:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd ">
...
</beans>

如果需要更强的Spring配置能力,可以在schema location属性中添加相应的schema,也可以指定schema版本:http://www.springframework.org/schema/beans/spring-beans-5.1.xsd,不过推荐使用默认的schma,以便升级spring库时无需修改配置文件。

配置文件既可以是一份,也可以分解为多份,以支持模块化配置。ApplicationContext的实现类支持读取多份配置文件。另一种选择是,通过一份主配置文件,将该文件导入到其他配置文件。

下面是导入其他配置文件的一个示例:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd ">
<import resource="config1.xml"/>
<import resource="module2/config2.xml"/>
<import resource="/resources/config3.xml"/>
...
</beans>

bean元素的配置在后面将会详细介绍。

四 Spring控制反转(IoC)容器的使用

本节将介绍Spring如何管理bean。

1、通过无参构造器创建一个bean实例

前面已经介绍,通过调用ApplicationContext的getBean()方法可以获取一个bean的实例。

下面我们将创建一个名为spring-intro的Java Project项目,然后我们需要导入5个Spring Jar包:

  • 从spring-framework-5.1.6.RELEASE\libs下复制

spring-beans-5.1.6.RELEASE.jar

spring-context-5.1.6.RELEASE.jar

spring-core-5.1.6.RELEASE.jar

spring-expression-5.1.6.RELEASE.jar

到当前项目中,右键当前项目JRE System Library——>Build Path——>Configure Build Path——>Add External JARs把这四个包导入;

  • 从spring-framework-3.0.2.RELEASE-dependencies\org.apache.commons\com.springsource.org.apache.commons.logging\1.1.1下复制

com.springsource.org.apache.commons.logging-1.1.1.jar

到当前项目中,右键当前项目JRE System Library——>Build Path——>Configure Build Path——>Add External JARs把这一个包导入;

如果是Dynamic Java Web项目,直接将这5个Jar包导入到WebContent/WEB-INF/lib即可。

下面为代码的编写,我们先创建一个Product类,位于包springintro.bean中:

package springintro.bean;
import java.io.Serializable; public class Product implements Serializable {
private static final long serialVersionUID = 748392348L;
private String name;
private String description;
private float price; public Product() {
} public Product(String name, String description, float price) {
this.name = name;
this.description = description;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
}

下面创建一个名为spring-config.xml的配置文件,其中定义了一个名为product的bean,该配置文件位于src文件夹下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd "> <bean name="product" class="springintro.bean.Product"/> </beans>

该bean的定义告诉Spring,通过默认无参的构造器来初始化Product类。如果不存在该构造器则会抛出一个异常。此外,该无参数的构造器并不要求是public签名。

注意:应采用id或者name属性标识一个bean。为了让Spring创建一个Product实例,应将bean定义的name值"product"和Product类型作为参数传给ApplicationContext的getBean()方法。

在包springintro下创建Main.java文件:

package springintro;

import java.time.LocalDate;
import java.util.Calendar; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import springintro.bean.Employee;
import springintro.bean.Product; public class Main {
public static void main(String[] args) {
//创建IoC容器对象 从类路径下(/src)加载xml配置文件 容器启动时就会创建容器中配置的所有对象
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"spring-config.xml"});
Product product1 = context.getBean("product", Product.class);
product1.setName("Excellent snake oil");
System.out.println("product1: " + product1.getName());
  }
}

输出如下:

product1: Excellent snake oil

2、通过静态工厂方法创建一个bean实例

大部分类可以通过构造器来实例化。然而,Spring还同样支持通过调用一个工厂的静态方法来初始化类。

下面的bean定义展示了通过静态工厂方法来实例化Java.time.LocalData,调用java.time.LocalDate的静态方法now()创建LocalDate 对象:

    <bean id="localDate" class="java.time.LocalDate"
factory-method="now"/>

本例中采用了id属性而非name属性来标识bean,采用了getBean()方法来获取LocalData实例:

        LocalDate localDate = context.getBean("localDate", java.time.LocalDate.class);
System.out.println("today:" + localDate);

输出如下:

today:2019-04-29

3、销毁方法的使用(bean元素的destroy-method属性)

有时,我们希望一些对象在被销毁前能执行一些方法。Spring考虑到这样的需求,可以在bean定义中配置destroy-method属性,来指定在销毁前要执行的方法。

下面的例子中,我们配置Spring通过java.util.concurrent.Exceutors的静态方法newCachedThreadPool()来创建一个java.util.concurrent.ExceutorService实例,并指定了destroy-method属性值为shutdown()方法。这样,Spring会在销毁ExceutorService实例前调用shutdown()方法:

     <bean id="executorService" class="java.util.concurrent.Executors"
factory-method="newCachedThreadPool"
destroy-method="shutdown">
</bean>
        ExecutorService executorService = context.getBean("executorService", ExecutorService.class);
//强制关闭IoC容器,在容器关闭之前会销毁容器中所有对象
((ClassPathXmlApplicationContext)context).close();

在程序中我们强制关闭IoC容器,这样就会销毁ExceutorService实例,从而会触发executorService.shutdown()方法的执行。

4、初始化方法的使用(bean元素的init-method属性)

与销毁方法相对应的还有一个初始化方法,会在对象实例创建之后调用,可以在bean定义中配置init-method属性,来指定初始化要执行的方法:

     <bean id="executorService" class="java.util.concurrent.Executors"
factory-method="newCachedThreadPool"
init-method="shutdown">
</bean>

5、bean元素的scope属性

  • singleton(默认值):单例模式,被标识为单例的对象,在IoC容器中只会存在一个实例;
  • prototype:多例,被标识为多例的对象,每次在获取时才会创建,每次创建都是新的对象,整合struct2时,ActionBean必须配置为多例的;

  • request(了解):web环境下,与request声明周期一致;
  • session(了解):web环境下,与session声明周期一致;

如下案例:

 <bean name="product" class="springintro.bean.Product" scope="singleton"/>

五 Spring依赖注入(DI)方式

本节将详细介绍Spring的依赖注入方式。

1、构造器方式依赖注入

前面已经介绍了使用无参构造函数来初始化类,此外,Spring支持通过带参数的构造器来初始化类。

我们仍然以Product类为例,以下的定义展示了如何通过Product类构造函数的参数名传递参数:

    <bean name="featuredProduct" class="springintro.bean.Product">
<constructor-arg name="name" value="Ultimate Olive Oil"/>
<constructor-arg name="description" value="The purest olive oil on the market"/>
<constructor-arg name="price" value="9.95"/>
</bean>

这样,在IoC容器创建Product实例时,Spring会调用如下构造器:

        Product featuredProduct = context.getBean("featuredProduct", Product.class);
System.out.println(featuredProduct.getName() + ", " + featuredProduct.getDescription()
+ ", " + featuredProduct.getPrice());
    public Product(String name, String description, float price) {
this.name = name;
this.description = description;
this.price = price;
}

输出如下:

Ultimate Olive Oil, The purest olive oil on the market, 9.95

除了通过参数名称传递参数外,Spring还支持通过指数方式来传递参数,具体如下:

    <bean name="featuredProduct2" class="springintro.bean.Product">
<constructor-arg index="0" value="Ultimate Olive Oil"/>
<constructor-arg index="1" value="The purest olive oil on the market"/>
<constructor-arg index="2" value="9.95"/>
</bean>

上面index="0",表示第一个参数传入"Ultimate Olive Oil",同理...

但是如果还存在一个构造函数如下:

    public Product(String name, String description, String price) {
this.name = name;
this.description = description;
this.price = Float.parseFloat(price);
}

我们会发现这个参数名和之前的一样,并且顺序也一样,此时为了区分构造函数,我们还需要指定type参数:

    <bean name="featuredProduct3" class="springintro.bean.Product">
<constructor-arg name="name" value="Ultimate Olive Oil"/>
<constructor-arg name="description" value="The purest olive oil on the market"/>
<constructor-arg name="price" value="9.95" type="java.lang.String" index="2"/>
</bean>

如果构造函数的参数不是指类型,而是引用类型,则需要将value更改为ref。

2、Setter方式依赖注入

下面以Employee类和Address类为例,介绍setter方式依赖注入。

Employee类代码如下:

package springintro.bean;

public class Employee {
private String firstName;
private String lastName;
private Address homeAddress; public Employee() {
} public Employee(String firstName, String lastName, Address homeAddress) {
this.firstName = firstName;
this.lastName = lastName;
this.homeAddress = homeAddress;
} public String getFirstName() {
return firstName;
} public void setFirstName(String firstName) {
this.firstName = firstName;
} public String getLastName() {
return lastName;
} public void setLastName(String lastName) {
this.lastName = lastName;
} public Address getHomeAddress() {
return homeAddress;
} public void setHomeAddress(Address homeAddress) {
this.homeAddress = homeAddress;
} @Override
public String toString() {
return firstName + " " + lastName
+ "\n" + homeAddress;
} }

Address类代码如下:

package springintro.bean;

public class Address {
private String line1;
private String line2;
private String city;
private String state;
private String zipCode;
private String country; public Address(String line1, String line2, String city,
String state, String zipCode, String country) {
this.line1 = line1;
this.line2 = line2;
this.city = city;
this.state = state;
this.zipCode = zipCode;
this.country = country;
} public String getLine1() {
return line1;
} public void setLine1(String line1) {
this.line1 = line1;
} public String getLine2() {
return line2;
} public void setLine2(String line2) {
this.line2 = line2;
} public String getCity() {
return city;
} public void setCity(String city) {
this.city = city;
} public String getState() {
return state;
} public void setState(String state) {
this.state = state;
} public String getZipCode() {
return zipCode;
} public void setZipCode(String zipCode) {
this.zipCode = zipCode;
} public String getCountry() {
return country;
} public void setCountry(String country) {
this.country = country;
} @Override
public String toString() {
return line1 + "\n"
+ line2 + "\n"
+ city + "\n"
+ state + " " + zipCode + "\n"
+ country;
} }

Employee类依赖于Address类,可以通过如下配置来保证每个Employee实例都能包含Address实例:

    <bean name="simpleAddress" class="springintro.bean.Address">
<constructor-arg name="line1" value="151 Corner Street"/>
<constructor-arg name="line2" value=""/>
<constructor-arg name="city" value="Albany"/>
<constructor-arg name="state" value="NY"/>
<constructor-arg name="zipCode" value="99999"/>
<constructor-arg name="country" value="US"/>
</bean>

simpleAddress对象是Address类的一个实例,它通过构造器方式实例化。

    <bean name="employee1" class="springintro.bean.Employee">
<property name="homeAddress" ref="simpleAddress"/>
<property name="firstName" value="Junior"/>
<property name="lastName" value="Moore"/>
</bean>

employee1对象则通过配置property元素来调用setter方法以设置字段值。需要注意的是,homeAddress属性配置的是simpleAddress对象的引用。

被引用对象的配置定义无需早于引用其对象的定义,在本例中,employee1对象可以出现在simpleAddress对象定义之前。

这样,在IoC容器创建employee1实例时,Spring会调用默认构造器,并通过setter方法设置值:

        Employee employee1 = context.getBean("employee1", Employee.class);
System.out.println(employee1.getFirstName() + " " + employee1.getLastName());
System.out.println(employee1.getHomeAddress());

输出如下:

Junior Moore
151 Corner Street Albany
NY 99999
US

我们还可以通过调用有参构造器创建Employee实例,并将Address对象通过构造器注入:

    <bean name="employee2" class="springintro.bean.Employee">
<constructor-arg name="firstName" value="Senior"/>
<constructor-arg name="lastName" value="Moore"/>
<constructor-arg name="homeAddress" ref="simpleAddress"/>
</bean>

这样,在IoC容器创建employee2实例时,Spring会调用有参的构造器,具体是哪个构造器,由设置的constructor-arg确定:

        Employee employee2 = context.getBean("employee2", Employee.class);
System.out.println(employee2.getFirstName() + " " + employee2.getLastName());
System.out.println(employee2.getHomeAddress());

输出如下:

Senior Moore
151 Corner Street Albany
NY 99999
US

3、p名称空间方式依赖注入

4、spel方式依赖注入、

p名称空间方式依赖注入和spel方式依赖注入使用不多,因此不做介绍,有兴趣可以参考其他博客。

5、复杂类型注入

如果类中的字段存在复杂类型,如数组,集合(List,Set,Map),Properties,这时依赖注入的配置文件将会有一些小的变化,具体可以参考博客:Spring依赖注入之数组,集合(List,Set,Map),Properties的注入

注:本文所使用的程序来自控制反转和依赖注入的理解(通俗易懂),代码下载地址:https://github.com/pauldeck/springmvc-2ed

参考文献

[1]Spring框架下载方法

[2]Spring MVC学习指南

[3]控制反转和依赖注入的理解(通俗易懂)

[4]Spring MVC(推荐)

[5]Spring装配Bean---使用xml配置(推荐)

[6]Spring应用上下文中Bean的生命周期

最新文章

  1. 11g新特性-查询缓存(1)
  2. 【JAVA IO流之字符流】
  3. ACM:HDU 2199 Can you solve this equation? 解题报告 -二分、三分
  4. 记录JVM内存模型,参数含义和优化
  5. traits的使用
  6. Sqrt(x) - LintCode
  7. 引用计数gc机制使用不当导致内存泄漏
  8. Linux命令学习-mpstat
  9. Oracle SQL语句追踪
  10. .NET中的IO操作之文件流
  11. Web.config配置详解【转 】
  12. Swift得知——使用和分类功能(四)
  13. 【转】人工智能(AI)资料大全
  14. Mobile game analysis
  15. Ngnix技术研究系列2-基于Redis实现动态路由
  16. 深入理解Fabric环境搭建的详细过程
  17. PP-物料清单用户处理手册
  18. 洛谷 P2057 [SHOI2007]善意的投票 解题报告
  19. python random函数
  20. Spring Cloud Eureka 服务治理

热门文章

  1. java设计模式——适配器模式 Java源代码
  2. 如何用git上传代码到github详细步骤
  3. Windows Server 2012 NIC Teaming 网卡绑定介绍及注意事项
  4. python开发规范和(configparser、random模块)
  5. Linux中Zabbix4.0的搭建
  6. 我的第一个python web开发框架(28)——定制ORM(四)
  7. 使用idea搭建Scala 项目
  8. OpenResty:通过 Lua 扩展 NGINX 实现的可伸缩的 Web 平台
  9. MySQL之记录相关操作
  10. InetAddress类和InetSocketAddress类