Spring - AOP/事务 实现原理

AOP 基本概念

  1. 官方文档: Aspect Oriented Programming with Spring

Spring AOP supports the following AspectJ pointcut designators (PCD) for use in pointcut expressions:

within - limits matching to join points within certain types (simply the execution of a method declared within a matching type when using Spring AOP):AOP学习之within

@within - limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP)

  1. What is the difference between Advisor and Aspect in AOP?

Most aspects are a combination of advice that defines the aspect’s behavior and a pointcut defining where the aspect should be executed.Spring recognizes this and offers advisors (such as PointcutAdvisor), which combine advice and pointcuts into one object.

public interface Advisor {
	Advice getAdvice();
}
public interface PointcutAdvisor extends Advisor {
	Pointcut getPointcut();
}

Advisor 是一个接口,实现该接口的类必须提供一个 Advice 实例。

  1. Pointcut vs Joinpoint
  • Difference between Joint Point and Point Cut

Join points are the options on the menu and pointcuts are the items you select. A joinpoint is an opportunity within code for you to apply an aspect…just an opportunity. Once you take that opportunity and select one or more joinpoints and apply an aspect to them, you’ve got a pointcut.

  • Spring AOP: What’s the difference between JoinPoint and PointCut?
    Joinpoints / Pointcut / Advice

AOP 使用

通过在 Spring 配置组件类上增加 @EnableAspectJAutoProxy 即可开启 AOP 的使用

  1. Enables support for handling components marked with AspectJ’s {@code @Aspect} annotation.
  2. boolean proxyTargetClass() default false; // Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to standard Java interface-based proxies.

注意:SpringBoot 通过 SPI 机制将 org.springframework.boot.autoconfigure.aop.AopAutoConfiguration 注入到 Spring 容器中,从而无需手动设置 @EnableAspectJAutoProxy 即自动支持 AOP 。原理为通过 AopAutoConfiguration 引入了 @EnableAspectJAutoProxy,从而开启了 AOP 功能,且默认使用 CGLib 代理

@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = false)
	// 在配置参数 spring.aop.proxy-target-class 值被明确设置为 false 时生效
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
			matchIfMissing = false)
	public static class JdkDynamicAutoProxyConfiguration {

	}

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = true)
	// 仅在属性 spring.aop.auto 【缺失】或者明确指定为 true 时生效
	// SpringBoot 默认使用 CGLib 实现 AOP
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
			matchIfMissing = true)
	public static class CglibAutoProxyConfiguration {

	}
}

AOP 原理

概述

Spring的Aop实现原理,Spring AOP 与 AspectJ 的关系 转载

Aop源码大概分为以下几步:

  1. spring boot 自动配置AopAutoConfiguration类中带有@EnableAspectJAutoProxy,项目启动即开启对spring AOP的支持,该注解注册了AnnotationAwareAspectJAutoProxyCreator 类,该类实现了bean的后置处理器,可以在类创建过程中做一些其他操作
  2. 在bean后置处理器的 postProcessBeforeInstantiation方法中,解析切面类,把通知封装成Advisor,并放入缓存advisorsCache中!
  3. 在创建每一个bean时,在bean的后置处理器中的postProcessAfterInitialization方法中,拿到缓存中所有的Advisor,根据切入点PointCut与当前bean做匹配,匹配成功与否决定是否需要创建动态代理!如果匹配到了,则根据实际情况(调用 AbstractAutoProxyCreator#wrapIfNecessary 方法)创建动态代理。
  4. 调用目标方法时,会调用经过动态代理增强的方法逻辑 !

AnnotationAwareAspectJAutoProxyCreator

@EnableAspectJAutoProxy 向 Spring 容器中注入了实现 AOP 的关键类AnnotationAwareAspectJAutoProxyCreator(名为 internalAutoProxyCreator 的 BeanDefinition 实例)

在这里插入图片描述

AnnotationAwareAspectJAutoProxyCreator 继承体系为:
AnnotationAwareAspectJAutoProxyCreator 继承关系

注意 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 是在 Bean 对象创建之前被调用用来尝试生成 Bean 实例的代理对象,而 BeanPostProcessor#postProcessBeforeInitialization 则是在 Bean 对象创建完成后,初始化前被执行。

AnnotationAwareAspectJAutoProxyCreator 则会在 Spring 刷新容器(执行 AbstractApplicationContext#refreshrefresh() 方法)时,通过

// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory); 

注入将到 Spring 后置处理器集合中(将 BeanDefinition 转换为具体的实例),其注入的核心代码为:

// 代码来自 PostProcessorRegistrationDelegate#registerBeanPostProcessors 方法
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);


// 代码来自:PostProcessorRegistrationDelegate#registerBeanPostProcessors 方法
for (BeanPostProcessor postProcessor : postProcessors) {
	beanFactory.addBeanPostProcessor(postProcessor);
}

生效流程

动态代理增强 Bean 功能的入口:InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 方法的调用:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean,该方法的调用发生在 Spring 容器刷新的最后一个阶段 finishBeanFactoryInitialization 中的 beanFactory.preInstantiateSingletons() 中的 AbstractBeanFactory#createBean

在这里插入图片描述
然后执行 AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation 遍历执行所有的 InstantiationAwareBeanPostProcessor (AOP 代理创建类 AnnotationAwareAspectJAutoProxyCreator 就是实现了该接口)实例

protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
	for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
		Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
		if (result != null) {
			return result;
		}
	}
	return null;
}

