点击事件为什么会失效_Spring事务原理?事务在方法间如何传播?为什么会失效?...

推荐学习

  • 疯狂膜拜!阿里出品Spring Security王者晋级文档
  • 肝了十天半月,献上纯手绘“Spring/Cloud/Boot/MVC”全家桶脑图
009dad5a8eda6f734bf6ecb5755fd7a8.png

前言

事务我们都知道是什么,而Spring事务就是在数据库之上利用AOP提供声明式事务编程式事务帮助我们简化开发,解耦业务逻辑和系统逻辑。但是Spring事务原理是怎样?事务在方法间是如何传播的?为什么有时候事务会失效?接下来咱就一一解答~重点分析Spring事务源码,让我们彻底搞懂Spring事务的原理。

正文

XML标签的解析

配置过事务的应该都不陌生,上面这个配置就是Spring开启事务注解(@Transactional)支持的配置,而看过我之前文章的应该知道,这个带前缀的标签叫自定义标签,我在之前的文章也分析过自定义标签的解析过程,所以这里我直接找到对应的handler:

public class TxNamespaceHandler extends NamespaceHandlerSupport {static 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);}@Overridepublic void init() {registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());}}

可以看到对应的注解解析器就是AnnotationDrivenBeanDefinitionParser类,在该类中一定会有一个parse方法:

public BeanDefinition parse(Element element, ParserContext parserContext) {registerTransactionalEventListenerFactory(parserContext);String mode = element.getAttribute("mode");if ("aspectj".equals(mode)) {// mode="aspectj"registerTransactionAspect(element, parserContext);if (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader())) {registerJtaTransactionAspect(element, parserContext);}}else {// mode="proxy"AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);}return null;}

首先拿到mode属性的值判断是使用AspectJ生成代理还是JDK生成代理,这里我们主要看proxy模式,进入configureAutoProxyCreator方法:

public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {// 注册AOP的入口类AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {Object eleSource = parserContext.extractSource(element);// Create the TransactionAttributeSource definition.// @Transactional注解的属性封装RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");sourceDef.setSource(eleSource);sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);// Create the TransactionInterceptor definition.// AOP执行链RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);interceptorDef.setSource(eleSource);interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);// 拿到transaction-manager属性的值registerTransactionManager(element, interceptorDef);interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);// Create the TransactionAttributeSourceAdvisor definition.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")) {advisorDef.getPropertyValues().add("order", element.getAttribute("order"));}parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));parserContext.registerComponent(compositeDef);}}

这里的流程比较长,但逻辑很简单。首先来看注册事务AOP入口类是哪个:

public static void registerAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {// 将优先级更高的AOP入口类放入到IOC容器中BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement));// 设置代理生成的方式以及是否缓存代理类到当前线程useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);registerComponentIfNecessary(beanDefinition, parserContext);}

主要看registerAutoProxyCreatorIfNecessary方法:

public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);}private static BeanDefinition registerOrEscalateApcAsRequired(Class> cls, BeanDefinitionRegistry registry, @Nullable Object source) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");// 判断传进来的类和ICO中当前存在的类哪个优先级更高,将更高的放入IOC中if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);if (!cls.getName().equals(apcDefinition.getBeanClassName())) {int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());int requiredPriority = findPriorityForClass(cls);if (currentPriority < requiredPriority) {apcDefinition.setBeanClassName(cls.getName());}}return null;}//把AOP入口类封装成beanDefinition对象,要实例化RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);beanDefinition.setSource(source);beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);//注解aop入口类的beanName名称 AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAMEregistry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);return beanDefinition;}

首先判断容器中是否已经存在AOP入口类,如果不存在则直接创建InfrastructureAdvisorAutoProxyCreator的BeanDefinition对象注册到容器中,这个类也是我之前分析的AOP入口类AbstractAutoProxyCreator的子类,再来看看其继承关系:

76bae540282a5ab47445f2c5f83f8a42.png

你会不会疑惑,这么多子类,到底会使用哪一个呢?回到刚刚的代码中,可以看到如果已经存在一个入口类了,就会通过findPriorityForClass获取两个类的优先级,最终就会使用优先级更大的那个,那么它们的优先级顺序是怎样的呢?

private static final List> APC_PRIORITY_LIST = new ArrayList<>(3);static {// Set up the escalation list...APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);}private static int findPriorityForClass(@Nullable String className) {// 索引即是优先级,越大优先级越高,IOC中只会存在一个事务AOP入口类for (int i = 0; i < APC_PRIORITY_LIST.size(); i++) {Class> clazz = APC_PRIORITY_LIST.get(i);if (clazz.getName().equals(className)) {return i;}}throw new IllegalArgumentException("Class name [" + className + "] is not a known auto-proxy creator class");}

可以看到,InfrastructureAdvisorAutoProxyCreator是优先级最低的,基本上不会起作用;AspectJAwareAdvisorAutoProxyCreator是当我们配置了标签时会注册,也就是xml配置的AOP的入口类;而AnnotationAwareAspectJAutoProxyCreator是当我们配置了或使用@EnableAspectJAutoProxy注解时注册,因此大部分情况下都是使用的AnnotationAwareAspectJAutoProxyCreator
注册完AOP的入口类后,回到configureAutoProxyCreator方法:

RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");sourceDef.setSource(eleSource);sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);String sourceName =parserContext.getReaderContext().registerWithGeneratedName(sourceDef);

AnnotationTransactionAttributeSource类的作用就是封装事务注解@Transactional的属性,这里需要记住其继承体系以及熟悉该类和其父类的属性和方法,对后面分析事物切面执行原理有帮助:

96a01f1ff7f930d8a590c41b5613a64e.png

紧接着就是创建了TransactionInterceptor对象,专门的事务拦截器,并且该类是MethodInterceptor的子类,看到这个应该不陌生了,我们知道AOP调用链在执行过程中主要就是调用该类的invoke的方法,因此它是事务切面执行的入口。既然有了Interceptor,那么必不可少的还应该有Advisor,而Advisor又是由AdvicePoincut组成的,这样才能构成一个完整的切面,所以该方法后面就是创建这两个对象。以上就是xml配置AOP注解支持的原理,很简单,下面再来看看零配置又是如何实现的。

AOP零配置原理

使用过SpringBoot的都知道,如果需要开启事务注解的支持,只需要一个注解就能搞定:@EnableTransactionManagement,不用再配置xml文件,这个又是怎么做到的呢?不多说,我们直接来看其源码:

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

在该注解下使用@Import导入了一个类TransactionManagementConfigurationSelector,首先该注解的作用就是导入一个类的实例到IOC容器中,你可能会说不是在类上加@Component注解就行了么,但是有些类它并不在你扫描的路径下,而该注解依然可以将其导入进来,所以我们主要看TransactionManagementConfigurationSelector类中做了些啥:

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector {@Overrideprotected 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;}}private String determineTransactionAspectClass() {return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);}}

