Spring
全栈开源框架
一、解决的问题
1. IOC 解偶
透过 Spring 的 IOC 容器,将对象间的依赖关系交给 Spring 来控制,避免硬编码造成的程序耦合。
2. 简化 AOP 编程
透过 Spring 的 AOP 功能,让我们可以更方便地进行面向切面编程。
3. 声明式事务 @Transaction
可以通过注解方式进行事务控制。
4. 集成各种框架、API,降低使用难度
框架 : Struts、 Hibernate、MyBatis、 Hessian、Quartz
API : JDBCTemplate、 JavaMail、RestTemplate
二、核心思想
1. IOC Inversion of Control 控制反转
将 Java 对象的创建、管理的权力 交给第三方 (Spring 框架)
目的 : 解偶
原理 : 透过反射调用无参构造函数实例化对象并存到容器中。【没有无参构造将实例化失败】
2. DI Dependancy Injection 依赖注入
将被管理的 Java 对象赋值给某个属性
目的 : 解偶
原理 : 使用反射技术设置目标对象属性
3. AOP Aspect oriented Programming 面向切面编程
一种横向抽取技术,在特定的方法前、方法后,运行特定的逻辑
目的 : 减少重复代码
应用场景 : 事务控制、权限较验、日志纪录 、性能监控
三、特殊类
- BeanFactory - 容器类的顶层接口 - 基础规范
1. ApplictaionContext - 容器类的高级接口 - 国际化、资源访问( XML、配置类 )
1. ClassPathXmlApplicationContext - 项目根路径下加载配置文档
2. FileSystemXmlApplicationContext - 硬盘路径下加载配置文档
3. AnnotationConfigApplicationContext - 从注解配置类加载配置
- FactoryBean - 自定义复杂 Bean 的创建过程
- BeanFactoryPostProcessor - BeanFactory 初始化完成后,进行后置处理
- BeanPostProcessor - Bean 对象实例化、依赖注入后,进行 Bean 级别的后置处理
四、注解
1. IOC 类型
- @Component(“Bean的ID”) 【类上】 - 默认 ID 为类名首字母小写,等价以下三个注解
1. @Controller
2. @Service
3. @Repository
- @Scope(“Bean的生命周期”) 【类上】
1. singleton【默认】- 与容器生命周期相同
2. prototype - 每次获取都是新的
3. request - 一个HTTP请求范围内相同
4. session - Session 范围内相同
5. globalSession - portlet-based 应用范围内相同
- @PostConstruct 【方法上】- 初始化后调用
- @PreDestory 【方法上】- 销毁前调用
2. DI 类型
- @Autoweird - 依照类型注入
类型对应的对象非唯一时,可以搭配 @Qualifier(name=”Bean的ID”) 使用
2. @Resource(name=”Bean的ID”, type=类) - 默认依照ID注入,如果ID找不到则按类型注入
3. 配置类型
- @ComponentScan - 需要扫描的包路径
- @Configuration - 标明此类是配置类
- @PropertySource - 引入外部配置文档
- @Import - 加载其它配置类
- @Value - 将配置文档的数据赋值到属性上
- @Bean - 将方法返回的对象存入 IOC 容器中,对象ID为方法名,也可以手动指定
4. AOP 类型
- @Pointcut - 配置切入点
- @Before - 前置通知
- @AfterReturning - 后置通知
- @AfterThrowing - 异常通知
- @After - 最终通知
- @Around - 环绕通知
五、AOP
1. 术语
- Joinpoint 连接点 : 所有的方法
- PointCut 切入点 : 具体想要影响的方法
- Advice 通知/增强 : 横切逻辑
- Target 目标 : 要被代理的对象
- Proxy 代理 : 被 AOP 织入增强后的类
- Weaving 织入 : 把 Advice 应用到 Target 产生 Proxy 的过程
- Aspect 切面 : 切入点 + 增强
目的 : 为了锁定在哪个地方插入什么横切逻辑
1 | java复制代码@Component |
六、声明式事务 @Transaction
1. 四大特性
原子性 Atomicity : 操作要么都发生,要么都不发生
一致性 Consistency : 数据库从一个一致状态转换到另一个一致状态
隔离性 Isolation : 事务不能被其它的事务所干扰
持久性 Durability : 数据的改变是永久性的
2. 隔离级别
脏读 : 读取到另一个事务未提交的数据 - Read Committed 读已提交
不可重复读 : 读到另一个事务 update 的数据,两次读取到的数据 内容 不一样 - Repeatable Read 可重复读
幻读 : 读取到另一个事务 insert 或 delete 的数据,两次读取到的数据 数量 不一样 - Serializable 串行化
3. 传播行为
- REQUIRED【默认】 - 当前没有事务,就新建⼀个事务,如果已经存在⼀个事务,加入到这个事务中
- SUPPORTS - 支持当前事务,如果当前没有事务,就以非事务方式运行 - 查找
- MANDATORY - 使用当前的事务,如果当前没有事务,就抛出异常
- REQUIRES_NEW - 新建事务,如果当前存在事务,把当前事务挂起
- NOT_SUPPORTED - 以非事务方式运行操作,如果当前存在事务,就把当前事务挂起
- NEVER - 以非事务方式运行,如果当前存在事务,则抛出异常
1 | java复制代码@Transactional(readOnly = true, propagation = Propagation.SUPPORTS) |
4. 失效场景
- 应用在非 public 修饰的方法上
- 同一个类中方法调用
- 异常被 try catch 吃掉
- propagation 设置错误
- rollbackFor 设置错误
- 数据库引擎不支持事务
5. 原理
○ 透过 JDK 动态代理 与 Cglib 动态代理 实现,数据库事务归根结柢是 Connection 的事务
○ Connection 是从连接池(C3P0、Druid)拿来的,Connection 可以产生 preparedStatement,preparedStatement 可以运行 execute() 方法直接运行 SQL 语句
○ 在 JDK 1.8 的环境下,JDK 动态代理的性能已经优于 Cglib 动态代理,但缺点是使用 JDK 动态代理的被代理类需要至少实现一个接口
1. JDK 动态代理
被代理类至少需实现一个接口
1 | java复制代码public class JdkDynamicProxyTest implements InvocationHandler { |
2. Cglib 动态代理
通过字节码底层继承要代理类来实现
1 | java复制代码public class CglibProxyTest implements MethodInterceptor { |
测试
1 | java复制代码Target targetImpl = new TargetImpl(); |
七、Spring Bean 生命周期
- 反射调用无参构造实例化 Bean
- 使用反射设置属性值
- 如果 Bean 实现了 BeanNameAware 接口,则调用 setBeanName() 方法传入当前 Bean 的 ID 值
- 如果 Bean 实现了 BeanFactoryAware 接口,则调用 setBeanFactory() 方法传入当前工厂实例的引用
- 如果 Bean 实现了 ApplicationContextAware 接口,则调用 setApplictaionContext() 方法传入当前 ApplicationContext 实例的引用
- 如果 Bean 和 BeanPostProcessor 关联,则调用 postProcessBeforeInitialization() 方法,对 Bean 进行加工 - Spring 的 AOP 在此实现
- 如果 Bean 实现了 InitializingBean 接口,则调用 afterPropertiesSet() 方法
- 如果配置文档中指定了 init-method 属性,则调用该属性指定的方法
- 如果 Bean 和 BeanPostProcessor 关联,则调用 postProcessAfterInitialization() 方法 - 此时 Bean 已可在应用中使用
- 如果 Bean 的作用范围为 singleton,则将 Bean 放入 IOC 容器中;如果作用范围是 prototype,则将 Bean 交给调用者
- 如果 Bean 实现了 DisposableBean 接口,销毁 Bean 时将调用 destory() 方法 - 如果配置文档中设置 destory-method 属性,则调用该属性指定的方法
八、Spring Bean 循环依赖
流程
- SpringBean A 实例化,将自己放入 三级缓存 中,在实例化过程中,发现依赖 SpringBean B
- SpringBean B 实例化,将自己放入 三级缓存 中,在实例化过程中,发现依赖 SpringBean A
- SpringBean B 到 三级缓存 中获取尚未成形的 SpringBean A
- 将 SpringBean A 升级到 二级缓存,并进行一些扩展操作
- SpringBean B 创建完成后将自己放入 一级缓存
- SpringBean A 从 一级缓存 中获取 SpringBean B
无法处理场景
- 单例 Bean 构造器循环依赖
- 多例 Bean 循环依赖
本文转载自: 掘金