IoC容器
本章介绍了Spring的控制反转(IoC)容器。
1.1。Spring IoC容器和Bean简介
本章介绍了反转控制(IoC)原则的Spring框架实现。
IoC也称为依赖注入(DI)。
在此过程中,对象可以通过①构造函数参数(),②工厂方法的参数③或在构造或从工厂方法返回后在对象实例上设置的属性来定义其依赖项(即,与它们一起使用的其他对象) 。 然后,容器在创建bean时注入那些依赖项。
什么叫做控制反转?
举例:
定义一个接口 CacheService.java 作为顶级的缓存接口
定义 RedisCacheServiceImpl.java 实现redis缓存
定义 LocalCacheServiceImpl.java 实现本地缓存
这时候,如果没有Spring框架的话那我们的代码
CacheService cache = new RedisCacheServiceImpl();
cache.set("xxx","xxx")
...
...
等到想替换其他缓存方式的时候就改动代码
CacheService cache = new LocalCacheServiceImpl();
这也就是我们说的面向接口编程,我们写代码需要自己主动创建对象来引用不同的实现
但是IOC出现之后,代码编写就不一样了,我们直接拿Spring中代码举例:
@Service("localCache")
public class LocalCacheServiceImpl implements CacheService{}
@Service("redisCache")
public class RedisCacheServiceImpl implements CacheService{}
@Autowired
@Qualifier("redisCache")
CacheService cache;
这样编写之后,new关键字不见了,多了一些陌生的注解,
简单来说这些注解,
@Service是把被标记了的服务告诉IOC容器,
@Autowired是告诉IOC容器我想要引用什么服务
当一个接口有多个实现类的时候我们就需要 @Qualifier 来指定注入对应名称的服务
通过解释发现,我们通过注解将服务都报告给了IOC容器,也通过注解引用了想要的服务。
也就是我们所依赖对象的获取方式被IOC容器控制了,
由之前的我们主动创建对象到现在的IOC容器给我们注入,这个过程就是 控制反转
上边只是演示了Spring IOC的一种方式注入对象
在org.springframework.beans和org.springframework.context包是Spring框架的IoC容器的基础。
BeanFactory 接口提供了一种高级配置机制,能够管理任何类型的对象。
ApplicationContext 是BeanFactory的子接口。 它增加了:
**与Spring的AOP功能轻松集成
消息资源处理(用于国际化)
事件发布
应用层特定的上下文,例如WebApplicationContext 用于Web应用程序中的。**
简而言之,BeanFactory提供了配置框架和基本功能,并ApplicationContext增加了更多针对企业的功能。该ApplicationContext是对一个完整的超集BeanFactory,并在Spring的IoC容器的描述本章独占使用。
总的来说ApplicationContext是一个大而全的类,包括很多常用的功能,
只要获取到容器中的ApplicationContext的对象就可以直接获取对象,发布消息等,
在Spring中,构成应用程序主干并由SpringIoC容器管理的对象称为bean。Bean是由Spring IoC容器实例化,组装和以其他方式管理的对象。否则,bean仅仅是应用程序中许多对象之一。Bean及其之间的依赖关系反映在容器使用的配置元数据中。
注意:注册为bean的对象才可以用Spring的注解来注入它的依赖项
1.2。容器概述
该org.springframework.context.ApplicationContext接口代表Spring IoC容器,并负责实例化,配置和组装Bean。容器通过读取元数据配置来获取有关要实例化,配置和组装哪些对象的指令。配置元数据以XML,注解或Java代码表示。它使您能够表达组成应用程序的对象以及这些对象之间的丰富相互依赖关系。
几种配置元数据的方式
①xml
<bean id="..." class="...">
②注解
@Service @Component 等
③java code
@Configuration
public class CommonConfig {
@Bean
public Validator validator(){}
}
Spring提供了该接口(ApplicationContext)的几种实现。在独立应用程序中,
通常创建ClassPathXmlApplicationContext 或 FileSystemXmlApplicationContext的实例。尽管XML是定义元数据配置的传统格式,但是您可以通过提供少量XML配置来声明性地启用对这些其他元数据格式的支持,从而指示容器将Java注解或代码用作元数据格式。
Spring大方向来说是两部分
首先所有的需要被注册的bean,解析bean
然后实例化bean和解决依赖
ApplicationContext 各种实现类的区别其实就是第一步:查找和解析bean的方式有区别
比如有从xml中解析,有从Configuration类中解析,所以在当前解析方式上增加一些扫描路径来实现其他类型的元数据解析是完全可以的
比如:通过xml配置下边一行
<context:component-scan base-package="org.springframework.example"/>
可以让Spring扫描org.springframework.example包下的带注解的类,从而进行xml和注解的双解析
1.2.1。配置元数据
Spring IoC容器使用元数据配置的方式来实现容器功能。此元数据的配置表示您作为应用程序开发人员如何告诉Spring容器实例化,配置和组装应用程序中的对象。
传统上,配置元数据以简单直观的XML格式提供,这是本章大部分内容用来传达Spring IoC容器的关键概念和功能的内容。
基于XML的元数据不是配置元数据的唯一允许形式。
Spring IoC容器本身与实际写入此配置元数据的格式完全脱钩。如今,许多开发人员为他们的Spring应用程序选择基于Java的配置的方式(Spring 3.0开始)。
基于注解的配置:Spring 2.5引入了对基于注解的配置元数据的支持。
基于Java的配置:从Spring3.0开始,Spring JavaConfig项目提供的许多功能
成为核心Spring Framework的一部分。
因此,您可以使用Java而不是XML文件来定义应用程序类外部的bean。
要使用这些新功能,请参阅 @Configuration, @Bean, @Import,和@DependsOn注释。
以下示例显示了基于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="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
</beans>
该id属性是标识单个bean定义的字符串。
该class属性定义Bean的类型,并使用完全限定的类名。
该id属性的值是指协作对象。在此示例中未显示用于引用协作对象的XML。有关更多信息,请参见 依赖项。
1.2.2。实例化容器
提供给ApplicationContext构造函数的一个或多个位置路径是资源字符串,可让容器从各种外部资源(例如本地文件系统,Java CLASSPATH等)加载配置元数据。
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
了解了Spring的IoC容器之后,您可能想了解更多有关Spring的 Resource抽象(如参考资料中所述),它提供了一种方便的机制,用于从URI语法中定义的位置读取InputStream。具体而言,Resource如应用程序上下文和资源路径中所述, 路径用于构造应用程序上下文。
以下示例显示了服务层对象(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">
<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
</bean>
</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">
</bean>
<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
</bean>
</beans>
在前面的示例中,服务层的PetStoreServiceImpl类由两个数据访问对象JpaAccountDao和JpaItemDao(基于JPA对象关系映射标准)组成。
该property name元素是指JavaBean属性的名称,以及ref元素指的是另一个bean定义的名称。id和ref元素之间的这种联系表达了协作对象之间的依赖性。
组装基于XML的配置元数据
拥有多个XML文件可能很有用。通常,每个单独的XML配置文件都代表体系结构中的逻辑层或模块。
您可以使用应用程序上下文构造函数从所有这些XML片段中加载bean定义。
Resource如上一节中所示,该构造函数具有多个位置。或者,使用一个或多个出现的<import/>元素从另一个文件中加载bean定义。以下示例显示了如何执行此操作:
<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。
所有位置路径是相对于当前的定义文件来说的,因此services.xml相对于当前的文件路径必须在同一个目录或类路径位置,而messageSource.xml并themeSource.xml必须在resources目录下方的位置。
如您所见,斜杠被忽略。但是,鉴于这些路径是相对的,最好不要使用任何斜线。
可以但不建议使用相对的“../”路径引用父目录中的文件。这样做会创建对当前应用程序外部文件的依赖关系。
特别是,不建议对classpath:URL(例如classpath:../services.xml)使用此引用,在URL 中,运行时解析过程会选择“最近”的类路径根,
然后查看其父目录。类路径配置的更改可能导致选择其他错误的目录。
这段没咋看懂,追了下源码
// <import resource="classpath:../resources/bean.xml"/>
//找到 resource 后边的路径
String location = ele.getAttribute("resource");
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
//补充路径中的 $ 符包括的内容 e.g. "${user.dir}"
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<>(4);
// 判断当前url是否是绝对路径还是相对路径
// classpath*: classpath: 开头的都是绝对路径
// 能够成功 new URL(location) 不报错也是绝对路径
// new URI(location).isAbsolute() 返回true也是绝对路径
boolean absoluteLocation = false;
try {
absoluteLocation = ResourcePatternUtils.isUrl(location) ||
ResourceUtils.toURI(location).isAbsolute();
}
catch (URISyntaxException ex) {
}
if (absoluteLocation) {
绝对路径就要判断是不是 classpath*: 开头
是classpath*: 开头 一种解析方法
否则是另一种解析方法
}
else {
}
您可以使用绝对路径来代替相对路径:例如file:C:/config/services.xml或classpath:/config/services.xml。但是,请注意,您正在将应用程序的配置耦合到特定的绝对位置。最好为这样的绝对位置保留一个间接性—例如,通过“${…}”占位符,这些占位符在运行时根据JVM系统属性解析。
比如应用名称是application 文件为 file.json
在windows下路径为 c:/myApp/application/file.json
在linux下问 /home/xxx/application/file.json
如果在代码不变的情况下,必须将 application文件名前半截的东西配置成可变的
${prefixPath}/application/file.json
在运行是根据配置不同的变量找到不同的文件
命名空间本身提供了导入指令功能。Spring提供的一系列XML名称空间(例如context和util名称空间)中提供了超出普通bean定义的其他配置功能。
beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd"
1.2.3。使用容器
ApplicationContext是一个维护bean定义以及相互依赖的注册表的高级工厂的接口。
使用方法 T getBean(String name, Class<T>requiredType),您可以检索bean的实例。
用ApplicationContext读bean定义和访问它们,如下例所示:
// 读取配置文件 创建bean
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
// 获取bean对应的实例
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// 使用
List<String> userList = service.getUsernameList();
你可以在相同的ApplicationContext上混合和匹配这些阅读器,从不同的配置源读取bean定义。
这句话我理解为 同一个ApplicationContext
既支持读取xml配置的Bean
也支持读取注解注册的Bean
然后,您可以getBean用来检索bean的实例。该ApplicationContext 接口还有其他几种检索bean的方法,但是理想情况下,您的应用程序代码永远不要使用它们。实际上,您的应用程序代码应该根本不调用该 getBean()方法,因此完全不依赖于Spring API。例如,Spring与Web框架的集成为各种Web框架组件(例如控制器和JSF管理的Bean)提供了依赖注入,使您可以通过元数据(例如自动装配注释)声明对特定Bean的依赖。
一般情况下开发简单使用 @Autowired 完全可以做到自动注入了,
只有自己定制功能的时候可能会用到ApplicationContext中的方法