如果当前类没有经过 TargetSource 处理,则会继续执行AbstractAutoProxyCreator#postProcessAfterInitialization,最终通过 AbstractAutoProxyCreator#wrapIfNecessary,为被 AOP 拦截的 Bean 类生成代理对象。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
	if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
		return bean;
	}
	if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
		return bean;
	}
	if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

	// Create proxy if we have advice.
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
	if (specificInterceptors != DO_NOT_PROXY) {
		this.advisedBeans.put(cacheKey, Boolean.TRUE);
		// 核心方法:创建代理对象
		Object proxy = createProxy(
				bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
		this.proxyTypes.put(cacheKey, proxy.getClass());
		return proxy;
	}

	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return bean;
}
文章索引
  1. 视频讲解 - 结合 Spring 创建 Bean 流程梳理 AOP 实现原理
  2. Spring AOP的原理讲解以及源码分析

AOP 通知类型

通过 org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory

private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
		Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};

可以看出 Spring AOP 主要支持以下几种 AOP 类型(按执行顺序列出):

目标方法无异常时
①:前置通知
②:环绕通知的调用目标方法之前的代码
③:目标方法
④:环绕通知的调用目标方法之后的代码
⑤:返回通知
⑥:后置通知
在目标方法抛出异常的情况下
①:前置通知
②:环绕通知的调用目标方法之前的代码
③:目标方法(抛出异常)
④:异常通知
⑤:后置通知

@After 是如何实现的?

AspectJAfterAdvice#invoke

public Object invoke(MethodInvocation mi) throws Throwable {
	try {
		return mi.proceed();
	}
	finally {
		// 由于执行逻辑放到了 finally 块中,目标 bean 对象的方法被执行后,一定会执行该段代码
		invokeAdviceMethod(getJoinPointMatch(), null, null);
	}
}

AOP 核心组件

Pointcut:

public interface Pointcut {
	ClassFilter getClassFilter();
	MethodMatcher getMethodMatcher();
}

可以看出 Spring AOP 只能选择 class 或 method 作为切入点。

良好设计

Pointcut 之间支持逻辑运算,Spring 提供的工具类 MethodMatchers 与 ComposablePointcut 具有良好的设计体系,即抽象了逻辑运算过程,分别定义了 与运算类 和 或运算类,从而将复杂的逻辑运算变为两类对象的组合,这样在提高代码可读性(比如 IntersectionClassFilter 从名字上可以看出,其下的所有 classFilter 都满足时才为真)的同时,也加强了代码的复用性(不需要在每个需要实现 与运算 的类中再次定义一个集合,以及重复书写遍历集合的代码),值得思考和学习。

类层级体系(MethodMathcer)

MethodMathcer 相关类

