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
评论区