spring 事务源码阅读之实现原理

news2024/11/19 1:49:00

开启事务

使用@EnableTransactionManagement注解开启事务

该注解会引入TransactionManagementConfigurationSelector类,然后该类导入两个类AutoProxyRegistrar和ProxyTransactionManagementConfiguration。

1、添加bean后置处理器

AutoProxyRegistrar类的作用是注册InfrastructureAdvisorAutoProxyCreator类,InfrastructureAdvisorAutoProxyCreator是Spring中实现AOP代理的关键组件,它会扫描所有的Advisor(通知器),并将其与BeanFactory中的Bean进行匹配,以决定是否需要为该Bean创建AOP代理。

InfrastructureAdvisorAutoProxyCreator 继承AbstractAdvisorAutoProxyCreator 继承AbstractAutoProxyCreator,实现SmartInstantiationAwareBeanPostProcessor和BeanFactoryAware接口。

是一个bean后置处理器

public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {
   @Nullable
   private ConfigurableListableBeanFactory beanFactory;
   @Override
   protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
      super.initBeanFactory(beanFactory);
      this.beanFactory = beanFactory;
   }
   @Override
   protected boolean isEligibleAdvisorBean(String beanName) {
      return (this.beanFactory != null && this.beanFactory.containsBeanDefinition(beanName) &&
            this.beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE);
   }

}

代理的创建在上一篇AOP源码阅读中有说过就是在后置处理器的after方法寻找合适的advisor进行创建代理。上面的源码看到InfrastructureAdvisorAutoProxyCreator只是重写了initBeanFactory方法用来获取beanfacotry和isEligibleAdvisorBean方法用来判断是否是合适的bean。

2、引入Advisor

TransactionManagementConfigurationSelector引入的另一个类是ProxyTransactionManagementConfiguration,该类代码也不多,如下

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

   @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
   @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
   public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
         TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {

      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(TransactionAttributeSource transactionAttributeSource) {
      TransactionInterceptor interceptor = new TransactionInterceptor();
      interceptor.setTransactionAttributeSource(transactionAttributeSource);
      if (this.txManager != null) {
         interceptor.setTransactionManager(this.txManager);
      }
      return interceptor;
   }

}

ProxyTransactionManagementConfiguration类主要引入BeanFactoryTransactionAttributeSourceAdvisor通知类。并为其TransactionAttributeSource和TransactionInterceptor两个属性赋值。该类实现了Advisor接口,是一个PointcutAdvisor类型的Advisor,用来匹配@Transaction注解。具体如何匹配下面结合源码说。

代理创建

1、获取所有Advisor

在AbstractAutoProxyCreator的后置出来器afater方法里还是会调用wrapIfNecessary(bean, beanName, cacheKey)来判断当前bean是否需要包装代理。然后会调用getAdvicesAndAdvisorsForBean(bean.getClass(), beanName)方法尝试获取适用于该bean的advice。这个方法在AbstractAdvisorAutoProxyCreator 中实现

AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean

protected Object[] getAdvicesAndAdvisorsForBean(
      Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
   //获取符合条件的advisor
   List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
   if (advisors.isEmpty()) {
      return DO_NOT_PROXY;
   }
   return advisors.toArray();
}

	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        //获取候选的所有advisor
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
      //获取适用于当前bean的advisor
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}

findCandidateAdvisors()方法还是在该类中实现,使用了一个BeanFactoryAdvisorRetrievalHelper工具类进行获取Advisor

AbstractAdvisorAutoProxyCreator#findCandidateAdvisors

protected List<Advisor> findCandidateAdvisors() {
     return this.advisorRetrievalHelper.findAdvisorBeans();
}

BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans

public List<Advisor> findAdvisorBeans() {
   String[] advisorNames = this.cachedAdvisorBeanNames;
   if (advisorNames == null) {
      //从beanfacotry总获取所有的Advisor
      advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
            this.beanFactory, Advisor.class, true, false);
      this.cachedAdvisorBeanNames = advisorNames;
   }
   if (advisorNames.length == 0) {
      return new ArrayList<>();
   }

   List<Advisor> advisors = new ArrayList<>();
   for (String name : advisorNames) {
      if (isEligibleBean(name)) {//调用isEligibleBean判断bean是否满足基本条件
         advisors.add(this.beanFactory.getBean(name, Advisor.class));
      }
   }
   return advisors;
}

