AOP
定义
AOP 一般称为面向切面编程,用于将哪些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块(即切面),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。
 	AOP 主要应用于日志记录,性能统计,安全控制,事务处理等方面,实现公共功能性的重复使用
核心概念
先理解下 AOP 的核心概念
- 切点(Join Point) 
- 切面(Aspect) 
- 通知(Advice) - 前置通知(Before Advice):在方法执行之前执行的通知 
- 后置通知(After Advice):在方法执行之后执行的通知,不管方法是否发生异常 
- 返回通知(After Returning Advice):在方法正常返回后执行的通知 
- 异常通知(After Throwing Advice):在方法抛出异常时执行的通知 
- 环绕通知(Around Advice): - 在方法执行的前后都执行的通知,允许在方法调用之前和之后都添加自定义逻辑 
- 这种通知最为灵活,可以完全控制目标方法的执行流程 
 
 
- 目标对象(Target) 
- 代理(Proxy) - JDK 动态代理 
- CGLIB 代理 
 
- 织入(Weaving) - 编译时织入 
- 类加载时织入 
- 运行时织入 
 
AOP & Aspect
Spring AOP 和 Aspect 有什么区别
参考 Spring AOP 和 AspectJ 有什么区别?open in new window
核心原理
以注解开始 AOP 的代理为例说明
- 可以通过 @EnableAspectJAutoProxy 注解给 Spring 应用开启 AOP 代理。注意 proxyTargetClass 参与了后续是使用 JDK 代理还是 JVM 代理配置,详细逻辑见 DefaultAopProxyFactory 
- @EnableAspectJAutoProxy import 了 AspectJAutoProxyRegistrar。Spring 启动时会在 AbstractApplicationContext.refresh() 时通过 this.invokeBeanFactoryPostProcessors(beanFactory) 将 AspectJAutoProxyRegistrar 注入到容器中 
- AspectJAutoProxyRegistrar 的 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry) 最终注册了 AnnotationAwareAspectJAutoProxyCreator 对象到容器中 
- AnnotationAwareAspectJAutoProxyCreator 的父类中 AbstractAutoProxyCreator 有方法 getEarlyBeanReference 会触发创建代理对象 
- BeanPostProcessor.postProcessAfterInitialization 里面创建了动态代理 - addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))触发,对应源码- AbstractAutoProxyCreator.wrapIfNecessary的- createProxy代码块。详细在- Spring IOC 的加载原理小节中分析过,详细往上翻即可
事务
核心概念
Spring 事务是一系列动作组成,设计较为复杂,我们可以先理解一些概念或对象,对后续理解事务原理有一定帮助
- TransactionInfo。事务的信息详情、包含了一系列对象 - TransactionAttribute。事务属性。事务的传播机制、事务的回滚异常、隔离级别 
- TransactionManager。事务管理器,包含了数据源,如 DataSourceTransactionManager - DataSourceTransactionObject。和数据源关联的事务对象 - previousIsolationLevel。隔离级别 
- savepointAllowed。是否允许 savepoint 
- currentConnection。连接 
- transactionActive。事务是否活跃 
- mustRestoreAutoCommit。是否需要重置自动提交 
 
 
- TransactionStatus。事务状态的包装,如 DefaultTransactionStatus - rollbackOnly。是否只读事务 
- completed。是否完成 
- newTransaction。是否新开的事务 
- savepoint。底层数据库支持 
- transaction 
- newSynchronization。是否同步 
 
 
- 事务之保存点(savepoint)。通过文章理解 事务之保存点(savepoint)open in new window 
- 事务挂起。通过文章理解 MySQL Spring 框架下事务的挂起工作原理简述open in new window 
拦截 & 增强
Spring 事务是如何拦截、增强的
Spring 事务有声明式事务和编程式事务处理,这里用 Spring 5.2.18.RELEASE 源码版本分析如下
- 通过注解 @EnableTransactionManagement 开启事务。mode 配置默认为 proxy,代表是会进行后处理增强。该注解主要是用来拦截符合规则的业务类,并进行增强 
- @EnableTransactionManagement 会 import TransactionManagementConfigurationSelector 类 
- Selector 导入以下配置类。Spring 启动时 AbstractApplicationContext.refresh() 的 this.invokeBeanFactoryPostProcessors(beanFactory) 会执行 TransactionManagementConfigurationSelector 类的 selectImports 方法 - AutoProxyRegistrar。主要是给 Spring 容器注册一个代理(创建代理对象的代理)的后置处理器 - 注册 InfrastructureAdvisorAutoProxyCreator 为 internalAutoProxyCreator. 跟踪会找到 AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry),里面有这个逻辑 
- InfrastructureAdvisorAutoProxyCreator 的父类 AbstractAutoProxyCreator.getEarlyBeanReference(),这里复用了 Spring aop 代理那块逻辑。详细往上翻 
 
