iwehdio的博客园:https://www.cnblogs.com/iwehdio/
学习自:
1、基础
BeanDefinition和BeanPostProcessor
-
BeanDefinition:
-
BeanDefinition其实是bean定义的一个*接口:一个BeanDefinition是用来描述一个bean实例的。
-
功能其实就类似于用Class类描述一个类:BeanDefinition的实现类很多,这里以AbstractBeanDefinition为例,它实现了BeanDefinition。
-
Class只是描述了一个类有哪些字段、方法,但是无法描述如何实例化这个bean。BeanDefinition就完成了这些工作,比如:
- 单例吗?
- 是否需要延迟加载?
- 需要调用哪个初始化方法/销毁方法?
-
在容器内部,这些bean定义被表示为BeanDefinition对象,包含以下元数据:
- 包限定的类名:通常,定义bean的实际实现类。
- Bean行为配置:它声明Bean在容器中的行为(范围、生命周期回调,等等)。
- Bean依赖:对其他Bean的引用。
- 对当前Bean的一些设置:例如,池的大小限制或在管理连接池的bean中使用的连接数。
-
Spring解析
<bean/>
或者@Bean后,其实并不是就直接搞了一个bean存到一个大Map中。- Spring首先会扫描解析指定位置的所有的类得到Resources(可以理解为读取.Class文件)
- 然后依照TypeFilter和@Conditional注解决定是否将这个类解析为BeanDefinition
- 稍后再把一个个BeanDefinition取出实例化成Bean
-
-
后置处理器 :
-
后置处理器处理的就是,如何将BeanDefinition取出实例化成Bean。
- 比如,如果不加@Transactional,那么Controller层注入的就是普通的userServiceImpl,而加了注解以后返回的实际是代理对象。
-
大部分人把Spring比作容器,其实潜意识里是将Spring完全等同于一个Map了。其实,真正存单例对象的Map,只是Spring中很小很小的一部分,仅仅是BeanFactory子类的一个字段,我更习惯称它为“单例池”。
/** Cache of singleton objects: bean name --> bean instance */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
-
这里的ApplicationContext和BeanFactory是接口,实际上都有各自的子类。比如注解驱动开发时,Spring中最关键的就是AnnotationConfigApplicationContext和DefaultListableBeanFactory。
-
拿ApplicationContext来讲,它也实现了BeanFactory接口,说明它其实也是一个容器。但是同为容器,与BeanFactory不同的是,ApplicationContext主要用来包含各种各样的组件,而不是存bean:
-
后置处理器其实可以分好多种,属于Spring的扩展点之一:前三个BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor、BeanPostProcessor都算是后置处理器。
-
BeanFactoryPostProcessor是处理BeanFactory的,所以存在ApplicationContext中。而BeanPostProcessor是处理Bean的,所以存在BeanFactory中。
-
在普通Bean中如何注入ApplicationContext实例?
- 除了利用Spring本身的IOC容器自动注入以外,还可以让Bean实现ApplicationContextAware接口:实现ApplicationContextAware接口,并实现setApplicationContext()方法,用成员变量去接收形参applicationContext。
- 后期,Spring会调用setApplicationContext()方法传入ApplicationContext实例。
- 此时此刻一个类实现ApplicationContextAware接口,有两层含义:
- 作为后置处理器的判断依据,只有你实现了该接口我才处理你
- 提供被后置处理器调用的方法
-
自定义后置处理器,返回代理对象。
@Component public class MyAspectJAutoProxyCreator implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { final Object obj = bean; //如果当前经过BeanPostProcessors的Bean是Calculator类型,我们就返回它的代理对象 if (bean instanceof Calculator) { Object proxyObj = Proxy.newProxyInstance( this.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始计算...."); Object result = method.invoke(obj, args); System.out.println("结束计算..."); return result; } } ); return proxyObj; } //否则返回本身 return obj; } }
-
IOC和DI
-
IOC与DI:
- IOC理解成一种思想,而DI是实现该思想的一种具体方式。Spring被称为IOC容器,它实现IOC的方式除了DI(Dependency Inject,依赖注入),其实还有DL(Dependency Look,依赖查找)。
- 按照Spring官方文档的说法,Spring的容器配置方式可以分为3种:
- Schema-based Container Configuration(XML配置)
- 全部
<bean>
标签,但也可以配置自动装配
- 全部
- Annotation-based Container Configuration(注解)
- XML+注解:XML+
<context:component-scan>
+@Component - JavaConfig+注解:@Configuration+@ComponentScan+@Component
- XML+注解:XML+
- Java-based Container Configuration(@Configuration配置类)
- @Configuration+@Bean
- Schema-based Container Configuration(XML配置)
- 对于配置而言
<bean>
这个标签,其实承载着两个作用:- 定义bean,告诉Spring哪个Bean需要交给它管理(放入容器)
- 维护bean与bean之间的依赖关系
- 指定自动装配,可以使得bean标签的职责单一化:只表示放入容器。
- byName/byType对应setter方法注入。
- constructor对应构造方法注入。
- 降低耦合度,可以直接用
@Component
表示将bean放入容器。- 使用
<context:component-scan>
隐式地启用了<context:annotation-config>
的功能,让Spring具备解析@Component等注解的功能。
- 使用
- 用
@Component
,同时还需要指定自动装配:@Autowired
、@Resource
、@Inject
。 - @Autowired默认采用byType模式自动装配,如果找到多个同类型的,会根据名字匹配。都不匹配,则会报错。
-
如何把对象交给Spring管理:
-
首先明确Spring Bean和Java Object的概念:
- Bean指的是交给Spring管理、且在Spring中经历完整生命周期(创建、赋值、各种后置处理)的Java对象。
- Object指的是我们自己new的、且没有加入Spring容器的Java对象。
-
把Bean交给Spring管理的3种方式:
<bean>
- @Component
- @Configuration+@Bean
-
2、Bean容器
-
从ApplicationContext和BeanFactory开始:
-
这两个都是接口,而且ApplicationContext继承了BeanFactory。
-
看看BeanFactory:可以注意到的是,其中只有getXxx方法,而没有SetXxx方法。这是因为,BeanFactory接口内部并没有定义字段去存储bean,仅仅是提供了方法规范。
-
真正存bean的工厂,DefaultSingletonBeanRegistry(默认的单例bean注册表)是专门来管理单例bean的。
-
-
容器在哪?
- 最重要的三个成员变量:
- singletonObjects(真正存放单例bean的Map)
- earlySingletonObjects
- singletonFactories
-
作为专门管理单例bean的工厂,它如何保证单例?
- DefaultSingletonBeanRegistry搞了一个
Set<String> singletonsCurrentlyInCreation
,专门来存放正在创建的单例bean的名字(注意,只是名字而不是bean,因为bean还在创建中)。 - 一个单例bean在创建前,先往singletonsCurrentlyInCreation存自己的name,其他bean在创建时,会先来这里确认有无同名bean
- DefaultSingletonBeanRegistry搞了一个
- 最重要的三个成员变量:
-
如何存取?
-
但是,DefaultSingletonBeanRegistry没有getBean()方法,因为它压根就没实现BeanFactory!!
-
但是,AnnotationConfigApplicationContext#getBean()方法,经过层层调用最终还是找到了DefaultSingletonBeanRegistry的getSingleton()。
-
实际上,getBean()方法获取到单例并非是通过继承的方式,而是通过聚合的方式。
-
具体的是在GenericApplicationContext中,一个DefaultListableBeanFactory字段。
-
-
AnnotationConfigApplicationContext#getBean()整个过程应该是这样的:
-
DefaultListableBeanFactory作为中介,继承了DefaultSingletonBeanRegistry得以访问单例池,实现了BeanFactory对外提供getBean方法。
-
DefaultListableBeanFactory是ApplicationContext与SingletonBeanRegistry的中介:
-
-
ApplicationContext与BeanFactory:
- 除了BeanFactory,ApplicationContext还继承了很多乱七八糟的接口来扩展自己的功能,比如:
- ResourcePatternResolver接口,继承自ResourceLoader,我们常说的包扫描就是它负责的
- ApplicationEventPublisher接口,关系着Spring的监听机制
- EnvironmentCapable接口,提供了获取环境变量的方法。环境变量意味着什么?关系着@PropertySource、@Value、@Profile等注解
- 官方文档对ApplicationContext的解释:
- The
org.springframework.beans
和org.springframework.context
包是Spring Framework的IoC容器的基础。BeanFactory接口提供了能够管理任何类型对象的高级配置机制。 - ApplicationContext是BeanFactory的子接口,扩展了以下几点:
- 更容易与Spring的AOP特性集成
- 消息资源处理(用于国际化)
- 事件发布
- 应用程序层特定的上下文,如web应用程序中使用的WebApplicationContext。
- The
- 简而言之,BeanFactory提供了配置框架和基本功能,而ApplicationContext添加了更多企业特定的功能。ApplicationContext是BeanFactory的一个完整超集。
- 除了BeanFactory,ApplicationContext还继承了很多乱七八糟的接口来扩展自己的功能,比如:
3、AOP
-
掌握AOP所需的重要概念:
- 通知方法
- 目标对象
- 切面
- 切点表达式
-
对于动态代理而言,通知方法就是增强部分(比如打印日志),目标对象就是执行被代理对象的方法。
-
单纯的动态代理并没有所谓“切面”和“切点表达式”的概念,这两个概念是Spring AOP对动态代理的扩展。
- 所谓的切面,其实就是把所有通知方法集中起来,都放在一个类中,这个类叫做切面类。
- 所谓的切入点表达式,其实就是制定目标对象和通知方法之间的对应规则。即,那些被代理对象需要那些增强,进而让Spring完成自动代理。
- 通过切入点表达式,使得获得代理从硬编码方式改为了表达式方式。
- 总而言之,切面类是为了统一管理通知方法,切点表达式定制哪些对象需要代理,以及如何代理(用哪个通知方法增强)。
iwehdio的博客园:https://www.cnblogs.com/iwehdio/