可视化调用过程(ClassFilter)

调用栈可视化

Advice

其顶级标签接口为:Advice,其主要类层级结构为:
Advice 继承体系
可以看出 Spring 可以分为五种类型的增强:BeforeAdvice、AfterAdvice、Interceptor、DynamicIntroductionAdvice、AbstractAspectJAdvice

Interceptor

A generic interceptor can intercept runtime events that occur within a base program. Those events are materialized by (reified in) join points. Runtime joinpoints can be invocations, field access, exceptions… This interface is not used directly. Use the sub-interfaces to intercept specific events.

注意这里的 Interceptor 定义在 spring-aop 包中,spring-mvc 中也有一个 Interceptor 概念:HandlerInterceptor

MethodInterceptor

Intercepts calls on an interface on its way to the target. These are nested “on top” of the target. The user should implement the invoke(MethodInvocation) method to modify the original behavior.
MethodInterceptor

Aspect

Spring AOP 是借助 BeanPostProcessor 来实现的,其抽象过程封装在 org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor

  1. 定义 PointcutAdvisor(封装了 Pointcut 和 Advice)
  2. 通过 isEligible 判断(判断逻辑封装在 Pointcut 中),是否为当前 bean 执行增强代理
protected boolean isEligible(Class<?> targetClass) {
	Boolean eligible = this.eligibleBeans.get(targetClass);
	if (eligible != null) {
		return eligible;
	}
	if (this.advisor == null) {
		return false;
	}
	//
	eligible = AopUtils.canApply(this.advisor, targetClass);
	this.eligibleBeans.put(targetClass, eligible);
	return eligible;
}
  1. 构造 ProxyFactory 对象,选择 JdkDynamicAopProxy 或 ObjenesisCglibAopProxy 创建出代理对象

其它组件

TargetSource

在计算机世界,如果问题难以解决,可以想着多抽象出一层来解决该问题。

AOP 的抽象模板流程预留了一个提前创建代理对象的钩子 TargetSource,其代码如下:

// 代码来自:AbstractAutoProxyCreator#postProcessBeforeInstantiation

// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
	if (StringUtils.hasLength(beanName)) {
		this.targetSourcedBeans.add(beanName);
	}
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
	Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
	this.proxyTypes.put(cacheKey, proxy.getClass());
	return proxy;
}

创建的代理对象代理的不是 Bean 类的实例,而是持有 Bean 类的实例的 TargetSource 。(多抽出来的一层)体系为:
TargetSource 体系
为了解决依赖注入的循环依赖问题,Spring 可以在 Bean 对象初始化之前提前创建出代理对象
提前创建代理对象

TargetSource 类型
SingletonTargetSource

This is the default implementation of the TargetSource interface, as used by the Spring AOP framework.

SingletonTargetSource

LazyInitTargetSource

lazily accesses a singleton bean from a {@link org.springframework.beans.factory.BeanFactory}. Useful when a proxy reference is needed on initialization but the actual target object should not be initialized until first use.

该类对象通过 LazyInitTargetSourceCreator 创建出

文章索引
  1. Spring AOP 中 TargetSource 的作用及原理分析
  2. spring通过TargetSourceCreator提前生成代理

