Spring事务源码:创建代理类
参考文章:
《Spring事务源码解析之tx:annotation-driven标签解析》
《Spring 源码解析—事务执行》
参考资料:
《Spring AOP源码:开启注解读取》
《Spring AOP源码2:查找增强器》
《Spring AOP源码3:实现代理》
写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。
前言
本文我们介绍下Spring中的事务管理的源码,Spring的事务管理主要有申明式事务与编程式事务这两种,如果对这两种方式不了解的朋友可以先看下这篇文章(《Spring事务管理(详解+实例)》)然后再来深入源码分析。
我们通过如下方式开启事务,由于申明式事务的注入方式式基于代理方式实现的,因此这里也建议先了解下AOP的源码,这部分我前文已经做了完整的介绍,因此下文将不会对AOP的功能做深入的解析,有需要的朋友可以看下我之前AOP的文章。
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/>
</bean>
目录
前言
一、AnnotationDrivenBeanDefinitionParser
1、TxNamespaceHandler
2、AnnotationDrivenBeanDefinitionParser
二、、AopAutoProxyConfigurer
1、注册自动代理创建器
1.1、注册InfrastructureAdvisorAutoProxyCreator
1.2、设置proxy-target-class和expose-proxy属性
1.3、注册ComponentDefinition
2、注册了事务管理器等Bean
2.1、创建AnnotationTransactionAttributeSource
2.2、创建TransactionInterceptor
2.3、创建BeanFactoryTransactionAttributeSourceAdvisor
三、代理对象的创建
1、InfrastructureAdvisorAutoProxyCreator
2、canapply
2.1、getMethodMatcher
2.2、getTransactionAttribute
2.3、computeTransactionAttribute
2.4、findTransactionAttribute
2.5、parseTransactionAnnotation
补充
事务仅对 public 方法有效
一、AnnotationDrivenBeanDefinitionParser
在介绍IOC时我们讲过Spring通过spring.handlers配置文件处理自定义命名空间节点(《Spring IOC:parseCustomElement调用链》),从下面的配置中可以看出TX标签由TxNamespaceHandler类解析。
http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler
1、TxNamespaceHandler
TxNamespaceHandler中的init方法针对不通的属性配置实例化不通的解析对象,这里我们使用了annotation-driven,因此实例化的对象为AnnotationDrivenBeanDefinitionParser。
@Overridepublic void init() {registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());}
2、AnnotationDrivenBeanDefinitionParser
下面就来到了AnnotationDrivenBeanDefinitionParser的parse方法,因为Spring事务管理也是基于创建代理类来实现的,因此和AOP一样首先需要创建代理创建器。
@Overridepublic BeanDefinition parse(Element element, ParserContext parserContext) {registerTransactionalEventListenerFactory(parserContext);String mode = element.getAttribute("mode");// 判断是否配置了mode属性且为aspectj,满足条件则走aop切面的方式管理事务if ("aspectj".equals(mode)) {registerTransactionAspect(element, parserContext);}else {// 默认方式,配置自动代理创建器AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);}return null;}
二、、AopAutoProxyConfigurer
创建代理创建器的流程可以拆解成如下几个步骤,我们下面分别对其进行分析。
private static class AopAutoProxyConfigurer {public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {1、注册自动代理创建器2、注册了事务管理器等Bean }}
1、注册自动代理创建器
configureAutoProxyCreator调用registerAutoProxyCreatorIfNecessary注册自动代理创建器
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {// 1、创建代理创建器AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);2、注册了AnnotationTransactionAttributeSource、TransactionInterceptor和BeanFactoryTransactionAttributeSourceAdvisor }
registerAutoProxyCreatorIfNecessary分为三个步骤
(1)注册InfrastructureAdvisorAutoProxyCreator
(2)设置proxy-target-class和expose-proxy属性
(3)注册ComponentDefinition
public static void registerAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {// 向容器中注册InfrastructureAdvisorAutoProxyCreatorBeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement));// 设置proxy-target-class和expose-proxy配置useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);// 注册ComponentDefinitionregisterComponentIfNecessary(beanDefinition, parserContext);}
1.1、注册InfrastructureAdvisorAutoProxyCreator
很简单的方法,向容器中注册固定的BeanDefinition。
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);}
1.2、设置proxy-target-class和expose-proxy属性
解析标签中的proxy-target-class和expose-proxy属性值。proxy-target-class主要控制是使用Jdk代理还是Cglib代理实现,expose-proxy用于控制是否将生成的代理类的实例防御AopContext中,并且暴露给相关子类使用。
public static final String PROXY_TARGET_CLASS_ATTRIBUTE = "proxy-target-class";private static final String EXPOSE_PROXY_ATTRIBUTE = "expose-proxy";private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {if (sourceElement != null) {boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));if (proxyTargetClass) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));if (exposeProxy) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);}}}
1.3、注册ComponentDefinition
public static final String AUTO_PROXY_CREATOR_BEAN_NAME ="org.springframework.aop.config.internalAutoProxyCreator";private static void registerComponentIfNecessary(BeanDefinition beanDefinition, ParserContext parserContext) {if (beanDefinition != null) {parserContext.registerComponent(new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME));}}
2、注册了事务管理器等Bean
public static final String TRANSACTION_ADVISOR_BEAN_NAME ="org.springframework.transaction.config.internalTransactionAdvisor";public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);//1、注册自动代理创建器AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);// 判断容器中有无internalTransactionAdvisorString txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {//2、注册了事务管理器等Bean 创建AnnotationTransactionAttributeSource创建TransactionInterceptor创建BeanFactoryTransactionAttributeSourceAdvisor}}}
2.1、创建AnnotationTransactionAttributeSource
Object eleSource = parserContext.extractSource(element);
// 创建并注册AnnotationTransactionAttributeSource的BeanDefinition
RootBeanDefinition sourceDef = new RootBeanDefinition(
"org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
sourceDef.setSource(eleSource);
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
2.2、创建TransactionInterceptor
这一步创建了TransactionInterceptor的BeanDefinition,并将上一步创建的AnnotationTransactionAttributeSource作为transactionAttributeSource属性注入了进去,这个属性后面创建代理类的时候将会用到。
另外调用了registerTransactionManager创建了事务管理器。
RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
interceptorDef.setSource(eleSource);
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registerTransactionManager(element, interceptorDef);
interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
registerTransactionManager创建了事务管理器,其实就是添加事务管理器的beanName。
private static void registerTransactionManager(Element element, BeanDefinition def) {def.getPropertyValues().add("transactionManagerBeanName",TxNamespaceHandler.getTransactionManagerName(element));}
事务管理器的beanName在不指定transaction-manager属性的时候,会默认寻找id固定名为transactionManager的bean作为事务管理器。
// TxNamespaceHandler.javastatic final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";static String getTransactionManagerName(Element element) {return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);}
2.3、创建BeanFactoryTransactionAttributeSourceAdvisor
创建BeanFactoryTransactionAttributeSourceAdvisor的BeanDefinition,同时将前两个步骤中创建的AnnotationTransactionAttributeSource、TransactionInterceptor作为属性添加到当前Bean中,最后注册到容器中。
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
advisorDef.setSource(eleSource);
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
if (element.hasAttribute("order")) {dvisorDef.getPropertyValues().add("order", element.getAttribute("order"));}
parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);
总结一下,这两部其实主要是注册了InfrastructureAdvisorAutoProxyCreator与BeanFactoryTransactionAttributeSourceAdvisor,可以从名字看出来一个是代理自动创建器,一个是增强器。
三、代理对象的创建
1、InfrastructureAdvisorAutoProxyCreator
InfrastructureAdvisorAutoProxyCreator类的继承关系如下图,可以发现其和我们之前介绍AOP时提到的AnnotationAwareAspectJAutoProxyCreator一样继承了AbstractAdvisorAutoProxyCreator。
抽象类AbstractAdvisorAutoProxyCreator继承了BeanPostProcessor接口,因此在Bean实例化的时候会去为其创建代理对象。这部分内容如果不熟悉可以看下我之前的文章(《Spring IOC:bean的生命周期与@Autowire(3)》、《Spring AOP源码2:查找增强器》)
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean != null) {// 如果是普通bean,则返回beanName,如果是FactoryBean,则返回加上前缀&的&beanNameObject cacheKey = getCacheKey(bean.getClass(), beanName);// 判断当前bean是否需要被代理,如果需要则进行封装(earlyProxyReferences中缓存的是已经创建好的代理对象)if (!this.earlyProxyReferences.contains(cacheKey)) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;
}
2、canapply
在创建代理类时,会先查找能够应用在当前bean上的增强器,这部分内容我们前文有过介绍就不赘述了,这里直接从如何判断是否适配的判断方法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;/* 判断当前增强是否可以应用在当前bean上 */return canApply(pca.getPointcut(), targetClass, hasIntroductions);}else {return true;}
}
这里的advisor就是我们的增强器BeanFactoryTransactionAttributeSourceAdvisor,而pca.getPointcut()就是获取其内部创建的TransactionAttributeSourcePointcut。
@Overridepublic Pointcut getPointcut() {return this.pointcut;}private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {@Overrideprotected TransactionAttributeSource getTransactionAttributeSource() {return transactionAttributeSource;}};
该对象重写了getTransactionAttributeSource方法,获取的就是BeanFactoryTransactionAttributeSourceAdvisor在注册时添加的transactionAttributeSource属性,其实现类为AnnotationTransactionAttributeSource。
// configureAutoProxyCreator方法RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");sourceDef.setSource(eleSource);sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
继续往下深入,调用pc.getMethodMatcher()获取方法匹配器,这里的pc就是上文中的TransactionAttributeSourcePointcut类。
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {Assert.notNull(pc, "Pointcut must not be null");// 获取当前Advisor的CalssFilter,并且调用其matches()方法判断当前切点表达式是否与目标bean匹配,// 这里ClassFilter指代的切点表达式主要是当前切面类上使用的@Aspect注解中所指代的切点表达式if (!pc.getClassFilter().matches(targetClass)) {return false;}// 判断如果当前Advisor所指代的方法的切点表达式如果是对任意方法都放行,则直接返回MethodMatcher methodMatcher = pc.getMethodMatcher();if (methodMatcher == MethodMatcher.TRUE) {// No need to iterate the methods if we're matching any method anyway...return true;}IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;if (methodMatcher instanceof IntroductionAwareMethodMatcher) {introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;}// 获取目标类的所有接口Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));classes.add(targetClass);// 遍历目标类上的接口方法for (Class<?> clazz : classes) {Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);for (Method method : methods) {// 使用matches判断能否作用于该方法上if ((introductionAwareMethodMatcher != null &&introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||methodMatcher.matches(method, targetClass)) {return true;}}}return false;}
2.1、getMethodMatcher
TransactionAttributeSourcePointcut的getMethodMatcher方法是在它的父类StaticMethodMatcherPointcut中实现的。
public final MethodMatcher getMethodMatcher() {return this;
}
该方法直接返回了this,也就是下面methodMatcher.matches方法就是调用TransactionAttributeSourcePointcut的matches方法。getTransactionAttributeSource即我们之前说过的AnnotationTransactionAttributeSource,getTransactionAttribute则是调用的其父类AbstractFallbackTransactionAttributeSource的方法。
public boolean matches(Method method, Class<?> targetClass) {if (TransactionalProxy.class.isAssignableFrom(targetClass)) {return false;}TransactionAttributeSource tas = getTransactionAttributeSource();return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}
2.2、getTransactionAttribute
getTransactionAttribute中判断有无缓存,如果缓存不存在则调用computeTransactionAttribute方法查找事务属性,获取后进行缓存。
public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {if (method.getDeclaringClass() == Object.class) {return null;}// 先查询缓存Object cacheKey = getCacheKey(method, targetClass);Object cached = this.attributeCache.get(cacheKey);if (cached != null) {if (cached == NULL_TRANSACTION_ATTRIBUTE) {return null;}else {return (TransactionAttribute) cached;}}else {// 获取事务属性 TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);// 事务属性不存在if (txAttr == null) {this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);}else {String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);if (txAttr instanceof DefaultTransactionAttribute) {((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);}if (logger.isDebugEnabled()) {logger.debug("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);}// 将解析好的事务属性存入缓存this.attributeCache.put(cacheKey, txAttr);}return txAttr;}
}
2.3、computeTransactionAttribute
computeTransactionAttribute方法会依次从方法上和类上查找事务申明,使用的是findTransactionAttribute方法。
这里注意下第一行的内容,只对public的方法可继续往下走,这就解释了为什么事务只在public方法上生效。
protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}Class<?> userClass = ClassUtils.getUserClass(targetClass);// method为接口中的方法,specificMethod为具体类中的方法Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);// 查看方法中是否存在事务声明 TransactionAttribute txAttr = findTransactionAttribute(specificMethod);if (txAttr != null) {return txAttr;}// 查看方法所在类中是否存在事务声明 txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}if (specificMethod != method) {// 查看接口方法中是否存在事务声明txAttr = findTransactionAttribute(method);if (txAttr != null) {return txAttr;}// 查看接口类中是否存在事务声明txAttr = findTransactionAttribute(method.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}}return null;
}
2.4、findTransactionAttribute
findTransactionAttribute方法遍历所有的事务注解解析器,判断哪一个能用来进行解析,这里默认使用的是Spring的事务注解解析器SpringTransactionAnnotationParser。
protected TransactionAttribute findTransactionAttribute(Method method) {return determineTransactionAttribute(method);
}protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {return determineTransactionAttribute(clazz);
}protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {if (ae.getAnnotations().length > 0) {// 遍历事务注解解析器for (TransactionAnnotationParser annotationParser : this.annotationParsers) {// 解析事务注解TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);if (attr != null) {return attr;}}}return null;
}
this.annotationParsers是在AnnotationTransactionAttributeSource类初始化的时候初始化的
public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {this.publicMethodsOnly = publicMethodsOnly;this.annotationParsers = new LinkedHashSet<TransactionAnnotationParser>(2);// 添加Spring事务注解解析器this.annotationParsers.add(new SpringTransactionAnnotationParser());// 添加JTA事务注解解析器if (jta12Present) { this.annotationParsers.add(new JtaTransactionAnnotationParser());}// 添加EJB事务注解解析器if (ejb3Present) {this.annotationParsers.add(new Ejb3TransactionAnnotationParser());}
}
2.5、parseTransactionAnnotation
annotationParser.parseTransactionAnnotation调用了SpringTransactionAnnotationParser的parseTransactionAnnotation方法,内部又调用了parseTransactionAnnotation。
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);if (attributes != null) {// 解析事务注解属性return parseTransactionAnnotation(attributes);}else {return null;}
}
parseTransactionAnnotation方法则解析了解析注解@Transactional注解中声明的各种属性。
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();Propagation propagation = attributes.getEnum("propagation");rbta.setPropagationBehavior(propagation.value());Isolation isolation = attributes.getEnum("isolation");rbta.setIsolationLevel(isolation.value());rbta.setTimeout(attributes.getNumber("timeout").intValue());rbta.setReadOnly(attributes.getBoolean("readOnly"));rbta.setQualifier(attributes.getString("value"));ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<RollbackRuleAttribute>();Class<?>[] rbf = attributes.getClassArray("rollbackFor");for (Class<?> rbRule : rbf) {RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);rollBackRules.add(rule);}String[] rbfc = attributes.getStringArray("rollbackForClassName");for (String rbRule : rbfc) {RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);rollBackRules.add(rule);}Class<?>[] nrbf = attributes.getClassArray("noRollbackFor");for (Class<?> rbRule : nrbf) {NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);rollBackRules.add(rule);}String[] nrbfc = attributes.getStringArray("noRollbackForClassName");for (String rbRule : nrbfc) {NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);rollBackRules.add(rule);}rbta.getRollbackRules().addAll(rollBackRules);return rbta;
}
到这里,我们就找除了适配于当前Bean的增强,结合之前AOP的内容,整个步骤结束后会生成该Bean的代理类,在方法执行时调用的事务的生效正是由代理类来完成的,具体的事务控制我们将在下文讲解。
补充
事务仅对 public 方法有效
在本文内容的computeTransactionAttribute方法中,第一行在读取注解上的属性方法中,会优先判断方法是否是 public,如果不是 public,就不会读取事务配置信息。
因此@Transactional注解只能在public方法上生效。
相关文章:

