动静分离网站架构,网页制造工具,wordpress 嵌入 插件,wordpress登录页面空白文章目录 核心原理AnnotationConfigApplicationContextIoC 容器加载流程Spring 中如何创建一个对象Bean 的创建过程 (生命周期)单例 原型推断构造方法依赖注入AOP 动态代理判断是否需要 AOP 的大致流程CGLib 做 AOP 的大致流程事务事务代理对象执行方法的流程事务注解排至失效的… 文章目录 核心原理AnnotationConfigApplicationContextIoC 容器加载流程Spring 中如何创建一个对象Bean 的创建过程 (生命周期)单例 原型推断构造方法依赖注入AOP 动态代理判断是否需要 AOP 的大致流程CGLib 做 AOP 的大致流程事务事务代理对象执行方法的流程事务注解排至失效的原因为何下方加了 Configuration 事务才能生效 核心概念BeanDefinitionBeanDefinitionReaderAnnotatedBeanDefinitionReaderXmlBeanDefinitionReaderClassPathBeanDefinitionScanner BeanFactoryDefaultListableBeanFactoryApplicationContextAnnotationConfigApplicationContextClassPathXmlApplicationContext国际化 MessageSource 核心原理
AnnotationConfigApplicationContext
ClassPathXmlApplicationContext context new ClassPathXmlApplicationContext(spring.xml);
UserService userService (UserService) context.getBean(userService);
userService.test();AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService (UserService) context.getBean(userService);
userService.test();我们很少按照上面的方式使用 Spring, 而是使用 Spring MVC, 或者 Spring Boot, 但是它们本质上都是基于这种方式的, 都需要在内部去创建一个 ApplicationContext
SpringBoot 创建的是 AnnotationConfigApplicationContextSpringMVC 创建的是 XmlWebApplicationContext, 和 ClassPathXmlApplicationContext 类似都是基于 xml 的
AnnotationConfigApplicationContext 是研究学习的主类
IoC 容器加载流程
IoC容器加载流程可以分成两个步骤
准备, 注册一些底层基础架构工具类, 在容器初始化流程中使用, 然后注册配置类, 然后刷新容器扫描, 将配置的各种 Bean 解析成为 BeanDefinition, 存入 BeanDefinitionMap遍历 BeanDefinitionMap, 生产单例, 并缓存到单例池 singletonObjects
Spring 中如何创建一个对象
new AnnotationConfigApplicationContext(Config.class) 的过程就是 IoC 容器的加载流程, 扫描生成 BeanDefinition, 遍历生成单例 Bean. 最后人为调用 getBean(beanName) 去缓存中拿到对应的 Bean
Bean 的创建过程 (生命周期)
class - 反射newInstance - 原始对象 - 依赖注入(属性赋值) - 一堆Aware - 初始化前(PostConstruct) - 初始化(InitailizedBean) - 初始化后(AOP) - 代理对象(代理对象.target原始对象) - Bean
实例化, 通过反射调用类的某个构造方法创建对象, 如果有多个构造方法, 会进行选择, 叫做推断构造方法依赖注入(属性赋值), 遍历对象内的字段, 有 Autowired 的自动赋值Aware 织入, 判断对象是否是(即类是否实现了) BeanNameAware, ApplicationContextAware 等各种 Aware, 是的话就强转调用结构定义的 set 方法PostConstruct 处理, 判断是否有方法有该注解, 有的话则执行该方法InitializingBean 处理, 判断对象是否是该接口的实例, 是的话则执行接口定义的 afterPropertiesSet 方法自定义的 init-method 方法判断是否需要 AOP, 不需要时返回当前对象, 需要时则生成代理对象, 并返回如果是单例 Bean 则存入单例池
单例 原型
Bean 的作用域分为单例和原型两种
单例: 每次 getBean 拿到的都是同一个对象, 首次创建后就会缓存到单例池原型: 每次 getBean 都会重新创建一次, 不会放到单例池
懒加载的 Bean 在 IoC 容器加载时不会被创建和缓存, 在使用时才会创建和缓存
推断构造方法
实例化时默认使用无参构造器(写了有参默认就没有无参了, 除非显式定义)
有无参构造器就使用无参构造器, 没无参构造器则判断有几个有参构造器, 只有一个的话就使用这一个有参构造器, 有多个的话因为不能确认使用哪个, 所以报错找不到默认无参构造器
有多个有参构造器的话, 也可以加 Autowired 来指定使用哪个有参构造器
有参构造器的参数对象哪里来?
先根据类型到容器中找, 如果只有一个则直接使用, 如果有多个则根据名称来过滤, 如果又匹配名称的则直接使用, 如果没有匹配的, 则报错
依赖注入
先按类型到容器中过滤, 匹配的如果只有一个则直接使用, 多个则再按名称匹配, 有匹配到的直接使用, 没有则报错
AOP 动态代理
在创建 Bean 的最后一步, 会判断是否需要做 AOP, 需要则做动态代理
判断是否需要 AOP 的大致流程
找出所有切面 Bean遍历其中每个方法, 判断是否写了 Before, After 等注解如果写了, 则判断其所对应的 PointCut 和当前创建的 Bean 是否匹配如果匹配, 则说明当前 Bean 需要做 AOP
CGLib 做 AOP 的大致流程
生成继承原类的代理类, 代理类持有原类对象 target. 注意, 原类对象是经过 Bean 创建流程的, 包括依赖注入, 初始化等流程代理类中覆盖原类中的方法, 最终会调用到 target 的对应方法, 但是会在前后把切面逻辑都加上, 大致如下最后创建 Bean 返回的是持有原始对象的代理对象
public class UserService {Autowiredprivate OrderService orderService;public void test() {sout;}
}class UserServiceProxy extend UserService {UserService target;public void test() {// Before 的逻辑target.test();// After 的逻辑}
}代理类继承自原始类, 所以原始类的字段在代理类中也有, 但是 Spring 并不会为代理类做依赖注入, 因为没有必要
代理对象仅仅用于强化原始对象的某方法, 如果在切面逻辑中需要用到原始对象的依赖注入的字段, 也可以通过 JoinPoint.getTarget() 拿到原始对象来操作, 而原始对象中各字段已经做过依赖注入了
事务
某方法添加了 Transactional 注解后, 会在 AOP 阶段给本类生成代理类和代理对象
事务代理对象执行方法的流程
判断当前方法上有没有 Transactional 注解有的话由事务管理器创建一个数据库连接 (此连接不是来自连接池?)设置连接的 autoCommit 为 false (无事务注解时, 连接是从连接池获取, 每执行一条 SQL 自动提交一次)执行方法, 执行方法中的 SQL调用连接的提交或回滚方法
事务注解排至失效的原因
事务是否会失效, 就看执行 Transactional 注解方法的是哪个对象
代理对象: 不会失效原始对象: 失效, 因为执行的就是普通方法, 没有代理对象做的强化了
最常见的例子, 类中有两个事务方法 a 和 b, 而 a 中会调用 b, 单独执行 a 和 b 事务都不会失效, 但是在 a 中执行 b 时, b 的事务注解上的配置会失效
因为执行 a 的流程是这样的, 拿到类的代理对象, 执行其 a, 先走切面逻辑, 创建连接, 设置不自动提交, 然后才执行 target.a 即原始对象的 a 方法, 此时的主体是原始对象而非代理对象. 执行到 b 方法时, 本质是 this.b, 主体还是原始对象, 并没有切面逻辑, 所以在 a 里面的 b 方法的事务注解配置都会失效
当然还有很多其他原因, 需要具体分析
其他的 AOP 失效很多也是一样的原因, 都是自调用导致的
有解决办法, 就是自引用, 类中依赖注入自身 self, 此时的 self 是代理对象, 在 a 中调用 b 的时候, 用 self.b, 这样主体是代理对象, 有切面强化逻辑, b 的事务配置就会生效了
为何下方加了 Configuration 事务才能生效
Configuration
public class Config {Beanpublic TransactionManager transationManager() {return new DataSourceTransactionManager(dataSource());}Beanpublic JdbcTemplate jdbcTemplate() {return new JdbcTemplate(dataSource())}Beanpublic DataSource dataSource() {return new DataSource();}
}不加时, 调用两次 dataSource() 生成两个不同的数据源, 最终事务管理器和模板使用了不同的数据源加时, 会有特殊的处理, dataSource() 会被认为是一个 Bean, 传入两者的是同一个对象
在 JdbcTemplate 中获取连接时, 会检查当前是否为事务环境, 是的话会从 TransactionSynchronizationManager.getResource(dataSource); 中获取线程绑定的连接, 即事务管理器创建的那个连接, 需要使用同一个数据源对象才能拿到同一个连接, 这样事务管理器的提交和回滚操作才会对 JdbcTemplate 生效
核心概念
Spring 源码里有很多抽象和工具, 需要提前有一定了解, 读源码时能轻松一些
BeanDefinition
BeanDefinition 用来记录 Bean 配置的各种信息
class表示Bean类型scope表示Bean作用域单例或原型等lazyInit表示Bean是否是懒加载initMethodName表示Bean初始化时要执行的方法destroyMethodName表示Bean销毁时要执行的方法还有很多…
定义 Bean 的方式有申明式和编程式两种, 通过各种方式定义的 Bean 最终都会被解析为 BeanDefinition 并缓存起来
申明式: bean/BeanComponent(Service,Controller) 编程式AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class);// 生成一个BeanDefinition对象并设置beanClass为User.class并注册到ApplicationContext中
AbstractBeanDefinition beanDefinition BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(User.class);
context.registerBeanDefinition(user, beanDefinition);System.out.println(context.getBean(user));BeanDefinitionReader
用于根据某些规则将资源解析成为 BeanDefinition
AnnotatedBeanDefinitionReader
可以将某个类解析成为 BeanDefinition, 包括类上的注解(ConditionalScope、Lazy、Primary、DependsOn、Role、Description)
AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class);AnnotatedBeanDefinitionReader reader new AnnotatedBeanDefinitionReader(context);// 将User.class解析为BeanDefinition
reader.register(User.class);System.out.println(context.getBean(user));XmlBeanDefinitionReader
可以解析 bean/ 标签配置的 Bean
AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class);XmlBeanDefinitionReader reader new XmlBeanDefinitionReader(context);
int i reader.loadBeanDefinitions(spring.xml);System.out.println(context.getBean(user));ClassPathBeanDefinitionScanner
扫描器, 但是它的作用和 BeanDefinitionReader 类似, 它可以进行扫描, 扫描某个包路径, 对扫描到的类进行解析
如果扫描到的类上存在 Component 注解, 那么就会把这个类解析为一个 BeanDefinition
AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext();
context.refresh();ClassPathBeanDefinitionScanner scanner new ClassPathBeanDefinitionScanner(context);
scanner.scan(com.coder);System.out.println(context.getBean(userService));BeanFactory
Spring 容器的根接口, Bean 工厂, 负责创建 Bean 和获取 Bean, 提供各种 getBean() 方法的定义
DefaultListableBeanFactory
BeanFactory 有一个最核心的实现类 DefaultListableBeanFactory, 可以直接当作 BeanFactory 来使用, 可以替代 ApplicationContext 来使用, 就是功能会少一点而已
DefaultListableBeanFactory beanFactory new DefaultListableBeanFactory();AbstractBeanDefinition beanDefinition BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(User.class);beanFactory.registerBeanDefinition(user, beanDefinition);System.out.println(beanFactory.getBean(user));DefaultListableBeanFactory 的架构体系如上, 有很多接口(能力)和类
AliasRegistry: 支持别名功能, 定义了 alias 的注册/获取/判断/移除等功能, 在这里支持一个 BeanDefinition / Bean 有多个名称的功能SimpleAliasRegistry, 维护着 MapString, String aliasMap, 在这里是 alias 与 beanName 的多对一关系, 便于从别名找到原名(别名也能起别名), 当然也可以从原名找到所有别名BeanDefinitionRegistry: 定义了 BeanDefinition 的注册/获取/存在/移除等功能SingletonBeanRegistry: 定义了单例 Bean 的注册/存在/获取/获取数量/获取名称等功能BeanFactory: 定义了 Bean 的获取/存在/获取别名/判断作用域等功能ListableBeanFactory: 扩展了 BeanFactory, 提供了方法用于枚举和检索容器中的 bean 实例, 可以方便地获取所有 bean 的名称、按类型获取 bean、按条件检索 bean 等操作HierarchicalBeanFactory: 扩展了 BeanFactory, 提供了层次化的容器结构和继承机制, 每个子容器可以独立管理自己的 bean 定义和实例。子容器可以继承父容器中定义的 bean并可以在子容器中覆盖或扩展父容器中的 bean 定义。可以实现更好的模块化和组织化并灵活管理和定制 bean 的定义和作用域AutowireCapableBeanFactory: 扩展了 BeanFactory, 提供了自动装配 bean 的能力, 可以实现依赖注入、自动装配和解耦等功能ConfigurableBeanFactory, 除了 BeanFactory Bean 之外还提供用于配置 BeanFactory 的工具. 添加了设置父BeanFactory、类加载器表示可以指定某个类加载器进行类的加载、设置Spring EL表达式解析器表示该BeanFactory可以解析EL表达式、设置类型转化服务表示该BeanFactory可以进行类型转化、可以添加BeanPostProcessor表示该BeanFactory支持Bean的后置处理器可以合并BeanDefinition可以销毁某个Bean等等功能ConfigurableListableBeanFactory: 除了 ConfigurableBeanFactory 之外, 它还提供了分析和修改 Bean 定义以及预实例化单例的工具DefaultSingletonBeanRegistry: 主要用于管理和维护单例 bean 的注册和获取, singletonObjects 在这里FactoryBeanRegistrySupport: 主要用于支持 FactoryBean 的注册和获取, factoryBeanObjectCache 在这里AbstractBeanFactory: 功能已经很全面了, 但是不能自动装配和获取 beanNames, beanPostProcessors 在这里AbstractAutowireCapableBeanFactory: 拥有了自动装配的功能DefaultListableBeanFactory: 是 Spring 容器的一个关键组件负责管理 BeanDefinition 的注册、合并和查找以及 Bean 的创建、装配和销毁。它提供了丰富的功能和灵活的配置选项是 Spring 应用程序中常用的 BeanFactory 实现之一, beanDefinitionMap 在这里
ApplicationContext
BeanFactory 有一个最核心的子接口 ApplicationContext, 其定义如下
public interface ApplicationContext
extends EnvironmentCapable,
ListableBeanFactory,
HierarchicalBeanFactory,
MessageSource,
ApplicationEventPublisher,
ResourcePatternResolverHierarchicalBeanFactory拥有获取父BeanFactory的功能ListableBeanFactory拥有获取beanNames的功能ResourcePatternResolver资源加载器可以一次性获取多个资源文件资源等等EnvironmentCapable可以获取运行时环境没有设置运行时环境功能ApplicationEventPublisher拥有广播事件的功能没有添加事件监听器的功能MessageSource拥有国际化功能
ApplicationContext 的定位是 Spring 的应用上下文, 负责管理和组织应用程序的各个部分. 从代码层面来说, ApplicationContext 是一个 BeanFactory, 从架构层面来说, ApplicationContext 是比 BeanFactory 更加高级的存在, 它统御 BeanFactory, EnvironmentCapable, MessageSource 等这些组件完成相应的功能, BeanFactory 只是它的一个零件而已
照着这个思路来看, GenericApplicationContext 不继承 DefaultListableBeanFactory 而是将之作为一个属性, 从 BeanFactory 继承来的功能全部委托其持有的 DefaultListableBeanFactory 来执行, 就是非常合理的事情了
ApplicationContext 接口继承了 ListableBeanFactory 和 HierarchicalBeanFactory, 但它的定位是一个高位 BeanFactory, 只是聚焦于 BeanFactory 一定程度的基础功能即可, 并不需要中低层更强大的更加细节的全部功能
ApplicationContext 有两个重要的实现类
AnnotationConfigApplicationContextClassPathXmlApplicationContext
AnnotationConfigApplicationContext Lifecycle: 定义启动/停止生命周期控制方法的通用接口ConfigurableApplicationContext: 增加了添加事件监听器、添加BeanFactoryPostProcessor、设置Environment获取ConfigurableListableBeanFactory等功能AbstractApplicationContext: 实现通用上下文功能, 大名鼎鼎的 refresh 方法就在这里了GenericApplicationContext: 通用的应用上下文具体实现类AnnotationConfigRegistry: 用于注释配置应用程序上下文, 可以单独注册某个类为 BeanDefinition可以处理该类上的 Configuration Bean 注解AnnotationConfigApplicationContext: 是一种方便且强大的应用上下文实现适用于注解驱动的开发方式。它通过扫描和处理注解实现了自动化的 Bean 注册和装配减少了繁琐的 XML 配置
ClassPathXmlApplicationContext 同样继承了 AbstractApplicationContext但是相对于AnnotationConfigApplicationContext 而言功能没有AnnotationConfigApplicationContext 强大比如不能注册 BeanDefinition
国际化 MessageSource