在 spring aop 中,如果我们的类符合如下条件:被切面的 pointcut 匹配到、或者属于自定义的 Advisor 接口实现类,那么 spring 在 bean 完成实例化之后,会在 bean 的初始化阶段 (AbstractAutowireCapableBeanFactory#initializeBean 方法中调用 postProcessAfterInitialization) 为类生成代理对象。这是众所周知的 aop 流程。此外,spring还为我们提供了TargetSourceCreator接口,该接口的功能是:在bean实例化之前,就为类生成代理。

  1. Spring扩展点-TargetSource
  1. 我们可以看到它是在doGetBean调用之前去进行的干预,而如果有TargetSource,那么它直接完成代理返回了,后续的所有Bean的创建和初始化逻辑都不走了!这些逻辑都不走有什么问题吗?代表了XXXAware接口所有都不会生效,@Autowired/@Resource/@Value这些注解标注的属性赋值,都不会走!
  2. 执行目标方法的目标Bean,是从TargetSource隔离的BeanFactory当中去获取对象的,而隔离的BeanFactory中AOP相关的组件已经被移除了,因此不会存在getTarget获取到代理对象的可能性(如果获取到的是代理对象,那么很明显会出现StackOverFlow,因此不断的调用代理方法,没有尽头)。

动态代理类型

JdkDynamicAopProxy、DefaultAopProxyFactory(CGLib 动态代理)
Spring 动态代理继承体系

// DefaultAopProxyFactory#createAopProxy
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	// 注意:当 @EnableAspectJAutoProxy(proxyTargetClass = true) 时,则只用 CGLib 动态代理
	if (!NativeDetector.inNativeImage() &&
			(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
		Class<?> targetClass = config.getTargetClass();
		if (targetClass == null) {
			throw new AopConfigException("TargetSource cannot determine target class: " +
					"Either an interface or a target is required for proxy creation.");
		}
		// 动态代理使用 JDK 动态代理:被代理类是 interface 或 是 Proxy 的子类
		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
			return new JdkDynamicAopProxy(config);
		}
		return new ObjenesisCglibAopProxy(config);
	}
	else { // 当目标对象实现了自定义接口时,使用 JDK 动态代理
		return new JdkDynamicAopProxy(config);
	}
}

注意:当 @EnableAspectJAutoProxy(proxyTargetClass = true) 时,则只用 CGLib 动态代理。

why spring AOP use JDK Dynamic proxy?

Cglib is an external dependeny (…) Relying on third-party software is always a gamble, so it is best when as few people as possible rely on it." I.e., if you have a Spring application with very clean design, always programming against interfaces instead of directly against classes, you do not need CGLIB at all.

Spring 3.2 以后,spring-core 直接就把 CGLIB 和 ASM 的源码包括进来了,这也是为什么我们不需要显式的引入这两个依赖。即在 3.2 之前,程序员无需引入 CGLib 也可以使用 AOP 特性。

事务使用

@Configuration
@EnableTransactionManagement
public class AppConfig {

   @Bean
   public FooRepository fooRepository() {
       // configure and return a class having @Transactional methods
       return new JdbcFooRepository(dataSource());
   }

   @Bean
   public DataSource dataSource() {
       // configure and return the necessary JDBC DataSource
   }

   @Bean
   public PlatformTransactionManager txManager() {
       return new DataSourceTransactionManager(dataSource());
   }
}

注意事项: spring事务是基于spring aop实现的,采用的代理机制,所以一个类调用自己类的方法是不会走代理的,也就是即使被调用的方法声明了@Transaction,也不会开启事务。

事务原理

实现

TransactionInterceptor 和 Advice

@Configuration 类上有 @EnableTransactionManagement 注解后,在 ApplicationContext refresh 时,会调用 ConfigurationClassPostProcessor, ConfigurationClassPostProcessor 会在 postProcessBeanDefinitionRegistry 方法中处理 @EnableTransactionManagement 注解上的 TransactionManagementConfigurationSelector。

@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
    boolean proxyTargetClass() default false;
    AdviceMode mode() default AdviceMode.PROXY;
    int order() default Ordered.LOWEST_PRECEDENCE;
}

默认的 PROXY 模式下,会引入 AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration。

  • 通过 AutoProxyRegistrar 为 Spring 容器导入了 InfrastructureAdvisorAutoProxyCreator,利用后置处理器返回一个增强代理类
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

    @Override
    protected String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:
                return new String[] {AutoProxyRegistrar.class.getName(),
                        ProxyTransactionManagementConfiguration.class.getName()};
            case ASPECTJ:
                return new String[] {determineTransactionAspectClass()};
            default:
                return null;
        }
    }
}    
  • 通过 ProxyTransactionManagementConfiguration 会向 Spring 容器添加 BeanFactoryTransactionAttributeSourceAdvisor、TransactionAttributeSource、TransactionInterceptor 几个 Bean。
    • BeanFactoryTransactionAttributeSourceAdvisor 是用于 aop 的 Advisor
    • TransactionInterceptor 是 MethodIntercetor 的实现类。