这里看到Advisor的获取是直接从beanFacotry中查找Advisor.class类型的beanDef,这样就能找到我们最开始通过import导入的BeanFactoryTransactionAttributeSourceAdvisor。这和上一篇说的AOP的Aspect方式有一些区别(扫描所有的bean,反射解析@Aspect注解)。

2、寻找匹配的Advisor

找到所有的Advisor后下一步就是逐个判断是否适用于当前bean

protected List<Advisor> findAdvisorsThatCanApply(
      List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

   ProxyCreationContext.setCurrentProxiedBeanName(beanName);
   try {
      return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
   }
   finally {
      ProxyCreationContext.setCurrentProxiedBeanName(null);
   }
}

AopUtils#findAdvisorsThatCanApply

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
   if (candidateAdvisors.isEmpty()) {
      return candidateAdvisors;
   }
   List<Advisor> eligibleAdvisors = new ArrayList<>();
   //这里不是IntroductionAdvisor类型的不会进入循环
   for (Advisor candidate : candidateAdvisors) {
      if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
         eligibleAdvisors.add(candidate);
      }
   }
   boolean hasIntroductions = !eligibleAdvisors.isEmpty();
   for (Advisor candidate : candidateAdvisors) {
      if (candidate instanceof IntroductionAdvisor) {
         // already processed
         continue;
      }
      //进入canApply方法判断
      if (canApply(candidate, clazz, hasIntroductions)) {
         eligibleAdvisors.add(candidate);
      }
   }
   return eligibleAdvisors;
}

AopUtils#canApply

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
   if (advisor instanceof IntroductionAdvisor) {
      return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
   }
   else if (advisor instanceof PointcutAdvisor) {//走这里
      PointcutAdvisor pca = (PointcutAdvisor) advisor;
      return canApply(pca.getPointcut(), targetClass, hasIntroductions);
   }
   else {
      // It doesn't have a pointcut so we assume it applies.
      return true;
   }
}

最开始说过我们引入的advisor是一个PointcutAdvisor类型。所以会进入第二个if分支。拿出pointcut进行匹配。这里的advisor是BeanFactoryTransactionAttributeSourceAdvisor实例,器pointcut是TransactionAttributeSourcePointcut类型实例。下一个重载canApply方法主要匹配逻辑是下面代码

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
   //...
   //这里返回的matcher还是TransactionAttributeSourcePointcut本身
   MethodMatcher methodMatcher = pc.getMethodMatcher();
   for (Class<?> clazz : classes) {
     //拿出class所有方法与matcher进行匹配
      Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
      for (Method method : methods) {
         if (introductionAwareMethodMatcher != null ?
               introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
               methodMatcher.matches(method, targetClass)) {
            return true;
         }
      }
   }

   return false;
}

主要的匹配逻辑是methodMatcher.matches,也就是

TransactionAttributeSourcePointcut#matches

public boolean matches(Method method, Class<?> targetClass) {
   //获取TransactionAttributeSource,这是最开始在引入Advisor设置的
   TransactionAttributeSource tas = getTransactionAttributeSource();
   return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

这里TransactionAttributeSource是最开始引入由ProxyTransactionManagementConfiguration类内部设置的,其实例类型是AnnotationTransactionAttributeSource ,该类继承 AbstractFallbackTransactionAttributeSource。getTransactionAttribute()方法在其父类中

AbstractFallbackTransactionAttributeSource#getTransactionAttribute

public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
   if (method.getDeclaringClass() == Object.class) {//Object类方法跳过
      return null;
   }

   // First, see if we have a cached value.
   Object cacheKey = getCacheKey(method, targetClass);
   TransactionAttribute cached = this.attributeCache.get(cacheKey);
   if (cached != null) {//先从缓存获取,如果没有解析
      // Value will either be canonical value indicating there is no transaction attribute,
      // or an actual transaction attribute.
      if (cached == NULL_TRANSACTION_ATTRIBUTE) {
         return null;
      }
      else {
         return cached;
      }
   }
   else {
      // 解析当前方法事务属性
      TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
      // Put it in the cache.
      if (txAttr == null) {
         this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
      }
      else {
         String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
         if (txAttr instanceof DefaultTransactionAttribute) {
            DefaultTransactionAttribute dta = (DefaultTransactionAttribute) txAttr;
            dta.setDescriptor(methodIdentification);
            dta.resolveAttributeStrings(this.embeddedValueResolver);
         }

         this.attributeCache.put(cacheKey, txAttr);
      }
      return txAttr;
   }
}

