Spring - AOP/事务 实现原理

news2024/9/23 13:25:22

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.

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2核4g服务器能支持多少人访问?阿里云2核4g服务器在线人数

阿里云2核4G服务器多少钱一年&#xff1f;2核4G配置1个月多少钱&#xff1f;2核4G服务器30元3个月、轻量应用服务器2核4G4M带宽165元一年、企业用户2核4G5M带宽199元一年。可以在阿里云CLUB中心查看 aliyun.club 当前最新2核4G服务器精准报价、优惠券和活动信息。 阿里云官方2…

leetCode刷题 18. 删除链表的倒数第 N 个结点

目录 1. 思路 2. 解题方法 3. 复杂度 4. Code 题目&#xff1a; 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&…

IF= 13.4| 当eDNA遇上机器学习法

近日&#xff0c;凌恩生物客户重庆医科大学在《Water Research》&#xff08;IF 13.4&#xff09;发表研究论文“Supervised machine learning improves general applicability of eDNA metabarcoding for reservoir health monitoring”。该研究主要介绍了一种基于eDNA的机器学…

2023混合多比特层-RDHEI Based on the Mixed Multi-Bit Layer Embedding Strategy

RRBE 本文仅供自我学习记录,切勿转载和搬运,如有侵权联系立删! 方法总框架 首先,发送者将载体图像进行两轮的不重叠块分割,分为可用隐藏块(AHB)和不可用隐藏块(UHB),然后通过依次处理可用块的像素信息产生location图来创造空间,接着通过密钥将载体进行加密,最后使用…

怎样在NEXT.JS中设置next-auth并实现登入登出

参考代码link&#xff1a;https://github.com/luomi16/next-auth-demo 在Next.js中使用next-auth来实现登录和登出功能是一种流行且相对简单的方法。next-auth是一个专为Next.js开发的认证库&#xff0c;支持多种认证提供者&#xff0c;如Google、Facebook、Twitter以及基于邮…

档案四性检测可复用组件接口说明

nhdeep提供在归档、移交与接收、长期保存等各环节根据需求进行自主配置和调用的可复用组件&#xff0c;支持客户端和接口调用两种功能使用模式。档案四性检测组件为自建档案管理系统和各种业务系统&#xff08;如OA&#xff09;&#xff0c;提供标准化的档案四性检测功能利用&a…

【分享】CMMI V3.0版本做了哪些改变?哪些企业适合申请CMMI3.0

​ CMM是由美国卡内基梅隆大学软件工程研究所1987年开发成功的&#xff0c;它基于过去所有软件工程过程改进的成果&#xff0c;吸取了以往软件工程的经验教训&#xff0c;提供了一个基于过程改进的框架&#xff1b;CMMI(Capability Maturity Model Integration能力成熟度模型集…

esp单片机下arduino_gfx不相干显示驱动优化对flash空间的占用对比

一般情况下&#xff0c;很多esp32或者esp8266下的tft模块驱动都会包含很多种&#xff0c;而我们只需要其中一种&#xff0c;那就有个疑问这些被编译进的显示驱动到底占用了多少空间&#xff0c;是否需要把他优化掉&#xff1f; 这是默认的驱动列表&#xff1a; 84个文件&…

Android14之深入理解sp模板类(二百零二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

C语言例4-12:从键盘输入一个学生的学号、性别和一门课程的成绩后输出该学生的信息

代码如下&#xff1a; //从键盘输入一个学生的学号、性别和一门课程的成绩后输出该学生的信息 #include<stdio.h> int main(void) {int num; //定义整型变量num存放学生的学号char sex; //定义字符型变量sex存放学生的性别float score; //定义实型变量score存放学…

实现多线程方式你知道几种?

实现多线程通常有以下四种方式&#xff1a; 继承 Thread 类。实现 Runnable 接口。实现 Callable 接口。线程池实现多线程。 继承 Thread 类&#xff1a; public class ThreadDemo extends Thread{Overridepublic void run(){System.out.println("Thread 类实现多线程,…

现在的市场对 C++ 的需求大吗?

先说结论&#xff1a;需求还是很大&#xff0c;但是没有什么初级程序员能干的岗位。 游戏引擎&#xff0c;存储&#xff0c;推荐引擎&#xff0c;infra&#xff0c;各种各样的性能敏感场景。 在开始前我分享下我的经历&#xff0c;我刚入行时遇到一个好公司和师父&#xff0c;…

Linux系统服务

文章目录 什么是daemon与服务(service)systemd使用unit分类 通过systemctl管理服务通过systemctl管理单一服务(service unit)通过systemctl查看系统上所有的服务通过systemctl管理不同的操作环境(target unit)通过systemctl分析各服务之间的依赖性与systemd的daemon运行过程相关…

基于nodejs+vue铁路订票管理系统python-flask-django-php

该铁路订票管理系统采用前后端分离进行设计&#xff0c;并采用nodejs语言以及express框架进行开发。本系统主要设计并完成了用户登录管理过程、个人信息修改、用户管理、火车类型管理、火车信息管理、车票预订管理、车票退票管理、系统管理等功能。该系统操作简单&#xff0c;界…

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

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

2024华为产业链企业名单大全(附下载)

更多内容&#xff0c;请前往知识星球下载&#xff1a;https://t.zsxq.com/18fsVdcjA 更多内容&#xff0c;请前往知识星球下载&#xff1a;https://t.zsxq.com/18fsVdcjA

mapbox测距功能重写

// 使用 import MeatureTool from "/components/webgisMap/measureTool";measureDistance() {// ID可以自定义const layerId String(new Date().getTime())this.meatureTool new MeatureTool(this.mapBoxMap)this.meatureTool.measureDistance(layerId)// 防止函数…

动态内存管理-传值调用错题解析

首先我们来看这个错误代码 首先我们看代码逻辑&#xff0c;首先main函数调用test&#xff0c;test接收的是void类型&#xff0c;设置一个指针变量&#xff0c;指向null&#xff0c;传递给get函数&#xff0c;也就是传递一个空指针给getmemory函数&#xff0c;这个函数接收了&a…

单片机入门到精通:一站式在线学习平台!

介绍&#xff1a;单片机&#xff0c;也称为微控制器&#xff08;MCU&#xff09;&#xff0c;是一种集成了中央处理器&#xff08;CPU&#xff09;、随机存储器&#xff08;RAM&#xff09;、只读存储器&#xff08;ROM&#xff09;以及输入/输出接口于单一芯片上的微型计算机。…

设计模式学习笔记 - 设计模式与范式 -结构型:2.桥接模式:如何实现支持不同类型和渠道的消息推送系统?

概述 今天学习另外一种结构型模式&#xff1a;桥接模式。桥接模式的代码实现非常简单&#xff0c;但是理解起来稍微优点难度&#xff0c;并且应用场景也比较局限&#xff0c;所以&#xff0c;相对于代理模式来说&#xff0c;桥接模式在实际的项目中并没有那么常用&#xff0c;…