@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        advisor.setTransactionAttributeSource(transactionAttributeSource());
        advisor.setAdvice(transactionInterceptor());
        if (this.enableTx != null) {
            advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
        }
        return advisor;
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionAttributeSource transactionAttributeSource() {
        return new AnnotationTransactionAttributeSource();
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionInterceptor transactionInterceptor() {
        TransactionInterceptor interceptor = new TransactionInterceptor();
        interceptor.setTransactionAttributeSource(transactionAttributeSource());
        if (this.txManager != null) {
            interceptor.setTransactionManager(this.txManager);
        }
        return interceptor;
    }

}

TransactionInterceptor 中定义了代理的实现 invoke 方法,从这里能看到 spring 事务的执行逻辑。

public Object invoke(MethodInvocation invocation) throws Throwable {
    // Work out the target class: may be {@code null}.
    // The TransactionAttributeSource should be passed the target class
    // as well as the method, which may be from an interface.
    Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

    // Adapt to TransactionAspectSupport's invokeWithinTransaction...
    return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}

invokeWithinTransaction 实现在父类 TransactionAspectSupport 中。
invokeWithinTransaction 的执行逻辑为,先开启事务,然后保存到 ThreadLocal 中,执行被代理的方法,然后提交事务,并重置 ThreadLocal 的值然后提交事务。

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
        final InvocationCallback invocation) throws Throwable {

    // If the transaction attribute is null, the method is non-transactional.
    TransactionAttributeSource tas = getTransactionAttributeSource();
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

        Object retVal;
        try {
            // This is an around advice: Invoke the next interceptor in the chain.
            // This will normally result in a target object being invoked.
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            // target invocation exception
            // 执行业务逻辑遇到异常时,通过 事务管理器(PlatformTransactionManager)回滚事务
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            cleanupTransactionInfo(txInfo);
        }
		// 执行业务逻辑遇到异常时,通过 事务管理器(PlatformTransactionManager)提交事务
        commitTransactionAfterReturning(txInfo);
        return retVal;
    } else {
        final ThrowableHolder throwableHolder = new ThrowableHolder();

        // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
        try {
            Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
                TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                try {
                    return invocation.proceedWithInvocation();
                }
                catch (Throwable ex) {
                    if (txAttr.rollbackOn(ex)) {
                        // A RuntimeException: will lead to a rollback.
                        if (ex instanceof RuntimeException) {
                            throw (RuntimeException) ex;
                        }
                        else {
                            throw new ThrowableHolderException(ex);
                        }
                    }
                    else {
                        // A normal return value: will lead to a commit.
                        throwableHolder.throwable = ex;
                        return null;
                    }
                }
                finally {
                    cleanupTransactionInfo(txInfo);
                }
            });
		// Check result state: It might indicate a Throwable to rethrow.
            if (throwableHolder.throwable != null) {
                throw throwableHolder.throwable;
            }
            return result;
        }
        catch (ThrowableHolderException ex) {
            throw ex.getCause();
        }
        catch (TransactionSystemException ex2) {
            if (throwableHolder.throwable != null) {
                logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                ex2.initApplicationException(throwableHolder.throwable);
            }
            throw ex2;
        }
        catch (Throwable ex2) {
            if (throwableHolder.throwable != null) {
                logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
            }
            throw ex2;
        }
    }
}

BeanFactoryTransactionAttributeSourceAdvisor 类定义了 aop 的 Advisor,Pointcut 为TransactionAttributeSource,是有 @Transactional 注解的方法。 Advisor 的 advice 为上面的 TransactionInterceptor。

验证 @Transactional 注解修改生效的示例。

文章索引

  1. Spring 源码解析:如何保证事务
  2. ThreadLocal与Spring 事务管理
  3. spring jdbcTemplate 事务:从 Connection 到 Spring @Transactional