- ProxyTransactionManagementConfiguration。导入了 Spring 事务的 3 个核心对象 - BeanFactoryTransactionAttributeSourceAdvisor。事务的主要切面(切面的代理) 
- TransactionAttributeSource。规则器,切点过滤 advisor 时用到 
- TransactionInterceptor。即 getAdvice(),其实就是 TransactionInterceptor 
 
 
其中 BeanFactoryTransactionAttributeSourceAdvisor 是核心切面类,有 TransactionAttributeSource、TransactionInterceptor 两个参数
主要关心以下逻辑,详细内容可参考源码分析小节 10 事务 理解:
- 拦截符合业务规则的类(切点逻辑处理)。对应代码为 AbstractAutoProxyCreator.wrapIfNecessary 的 getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null) 
- 给其 Bean 创建代理对象(加了 @Transaction 注解方法所在的类)。对应代码为 createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)) 
- 增强器为 TransactionInterceptor - 当为 JdkDynamicAopProxy 时,JdkDynamicAopProxy.invoke 的 advised 为 BeanFactoryTransactionAttributeSourceAdvisor 类,事务增强同 CglibAopProxy 
- 当为 CglibAopProxy 时,CglibAopProxy.intercept 通过 CglibMethodInvocation.proceed() 为类实现动态代理处理,实际对应 ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this) 即 TransactionInterceptor.invoke() 的处理进行事务增强 
 
传播机制与检查点
Spring 事务的传播机制与检查点源码分析
事务传播机制处理源码如下,概念介绍可参考文章理解 Spring 事务传播行为详解open in new window
- AbstractPlatformTransactionManager.getTransaction - PROPAGATION_MANDATORY:如果当前无事务,则报错 
- PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED:如果当前无事务,则在需要事务,新事务、嵌套事务隔离时,新启动一个事务执行,如果有老的事务,老的事务会被挂起 
 