解析实际方法是computeTransactionAttribute()

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
   //step1 判断方法必须是public方法
   if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
      return null;
   }

   // The method may be on an interface, but we need attributes from the target class.
   // If the target class is null, the method will be unchanged.
   Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

   //step2 解析方法事务属性
   TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
   if (txAttr != null) {
      return txAttr;
   }

   // Second try is the transaction attribute on the target class.
   txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
   if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
      return txAttr;
   }

   if (specificMethod != method) {
      // Fallback is to look at the original method.
      txAttr = findTransactionAttribute(method);
      if (txAttr != null) {
         return txAttr;
      }
      // Last fallback is the class of the original method.
      txAttr = findTransactionAttribute(method.getDeclaringClass());
      if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
         return txAttr;
      }
   }

   return null;
}

step2处findTransactionAttribute()方法解析事务属性,该方法是AnnotationTransactionAttributeSource类实现,其调用determineTransactionAttribute(method)方法。

AnnotationTransactionAttributeSource#determineTransactionAttribute

protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
   for (TransactionAnnotationParser parser : this.annotationParsers) {
      TransactionAttribute attr = parser.parseTransactionAnnotation(element);
      if (attr != null) {
         return attr;
      }
   }
   return null;
}

这里拿出所有的annotationParsers进行解析方法是否定义事务。annotationParsers是在AnnotationTransactionAttributeSource的构造方法初始化,这里有两个SpringTransactionAnnotationParser和JtaTransactionAnnotationParser。我们主要看SpringTransactionAnnotationParser。

SpringTransactionAnnotationParser#parseTransactionAnnotation

public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
   AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
         element, Transactional.class, false, false);
   if (attributes != null) {
      return parseTransactionAnnotation(attributes);
   }
   else {
      return null;
   }
}

终于看到熟悉的Transactional注解了,也不往方法里面看了。这里就是解析@Transactional注解配置的事务属性。

3、事务管理

代理创建和AOP过程一致,这里还是以JDK代理类为例。代理对象是JdkDynamicAopProxy。原方法的执行会首先进入JdkDynamicAopProxy.invoke方法。

这里还是从invoke的获取拦截器链开始

List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

最后会调到

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
   List<MethodInterceptor> interceptors = new ArrayList<>(3);
   Advice advice = advisor.getAdvice();
   if (advice instanceof MethodInterceptor) {//会走这里
      interceptors.add((MethodInterceptor) advice);
   }
   for (AdvisorAdapter adapter : this.adapters) {//这是Aspect的情况
      if (adapter.supportsAdvice(advice)) {
         interceptors.add(adapter.getInterceptor(advisor));
      }
   }
   if (interceptors.isEmpty()) {
      throw new UnknownAdviceTypeException(advisor.getAdvice());
   }
   return interceptors.toArray(new MethodInterceptor[0]);
}

这里要回到开始ProxyTransactionManagementConfiguration引入Advisor引入的地方

public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
      TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {

   BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
   advisor.setTransactionAttributeSource(transactionAttributeSource);
   //设置的advisor是TransactionInterceptor类型
   advisor.setAdvice(transactionInterceptor);
   if (this.enableTx != null) {
      advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
   }
   return advisor;
}

所以最后会调用TransactionInterceptor的invoke方法

TransactionInterceptor#invoke

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, new CoroutinesInvocationCallback() {
      @Override
      @Nullable
      public Object proceedWithInvocation() throws Throwable {
         return invocation.proceed();
      }
      @Override
      public Object getTarget() {
         return invocation.getThis();
      }
      @Override
      public Object[] getArguments() {
         return invocation.getArguments();
      }
   });
}

最后事务的控制在invokeWithinTransaction方法完成。

总结

开启事务为我们引入了两个重要的

1、BeanFactoryTransactionAttributeSourceAdvisor事务Advisor。设置两个属性TransactionInterceptor用来代理管理事务,TransactionAttributeSource用来匹配事务方法。

