0%

Spring相关

⭐AOP

AOP就是按照一定的规则去匹配方法,然后在方法的执行前后添加一些额外的通用逻辑。

常用的场景有:日志、接口鉴权、异常处理、Spring中的事务也是通过AOP实现的。

  • 切面(Aspect):可以用@Aspect注解或者<aop:aspect>标签定义一个切面。
  • 切点(Pointcut):可以用来定义方法的匹配规则,当符合规则的方法被调用时,会触发对应的通知。
  • 通知(Advice):
    • 环绕通知(around):目标方法执行前和执行后都会触发。用@Around声明。
    • 前置通知(before):目标方法执行前触发。用@Before声明。
    • 后置通知(after):目标方法执行后触发。用@After声明。
    • 异常通知(exception):目标方法执行抛异常时触发。用@AfterThrowing声明。
    • 返回通知(return):目标方法返回时触发。用@AfterReturning声明。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 声明切面
@Aspect
@Component
public class LogUtil {

// 定义切点
@Pointcut("execution(public * com.ao.bing.demo.spring.aop..*.*(..))")
public void pctMethod() {
}

// 环绕通知
@Around(value = "pctMethod()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Object ret = pjp.proceed();
System.out.println("Around advice");
return ret;
}

// 前置通知
@Before("pctMethod()")
public void before() {
System.out.println("Before advice");
}

// 后置通知
@After(value = "pctMethod()")
public void after() {
System.out.println("After advice");
}

// 返回通知
@AfterReturning(value = "pctMethod()")
public void afterReturning() {
System.out.println("AfterReturning advice");
}

// 异常通知
@AfterThrowing(value = "pctMethod()")
public void afterThrowing() {
System.out.println("AfterThrowing advice");
}
}

⭐IOC

如果没有IOC,假设A依赖B,就需要自己手动创建B的实例,如果后面B又需要依赖C,这个时候就需要调整A的代码,让A再创建C的实例并注入给B,这样对象之间的耦合度就太高了。

1
2
3
4
5
public class A {
private B b = new B();
}
public class B {
}

如果之后B又需要依赖C,就需要调整A的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
public class A {
private B b = new B(new C());
}
public class B {
// 构造器注入
private final C c;
public B(C c) {
this.c = c;
}
}
public class C {

}

⭐Spring事务

⭐事务的隔离级别

TransactionDefinition接口中定义了五个表示隔离级别的常量。

  • TransactionDefinition.ISOLATION_DEFAULT:数据库默认的隔离级别(Mysql是RR,Oracle是RC)。
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:读未提交,可能会出现脏读。
  • TransactionDefinition.ISOLATION_READ_COMMITTED:读提交,可以解决脏读,会出现不可重复读。
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:可重复读,可以解决不可重复读,会出现幻读。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:串行化。

⭐事务的传播方式

  • TransactionDefinition.PROPAGATION_REQUIRED:(默认的隔离级别)如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

  • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

  • TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

    适用于对于一些不会单独执行,一旦被调用就必须包含事务的方法。

  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起,等新事物执行完之后,再恢复之前的事务继续执行。

  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起,方法执行完之后,再恢复之前的事务继续执行。

  • TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

  • TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则创建一个新事务。

    嵌套事务是指:子事务是父事务的一部分,父事务执行到子事务时,会创建一个回滚点,如果子事务回滚,父事务只会回滚到回滚点,也就是说,父事务之前的操作不会受到影响。

    如果父事务回滚,子事务也会一起回滚,因为子事务需要和父事务统一提交。