- AbstractPlatformTransactionManager.handleExistingTransaction - PROPAGATION_NEVER:有事务,报错 
- PROPAGATION_NOT_SUPPORTED:有事务,会挂起事务,并在无事务环境下执行业务逻辑 
- PROPAGATION_REQUIRES_NEW:有事务,挂起当前事务,使用新事务 
- PROPAGATION_NESTED:有事务,分布判断作如下处理 - nestedTransactionAllowed 为 false:即不支持嵌套事务,报错 
- useSavepointForNestedTransaction 为 true: 复用当前事务,使用保存点 
- useSavepointForNestedTransaction 为 false:有些不支持保存点,如 JTA 事务,此时使用新事务 
 
 
关于挂起事务和恢复事务,相关代码如下
// AbstractPlatformTransactionManager
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {
  private TransactionStatus handleExistingTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled) throws TransactionException {
    SuspendedResourcesHolder suspendedResources = this.suspend(transaction);
    try {
        return this.startTransaction(definition, transaction, debugEnabled, suspendedResources);
    } catch (Error | RuntimeException beginEx) {
        this.resumeAfterBeginException(transaction, suspendedResources, beginEx);
        throw beginEx;
    }
  }
  protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
        List<TransactionSynchronization> suspendedSynchronizations = this.doSuspendSynchronization();
        try {
            Object suspendedResources = null;
            if (transaction != null) {
              // ThreadLocal 解绑 
              suspendedResources = this.doSuspend(transaction);
            }
            String name = TransactionSynchronizationManager.getCurrentTransactionName();
            TransactionSynchronizationManager.setCurrentTransactionName((String)null);
            boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
            TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
            Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
            TransactionSynchronizationManager.setCurrentTransactionIsolationLevel((Integer)null);
            boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
            TransactionSynchronizationManager.setActualTransactionActive(false);
            return new SuspendedResourcesHolder(suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
        } catch (Error | RuntimeException ex) {
            this.doResumeSynchronization(suspendedSynchronizations);
            throw ex;
        }
    } else if (transaction != null) {
        Object suspendedResources = this.doSuspend(transaction);
        return new SuspendedResourcesHolder(suspendedResources);
    } else {
        return null;
    }
  }
  private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
      boolean newSynchronization = this.getTransactionSynchronization() != 2;
      // suspendedResources 保存下来,当前事务提交后,会重置使用它
      DefaultTransactionStatus status = this.newTransactionStatus(definition, transaction, true,
       newSynchronization, debugEnabled, suspendedResources);
      this.doBegin(transaction, definition);
      this.prepareSynchronization(status, definition);
      return status;
  }
}
// DataSourceTransactionManager
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager implements ResourceTransactionManager, InitializingBean {
  protected Object doSuspend(Object transaction) {
      DataSourceTransactionObject txObject = (DataSourceTransactionObject)transaction;
      txObject.setConnectionHolder((ConnectionHolder)null);
      return TransactionSynchronizationManager.unbindResource(this.obtainDataSource());
  }
}
// TransactionSynchronizationManager
public abstract class TransactionSynchronizationManager {
  public static Object unbindResource(Object key) throws IllegalStateException {
      Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
      Object value = doUnbindResource(actualKey);
      if (value == null) {
          throw new IllegalStateException("No value for key [" + actualKey + "] bound to thread");
      } else {
          return value;
      }
  }
}
// AbstractPlatformTransactionManager,执行后,挂起点设置回来
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {
  private void processCommit(DefaultTransactionStatus status) throws TransactionException {
    try {
    } finally {
        this.cleanupAfterCompletion(status);
    }
  }
  private void cleanupAfterCompletion(DefaultTransactionStatus status) {
      status.setCompleted();
      if (status.isNewSynchronization()) {
          TransactionSynchronizationManager.clear();
      }
      if (status.isNewTransaction()) {
          this.doCleanupAfterCompletion(status.getTransaction());
      }
      if (status.getSuspendedResources() != null) {
          if (status.isDebug()) {
              this.logger.debug("Resuming suspended transaction after completion of inner transaction");
          }
          // 挂起点恢复
          Object transaction = status.hasTransaction() ? status.getTransaction() : null;
          this.resume(transaction, (SuspendedResourcesHolder)status.getSuspendedResources());
      }
    }
}
TransactionSynchronizationManager
public abstract class TransactionSynchronizationManager {
    // key 为 datasource,value 为 ConnectionHolder
    // Why Map? 如一个线程同时连 Mysql、Oracle 等场景。
    private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");
}
事务嵌套回滚测试
对比上述逻辑分析,已经很好理解结论了
@Service
public class CityServiceImpl implements CityService {
    @Autowired
    private CityMapper cityMapper;
    @Transactional
    @Override
    public void insertCity() {
        City city = new City();
        city.setName("长沙1");
        city.setAge(200);
        cityMapper.insertCity(city);
        SpringUtil.getBean(CityService.class).insertCity2();
        // 此时测试,insertCity2 执行未回滚,但是事务1回滚了
        throw new RuntimeException("xxx");
    }
    /**
     * 模拟单线程有2个不同的事务
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void insertCity2() {
        City city = new City();
        city.setName("长沙2");
        city.setAge(200);
        cityMapper.insertCity(city);
    }
}
多线程事务
Spring 多线程事务,能否保证事务的一致性
- Spring 的事务信息是存在 ThreadLocal 中的,所以一个线程永远只能由一个事务。 
- 所以 Spring 的事务是无法实现事务一致性的。 
- 可以通过编程式事务,或者通过分布式事务的思路:二阶段提交方式。 
还有一些事务在多线程下的处理,可以关注下面文章。
事务失效
Spring 事务失效的场景
参考 Spring 事务失效的 12 种场景open in new window。
	补充,Spring 若未正确配置 Configuration 也会导致事务失效(类的 datasource() 实例不唯一,导致事务的 JdbcTemplate 不唯一导致)
Spring MVC
初始化原理
Spring MVC 示例详见 sample-spring-mvc-demoopen in new window 代码
跟着代码解析分析原理如下:
- web.xml 配置了 DispatcherServlet 类,其中有个参数设置了 contextConfigLocation - Tomcat 容器启动时会调用 DispatcherServlet 父类 HttpServletBean.init() 方法 
- 初始化和创建 WebApplicationContext 方法 - FrameworkServlet.initServletBean 
- FrameworkServlet.initWebApplicationContext 
- FrameworkServlet.createWebApplicationContext 
- 设置 ConfigLocation 配置,即 wac.setConfigLocation(configLocation) 
- 添加 Spring MVC ContextRefreshListener 事件。wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); 
- 创建好 wac 后,会触发刷新方法,即 AbstractApplicationContext.refresh() 
 
- DispatcherServlet.onRefresh() 前置处理 - AbstractApplicationContext.finishRefresh() 
- SimpleApplicationEventMulticaster.multicastEvent() 
- 进入 DispatcherServlet.onRefresh() 方法。其从 ContextRefreshListener.onApplicationEvent() 事件监听进来 
 
- DispatcherServlet.onRefresh(),即主要的处理逻辑 - 初始化 MultipartResolver 
- 初始化 LocaleResolver 
- 初始化 ThemeResolver 
- 初始化 HandlerMappings 
- 初始化 HandlerAdapters 
- 初始化 HandlerExceptionResolvers 
- 初始化 RequestToViewNameTranslator 
- 初始化 initViewResolvers 
- 初始化 initFlashMapManager 
 
 
至此,Spring 初始化逻辑分析差不多了,再结合工作原理描述下就可以了
工作原理
- 客户端发起请求(http)通过 web.xml 找到 DispatchServlet(前端控制器) 
- 由 DispatchServlet 控制器通过配置文件(servletName-servlet.xml)寻找到一个或多个 HandlerMapping(映射处理器),找到用于处理请求的 Controller(后端控制器),也有可能是其他处理器,我们用 Controller 处理器说明流程 
- DispatchServlet 将请求提交到 Controller 
- Controller 处理业务逻辑后 
- Controller 返回数据 ModelAndVIew 给 DispatchServlet 
- DispatchServlet 寻找到一个或多个 ViewResolver(视图解析器),找到 ModelAndVIew 指定的视图 
- DispatchServlet 负责将结果返给 View(客户端 JSP 页面),封装 Http 
- view 响应页面的 HTTP 请求,返回响应数据,浏览器绘制页面 
参考 SpringMVC 工作原理open in new window
问题
为啥加了 Async 注解后,会导致循环依赖解决不了?
如果要解决,怎么处理?
示例 DEMO 见 SpringCircularCtxTestopen in new window
通过 DEBUG 源码如下,找到报错代码
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
		implements AutowireCapableBeanFactory {
  public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
      // DEBUG 条件 processor.getClass().getName().indexOf("AsyncAnnotationBeanPostProcessor") >= 0
      Object current = processor.postProcessAfterInitialization(result, beanName);
      if (current == null) {
        return result;
      }
      result = current;
    }
    return result;
  }
  protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
    // Initialize the bean instance.
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper);
      // exposedObject 经过处理变为了代理对象
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}
		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
        // 重点:会进入到这里,代理对象和当前对象不一致,会报错
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}
  }
}
问题的核心点在于 AsyncAnnotationBeanPostProcessors 没有实现 SmartInstantiationAwareBeanPostProcessor
导致 B 在获取 A 的早期对象的时候不会获取到 A 的代理对象,而是获取到 A 的原始对象,因此报错了
解决方法是通过懒加载 @Lazy 的方式处理,不过示例代码中懒加载默认不支持,因此先忽略示例代码
详细理解可以参考博客 Spring 使用 @Async 出现循环依赖原因以及解决方案open in new window
 
             
           
             
                         
             
            
评论区