Spring事务源码:创建代理类
参考文章: 《Spring事务源码解析之tx:annotation-driven标签解析》 《Spring 源码解析—事务执行》 参考资料: 《Spring AOP源码:开启注解读取》 《Spring AOP源码2:查找增强器》 《Spring AOP源码3:实现代理》 …...
java14 使用增强的模式匹配切换表达式
野旷天低树,江清月近人。——唐代杜甫《月夜忆舍弟》 使用增强的模式匹配切换表达式(Switch Expressions with Enhanced Pattern Matching) Java 14中引入的“Switch Expressions with Enhanced Pattern Matching”这个功能。 这个功能可以让我们在使用switch cas…...
python【正则表达式】
正则表达式 1.正则的作用 正则表达式式一种可以让复杂的字符串变得简单的工具。 写正则表达式的时候就是用正则符号来描述字符串规则。 2.正则语法 需要导入模块 from re import fullmatch, findall, search2.1.第一类:匹配类符号 1)普通字符—在…...
Ubuntu常见系统问题解决方式
Ubuntu常见系统问题解决方式Ubuntu每次开机后提示检测到系统程序出现问题的解决方法Ubuntu循环登陆问题问题描述原因解决方法文件夹打开缓慢Ubuntu启动后GUI界面卡住不动Ubuntu18.04使用过程中常遇到的问题Ubuntu每次开机后提示检测到系统程序出现问题的解决方法 首先…...

