Spring AOP 实现原理

什么是AOP

AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。

使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”

实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。

AOP使用场景

AOP用来封装横切关注点,具体可以在下面的场景中使用:

Authentication 权限

Caching 缓存

Context passing 内容传递

Error handling 错误处理

Lazy loading 懒加载

Debugging  调试

logging, tracing, profiling and monitoring 记录跟踪 优化 校准

Performance optimization 性能优化

Persistence  持久化

Resource pooling 资源池

Synchronization 同步

Transactions 事务

AOP相关概念

方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的 Advisor或拦截器实现。

连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。

通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice

切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上

引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。Spring中要使用Introduction, 可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口

目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO

AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

Spring AOP组件

下面这种类图列出了Spring中主要的AOP组件

img

如何使用Spring AOP

可以通过配置文件或者编程的方式来使用Spring AOP。

配置可以通过xml文件来进行,大概有四种方式:

  1. 配置ProxyFactoryBean,显式地设置advisors, advice, target等

  2. 配置AutoProxyCreator,这种方式下,还是如以前一样使用定义的bean,但是从容器中获得的其实已经是代理对象

  3. 通过<aop:config>来配置

  4. 通过<aop: aspectj-autoproxy>来配置,使用AspectJ的注解来标识通知及切入点

也可以直接使用ProxyFactory来以编程的方式使用Spring AOP,通过ProxyFactory提供的方法可以设置target对象, advisor等相关配置,最终通过 getProxy()方法来获取代理对象

具体使用的示例可以google. 这里略去

Spring AOP代理对象的生成

Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。下面我们来研究一下Spring如何使用JDK来生成代理对象,具体的生成代码放在JdkDynamicAopProxy这个类中,直接上相关代码:

