Day11:Spring

目标

1
2
3
4
5
Spring
├── 为什么要有 Spring(★★★★★)
├── IOC(★★★★★)
├── Bean 生命周期(★★★★★)
├── AOP(★★★★★)

Bean 生命周期

1
2
3
4
5
1. 实例化(调用构造方法)
2. 依赖注入(@Autowired
3. 初始化(@PostConstruct、InitializingBean)
4. 使用
5. 销毁(@PreDestroy

AOP

AOP(面向切面编程)就是把日志、事务、权限等横切关注点从业务代码中抽离出来,统一增强目标方法。

1. 为什么Spring要IOC为什么比new好?

第一,降低耦合。对象不再依赖具体实现,而是依赖 Spring 容器管理。

第二,统一管理生命周期。Spring 可以统一管理 Bean 的创建、销毁、作用域(Singleton、Prototype 等)。

第三,方便扩展。例如以后 UserDao 换实现,或者增加代理、事务,都不用修改业务代码。

2. Bean生命周期五步是什么?

创建bean、注入依赖、初始化、使用、销毁

3. 为什么@PostConstruct里面@Autowired已经可以使用?

PostConstruct是在初始化之前,注入依赖之后。所以已经可以使用依赖的对象了。

构造方法里面不能保证@Autowired已经完成但是@PostConstruct一定可以。

4. 为什么AOP适合日志事务权限。为什么不适合业务?

aop适合通用的代码逻辑。具体的业务情况谁和谁都不一样。

5. 你的GIS平台如果不用AOP日志怎么写?用了AOP有什么好处?

如果不用aop就只能每个方法都手写一遍记录日志的代码。使用了aop后只需要写一次。

6. 为什么Spring默认Bean是Singleton为什么不是Prototype?

绝大部分 Bean 都是无状态的,比如service、dao,多个线程共享一个实例没有问题。
创建对象、GC会消耗性能,对象越多越占内存。
只有少数需要保存状态的对象才适合 Prototype。比如dto、entity。

7. 为什么SpringIOC底层大量使用反射为什么不用new UserService()

因为springIOC是在运行时创建的bean,Spring 在编译时根本不知道有哪些 Bean。
用new的话,要求编译时就知道类型。

8. 为什么@Autowired默认按类型(Type)注入而不是按名字(Name)?

原因一:面向接口编程
@Autowired可以注入接口或者抽象类。如果实现类只有一个,spring会把这个实现类注入进来。如果需要按名字注入可以用@Resource或者@Qualifier

原因二:名字容易变

@Autowired 默认按类型注入,是为了支持面向接口编程,降低实现类变更带来的影响。

9. 为什么Spring有时候可以解决循环依赖有时候又不能?

1
2
3
4
5
6
7
8
9
10
11
@Service
class AService {
@Autowired
BService b;
}

@Service
class BService {
@Autowired
AService a;
}

流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
创建A

A先new出来

A放进缓存(虽然还没初始化)

A需要B

开始创建B

B需要A

缓存里发现A已经new出来了

直接拿出来注入

B完成

继续完成A

Spring 能解决的是 Singleton Bean 的属性注入循环依赖。

那什么时候解决不了?

第一种:构造器注入
对象都创建不了

第二种:Prototype
每次都是new,没有三级缓存

Spring 能解决 Singleton Bean 的属性注入循环依赖,因为 Bean 实例化后会提前暴露到三级缓存,后续 Bean 可以先引用这个未完全初始化的对象;而构造器注入和 Prototype Bean 不支持这种机制,因此无法解决循环依赖。

Day12:事务

ACID

Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(持久性)

事务保证一组操作具有 ACID 特性,要么全部成功,要么全部失败。

1. 为什么Spring事务使用AOP为什么不用每个方法自己写commit?

事务属于横切逻辑。Spring利用aop已经完成了事务开启和事务提交回滚的通用代码。

事务属于横切关注点,每个业务方法都可能需要事务,因此 Spring 使用 AOP 将事务逻辑统一织入目标方法,避免每个方法重复编写 begin、commit、rollback 等代码。

2. 为什么自己调用this.save()事务失效?

因为 Spring 的事务是通过代理对象实现的。外部调用的是代理对象,所以事务能够生效;而 this.save() 是对象内部调用,没有经过代理对象,而是直接调用目标对象的方法,因此事务拦截器不会执行。

3. 为什么private 方法@Transactional失效?

JDK 动态代理是代理接口,接口里没有private方法。CGLIB代理的是子类,子类不能重写private方法。

private 方法不能被代理对象重写,因此事务增强无法织入。

4. REQUIRED和REQUIRES_NEW区别什么时候用REQUIRES_NEW?

REQUIRED是如果有事务就加入事务。没有就创建一个事务。
REQUIRES_NEW 会挂起当前事务,然后创建一个新事物。
当需要两个不同事物的情况下使用REQUIRES_NEW,比如业务代码和保存日志需要是两个事务,业务代码异常回滚,日志应该要保存下来。

流程:

1
2
3
4
5
6
7
8
9
A事务

暂停

B事务

提交

恢复A事务

5. 为什么Spring 默认只回滚 RuntimeException?为什么throw new IOException();事务默认不回滚?

非RuntimeException需要手动处理,spring就认为开发者已经知道这个异常,应该主动处理。RuntimeException可能是程序出现bug应该回滚。

Spring 默认认为 Checked Exception(受检异常)是开发者可以预期并处理的业务异常,因此默认不回滚;而 RuntimeException 通常表示程序发生了不可预期的错误,因此默认回滚事务。

6. 为什么下面代码事务最后提交了?

1
2
3
4
5
6
7
@Transactional
public void test(){
try{
throw new RuntimeException();
}catch(Exception e){
}
}

因为方法最终没有抛出RuntimeException。

Day13:Spring Boot

1. Spring Boot 和 Spring 有什么区别?

springboot基于spring,由于spring需要大量繁琐的配置,springboot简化了这一流程。他的约定大于配置。会根据依赖自动加载需要的bean。

Spring Boot 是 Spring 的快速开发框架。

2. @SpringBootApplication 为什么只要一个注解?它里面最重要的三个注解是什么?各自负责什么?

@SpringbootConfigurarion
标注配置启动类

@EnableAutoConfiguration
开启自动配置,根据依赖自动配置。

@ComponentScan
开启注解扫描。例如@Configuration @Componet @Service

3. 为什么导入:spring-boot-starter-data-redis RedisTemplate 就自动有了?

因为这个依赖里面有自动装配RedisTemplate的配置类。

4. 为什么Spring Boot不会把所有AutoConfiguration全部加载?

他会根据条件,比如@ConditionalOnClass、@ConditionalOnMissingBean等。

Spring Boot 会根据各种条件注解(如 @ConditionalOnClass、@ConditionalOnMissingBean 等)决定是否加载自动配置,而不是无条件全部加载。

5. 为什么spring-boot-starter-web叫Starter为什么不是spring-web?

Starter就是依赖管理。自动引入需要的全部依赖,本质就是依赖聚合。