C/C++中的虚拟内存
文章目录一、虚拟内存二、C中的虚拟内存分配模型三、C中的虚拟内存分配模型四、堆区和栈区的区别一、虚拟内存 虚拟内存是一种实现在计算机软硬件之间的内存管理技术,它将程序使用到的内存地址(虚拟地址)映射到计算机内存中的物理地址&#…...
Qt C++与Python混合编程:补充错误
在提示中,需要引用Python.h,出现错误。 1、找不到Python.h 如果是pro工程,需要在里面配置; INCLUDEPATH /Users/xinnianwang/opt/anaconda3/include LIBS /Users/xinnianwang/opt/anaconda3/lib 如果是CMakeLists.txt需要配…...

2023-04-01:当Go语言遇见FFmpeg视频解码器,使用Go语言改写decode_video.c文件,提升视频解码效率与开发体验。
2023-04-01:当Go语言遇见FFmpeg视频解码器,使用Go语言改写decode_video.c文件,提升视频解码效率与开发体验。 答案2023-04-01: 步骤如下: 1.导入必要的依赖库,包括 fmt、os、unsafe 和其它 FFmpeg 库相关…...

Solidity 学习笔记
主要参考网上资料学习,个人学习笔记有删改,参考出处在文末列出。 0 基础 IDE: remixType Bool: bool public _bool true; 默认false;整型:int、uint、uint256,默认0;地址类型:address,分为 payable 和普…...