拓展

  1. java中什么是bridge method(桥接方法)
    桥接方法字节码反编译示例
    博客中的错误:桥接方法方法的出现并不是为了兼容 JDK 1.5-,而是为了实现泛型。

  2. Type Erasure

Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:

  1. Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
  2. Insert type casts if necessary to preserve type safety.
  3. Generate bridge methods to preserve polymorphism in extended generic types. After type erasure, the method signatures do not match; the Node.setData(T) method becomes Node.setData(Object). As a result, the MyNode.setData(Integer) method does not override the Node.setData(Object) method.
  1. org.springframework.beans.factory.config.AutowireCapableBeanFactory#ORIGINAL_INSTANCE_SUFFIX

Suffix for the “original instance” convention when initializing an existing bean instance: to be appended to the fully-qualified bean class name, e.g. “com.mypackage.MyClass.ORIGINAL”, in order to enforce the given instance to be returned, i.e. no proxies etc.

切面类
声明 bean 对象
非目标 bean 的代理对象
约定命名的解析代码:org.springframework.aop.framework.autoproxy.AutoProxyUtils#isOriginalInstance

static boolean isOriginalInstance(String beanName, Class<?> beanClass) {
	// 类的全限定名 + .ORIGINAL
	if (!StringUtils.hasLength(beanName) || beanName.length() !=
			beanClass.getName().length() + AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX.length()) {
		return false;
	}
	return (beanName.startsWith(beanClass.getName()) &&
			beanName.endsWith(AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX));
}

注意:@Bean 的第一个 name 非 aop.spring.OriginalBeanClass.ORIGINAL 时,则从容器中拿到的依然是目标 bean 的代理对象。
目标 bean 的代理对象
在 Bean 有多个名字(比如:A、B)时,Spring 会拿 bean 的第一个名字(比如:A)将 Bean 对象注册到容器中,使用其它名字(比如:B)调用 getBean 方法试图拿到 Bean 对象时,会先将其转换为容器中实际存储的名字(比如:A),然后再去容器中查找。org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

public String canonicalName(String name) {
	String canonicalName = name;
	// Handle aliasing...
	String resolvedName;
	do {
		resolvedName = this.aliasMap.get(canonicalName);
		if (resolvedName != null) {
			canonicalName = resolvedName;
		}
	}
	while (resolvedName != null);
	return canonicalName;
}
  1. Spring Boot 中的 AOP,到底是 JDK 动态代理还是 Cglib 动态代理?
  1. Spring 中的 AOP,有接口就用 JDK 动态代理,没有接口就用 Cglib 动态代理。
  2. Spring Boot 中的 AOP,2.0 之前和 Spring 一样;2.0 之后首选 Cglib 动态代理,如果用户想要使用 JDK 动态代理,需要自己手动配置。
  1. Spring AOP中的JDK和CGLib动态代理哪个效率更高?

在1.6和1.7的时候,JDK动态代理的速度要比CGLib动态代理的速度要慢,但是并没有教科书上的10倍差距,在JDK1.8的时候,JDK动态代理的速度已经比CGLib动态代理的速度快很多了?

  • The performance implications of Java reflection

This code contains an if block that will be entered after an invocation threshold is reached, such as after the reflective method has been called a certain number of times. If the invocation threshold has not yet been reached, the code proceeds with the native call.

Once the threshold has been reached, NativeMethodAccessorImpl will use a code generation factory, contained in MethodAccessorGenerator.generateMethod(), to create a custom class that contains bytecode that calls the target of the reflective call.

After creating an instance of this dynamically created class, the call to setDelegate() uses an uplevel reference to the parent accessor to replace the current object with acc, the newly created custom object.

For technical reasons related to class verification, the JVM must be aware of the special nature of the reflective accessor classes. For this reason, there is a special accessor class in the inheritance hierarchy that acts as a marker for the JVM. The precise details of this need not concern you, so don’t worry.

Overall, the mechanism as described represents a performance trade-off—some reflective calls are made only a few times, so the code generation process could be very expensive or wasteful. On the other hand, switching from Java into a native call is slower than remaining in pure Java. This approach allows the runtime to avoid code generation until it seems likely that the reflective call will be made relatively often.

As a result, the costs of code generation can then be amortized over the lifetime of the program, while still providing better performance for later calls than the native implementation can achieve.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/489009.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

