SpringIOC不可不知
Spring是一个非侵入的、轻量级的构建企业级应用的解决方案。它采用的是模块化设计,能够简单的开箱即用,它的无侵入设计,使得开发人员能够专注于业务逻辑的开发,而这些都是建立在IOC容器基础上的。
定版本
Spring不同的版本的实现还是有些差距的,不定版本,将没法达成共识,后续的代码都是通过spring-framework-bom约定4.3.20.RELEASE版本。
<parent>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.3.20.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
</dependencies>
使用介绍
通过下面三个类,采用Java-based配置方式启动容器,来进行SpringIOC的演示。
@Component
public class Car {
@Autowired
private Wheel wheel;
public void work() {
System.out.println("轮子【 " + wheel + " 】动了");
}
}
@Component
public class Wheel {
}
@Configuration
//扫描的路径以实际为准
@ComponentScan(basePackages = "com.esy.stu")
public class BootStrap {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(BootStrap.class);
context.getBean(Car.class).work();
}
}
运行:轮子【 com.esy.stu.Wheel@73d4cc9e 】动了
例子展示的是Spring最核心的功能,具有依赖注入,控制反转功能的IOC容器。
如果没有Spring,上述的功能,将为以下面的方式实现:
Car car = new Car();
car.wheel = new Wheel();
car.work();
虽然例子比较简单,不能体现实际情况,但不通过Spring,在真实的业务场景下,每个类都依赖一堆的对象,每个对象又有一堆依赖,要得到完整的目标对象,进行的组装工作将会是一个怎样的噩梦?
由此可见,在现在的企业级开发中,Spring可以说极大的简化了开发工作,几乎不可或缺。
这种对象自动装配的功能也被称作依赖注入,也叫控制反转。
- 依赖注入:被依赖的对象,由容器进行注入、装配。
- 控制反转:对象实例化的控制权从使用者转移到了容器。
BeanFactory是IOC容器,AnnotationConfigApplicationContext是Spring上下文也叫Spring容器,是Spring对外的门面,它通过代理模式,将调用传递给真正的组件,例如:context.getBean()真正的处理逻辑在BeanFactory中实现。
过程分解
AnnotationConfigApplicationContext是Spring对外的门面,内部包含了很多可拔插的组件,用来提供Spring对外的服务。也正是由于这些灵活的可拔插的组件,使得Spring这样大型的项目便于维护。
Spring整个容器的启动,其实可以粗略的分为三步:
而组件按照功能也可以简单的分为两类:一类搜集bean定义、一类实例化组装bean。
搜集Bean
- PathMatchingResourcePatternResolver(资源扫描工具)
- ClassPathBeanDefinitionScanner (bean扫描)
- AnnotatedBeanDefinitionReader(读取配置类(@Configuration标志))
- ConfigurationClassPostProcessor(解析配置类)
实例化Bean
- DefaultListableBeanFactory(Bean工厂:生成且缓存Bean)
- AutowiredAnnotationBeanPostProcessor(装配Bean)
- RequiredAnnotationBeanPostProcessor (校验bean)
- CommonAnnotationBeanPostProcessor (初始化方法调用)
DefaultListableBeanFactory生成Bean
首先通过一个简单的例子,演示DefaultListableBeanFactory功能。
public static void main(String[] args) {
class Foo {
public String fun() {
return "我是Foo";
}
}
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("foo", new AnnotatedGenericBeanDefinition(Foo.class));
System.out.println(beanFactory.getBean(Foo.class).fun());
}
运行:我是Foo
beanFactory是生成Bean的工厂,原料就是BeanDefinition。他本身实例化过程中需要调用很多的接口方法,通过这些接口方法来完善整个Bean的组装。所以上面的例子中,并没有依赖注入的功能。
AutowiredAnnotationBeanPostProcessor 装配Bean
Spring的实例化过程要做的事情非常多,如果整个流程是流水线模式,那么不但代码极度臃肿,并且难以维护与测试。所以整个实例化Bean的功能,被分解成为多个实现,例如:装配、校验、初始化方法以及Aop等等,下面演示下依赖注入:
public static void main(String[] args) {
class Foo {
public String fun() {
return "我是Foo";
}
}
class Bar {
@Autowired
Foo foo;
public String fun() {
return "我是Bar , " + foo.fun();
}
}
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("foo", new AnnotatedGenericBeanDefinition(Foo.class));
beanFactory.registerBeanDefinition("bar", new AnnotatedGenericBeanDefinition(Bar.class));
AutowiredAnnotationBeanPostProcessor beanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
beanPostProcessor.setBeanFactory(beanFactory);
beanFactory.addBeanPostProcessor(beanPostProcessor);
System.out.println(beanFactory.getBean(Bar.class).fun());
}
运行:我是Bar , 我是Foo
可见@Autowired注解标注的Foo,被自动的进行注入装配了,形成了完整的Bean实例。而装配的思路其实也是非常的简单。
- 实例化bean对象后,找寻拥有Autowired、Value、Resource注解的方法或者字段,通过反射进行赋值。
- 用来赋值的对象如果没有被实例化,那么将会重复这个过程,递归下去,直到依赖的对象都实例化完成。
上面的过程貌似存在循环引用的问题,举个例子,看看循环引用我们是怎么解决的,
static class Foo {
@Autowired
Bar bar;
}
static class Bar {
@Autowired
Foo foo;
}
public static void main(String[] args) {
Foo foo = new Foo();
Bar bar = new Bar();
foo.bar = bar;
bar.foo = foo;
}
可以看出,一个对象能进行赋值操作的唯一条件就是,它被实例化了。所以在Spring中也是一样,只需要分解为两个步骤,实例化与装配,就完美的解决了这个问题。
整个实例化装配的过程如下图:
ConfigurationClassPostProcessor配置解析
通过java-based方式启动容器的过程中,最为主要的就是带有Configuration注解的配置类,ConfigurationClassPostProcessor就是用来解析配置类中相关注解的,包括:
- @PropertySource(Properties文件配置)
- @ComponentScan(路径扫描)
- @Import(引入其他配置类)
- @ImportResource(引入xml配置文件)
- @Bean(定义单个bean)
@Configuration
public class ConfigurationClassPostProcessorDemo {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("ConfigurationClassPostProcessorDemo", new AnnotatedGenericBeanDefinition(ConfigurationClassPostProcessorDemo.class));
ConfigurationClassPostProcessor configurationClassPostProcessor = new ConfigurationClassPostProcessor();
configurationClassPostProcessor.postProcessBeanDefinitionRegistry(beanFactory);
System.out.println(beanFactory.getBean(Executor.class));
}
@Bean
public Executor executor() {
return Executors.newCachedThreadPool();
}
}
执行结果:java.util.concurrent.ThreadPoolExecutor@67b6d4ae[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
通过这三个例子可以看出,BeanFactory中实现的是实例化,组装Bean的流程模版,通过插件增强整个流程。