/*** <ol>* <li>获取代理类要实现的接口,除了Advised对象中配置的,还会加上SpringProxy, Advised(opaque=false)* <li>检查上面得到的接口中有没有定义 equals或者hashcode的接口* <li>调用Proxy.newProxyInstance创建代理对象* </ol>*/public Object getProxy(ClassLoader classLoader) {if (logger.isDebugEnabled()) {logger.debug("Creating JDK dynamic proxy: target source is " +this.advised.getTargetSource());}Class[] proxiedInterfaces =AopProxyUtils.completeProxiedInterfaces(this.advised);findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

那这个其实很明了,注释上我也已经写清楚了,不再赘述。

下面的问题是,代理对象生成了,那切面是如何织入的?

我们知道InvocationHandler是JDK动态代理的核心,生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法。而通过JdkDynamicAopProxy的签名我们可以看到这个类其实也实现了InvocationHandler,下面我们就通过分析这个类中实现的invoke()方法来具体看下Spring AOP是如何织入切面的。

publicObject invoke(Object proxy, Method method, Object[] args) throwsThrowable {MethodInvocation invocation = null;Object oldProxy = null;boolean setProxyContext = false;TargetSource targetSource = this.advised.targetSource;Class targetClass = null;Object target = null;try {//eqauls()方法,具目标对象未实现此方法if (!this.equalsDefined && AopUtils.isEqualsMethod(method)){return (equals(args[0])? Boolean.TRUE : Boolean.FALSE);}//hashCode()方法,具目标对象未实现此方法if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)){return newInteger(hashCode());}//Advised接口或者其父接口中定义的方法,直接反射调用,不应用通知if (!this.advised.opaque &&method.getDeclaringClass().isInterface()&&method.getDeclaringClass().isAssignableFrom(Advised.class)) {// Service invocations onProxyConfig with the proxy config...return AopUtils.invokeJoinpointUsingReflection(this.advised,method, args);}Object retVal = null;if (this.advised.exposeProxy) {// Make invocation available ifnecessary.oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}//获得目标对象的类target = targetSource.getTarget();if (target != null) {targetClass = target.getClass();}//获取可以应用到此方法上的Interceptor列表List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);//如果没有可以应用到此方法的通知(Interceptor),此直接反射调用 method.invoke(target, args)if (chain.isEmpty()) {retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);} else {//创建MethodInvocationinvocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);retVal = invocation.proceed();}// Massage return value if necessary.if (retVal != null && retVal == target &&method.getReturnType().isInstance(proxy)&&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {// Special case: it returned"this" and the return type of the method// is type-compatible. Notethat we can't help if the target sets// a reference to itself inanother returned object.retVal = proxy;}return retVal;} finally {if (target != null && !targetSource.isStatic()) {// Must have come fromTargetSource.targetSource.releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}}}

主流程可以简述为:获取可以应用到此方法上的通知链(Interceptor Chain),如果有,则应用通知,并执行joinpoint; 如果没有,则直接反射执行joinpoint。而这里的关键是通知链是如何获取的以及它又是如何执行的,下面逐一分析下。

首先,从上面的代码可以看到,通知链是通过Advised.getInterceptorsAndDynamicInterceptionAdvice()这个方法来获取的,我们来看下这个方法的实现:

public List<Object>getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {MethodCacheKeycacheKey = new MethodCacheKey(method);List<Object>cached = this.methodCache.get(cacheKey);if(cached == null) {cached= this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this,method, targetClass);this.methodCache.put(cacheKey,cached);}returncached;}

可以看到实际的获取工作其实是由AdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice()这个方法来完成的,获取到的结果会被缓存。

下面来分析下这个方法的实现:

/*** 从提供的配置实例config中获取advisor列表,遍历处理这些advisor.如果是IntroductionAdvisor,* 则判断此Advisor能否应用到目标类targetClass上.如果是PointcutAdvisor,则判断* 此Advisor能否应用到目标方法method上.将满足条件的Advisor通过AdvisorAdaptor转化成Interceptor列表返回.*/publicList getInterceptorsAndDynamicInterceptionAdvice(Advised config, Methodmethod, Class targetClass) {// This is somewhat tricky... we have to process introductions first,// but we need to preserve order in the ultimate list.List interceptorList = new ArrayList(config.getAdvisors().length);//查看是否包含IntroductionAdvisorboolean hasIntroductions = hasMatchingIntroductions(config,targetClass);//这里实际上注册一系列AdvisorAdapter,用于将Advisor转化成MethodInterceptorAdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();Advisor[] advisors = config.getAdvisors();for (int i = 0; i <advisors.length; i++) {Advisor advisor = advisors[i];if (advisor instanceof PointcutAdvisor) {// Add it conditionally.PointcutAdvisor pointcutAdvisor= (PointcutAdvisor) advisor;if(config.isPreFiltered() ||pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {//TODO: 这个地方这两个方法的位置可以互换下//将Advisor转化成InterceptorMethodInterceptor[]interceptors = registry.getInterceptors(advisor);//检查当前advisor的pointcut是否可以匹配当前方法MethodMatcher mm =pointcutAdvisor.getPointcut().getMethodMatcher();if (MethodMatchers.matches(mm,method, targetClass, hasIntroductions)) {if(mm.isRuntime()) {// Creating a newobject instance in the getInterceptors() method// isn't a problemas we normally cache created chains.for (intj = 0; j < interceptors.length; j++) {interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptors[j],mm));}} else {interceptorList.addAll(Arrays.asList(interceptors));}}}} else if (advisor instanceof IntroductionAdvisor){IntroductionAdvisor ia =(IntroductionAdvisor) advisor;if(config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {Interceptor[] interceptors= registry.getInterceptors(advisor);interceptorList.addAll(Arrays.asList(interceptors));}} else {Interceptor[] interceptors =registry.getInterceptors(advisor);interceptorList.addAll(Arrays.asList(interceptors));}}return interceptorList;
}

这个方法执行完成后,Advised中配置能够应用到连接点或者目标类的Advisor全部被转化成了MethodInterceptor.

接下来我们再看下得到的拦截器链是怎么起作用的。

if (chain.isEmpty()) {retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);} else {//创建MethodInvocationinvocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);retVal = invocation.proceed();}

​ 从这段代码可以看出,如果得到的拦截器链为空,则直接反射调用目标方法,否则创建MethodInvocation,调用其proceed方法,触发拦截器链的执行,来看下具体代码

public Object proceed() throws Throwable {//  We start with an index of -1and increment early.if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()- 1) {//如果Interceptor执行完了,则执行joinPointreturn invokeJoinpoint();}Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);//如果要动态匹配joinPointif (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher){// Evaluate dynamic method matcher here: static part will already have// been evaluated and found to match.InterceptorAndDynamicMethodMatcher dm =(InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;//动态匹配:运行时参数是否满足匹配条件if (dm.methodMatcher.matches(this.method, this.targetClass,this.arguments)) {//执行当前Intercetporreturndm.interceptor.invoke(this);}else {//动态匹配失败时,略过当前Intercetpor,调用下一个Interceptorreturn proceed();}}else {// It's an interceptor, so we just invoke it: The pointcutwill have// been evaluated statically before this object was constructed.//执行当前Intercetporreturn ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}
}

代码也比较简单,这里不再赘述。

理解:

面向对象编程更多的操作是在纵向部分(即继承,接口实现之类),这就导致一些需要在横向上(即业务代码方法中的前后)嵌入的非核心代码得在每一个方法上都要去写(比如日志,权限,异常处理等)。它们散布在各方法的横切面上,造成代码重复,也不利于各个模块的重用(毕竟,不同方法还是有所区别)。 AOP就是为了解决这种男题而生的 从AOP这个英文缩写来看就好。。。A是一把刀,把P的突出部分切出来(类比于围绕方法设定的日志,权限等需求,它们都是属于核心方法外的通用服务),它们有一个共性----圆溜溜的(就像一个工具箱中的扳手,钳子,螺丝刀之类的),所以能把它们集合成一块儿(它们都具有’工具‘的属性),就是中间的O。重新给接回去的时候,就着不同的需求,用O中不同的工具就好(通过不同的方法或注解指明)。 概念陈列: 目标对象,AOP代理对象,连接点,切入点,拦截器,通知,织入, 假设有一个对象A(目标对象),外部的请求人B要想访问到A,需要通过一个安检过程(连接点,比如验证权限m1,登录密码m2,身份识别m3等)。B开始访问后,首先得经过第一层的安检(准备走谁(introductionInterceptor)的哪一层安检(PointCut–》指定到具体的安检流程),由你定义的interceptor拦截器决定),即权限验证m1(切入点)。通过这一层后,监控整个访问过程的你可以决定是否要向大家伙儿通报外部请求的访问情况【像:B那孙子进来啦 OR B那孙子带着贪玩蓝月系来嘞 OR B那小子是渣渣辉的部下】(在访问开始前,还是结束后,还是全程播报---->这就是’通知‘)。于A而言,他觉得直接跟B接触可能不太安全,所以A把自己的一些权限给到了代理对象Proxy_A,并让Proxy_A去正面’刚(也即织入,A间接的给自己加持了一副铠甲)‘B(或许是来者不善乜)。Proxy_A是怎样产生的呢?这就是AOP动态代理的辅助了。简单来讲,不论你是什么代理—》Proxy_某个目标对象,只要是通过JDK或者CGLib的代理副本传送门(类比于抽象)进入到刚B的’对战场景‘中,那么,他都算是A(或者其他目标对象)的代言人。

理解:

aop切面编程就是在常规的执行java类中方法前或执行后加入自定义的方法。比如你本来每天都去打酱油,去,打酱油,回。现在我每天在你打酱油路上等着,你去打酱油的时候我打你一顿,回来的时候给你点糖果吃。你根本不知道为什么我会在路上拦住打你。所以在切面中插入你自定义的方法,这个方法的执行和本身要执行的类方法无关系,也就是不是这个类的方法来调用你写的方法的,你写的方法什么时候执行都是要通过在配置指定。我打完你,你该打酱油还是去打酱油,当然我如果是拦住你让你酱油打少点,你打酱油的时候还是会打那么多,但是在你打完酱油回来的时候我可以把你的酱油倒些出去,所以嵌入的自定义方法对要调用的类方法本身没有影响,但是可以操纵这个方法的返结果或者处理结果。

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

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

相关文章

java string类的方法_Java-String类的常用方法总结

一、String类String类在java.lang包中&#xff0c;java使用String类创建一个字符串变量&#xff0c;字符串变量属于对象。java把String类声明的final类&#xff0c;不能有类。String类对象创建后不能修改&#xff0c;由0或多个字符组成&#xff0c;包含在一对双引号之间二、Str…

python爬虫入门实战---------一周天气预报爬取_Python爬虫入门实战--------一周天气预报爬取【转载】【没有分析...

Python爬虫入门实战--------一周天气预报爬取【转载】【没有分析 Python爬虫入门实战--------一周天气预报爬取【转载】【没有分析】 来源&#xff1a;https://blog.csdn.net/qq_40705355/article/details/83856960七天天气来源&#xff1a;http://www.weather.com.cn/weather/…

Spring IOC原理总结

Spring容器高层视图 Spring 启动时读取应用程序提供的Bean配置信息&#xff0c;并在Spring容器中生成一份相应的Bean配置注册表&#xff0c;然后根据这张注册表实例化Bean&#xff0c;装配好Bean之间的依赖关系&#xff0c;为上层应用提供准备就绪的运行环境。 Bean缓存池&…

Linux开发工具——gcc篇

gcc的使用 文章目录 gcc的使用 历史遗留问题&#xff08;普通用户sudo&#xff09; gcc编译过程 预处理&#xff08;进行宏替换&#xff09; 编译&#xff08;生成汇编&#xff09; 汇编&#xff08;生成机器可识别代码&#xff09; 链接&#xff08;生成可执行文件或库文件&a…

Spring中ioc的实现原理

学习过Spring框架的人一定都会听过Spring的IoC(控制反转) 、DI(依赖注入)这两个概念&#xff0c;对于初学Spring的人来说&#xff0c;总觉得IoC 、DI这两个概念是模糊不清的&#xff0c;是很难理解的&#xff0c;今天和大家分享网上的一些技术大牛们对Spring框架的IOC的理解以及…

如何将一个向量投影到一个平面上_CameraLidar投影:2D3D导航

点击上方“AI小白学视觉”&#xff0c;选择加"星标"或“置顶”重磅干货&#xff0c;第一时间送达图1.图像上的激光雷达点激光雷达和照相机是用于感知和理解场景的两个基本传感器。他们建立周边环境模型、提供检测和确定其他对象位置的方法&#xff0c;从而为机器人提…

JAVA中和、||和|的区别?

问题一&#xff1a;JAVA中&&和&、||和|&#xff08;短路与和逻辑与、短路或和逻辑或&#xff09;的区别&#xff1f; 首先名称是不同的 逻辑运算符&#xff1a;&#xff06;&#xff06;逻辑与  &#xff5c;&#xff5c;逻辑或  它们都是逻辑运算符 位运算…

pppoe拨号的外网ip无法ping通_【思唯网络学院】 五大网络概念:IP地址、子网掩码、网关、DHCP服务和PPPoE拨号...

5G技术的更新&#xff0c;推动了新一代的网络通信发展&#xff0c;家庭宽带上网也从最初的十几K的速度&#xff0c;提升到了现在动则上百上千兆的速度&#xff0c;很多有部署了家庭NAS的用户&#xff0c;甚至都已经更新到了10G级别的内部局域网了。在这个信息互联的时代&#x…

MySQL数据库的数据类型以及取值范围详解

主要包括以下五大类&#xff1a; 整数类型&#xff1a;BIT、BOOL、TINY INT、SMALL INT、MEDIUM INT、 INT、 BIG INT 浮点数类型&#xff1a;FLOAT、DOUBLE、DECIMAL 字符串类型&#xff1a;CHAR、VARCHAR、TINY TEXT、TEXT、MEDIUM TEXT、LONGTEXT、TINY BLOB、BLOB、MEDI…

flex布局_flex布局的 flex(felx-grow、flex-shrink、flex-basis)详解

flex布局中的flex-grow,flex-shrink,flex-basis接上篇文章&#xff0c;1. flex-grow属性flex-grow定义剩余空间的分成。默认为0&#xff0c;即如果存在剩余空间&#xff0c;也不放大。如何理解这里的剩余空间呢&#xff0c;用例子来说明吧。默认的情况(flex-grow:0)在浏览器下是…

Mina网络通信框架

认识 Mina Apache Mina Server 是一个网络通信应用框架&#xff0c;与 Netty 出自同一作者&#xff0c;Netty 借鉴了部分 Mina 的设计思路。 Mina 主要是对基于 TCP/IP、UDP/IP 协议栈的通信框架&#xff0c;Mina 可以帮助我们快速开发高性能、高扩展性的网络通信应用&#x…

每个tabpage中都有一个dategridview_每个女人,都有一个礼服梦

●今天&#xff0c;我要带着大家跟随几部经典电影&#xff0c;来开启一场关于礼服的时空穿越。01. 《爱玛》//关键词&#xff1a;19世纪初英国乡村经历了巴洛克风格的洗礼&#xff0c;服饰的整体风格变得柔和了不少&#xff0c;蕾丝、细纱、蝴蝶结这些浪漫元素&#xff0c;是这…

ssm框架sql换成MySQL_搭建ssm框架,可实现登录和数据展示以及增删改查

需求&#xff1a;后台使用ssm(spring-springMVC-mybatis)进行整合前台使用bootstrap框架前后台交互使用Ajax进行发送表结构&#xff1a;登录页面后显示所有用户信息&#xff0c;可对每条进行增删改查登录时也使用本表的user_name和user_pwd进行校验项目目录结构步骤一&#xff…

巧妙mybatis避免Where 空条件的尴尬

我就废话不多说了&#xff0c;大家还是直接看代码吧~ <select id"findActiveBlogLike" resultType"Blog">SELECT * FROM BLOGWHERE<if test"state ! null">state #{state}</if> </select>如果state参数为空时&#x…

numpy 矩阵与向量相乘_高能!8段代码演示Numpy数据运算的神操作

作者&#xff5c;王天庆来源&#xff5c;大数据(ID&#xff1a;hzdashuju)导读&#xff1a;本文介绍一下在Python科学计算中非常重要的一个库——Numpy。Numpy是Numerical Python extensions 的缩写&#xff0c;字面意思是Python数值计算扩展。Numpy是Python中众多机器学习库的…

Spring Boot——日志配置

日志&#xff0c;通常不会在需求阶段作为一个功能单独提出来&#xff0c;也不会在产品方案中看到它的细节。但是&#xff0c;这丝毫不影响它在任何一个系统中的重要的地位。 为了保证服务的高可用&#xff0c;发现问题一定要即使&#xff0c;解决问题一定要迅速&#xff0c;所…

python2转python3代码_Python2代码转成Python3代码

1.利用anaconda软件自带的功能: 1.找2to3.py文件 我的anaconda装在了D盘下的Anaconda文件夹下 你需要找到anaconda下的script文件夹里面的2to3-script.py文件(由于版本不同,可能名字不太一样,但是一定是2to3开头,而且是py文件)打开2to3-script.py文件后(内容肯定都是一样的):2.…

haproxy keepalived_Haproxy+KeepAlived+Mycat实现高可用集群

1.什么是HaproxyHAProxy是一个使用C语言编写的自由及开放源代码软件[1]&#xff0c;其提供高可用性、负载均衡&#xff0c;以及基于TCP和HTTP的应用程序代理。HAProxy特别适用于那些负载特大的web站点&#xff0c;这些站点通常又需要会话保持或七层处理。HAProxy运行在当前的硬…

IO与NIO

1、阻塞与非阻塞 阻塞与非阻塞是描述进程在访问某个资源时&#xff0c;数据是否准备就绪的的一种处理方式。当数据没有准备就绪时&#xff1a; 阻塞&#xff1a;线程持续等待资源中数据准备完成&#xff0c;直到返回响应结果。非阻塞&#xff1a;线程直接返回结果&#xff0c…

java spark wordcount_提交任务到spark(以wordcount为例)

1、首先需要搭建好hadoopspark环境&#xff0c;并保证服务正常。本文以wordcount为例。2、创建源文件&#xff0c;即输入源。hello.txt文件&#xff0c;内容如下&#xff1a;tom jerryhenry jimsuse lusy注&#xff1a;以空格为分隔符3、然后执行如下命令&#xff1a;hadoop fs…