怎么建设数据中台?详解数据中台架构内的三大平台

一、什么是数据中台&#xff1f; 要知道“中台”是什么&#xff0c;就得先了解“前台”和“后台”。 前台&#xff0c;就是我们日常使用的过程中可以直接看到和感知到的东西&#xff0c;比如你打开某东app买了个3080显卡&#xff0c;在这个过程中你看到的页面以及搜索、点击详…

考研数学|武忠祥高数全年学习包分享

u1s1&#xff0c;武忠祥老师的课程真的不错&#xff0c;宝藏级老师 其实我觉得没必要对比每一个考研数学老师&#xff0c;汤家凤还有张宇以及武忠祥都是非常受欢迎的老师&#xff0c;也都很有实力&#xff0c;只不过讲课的风格有所区别。 比如汤家凤老师就像是高中那种不苟言…

洁净环境监测相关法规指南汇总

一 洁净级别确认 1. 用于生产无菌药品的洁净室和洁净空气设备如单向流系统&#xff08;UDAF&#xff09;、限制进入屏障系统&#xff08;RABS&#xff09;和隔离器&#xff0c;应根据所需环境特性进行确认。生产操作需要在适当洁净度级别的环境中进行&#xff0c;以降低粒子或…

M1 mac安装 Parallels Desktop 18 激活

M1 mac安装 Parallels Desktop 18 激活 下载安装Parallels Desktop 18.1.1 (53328) 激活1. 拷贝prl_disp_service2. 在终端打开Crack所在位置3. 输入命令&#xff0c;激活成功 下载 安装包和激活文件下载地址 链接: https://pan.baidu.com/s/1EjT7xeEDcntIIoOvvhBDfg?pwd9pue …

智能无人集群系统跨域协同技术研究现状与展望

源自&#xff1a;中国工程院院刊 作者&#xff1a;江碧涛&#xff0c;温广辉&#xff0c;周佳玲&#xff0c;郑德智 “人工智能技术与咨询” 发布 编者按 随着智能化技术和无人系统技术的快速发展&#xff0c;智能无人集群系统跨域协同的概念应运而生并得到了广泛关注与深…

element-ui radio-group 组件源码分享

接着上篇的 radio 组件源码分享&#xff0c;继续探索 radio-group 源码部分的实现过程&#xff0c;主要从以下四个方面来讲解&#xff1a; 1、el-radio-group 页面结构 2、el-radio-group 组件属性 3、el-radio-group 组件方法 4、核心代码部分 一、页面结构&#xff0c;如…

成都伊理威:抖音开网店如何找好货源

在数字浪潮的推动下&#xff0c;抖音已成为创业者开展电子商务的热土。开设一家抖音网店&#xff0c;如何找寻优质的货源成为成功的关键一环。选择货源&#xff0c;犹如为网店插上腾飞的翅膀&#xff0c;既要注重品质&#xff0c;也要考虑成本&#xff0c;确保产品能够在激烈的…

jspssm_maven项目——KTV点歌系统

目录 背景 技术简介 系统简介 界面预览 背景 随着互联网的广泛渗透和进步&#xff0c;基于网络技术的KTV点歌系统迅速壮大&#xff0c;其发展始终围绕用户的实际需求展开。通过深入洞察用户的需求&#xff0c;开发出高度定制的管理平台&#xff0c;利用网络的便捷性对系统…

【晴问算法】入门篇—字符串处理—首字母大写

题目描述 给定一堆用空格隔开的英文单词&#xff0c;将每个单词的首字母改为大写后输出。输入描述 一堆英文单词&#xff0c;每个单词不超过10个字符&#xff0c;且仅由小写字母组成;每两个单词之间用一个空格隔开&#xff0c;整个字符串的长度不超过1000。输出描述 输出每个单…

基于React的低代码平台开发实践

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;在线地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

浏览器输入框自动填充默认样式移除

文章目录 浏览器输入框自动填充默认样式移除问题现象以及探索过程尝试代码有效关键代码&#xff08;解决方案&#xff09; 浏览器输入框自动填充默认样式移除 问题现象以及探索过程 &#xff08;在 uniapp 语法下&#xff09;本文的写法在 Edge 119.0.2151.58 (正式版本) (64 …