1、InfrastructureAdvisorAutoProxyCreator是bean后置处理器,用来判断当前bean是否需要代理。会拿出第一步引入的事务Advisor,与当前bean的所有方法进行匹配看是否有@Transaction注解。有就创建代理,最后使用BeanFactoryTransactionAttributeSourceAdvisor中设置的TransactionInterceptor拦截器进行事务管理。

整理流程
在这里插入图片描述

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

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

相关文章

数字IC前端学习笔记:数字乘法器的优化设计(基2布斯乘法器)

相关阅读 数字IC前端https://blog.csdn.net/weixin_45791458/category_12173698.html?spm1001.2014.3001.5482 ​​​​​​​在之前的文章中&#xff0c;进位保留乘法器、华莱士树乘法器、dadda树乘法器等方法都是优化了部分积累加这个过程。还有一些​​​​​​​方法能对…

再获深交所认可,Smartbi实力领跑金融BI赛道

“十四五”规划中提到&#xff0c;健全具有高度适应性、竞争力、普惠性的现代金融体系&#xff0c;构建有效支撑实体经济的体制机制。《证券期货业科技发展“十四五”规划》作为指导证券期货业科技发展的纲领性文件&#xff0c; 秉承国家“十四五”规划的数字化发展理念&#x…

在PCB走线中线不直,有小疙瘩,让走线变直的方法

在PCB走线中线不直,导线上有小疙瘩,那么如何去除呢? 在PCB走线的过程中,鼠标右击,点击Enhanced Pad Entry(增强焊盘入口)选项去勾。 Enhanced Pad Entry去勾后,然后再走线,走线就很直了。 如下图所示。 博主专注职场硬件设计,如果文章对你有帮助,请关注,点赞,收藏…

clickhouse之readonly解决方法

1&#xff0c;问题描述 日志报错写入ck报错&#xff1a;ru.yandex.clickhouse.except.ClickHouseUnknownException: ClickHouse exception, code: 1002, host: 192.16.1.19, port: 8123; Code: 242. DB::Exception: Table is in readonly mode: replica_path/clickhouse/tables…

8+肿瘤+多组机器学习+分型。

今天给同学们分享一篇肿瘤多组机器学习分型的生信文章“Integrated multiomics analysis and machine learning refine molecular subtypes and prognosis for muscle-invasive urothelial cancer”&#xff0c;这篇文章于2023年6月5日发表在Mol Ther Nucleic Acids期刊上&…

全志R128芯片应用开发案例——按键输入

按键输入 本文案例代码下载地址按键输入案例代码https://www.aw-ol.com/downloads?cat24 首先我们搭建电路&#xff0c;如下&#xff1a; 引脚按键PA25按键1脚GND按键3脚 载入方案 我们使用的开发板是 R128-Devkit&#xff0c;需要开发 C906 核心的应用程序&#xff0c;所…

金融行业蓄电池监控,究竟有多厉害!

金融行业是现代社会的重要组成部分&#xff0c;金融机构依赖于高度可靠的电力系统来支持其日常运营。随着金融交易日益数字化和自动化&#xff0c;电力中断或失效可能导致严重的财务损失和风险。 因此&#xff0c;金融行业对电力供应的可靠性要求极高&#xff0c;而蓄电池监控正…

Vue3+vite 添加router后的2个坑

废话不多说&#xff0c;发现问题并解决问题 第 1 个坑&#xff1a;关于 …/ router 报红 正常引入页面路由&#xff0c;页面也显示出来了&#xff0c;路径也是正确的&#xff0c;但是却报红了。 原因是&#xff1a;当前路径正则有问题 解决方案&#xff1a; 在 src 文件下 v…

品牌的学习

2023年快消时尚品牌排行榜 时尚快消品牌推荐|除了Zara还有这么多好品牌在向你招手呢&#xff01; - 知乎 (zhihu.com) 2019年快消时尚品牌排行榜 (72 封私信 / 81 条消息) 如何评价UR这个品牌的衣服&#xff1f; - 知乎 (zhihu.com)