ThreadLocal原理
关键点总结: ThreadLocal更像是对其他类型变量的一层包装,通过ThreadLocal的包装使得该变量可以在线程之间隔离和当前线程全局共享。在Thread中有一个threadLocals变量,类型为ThreadLocal.ThreadLocalMap,ThreadLocalMap中key是Th…...
串操作指令详解 MOVS,LODS,STOS,CMPS,SCAS,REP
指令包括:MOVS,LODS,STOS,CMPS,SCAS,REP 串的概念:串是连续存放再内存中的字节块或字块。每个串有一个起始地址和长度, 待操作的数据串称为源串,目的地址称为目标串 目录…...

Java实现判断素数
1 问题 判断101-200之间有多少个素数,并输出所有素数。 2 方法 package homework04; public class Test05 { public static void main(String[] args) { for (int i 101; i < 201; i) { boolean flag true; for (int j 2; j…...

PHP初级教程------------------(2)
目录 运算符 赋值运算符 算术运算符 比较运算符 逻辑运算符 连接运算符 错误抑制符 三目运算符 自操作运算符 编辑 计算机码 位运算符 运算符优先级 流程控制 控制分类 顺序结构 分支结构 If分支 Switch分支 循环结构 For循环 while循环 do-while循环 循环控制 …...
【SQL开发实战技巧】系列(三十五):数仓报表场景☞根据条件返回不同列的数据以及Left /Full Join注意事项
系列文章目录 【SQL开发实战技巧】系列(一):关于SQL不得不说的那些事 【SQL开发实战技巧】系列(二):简单单表查询 【SQL开发实战技巧】系列(三):SQL排序的那些事 【SQL开发实战技巧…...

springBoot自动配置过程介绍
什么是自动配置 以前整合spring mybatis框架时候,需要加很多的bean, 比如说sqlSessionFactory等等 现在springboot帮我们干了,我们只需要引入对应的starter就可以了。 springBoot可以帮我们配置好了一些bean. 如mysql, mogondb相关操作等等ÿ…...

PostgreSQL最后的救命稻草 — pg_resetwal
pg_resetwal— 重置 PostgreSQL 数据库集群的预写日志和其他控制信息 适用版本:PostgreSQL 12/13/14/15语法 pg_resetwal [ -f | --force ] [ -n | --dry-run ] [option...] [ -D | --pgdata ]datadir描述pg_resetwal清除预写日志 WAL,并可选地重置pg_c…...

彻底关闭Windows更新
一、关闭Windows Update服务 1、按“Windows R”键,打开运行对话框,并输入“services.msc”,然后再单击“确定”。 2、在弹出的服务窗口中,找到“Windows Update”选项并双击打开它。 3、在弹出的“Windows Update的属性”对话框…...

Java正则表达式语法
Java正则表达式的语法与示例 | |目录 1匹配验证-验证Email是否正确 2在字符串中查询字符或者字符串 3常用正则表达式 4正则表达式语法 1匹配验证-验证Email是否正确 public static void main(String[] args) { // 要验证的字符串 String str "servicexsoftlab.net&q…...
【2023-3-29】JavaScript使用promise顺序调用函数并抛出异常
JavaScript使用promise顺序调用函数并抛出异常 场景 新建或者编辑时,一个页面中存在多个表单,每个表单都有单独进行表单验证。点击提交时,若有一个表单校验失败,则不能提交。 ps:为啥不放在一个表单中? (…...

Python实现GWO智能灰狼优化算法优化随机森林分类模型(RandomForestClassifier算法)项目实战
说明:这是一个机器学习实战项目(附带数据代码文档视频讲解),如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 灰狼优化算法(GWO),由澳大利亚格里菲斯大学学者 Mirjalili 等人于2014年提出来的一种群智能…...
从redis到epoll到mmap
redis为什么这么快? 比较容易答出的答案 1)纯粹的内存操作 2)单线程操作,不用考虑线程切换 其他优势 3)I/O 多路复用,使用epoll 4)Reactor 设计模式 I/O 多路复用有三种 select、poll、epoll select:使用数组存储轮询 poll:使用链表轮询 epo…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...

如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...
深入理解 React 样式方案
React 的样式方案较多,在应用开发初期,开发者需要根据项目业务具体情况选择对应样式方案。React 样式方案主要有: 1. 内联样式 2. module css 3. css in js 4. tailwind css 这些方案中,均有各自的优势和缺点。 1. 方案优劣势 1. 内联样式: 简单直观,适合动态样式和…...

Qwen系列之Qwen3解读:最强开源模型的细节拆解
文章目录 1.1分钟快览2.模型架构2.1.Dense模型2.2.MoE模型 3.预训练阶段3.1.数据3.2.训练3.3.评估 4.后训练阶段S1: 长链思维冷启动S2: 推理强化学习S3: 思考模式融合S4: 通用强化学习 5.全家桶中的小模型训练评估评估数据集评估细节评估效果弱智评估和民间Arena 分析展望 如果…...
算法刷题-回溯
今天给大家分享的还是一道关于dfs回溯的问题,对于这类问题大家还是要多刷和总结,总体难度还是偏大。 对于回溯问题有几个关键点: 1.首先对于这类回溯可以节点可以随机选择的问题,要做mian函数中循环调用dfs(i&#x…...

Python异步编程:深入理解协程的原理与实践指南
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 持续学习,不断…...