⭐事务失效的情况

  • @Transactional注解只能在public修饰的方法上才起作用。

    因为Spring是通过动态代理的方式实现AOP,private修饰的方法无法被代理到。

  • 如果数据库不支持事务,事务也会不起作用。

    比如MySQL的MyISAM引擎。

  • 默认情况下,如果抛出了非RuntimeException异常,事务不会回滚。

    可以在@Transactional注解中指定rollbackFor为特定的异常类。@Transactional(rollbackFor = Exception.class)。

  • 如果用try-catch捕获了异常,但是却没有手动回滚事物,事务不会回滚。

    在Spring的TransactionAspectSupport类中有个invokeWithTransaction()方法,这里面就是处理事物的逻辑,只有在捕获到异常之后才会进行后续的事物处理。

    可以在catch代码块中使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();手动提交事务。

  • 在同一个类中有a()和b()两个方法,在b()方法上加@Transactional注解,然后在a()方法中调用b()方法,这种情况b()方法中得事务不会生效。

    因为必须通过代理过的类从外部调用目标方法才能生效。可以在代码中注入一个this,然后通过this在调用b()方法。

⭐循环依赖

多个bean之间相互引用,比如A依赖B,B也依赖A。

出现循环引用的bean必须是单例的,多例模式(propotype)的bean是不支持循环依赖的,会抛异常。因为propotype多例模式每次都会创建一个新的bean,当创建A时,发现引用了B就会创建B,发现B引用了A,这时又会去创建A,这就套娃了。

Spring内部维护了三个Map,就是通常说的三级缓存。

1
2
3
4
5
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>;

private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>;

private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>;
  • singletonObjects:一级缓存,缓存的是创建完成的Bean。
  • earlySingletonObjects:二级缓存,缓存的是提前暴露出来的Bean,在这个Map里的Bean还未完成初始化,属于半成品。
  • singletonFactories:三级缓存,映射了创建Bean的原始工厂。

假设A和B互相依赖:

  1. 首先A会被创建出来,并将自己暴露到三级缓存中,然后进行初始化。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
    if (!this.singletonObjects.containsKey(beanName)) {
    this.singletonFactories.put(beanName, singletonFactory); // 将bean添加到三级缓存中
    this.earlySingletonObjects.remove(beanName);
    this.registeredSingletons.add(beanName);
    }
    }
    }
  2. 在初始化的过程中发现A依赖B,此时就会尝试从一级缓存中去获取B,然后发现B还没有被创建,这个时候就会触发B的创建。

  3. B在初始化的时候发现依赖A,就会尝试从一级缓存中获取A。这个时候A还没有初始化完成,所以一级缓存中肯定没有,然后尝试从二级缓存中获取,此时也没有,最后尝试从三级缓存中获取A的工厂类,然后调用其getObject()就可以获取到A。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName); // 1.从一级缓存中获取bean
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    synchronized (this.singletonObjects) {
    singletonObject = this.earlySingletonObjects.get(beanName); // 2.一级缓存如果没有,就从二级缓存中获取
    if (singletonObject == null && allowEarlyReference) {
    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); // 3.如果二级缓存中也没有,就从三级缓存中获取其工厂类
    if (singletonFactory != null) {
    singletonObject = singletonFactory.getObject(); // 4.调用其工厂类的getObject()方法获取bean
    this.earlySingletonObjects.put(beanName, singletonObject); // 5.将其加入到二级缓存
    this.singletonFactories.remove(beanName); // 6.从三级缓存中删除
    }
    }
    }
    }
    return singletonObject;
    }
  4. B拿到A对象的引用之后可以顺利的完成自己的初始化,并将自己加入到一级缓存中。

  5. 然后A得到B对象的引用之后也可以完成初始化,进入到一级缓存中。

⭐Bean生命周期

  1. 实例化Bean对象,设置Bean属性。
  2. 如果实现了各种Aware接口,则会对Bean注入一些其它信息。比如BeanNameAware会注入Bean ID、BeanFactoryAware会注入Bean Factory、ApplicationContextAware会注入ApplicationContext。
  3. 如果实现了BeanPostProcessor接口,则会调用postProcessBeforeInitialization()postProcessAfterInitialization()方法。
  4. 调用Bean自身定义的init方法。
  5. 最后依次调用DisposableBean的destroy()方法和Bean自身定制的destroy()方法。