TLP265J(TPR,E(T Triac输出 光隔离器助力工业应用实现稳定高效解决方案

关于Triac输出光隔离器&#xff1a; 它是一种用于隔离和调节交流电压的电子设备。它通常被用于控制交流电源下的电阻性或容性负载&#xff0c;例如灯泡、加热器和电动工具等。该设备采用光电耦合器将输入信号和输出信号隔离开来&#xff0c;以保证安全性和稳定性。 光隔离器的…

嵌入式养成计划-33--数据库-sqlite3

七十一、 数据库 71.1 数据库基本概念 数据&#xff08;Data&#xff09; 能够输入计算机并能被计算机程序识别和处理的信息集合数据库 &#xff08;Database&#xff09;数据库是在数据库管理系统管理和控制之下&#xff0c;存放在存储介质上的数据集合 常用的数据库 大型数…

信息系统项目管理师第四版学习笔记——项目进度管理

项目进度管理过程 项目进度管理过程包括&#xff1a;规划进度管理、定义活动、排列活动顺序、估算活动持续时间、制订进度计划、控制进度。 规划进度管理 规划进度管理是为规划、编制、管理、执行和控制项目进度而制定政策、程序和文档的过程。本过程的主要作用是为如何在…

Unity官方文档中关于内存管理的翻译(2021.3)

原文:Memory in Unity - Unity 手册 Unity内存管理 为了确保您的应用程序运行时没有性能问题&#xff0c;了解Unity如何使用和分配内存非常重要。本文档的这一部分解释了Unity中内存是如何工作的&#xff0c;适用于希望了解如何提高应用程序内存性能的读者。 Unity使用三个内…

4+经典思路,肿瘤+WGCNA+预后模型鉴定预后标志物

今天给同学们分享一篇4经典思路的生信文章“Identification and validation of a five-gene prognostic signature based on bioinformatics analyses in breast cancer”&#xff0c;这篇文章于2023年1月27日发表在Heliyon期刊上&#xff0c;影响因子为4。 乳腺癌&#xff08;…

Java集合(三)--- List接口

文章目录 一、List接口常用实现类的对比二、List接口中的常用方法代码 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、List接口常用实现类的对比 二、List接口中的常用方法 代码 package com.tyust.edu;import org.junit.Test;import java.util.A…

界面组件DevExpress WinForms v23.2新功能预览 - 增强MVVM相关功能

本文主要描述了DevExpress WinForms即将在几个月之后发布的v23.2中包含的新功能&#xff0c;持续关注我们获取更多最新资讯哦~ DevExpress WinForms有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。同时能完美构建流畅、美观且易于使用的应用…

Linux 安全 - LSM源码分析

文章目录 前言一、简介1.1 DAC 和 MAC1.2 LSM 调用流程图 二、LSM相关数据结构2.1 struct security_hook_list2.2 union security_list_options2.3 structure security_hook_heads 三、security_bprm_check四、LSM 源码分析3.1 early_security_init3.2 security_init3.2.1 secu…

代码随想录第39天 | ● 198.打家劫舍 ● 213.打家劫舍II ● 337.打家劫舍III

198.打家劫舍 /*** param {number[]} nums* return {number}*/ var rob function(nums) {//dp[i]max(dp[i-2],dp[dp-3])if(nums.length2)return Math.max(nums[0],nums[1])let dpnew Array(nums).fill(0)dp[0]nums[0]dp[1]Math.max(nums[0],nums[1])for(let i2;i<nums.le…

MacDroid pro 1.8(安卓设备文件传输)

MacDroid是一款适用于Mac OS的Android设备管理应用程序&#xff0c;它可以让用户轻松地将Android手机或平板电脑连接到Mac电脑&#xff0c;并在两者之间进行文件传输和数据同步。 通过MacDroid&#xff0c;用户可以通过USB或Wi-Fi连接&#xff0c;快速、简单地将文件从Android…

JVM第一讲:JVM相关知识体系详解+面试(P6熟练 P7精通)

JVM相关知识体系详解面试(P6熟练 P7精通) 面试时常常被面试官问到JVM相关的问题。本系列将给大家构建JVM核心知识点全局知识体系&#xff0c;本文是JVM第一讲&#xff0c;JVM相关知识体系详解和相关面试题梳理。 文章目录 JVM相关知识体系详解面试(P6熟练 P7精通)1、JVM学习建议…