Spring Framework 5 私房菜之IOC
这篇博文主要讲解Spring 5 IOC 容器基础知识.
1.Spring Framework 核心技术
Spring Framwork 有两大核心技术:
- Inversion of Control (IoC) container
- Aspect-Oriented Programming (AOP)
1.1 什么是IOC?
- IOC 也叫dependency injection (DI)
- 它是一个过程,通过构造参数,工厂方法,属性注入返回一个实例
1.2 IOC的基础
Spring IOC 基础是
org.springframework.beans和org.springframework.context
pom.xml 添加依赖如下:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
1.3 IOC 接口类
Spring Framework IOC容器抽象出两个接口
- BeanFactory
- ApplicationContext
1.3.1 BeanFactory VS ApplicationContext
代码关系如下所示:
public interface ApplicationContext extends BeanFactory {}
功能性对比如下:
| BeanFactory | ApplicationContext |
| IOC的基础功能 | ApplicationContext 是BeanFactory的子类 |
| ApplicationContext是BeanFactory的超集 | |
| ApplicationContext 更容易集成Spring AOP | |
| ApplicationContext 提供国际化支持 |
1.4 IOC实现类
ApplicationContext 有很多实现类
org.springframework.context.support.ClassPathXmlApplicationContextorg.springframework.context.support.FileSystemXmlApplicationContext
但是在大多数应用程序方案中,不需要显式用户代码来实例化Spring IoC容器的一个或多个实例。
而是在web.xml配置
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
1.5 配置元数据
配置元数据有三种方式
- 1.基于XML配置元数据
<beans><bean></bean></beans>- 2.基于Java 注解配置元数据
- Spring 2.5后开始支持
@Compent,@Controller- 3.基于Java 代码配置元数据
- Spring 3.0后开始支持
@Configuration,@Bean,@Import, 和@DependsOn@Configuration作用于类上,@Bean作用于方法上
1.5.1 基于XML配置元数据
application-bean.xml
<?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 id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
- id属性是一个标识单个bean定义的字符串
- class属性定义bean的类型并使用完全限定的classname
1.5.1.1 初始化一个容器
Java 代码中调用如下:
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
services.xml
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- services -->
<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for services go here -->
</beans>
daos.xml
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountDao"
class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for data access objects go here -->
</beans>
1.5.1.1.1 引入多个xml配置文件
通常情况下,我们会按照业务模块或者业务分层设计多个xml文件,比如刚才的service 相关的全部放到services.xml 中,dao 层相关的全部放到daos.xml中。
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>
- 在上面这个示例中,外部bean定义从三个文件加载:
services.xml,messageSource.xml和themeSource.xml- 虽然支持
classpath:../services.xml这样相对路径的写法但是不建议这么用。- 不建议使用相对路径写法但是可以使用绝对路径代替
file:C:/config/services.xml或classpath:/config/services.xml
1.5.1.2 使用容器
1.5.1.2.1 使用ClassPathXmlApplicationContext
ApplicationContext是BeanFactory的子类,都是接口,拥有很多高级功能。- 比如我们可以通过
T getBean(String name, Class<T> requiredType)方法获取实例
Java 代码中调用如下:
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// use configured instance
List<String> userList = service.getUsernameList();
1.5.1.2.2 使用GenericApplicationContext
GenericApplicationContext是实现了ApplicationContext的类
代码关系如下所示:
public GenericApplicationContext implements ApplicationContext{
}
注意,实际情况是间接实现这个接口的,这里为了理解这么写了。
GenericApplicationContext 使用方式如下:
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
1.6 Bean 概览
- 在Spring里,所有被 Spring IoC container 管理的对象都叫Bean.
- 一个Bean 是一个对象的实例
- 一个Spring IoC container 管理一个或多个Bean.
Spring IoC container本身定义为BeanDefinition对象
org.springframework.beans.factory.config.BeanDefinition- 这个对象包含了如下内容:
- 包限定类名:通常是正在定义的bean的实际实现类。
- Bean行为配置元素,它说明了bean在容器中的行为方式(范围,生命周期回调等等
- 引用bean执行其工作所需的其他bean。 这些引用也称为协作者或依赖项。
- 要在新创建的对象中设置的其他配置设置 - 例如,池的大小限制或在管理连接池的Bean中使用的连接数。
| 属性 | 解释 |
|---|---|
| Class | 实例化的Bean |
| Name | Bean的名称 |
| Scope | Bean的范围 |
| Constructor arguments | 依赖注入 |
| Properties | 依赖注入 |
| Autowiring mode | 自动装配 |
| Lazy initialization mode | 延迟初始化Bean |
| Initialization method | 初始化方法回调 |
| Destruction method | 销毁方法回调 |
1.6.1 Bean的命名
- 每个bean都有一个或多个标识符。 这些标识符在托管bean的容器中必须是唯一的。
- bean通常只有一个标识符。但是,如果它需要多个,那么我们可以考虑使用别名。
- 在基于XML配置元数据中,我们可以使用id节点和name节点,或者两个都配置。
- id属性允许我们指定一个id通常,这些名称是字母数字(‘myBean’,'someService’等),但它们也可以包含特殊字符。
- 如果要为bean引入其他别名,还可以在name属性中指定它们,用逗号(,),分号(;)或空格分隔。
- 作为历史记录,在Spring 3.1之前的版本中,id属性被定义为xsd:ID类型,它约束了可能的字符。 从3.1开始,它被定义为xsd:string类型。 请注意,bean ID唯一性仍由容器强制执行,但不再由XML解析器强制执行。
- 我们可以不指定bean的name或id。 如果没有明确提供name或id,则容器会为该Bean生成唯一的名称。 但是,如果要通过name或者id引用该bean,通过使用ref元素或Service Locator样式查找,则必须提供name或id。
- Bean的默认命名规则是小写字母开头,之后遇到第二个单词开始大写。比如:
accountManager,accountService,userDao,loginController但是也有例外,比如命名为BDBManager.java就会完全保留bean的名称,使用BDBManager作为bean的名称。
1.6.1.1 给Bean定义别名
- 在bean定义本身中,我们可以为bean提供多个名称,方法是使用id属性指定的最多一个名称和name属性中的任意数量的其他名称。
- 这些名称可以是同一个bean的等效别名,对某些情况很有用,例如让应用程序中的每个组件通过使用特定于该组件本身的bean名称来引用公共依赖项。
- 但是,指定实际定义bean的所有别名并不总是足够的。 有时需要为其他地方定义的bean引入别名。
- 在大型系统中通常就是这种情况,其中配置在每个子系统之间分配,每个子系统具有其自己的一组对象定义。
- 在基于XML的配置元数据中,您可以使用元素来完成此任务。 以下示例显示了如何执行此操作:
<alias name="fromName" alias="toName"/>
在这种情况下,在使用此别名定义之后,名为fromName的bean(在同一容器中)也可以称为toName。
- 例如,子系统A的配置元数据可以通过subsystemA-dataSource的名称来引用DataSource。
- 子系统B的配置元数据可以通过subsystemB-dataSource的名称引用DataSource。
- 在编写使用这两个子系统的主应用程序时,主应用程序通过myApp-dataSource的名称引用DataSource。
- 要使所有三个名称引用同一对象,可以将以下别名定义添加到配置元数据中:
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/> <alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
现在,每个组件和主应用程序都可以通过一个唯一的名称引用dataSource,并保证不与任何其他定义冲突(有效地创建命名空间),但它们引用相同的bean。
如果我们使用Java configuration,则也可以使用
@Bean注解来提供别名。
1.6.2 实例化Bean
bean定义本质上是用于创建一个或多个对象的新型方式。我们根据bean Id 或者name来从容器中获取实例化后的对象。
- 如果使用基于XML的配置元数据,则指定要在
<bean/>元素的class属性中实例化的对象的类型(或类)。- 对Bean进行实例化一般来说有三种方法:
- 第一种方法:构造方法
通常,在容器本身通过反向调用其构造函数直接创建bean的情况下指定要构造的bean类,稍微等同于使用new运算符的Java代码。- 第二种方法:静态工厂方法
要指定包含为创建对象而调用的静态工厂方法的实际类,在较不常见的情况下,容器在类上调用静态工厂方法来创建bean。从静态工厂方法的调用返回的对象类型可以完全是同一个类或另一个类。
第三种方法:实例化的工厂方法
内部匿名类
- 如果要为静态嵌套类配置bean定义,则必须使用嵌套类的二进制名称。
- 例如,如果在
com.example包中有一个名为SomeThing的类,并且此SomeThing类具有一个名为OtherThing的静态嵌套类,则bean定义上的class属性值将为com.example.SomeThing$OtherThing- 请注意,在名称中使用
$字符可以将嵌套类名与外部类名分开
1.6.2.1 通过构造方法实例化Bean
当我们通过构造方法创建bean时,所有普通类都可以使用并与Spring兼容。 但是,我们在类中需要有一个默认(空)的构造函数。
Spring IoC容器几乎可以管理您希望它管理的任何类
使用基于XML的配置元数据,您可以按如下方式指定bean类:
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
1.6.2.2 通过静态工厂方法实例化Bean
定义使用静态工厂方法创建的bean时,请使用class属性指定包含静态工厂方法的类和名为factory-method的属性,以指定工厂方法本身的名称
在下面的示例中,我们将会通过调用工厂方法来创建bean。其中createInstance()方法必须是静态方法。
以下示例显示如何指定工厂方法:
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
代码中定义如下:
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
1.6.2.3 通过实例工厂方法实例化Bean
与通过静态工厂方法实例化类似,使用实例工厂方法进行实例化会从容器调用现有bean的非静态方法来创建新bean。
xml中配置如下:
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
相应的Java类内容如下:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
一个工厂类也可以包含多个工厂方法,如以下示例所示:
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
<bean id="accountService"
factory-bean="serviceLocator"
factory-method="createAccountServiceInstance"/>
Java 代码编写如下:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
- 在Spring文档中,”factory bean“是指在Spring容器中配置并通过实例或静态工厂方法创建对象的bean。
- 相比之下,FactoryBean(注意大小写)是指特定于Spring的FactoryBean。
1.6.3 factory bean VS BeanFactory
| factory bean | BeanFactory |
|---|---|
| 在Spring容器中配置并通过实例或静态工厂方法创建对象的bean | IOC容器最顶层接口 |
本篇完~





还没有评论,来说两句吧...