爱校对:网站内容的温暖守护者

在这个快速变化的信息时代&#xff0c;网站如同一个生动的生态系统&#xff0c;每时每刻都在更新和进化。但是&#xff0c;随之而来的是一个挑战&#xff1a;如何确保这个生态系统中的每一条信息都是准确、合法、并且对用户友好呢&#xff1f;这就是我们&#xff0c;爱校对网站…

​企业是否需要向个人信息主体提供《标准合同》副本文件?​

企业是否需要向个人信息主体提供《标准合同》副本文件&#xff1f; 目前未见有规定强制要求企业需要主动向个人信息主体提供《标准合同》的副本文件&#xff0c;但个人信息主体具有要求个人信息处理者提供其所签订的《标准合同》副本的权利&#xff0c;企业必须配合。在提供副…

Jetson AGX ORIN 配置 FGVC-PIM 神经网络(包含 arm64 下面 torch 和 torchvision 配置内容)

Jetson AGX ORIN 配置 FGVC-PIM 神经网络 文章目录 Jetson AGX ORIN 配置 FGVC-PIM 神经网络配置 ORIN 环境创建 FGVC-PIM 虚拟环境安装 PyTorch安装 torchvision安装其他依赖包 配置 ORIN 环境 首先先配置 ORIN 的环境&#xff0c;可以参考这个链接&#xff1a; Jetson AGX …

【考研数学】张宇全程学习包

可以全程张宇老师的高等数学&#xff0c;张宇老师的拿手绝活是高数 但是其他科目&#xff0c;还有更好的选择&#xff0c;比如线性代数&#xff0c;汤家凤老师还有李永乐老师讲的都不错&#xff0c;概率论&#xff0c;余丙森老师还有方浩老师讲的很好。下面我就讲清楚&#xf…

1978-2022年全国及31省市农业机械总动力(万千瓦)(无缺失)

1978-2022年全国及31省市农业机械总动力&#xff08;万千瓦&#xff09;&#xff08;无缺失&#xff09; 1、时间&#xff1a;1978-2022年 2、范围&#xff1a;全国及31省 3、来源&#xff1a;整理自各省年鉴 中国农业统计年鉴、国家统计局 4、指标&#xff1a;农业机械总动…

云电脑火爆出圈,如何选择和使用?--腾讯云、ToDesk云电脑、青椒云使用评测和攻略

前言&#xff1a; Hello大家好&#xff0c;我是Dream。在当下&#xff0c;科技的飞速发展已经深入影响着我们的日常生活&#xff0c;特别是随着物联网的兴起和5G网络的普及&#xff0c;云计算作为一个重要的技术概念也逐渐走进了我们的视野。云计算早已不再是一个陌生的名词&am…

基于nodejs+vue电子产品销售系统设计与实现python-flask-django-php

该系统采用了nodejs技术、express框架&#xff0c;连接MySQL数据库&#xff0c;具有较高的信息传输速率与较强的数据处理能力。包含管理员和用户两个层级的用户角色&#xff0c;系统管理员可以对个人中心、用户管理、产品类别管理、电子产品管理、留言板管理、系统管理、订单管…

MySQL数据库备份策略与实践详解

目录 引言 一、MySQL数据库备份的重要性 &#xff08;一&#xff09;数据丢失的原因 &#xff08;二&#xff09;数据丢失的后果 二、MySQL备份类型 &#xff08;一&#xff09;根据数据库状态 &#xff08;二&#xff09;根据数据的完整性 &#xff08;三&#xff09;…

【Linux】Linux查看物理CPU个数、核数、逻辑CPU个数,以及内存情况

总核数 物理CPU个数 X 每颗物理CPU的核数 总逻辑CPU数 物理CPU个数 X 每颗物理CPU的核数 X 超线程数 查看物理CPU个数 cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l显示这台机器有2块CPU 查看每个物理CPU中core的个数(即核数) cat /proc/cpuinfo|…