2. IOC 和 DI
什么是IoC控制反转?
通俗地但不严谨地讲,以前传统方式都是应用程序需要一个对象,直接通过new的方式来生成,该对象的管理也是由当前程序自己控制。现在有一个容器,负责将应用程序需要的所有对象都new好了,对象都统一由这个容器管理,应用程序需要对象的时候直接找容器要,应用程序说我不关系对象是怎么来的反正你给我就行。这样和以前的方式不一样了,以前是应用程序自己创建和管理,现在交给容器统一创建管理了,控制权发生反转了,这简单理解为IoC。
什么是DI依赖注入?
通俗地但不严谨地讲,应用程序需要的对象A依赖于B,由容器直接自动将B依赖给到对象A,可以理解为自动将依赖B注入到A 中了。
Spring官方的对于这个2个概念的说明,比较绕,这里也附上。
IoC is also known as dependency injection (DI). It 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) of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes or a mechanism such as the Service Locator pattern.
IoC也称为依赖项注入(DI)。在这个过程中,对象只能通过构造函数参数、工厂方法的参数或在对象实例被构造或从工厂方法返回后在对象实例上设置的属性来定义它们的依赖关系(即它们所处理的其他对象)。容器然后在创建bean时注入这些依赖项。这个过程本质上是bean本身的逆过程(因此得名“控制反转”),它通过直接构造类来控制依赖项的实例化或位置
Spring容器的概念和使用
Spring IoC容器负责实例化、配置和组装bean。容器通过读取配置元数据来获取关于实例化、配置和组装哪些对象的指令。配置元数据以XML、Java注释或Java代码表示。
Talk is cheap. Show me the code
屁话少说,放码过来
下面将通过一个案例来快速演示Spring容器的简单使用。
环境准备
IDE: IDEA
Maven: 3.5.6
JDK: java8
创建Maven工程并Spring引入依赖
pom
文件如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring-ioc-quickstart</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring.verison>5.2.19.RELEASE</spring.verison>
</properties>
<dependencies>
<!--Spring上下文的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.verison}</version>
</dependency>
</dependencies>
<build>
<!--配置文件相关-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
创建2个简单类A、B
A依赖B如下
public class A {
private B b;
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
B如下
public class B {
}
创建Spring的XML配置文件
在resources
目录创建spring.xml
配置文件,并配置bean
<?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">
<!--A的定义信息-->
<bean id="a" class="com.crab.spring.A">
<!--注入B对象依赖-->
<property name="b" ref="b"/>
</bean>
<!--B的定义信息-->
<bean id="b" class="com.crab.spring.B"></bean>
</beans>
创建一个测试类
public class MainTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
A a = context.getBean("a", A.class);
B b = context.getBean("b", B.class);
System.out.println("A对象:" + a);
System.out.println("A对象中的B依赖: " + a.getB() );
System.out.println("B对象:" + b);
}
}
最终文件目录结构
运行结果
对象AB都统一由ClassPathXmlApplicationContext
容器管理,并在A需要B依赖的时候容器自动注入,应用程序需要的时候直接从容器拿接口如context.getBean()
,。结合着里面下IoC和DI的概念。以上就是Spring容器的简单使用。
A对象:com.crab.spring.A@2d928643
A对象中的B依赖: com.crab.spring.B@5025a98f
B对象:com.crab.spring.B@5025a98f
Spring 容器对象
BeanFactory
接口
BeanFactory
接口提供了一种高级配置机制,能够管理任何类型的对象,是Spring IoC容器根接口。主要的定义方法如下。
package org.springframework.beans.factory;
public interface BeanFactory {
// 指定名称获取bean
Object getBean(String name) throws BeansException;
// 指定名称和类型获取bean
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
// 指定类型获取bean
<T> T getBean(Class<T> requiredType) throws BeansException;
// 容器中是否存在bean
boolean containsBean(String name);
// 是否是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
//
}
非常建议阅读
BeanFactory
的源码上的注释说明,非常的详尽,常见的面试:请描述下Spring的生命周期?注释上就有非常官方的完整说明
ApplicationContext
接口
ApplicationContext
接口是BeanFactory
的子接口,在其基础上提供更多企业级的功能。负责实例化、配置和组装bean,支持xml配置文件、java注解、Java-based等方式进行bean的配置。常使用的实现类有: ClassPathXmlApplicationContext
、FileSystemXmlApplicationContext
、AnnotationConfigApplicationContext
等,后面详细讲解和使用,请保持阅读的热情。
bean定义对象
在 Spring 中,构成应用程序并由 Spring IoC 容器管理的对象称为 bean。这些 bean 是使用提供给容器的配置元数据创建的,如以 XML 定义的形式。
<!--A的定义信息-->
<bean id="a" class="com.crab.spring.A">
<!--注入B对象依赖-->
<property name="b" ref="b"/>
</bean>
xml中<bean />
配置格式和支持元素如下:
<bean id="myChild" class="com.crab.spring.demo02.ChildBean"
init-method="init"
destroy-method="destroy"
name="childBean,aliasName"
parent="myParent"
scope="singleton"
primary="true"
depends-on="myParent"
autowire="byType"
autowire-candidate="true"
factory-bean="myFactory"
factory-method="getObj"
abstract="false"
></bean>
元素 | 说明 |
---|---|
id | 唯一ID |
class | 对应的类,全路径 |
name | 支持名称,可以逗号/分号/空格来分隔多个,多个名称可用作别名 |
init-method | 自定义的初始化方法 |
destroy-method | 自定义的bean被销毁的方法 |
parent | 指定父类引用 |
scope | 指定作用域,如单例和原型,后面详细讲 |
primary | 是否是主要的,用于依赖注入时候容器内有多个同类型的bean时做选择 |
depends-on | 依赖于容器中的另外一个bean的引用 |
autowire | 自动注入依赖,指定通过名称或是类型 |
autowire-candidate | 标记自动依赖注入时候选 |
factory-bean | 指定创建该bean的工厂 |
factory-method | 指定创建的工厂方法 |
abstract | 是否是抽象 |
lazy-init | 延迟加载 |
通常情况下,如果不显式指定
name
,Spring默认生成名称的规则是:将类名称转成小驼峰式来命名bean。如AccountManager
生成accountManager
。
在Spring容器内,bean的定义信息最终是通过接口BeanDefinition
及其实现类体现。BeanDefinition接口和抽象类
AbstractBeanDefinition`,主要定义如下
package org.springframework.beans.factory.config;
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
void setParentName(@Nullable String parentName);
void setBeanClassName(@Nullable String beanClassName);
void setScope(@Nullable String scope);
void setLazyInit(boolean lazyInit);
void setDependsOn(@Nullable String... dependsOn);
void setAutowireCandidate(boolean autowireCandidate);
void setPrimary(boolean primary);
void setFactoryBeanName(@Nullable String factoryBeanName);
void setFactoryMethodName(@Nullable String factoryMethodName);
void setInitMethodName(@Nullable String initMethodName);
void setDestroyMethodName(@Nullable String destroyMethodName);
// 省略
}
package org.springframework.beans.factory.support;
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
private volatile Object beanClass;
private String scope = SCOPE_DEFAULT;
private boolean abstractFlag = false;
private Boolean lazyInit;
private int autowireMode = AUTOWIRE_NO;
private String[] dependsOn;
private boolean autowireCandidate = true;
private boolean primary = false;
private String factoryBeanName;
private String factoryMethodName;
private String initMethodName;
private String destroyMethodName;
}
总结
本文主要讲解了Ioc的概念,演示Spring Ioc容器的快速使用,并详细说明bean定义元素。下一篇将讲bean的依赖注入。