第一章
依赖注入的多种方式
- 构造器注入: 这是最常见的注入方式,通过构造函数传参的方式来初始化依赖对象。
public class TestDay0330 {
private TestDay0330 day0330;
public TestDay0330(TestDay0330 day0330) {
this.day0330 = day0330;
}
}
- setter方法注入: 在对象已经实例化之后,容器会找到对应的set方法并调用,把所需的对象作为参数传进去。
public class MyClass {
private Dependency dependency;
public void setDependency(Dependency dependency) {
this.dependency = dependency;
}
}
- 接口注入: 这种注入方式需要实现特定的接口,通过重写接口中的方法来实现对依赖对象的初始化。这种方式相对不常用,因为相比其他注入方式更显复杂和间接
public interface TestInterface0331 {
void findDependency(TestDay0331 day0331);
}
public class TestAnotherClass0331 implements TestInterface0331{
private TestDay0331 testDay0331;
@Override
public void findDependency(TestDay0331 day0331) {
testDay0331 = day0331;
}
}
- 字段注入: 直接通过反射修改类的私有字段来注入依赖,这种方式在某些依赖注入框架(如Guice)中可用,但通常被认为不如构造函数注入或setter注入清晰和安全
依赖注入能够让相互协作的软件组件保持松散耦合,而面向切面编程允许你把遍布应用各处的功能分离出来形成可重用的组件。
AOP能够是这些服务组件模块化,组件只需要关注自身的业务,不需要考虑安全或者什么事务之类的,只关注自身,总之,AOP能保证POJO的简单性。
可以把切面想象为覆盖在很多组件上的一个外壳,借助AOP能够使用各种功能去包裹核心业务层,被包裹的应用甚至不知道有AOP的存在。
我理解的就是 依赖注入 是将对象和对象之间的关系变得简单,也方便了之后扩展; 面向切面编程则是剥离出公用部分,提高了可重用性。
容器
容器是Spring框架的核心,Spring容器使用DI管理构成应用的组件。Spring容器可以归类为两个:
- Bean工厂(org.springframework.beans.factory.BeanFactory)是最基础的容器,提供基本的DI支持。
- 应用上下文 (org.springframework.context.ApplicationContext)基于Bean工厂构建,提供应用框架级别的服务,例如从属性文件解析文本信息以及发布应用事件给感兴趣的事件监听者。bean工厂更底层一点,因此应用上下文用的更多一点。
应用上下文
- AnnotationConfigApplicationContext:从一个或多个基于Jav的配置类中加载Spring应用上下文。
- AnnotationConfigWebApplicationContext: 从一个或多个基于Java的配置类中加载SpringWeb应用上下文。
- ClassPathXmlApplicationContext: 从类路径下的一个或多个XML配置文件中加载上下文定义,把应用上下文的定义文件作为类资源。
- FileSystemXmlApplicationContext: 从文件系统下的一个或多个XML配置文件中加载上下文定义。
- XmlWebApplicationContext: 从Web应用下的一个或多个XML配置文件中加载上下文定义。
ApplicationContext context = new FileSystemXmlApplicationContext("d://test//one.xml");
ApplicationContext context1 = new FileSystemXmlApplicationContext("two.xml");
ApplicationContext context2 = new AnnotationConfigApplicationContext(TestDay0331.class);
两者的区别就是FileSystemXmlApplicationContext,和ClassPathXmlApplicationContext的区别在于前者是在指定的文件系统路径下,后者是在所有的类路径下查找xml文件,根据参数也能看出来。
也可以使用AnnotationConfigApplicationContext来从一个配置类中加载bean。
应用上下文准备就绪之后,就可以使用getBean()方法来从Spring容器中获取bean。
bean的生命周期
- Spring对bean进行初始化,初始化的大概操作有: 创建Bean实例(从xml或者配置类中读取Bean的定义信息,包括类名,构造器等等),创建BeanDefinition对象(eanDefinition中包含了Bean的完整配置信息,如类名、别名、作用域、属性),存储BeanDefinition(Spring将这些BeanDefinition对象存储在BeanDefinitionMap这样的Map集合中,以便后续通过遍历这个集合来获取各个Bean的信息),实例化Bean(Spring容器通过Java反射API调用适当的构造函数来创建Bean的实例。如果Bean定义指定了工厂方法,那么容器会调用相应的工厂方法来获得Bean的实例)
- Spring将值和bean的引用注入到bean对应的属性中
- 如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBeanName方法
- 如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory,将BeanFactory容器实例传入。
- 如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationConetxt,将bean所在的应用上下文的引用传入进来。
- 如果bean实现了BeanPostProcessor接口,Spring将调用他们的PostProcessBeforeIntialization方法。
- 如果bean实现了InitializingBean即可偶,Spring将调用他们的afterPropertiesSet方法,类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用。
- 如果bean实现了BeanPostProcessor接口,Spring将会调用他们的postProcessAfterInitialization方法。
- 此时bean已经准备就绪,可以被应用程序使用了,他们将一直驻留在应用上,知道该应用上下文被销毁。
- 如果bean实现了DisposableBean接口,Spring将调用他们的destory方法,同样的,如果bean使用detory-method声明了销毁方法,该方法也会被调用。
init-method: 配合@Bean使用,等同于@PostConstruct注解。
detory-method: 配合@Bean使用,等同于preDestory注解。