可以看到在selectImports方法中返回了AutoProxyRegistrarProxyTransactionManagementConfiguration类,返回后会被封装为BeanDefinition对象,那这个方法是在哪里调用的呢?这个在之前的文章中也分析过,ConfigurationClassPostProcessor类中会调用ConfigurationClassParser类的parse方法解析@Configuration、@Import、@ImportSource等注解,具体过程这里就不再赘述了。我们继续来分别看看AutoProxyRegistrarProxyTransactionManagementConfiguration类:

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {private final Log logger = LogFactory.getLog(getClass());@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {boolean candidateFound = false;Set annoTypes = importingClassMetadata.getAnnotationTypes();for (String annoType : annoTypes) {AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);if (candidate == null) {continue;}Object mode = candidate.get("mode");Object proxyTargetClass = candidate.get("proxyTargetClass");if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&Boolean.class == proxyTargetClass.getClass()) {candidateFound = true;if (mode == AdviceMode.PROXY) {//注册事务AOP的入口类InfrastructureAdvisorAutoProxyCreator,实际上这个AOP入口类起不了作用AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);if ((Boolean) proxyTargetClass) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);return;}}}}}}public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {/** 明显是创建事务切面实例* BeanFactoryTransactionAttributeSourceAdvisor** */@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.getNumber("order"));}return advisor;}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionAttributeSource transactionAttributeSource() {return new AnnotationTransactionAttributeSource();}/** 创建事务advice* TransactionInterceptor* */@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;}}

看到这就很清楚了,前者是注册AOP的入口类(这里注册的入口类依然是InfrastructureAdvisorAutoProxyCreator),后者则是创建事务AOP的组件的实例到IOC中,到这里相信不仅仅是对于事务的零配置,而是整个SpringBoot的零配置实现原理都心中有数了。

总结

本篇结合之前所学分析了事务配置解析的原理,也带出了SpringBoot零配置实现的原理。我们需要在脑海将加载、解析和调用串联起来,从微观到宏观整体把握Spring,才能真正的理解Spring。

作者:夜勿语

原文链接:https://blog.csdn.net/l6108003/article/details/106650023

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

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

相关文章

wcdma系统随机接入过程的流程图_招聘电信协优初中级优化工程师、联通系统高级...

点击上方“通信人才网” 订阅&#xff01;招聘岗位招聘职位&#xff1a; 电信协优初中级优化工程师岗位类型&#xff1a;5G无线网络优化,4G-LTE无线网络优化,无线网优项目负责人,DT/CQT测试与分析,2G/3G无线网络优化工作地点&#xff1a; 西宁市,果洛藏族自治州,海南藏族自治州…

基于wincc的虚拟电梯设计_一文带你了解西门子整个虚拟调试与仿真软硬件体系...

写在面前大家好&#xff0c;我是小智&#xff0c;智能制造之家号主~经常有朋友关注到PLCSIM、SIMIT、Amesim、NX MCD、 Process Simulate、Plant Simulation等&#xff0c;但都不知道它们到底做啥的~因此今天对西门子仿真与虚拟调试进行整体介绍&#xff0c;力求让小白也能熟悉…

jedis使用 api_通过Jedis API使用排序集

jedis使用 api在上一篇文章中&#xff0c;我们开始研究Jedis API和Java Redis Client。 在这篇文章中&#xff0c;我们将研究Sorted Set&#xff08;zsets&#xff09;。 Sorted Set的工作方式类似于Set&#xff0c;它不允许重复的值。 最大的区别是&#xff0c;在“排序集”中…

三星ml1660拆机图解_炫龙DD2拆机,高性能的背后是怎样的面容?

双内存插槽 如标题所说&#xff0c;今天为大家带来炫龙DD2的图解拆机&#xff0c;废话少说&#xff0c;直接进入主题吧&#xff01;笔者对炫龙毁灭者DD2进行了简单拆解&#xff0c;拆解方式为打开D壳&#xff0c;查看散热模组和各部分硬件。炫龙毁灭者DD2延续了品牌一贯传统&am…

jwt-cli:一个用于解码JSON Web令牌(JWT令牌)的Shell库

当我开始经常需要解码JSON Web令牌时&#xff0c;我感到迫切需要编写允许我快速进行操作的程序。 有很多不错的选项&#xff0c;例如jwt.io &#xff0c;但是一旦您需要执行此操作&#xff0c;它通常就会变得笨拙。 而且&#xff0c;如果您需要处理多个令牌或进一步处理输出&am…

目录页码错误未定义书签怎么解决_目录页码对不齐应该怎么办?这2种方法,工作效率大增...

在制作文档目录时&#xff0c;你有没有遇到下图情况&#xff1a;右侧页码不对齐或者左侧文本不对齐&#xff0c;你一般是如何处理的&#xff0c;与大家分享相关的2种解决办法。工作效率大增&#xff01;1、目录页码不对齐该怎么办&#xff1f;遇到这种情况&#xff0c;一般是制…

三阶魔方大中小魔公式_三阶魔方的入门玩法教程|魔方玩法|魔方视频教程|魔方公式图解|--想成为魔方高手就来魔方乐园吧...

深圳魔方爱好者可以到实体店跟魔方老师学魔方了&#xff01;查询店铺地址在正式开始学习魔方还原前&#xff0c;请进入网站的首页&#xff0c;抽上10分钟的时间学习一些魔方的基础知识&#xff0c;这样会很有利于我们下面的学习&#xff0c;在还原的过程中有碰到疑问可以在线和…

初步了解Redis Streams以及如何在Java中使用它们

自今年年初以来&#xff0c;Redis Streams已进入Redis的unstable分支&#xff0c;并且第一个客户端始于采用Redis Streams API。 因此&#xff0c;这是一个绝佳的时机&#xff0c;可以从客户端角度看一下Redis Streams提供的功能以及如何使用它们。 免责声明&#xff1a;Redis…

权限丢失_不要让任何“账户权限”问题困扰你!

在Win10系统使用中时不时被“账户权限”问题困扰&#xff1f;别怕&#xff0c;有我呢~关闭更改系统相关设置时的通知不愿被安装软件、更改Windows设置时弹出的通知所打扰时&#xff0c;可以搜索【UAC】&#xff08;若没有搜索框&#xff0c;请使用【Win】【Q】快捷键调出&#…

python函数返回的元组_python – 从函数返回一个单独的元组元组

刚刚在Python中遇到了这样一些奇怪的事情&#xff0c;并且认为我会将其记录为一个问题&#xff0c;以防其他任何人试图用相同的无效搜索条件找到答案看起来像元组解包使得它不能返回长度为1的元组&#xff0c;如果您希望迭代返回值。虽然看起来似乎是欺骗。看到答案。>>&…

java 反射操作字段_x86上的Java最终字段没有操作?

java 反射操作字段我一直很乐于深入研究多线程编程的细节&#xff0c;并且尽管阅读了多年的CPU内存一致性模型&#xff0c;无等待和无锁算法&#xff0c;Java内存模型&#xff0c;实践中的Java并发性等知识&#xff0c;但我总是很喜欢。等等-我仍然会创建多线程编程错误。 总是…

卡方分布分位数_卡方检验和精确概率法及两两比较

看过许多统计教程&#xff0c;这篇是我最推荐的介 绍数值变量如果服从正态分布&#xff0c;采用均数标准差进行统计描述&#xff0c;采用方差分析进行组间比较&#xff0c;如果组间差异有统计学意义&#xff0c;进一步采用LSD法(也可以是其它方法)进行两两比较。如果不服从正…

arcgis渔网分割提取栅格图_【操作】ArcGIS中字段的合并、分割、提取

由于种种原因&#xff0c;可能涉及到要对ArcGIS属性字段进行合并、分割、提取等操作&#xff0c;我们可以在Excel中进行运算&#xff0c;然后连接到ArcGIS中&#xff0c;这里主要介绍下通过属性表字段计算器的操作方法。目标① 合并“名称”、“代码”字段内容到新建字段“用地…

ds排序--希尔排序_图解直接插入排序和希尔排序

前言这次我们介绍插入类排序中的 直接插入排序 和 希尔排序 。对于直接插入排序&#xff0c;虽然它的时间复杂度也是 O(n^2) &#xff0c;但是在元素 有序或近乎有序 的情况下&#xff0c;时间复杂度可以降为 O(n) &#xff0c;效率比 O(nlogn) 的算法还要高。然而对于大规模的…

jsf 配置_JSF Tomcat配置示例

jsf 配置JavaServer Faces (JSF)是一个Web应用程序框架&#xff0c;旨在简化基于Web的用户界面的开发集成。 它用于开发和构建服务器端用户界面组件&#xff0c;并在Web应用程序中使用它们。 JSF技术基于Model-View-Controller (MVC)架构&#xff0c;并且通过在页面中使用可重用…

tomcat8日志乱码_Tomcat输出日志乱码解决

请先检查一下是不是由于log4J没有指定编码方式所导致的.log4j.appender.File org.apache.log4j.DailyRollingFileAppender#重点是看一下有没有这个,如果没有则加上.#加上之后需要将之前的日志文件删除,否则有可能不会重新写入日志.log4j.appender.File.Encoding UTF-8log4j.a…

使用Datastax Java驱动程序与Cassandra进行交互

今天&#xff0c;我这次返回了更多的Cassandra和Java集成&#xff0c;重点是使用Datastax Java驱动程序&#xff0c;而不是我已经写了很多文章的Spring Data Cassandra。 Spring Data实际上使用了Datastax驱动程序来与Cassandra进行交互&#xff0c;但是在其之上还附带了一些额…

java forkjoin MySQL_Java并发fork-join框架

fork-join框架允许在几个工作进程中断某个任务&#xff0c;然后等待结果组合它们。 它在很大程度上利用了多处理器机器的生产能力。 以下是fork-join框架中使用的核心概念和对象。ForkFork是一个进程&#xff0c;其中任务将其分成可以并发执行的较小且独立的子任务。语法Sum le…

无法加载可扩展计数器_多核可扩展计数器

无法加载可扩展计数器到处都需要计数器&#xff0c;例如&#xff0c;查找应用程序的关键KPI&#xff0c;应用程序的负载&#xff0c;服务的请求总数&#xff0c;用于查找应用程序吞吐量的一些KPI等。 由于所有这些需求&#xff0c;并发复杂性也增加了&#xff0c;这使这个问题…

linux忘记mysql密码_linux下忘记mysql root密码解决办法 | 系统运维

引言&#xff1a;在linux系统中&#xff0c;如果忘记了MySQL的root密码&#xff0c;有没有办法重新设置新密码呢&#xff1f;答案是肯定的&#xff0c;下面教大家一个比较简单的重置MySQL root密码的办法&#xff1a;1、编辑MySQL配置文件my.cnf系统运维www.osyunwei.com温馨提…