03-spring框架—— AOP 面向切面编程
3.1 动态代理
动态代理是指,程序在整个运行过程中根本就不存在目标类的代理类,目标对象的代理
对象只是由代理生成工具(不是真实定义的类)在程序运行时由 JVM 根据反射等机制动态
生成的。代理对象与目标对象的代理关系在程序运行时才确立。
3.1.1 JDK
动态代理的实现方式常用的有两种:使用 JDK 的 Proxy,与通过 CGLIB 生成代理。
Jdk 的动态要求目标对象必须实现接口,这是 java 设计上的要求。
从 jdk1.3 以来,java 语言通过 java.lang.reflect 包提供三个类支持代理模式 Proxy, Method 和
InovcationHandler。
3.1.2 CGLIB 动态代理( 了解)
CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的 Code
生成类库,它可以在运行期扩展 Java 类与实现 Java 接口。它广泛的被许多 AOP 的框架
使用,例如 Spring AOP。
使用 JDK 的 Proxy 实现代理,要求目标类与代理类实现相同的接口。若目标类不存在
接口,则无法使用该方式实现。但对于无接口的类,要为其创建动态代理,就要使用 CGLIB
来实现。
CGLIB 代理的生成原理是生成目标类的子类,而子类是增强过的,这个子类对象就是代
理对象。所以,使用 CGLIB 生成动态代理,要求目标类必须能够被继承,即不能是 final 的
类。
CGLIB 经常被应用在框架中,例如 Spring ,Hibernate 等。cglib 的代理效率高于 Jdk。
项目中直接使用动态代理的地方不多。一般都使用框架提供的功能。
3.2 不使用 AOP 的开发方式 (理解)
Step1: 项目 aop_leadin1
先定义好接口与一个实现类,该实现类中除了要实现接口中的方法外,还要再写两个非
业务方法。非业务方法也称为交叉业务逻辑:
➢ doTransaction():用于事务处理
➢ doLog():用于日志处理
然后,再使接口方法调用它们。接口方法也称为主业务逻辑。
Step2: 项目 aop_leadin2
当然,也可以有另一种解决方案:将这些交叉业务逻辑代码放到专门的工具类或处理类
中,由主业务逻辑调用。
Step3: 项目 aop_leadin3
以上的解决方案,还是存在弊端:交叉业务与主业务深度耦合在一起。当交叉业务逻辑
较多时,在主业务代码中会出现大量的交叉业务逻辑代码调用语句,大大影响了主业务逻辑
的可读性,降低了代码的可维护性,同时也增加了开发难度。
所以,可以采用动态代理方式。在不修改主业务逻辑的前提下,扩展和增强其功能。
功能增强:
package com.bjpowernode.proxy; import com.bjpowernode.utils.ServiceTools; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; /**
* Author: 动力节点
* 2019/5/10
*/
public class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler() {
} public MyInvocationHandler(Object target) {
this.target = target;
} /**
* 实现业务方法的功能增强。 加入日志,事务功能
* @param proxy 生成的代理对象
* @param method 业务方法
* @param args 业务方法的参数
* @return Object 业务方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; String mname = method.getName();
if( "doSome".equals(mname)){
//在目标方法调用之前加入日志
ServiceTools.doLog(); //执行目标方法
result = method.invoke(target, args); //doSome //在目标方法调用之后加入事务
ServiceTools.doTrans();
} else {
//doOther
//执行目标方法
result = method.invoke(target, args);
} //目标方法的执行结果
return result;
}
}
代码第二部分
package com.bjpowernode; import com.bjpowernode.proxy.MyInvocationHandler;
import com.bjpowernode.service.SomeService;
import com.bjpowernode.service.SomeServiceImpl; import java.lang.reflect.Proxy; /**
* Hello world!
*
*/
public class App
{
/*public static void main( String[] args )
{
System.out.println( "Hello World!" );
SomeService service = new SomeServiceImpl();
service.doSome();
System.out.println("====================");
service.doOther();
}*/ public static void main( String[] args )
{
System.out.println( "Hello World!" );
//目标对象
SomeService target = new SomeServiceImpl();
//创建InvocationHandler的实现类对象
MyInvocationHandler handler = new MyInvocationHandler(target);
//创建代理对象
SomeService proxy = (SomeService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler);
//com.sun.proxy.$Proxy0 jdk的动态代理类型
System.out.println("proxy:"+proxy.getClass().getName()); //通过代理对象执行业务方法,实现功能的增强
proxy.doSome();
System.out.println("-----------------------------");
proxy.doOther(); }
}
3.3 AOP 概述
3.4 AOP
AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程
序运行过程。
AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB
的动态代理。
AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,可通过运行期动态
代理实现程序功能的统一维护的一种技术。AOP 是 Spring 框架中的一个重要内容。利用 AOP
可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程
序的可重用性,同时提高了开发的效率。
面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到
主业务逻辑中。所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、
事务、日志、缓存等。
若不使用 AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样,
会使主业务逻辑变的混杂不清。
例如,转账,在真正转账业务逻辑前后,需要权限控制、日志记录、加载事务、结束事
务等交叉业务逻辑,而这些业务逻辑与主业务逻辑间并无直接关系。但,它们的代码量所占
比重能达到总代码量的一半甚至还多。它们的存在,不仅产生了大量的“冗余”代码,还大
大干扰了主业务逻辑---转账。
3.5 面向切面编程对有什么好处?
1.减少重复;
2.专注业务;
注意:面向切面编程只是面向对象编程的一种补充。
使用 AOP 减少重复代码,专注业务实现:
3.6 AOP 编程术语( 掌握)
(1 ) 切面(Aspect )
切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面
是通知(Advice)。实际就是对主业务逻辑的一种增强。
(2 ) 连接点(JoinPoint
连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
(3 ) 切入点(Pointcut )
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。
被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不
能被增强的。
(4 ) 目标对象(Target
目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。上例中的
StudentServiceImpl 的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然,
不被增强,也就无所谓目标不目标了。
(5 ) 通知(Advice
通知是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。Advice
也叫增强。上例中的 MyInvocationHandler 就可以理解为是一种通知。换个角度来说,通知
定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通
知类型不同,切入时间不同。
切入点定义切入的位置,通知定义切入的时间。
3.7 AspectJ 对 对 AOP 的实现( 掌握)
对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向
切面编程。然而,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便,
而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框
架中。
在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。
AspectJ 简介
AspectJ 是一个优秀面向切面的框架,它扩展了 Java 语言,提供了强大的切面实现。
官网 地址:http://www.eclipse.org/aspectj/
AspetJ 是 Eclipse 的 开源项目,官网介绍如下:
a seamless aspect-oriented extension to the Javatm programming language(一种基于 Java 平台
的面向切面编程的语言)
Java platform compatible(兼容 Java 平台,可以无缝扩展)
easy to learn and use(易学易用)
3.7.1 AspectJ 的通知类型(理解)
AspectJ 中常用的通知有五种类型:
(1)前置通知
(2)后置通知
(3)环绕通知
(4)异常通知
(5)最终通知
3.7.2 AspectJ 的切入点表达式(掌握)
AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:
execution ( [modifiers-pattern] 访问权限类型
ret-type-pattern 返回值类型
[declaring-type-pattern] 全限定性类名
name-pattern(param-pattern)方法名(参数类型和参数个数)
[throws-pattern] 抛出异常类型
)
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就
是方法的签名。注意,表达式中加[ ]的部分表示可省略部分,各部分间用空格分开。在其中
可以使用以下符号:
举例:
execution(public * *(..))
指定切入点为:任意公共方法。
execution(* set*(..))
指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.xyz.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后
面必须跟“*”,表示包、子包下的所有类。
execution(* *..service.*.*(..))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(* *.service.*.*(..))
指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(* *.ISomeService.*(..))
指定只有一级包下的 ISomeSerivce 接口中所有方法为切入点
execution(* *..ISomeService.*(..))
指定所有包下的 ISomeSerivce 接口中所有方法为切入点
execution(* com.xyz.service.IAccountService.*(..))
指定切入点为:IAccountService 接口中的任意方法。
execution(* com.xyz.service.IAccountService+.*(..))
指定切入点为:IAccountService 若为接口,则为接口中的任意方法及其所有实现类中的任意
方法;若为类,则为该类及其子类中的任意方法。
execution(* joke(String,int)))
指定切入点为:所有的 joke(String,int)方法,且 joke()方法的第一个参数是 String,第二个参
数是 int。如果方法中的参数类型是 java.lang 包下的类,可以直接使用类名,否则必须使用
全限定类名,如 joke( java.util.List, int)。
execution(* joke(String,*)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,第二个参数可以是任意类
型,如joke(String s1,String s2)和joke(String s1,double d2)都是,但joke(String s1,double d2,String
s3)不是。
execution(* joke(String,..)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,后面可以有任意个参数且
参数类型不限,如 joke(String s1)、joke(String s1,String s2)和 joke(String s1,double d2,String s3)
都是。
execution(* joke(Object))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型。joke(Object ob)
是,但,joke(String s)与 joke(User u)均不是。
execution(* joke(Object+)))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型或该类的子类。
不仅 joke(Object ob)是,joke(String s)和 joke(User u)也是。
3.7.3 AspectJ 的开发环境(掌握)
(1 ) maven 依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<!--插件-->
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
(2 ) 引入 AOP
在配置文件头部,要引入关于 aop 的约束。在 Spring 框架的解压目录中,
\docs\spring-framework-reference\html 下的 xsd-configuration.html 文件中。
在前面 Spring 实现 AOP 时,并未引入 AOP 的约束,而在 AspectJ 实现 AOP 时,才提出
要引入 AOP 的约束。说明,配置文件中使用的 AOP 约束中的标签,均是 AspectJ 框架使用的,
而非 Spring 框架本身在实现 AOP 时使用的。
AspectJ 对于 AOP 的实现有注解和配置文件两种方式,常用是注解方式。
3.7.4 AspectJ 基于注解的 AOP 实现(掌握)
AspectJ 提供了以注解方式对于 AOP 的实现。
(1 ) 实现步骤
A 、 Step1:定义业务接口与实现类
B 、 Step2:定义切面类
类中定义了若干普通方法,将作为不同的通知方法,用来增强功能。
C 、 Step3: 声明目标对象切面类对象
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--声明目标类对象-->
<bean id="someServiceTarget" class="com.bjpowernode.service.SomeServiceImpl" /> <bean id="orderService" class="com.bjpowernode.service.OrderServiceImpl"/> <!--声明切面类对象-->
<bean id="myAspect" class="com.bjpowernode.aspect.MyAspect" /> <!--声明自动代理生成器:使用aspectj把spring容器中目标类对象生成代理
proxy-target-class="true"表示使用cglib动态代理 目标类有接口,默认使用jdk动态代理。
目标类没有接口,默认时候cglib动态代理
目标类有接口,也可以使用cglib动态代理,需要设置proxy-target-class="true"
-->
<!-- <aop:aspectj-autoproxy proxy-target-class="true" />-->
<aop:aspectj-autoproxy/> </beans>
配置图
D 、 Step4 :注册 AspectJ 的自动代理
在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类+ 切面”的代理
对象。这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj 的
自动代理生成器,其就会自动扫描到@Aspect 注解,并按通知类型与切入点,将其织入,并
生成代理。
<aop:aspectj-autoproxy/>的底层是由 AnnotationAwareAspectJAutoProxyCreator 实现的。
从其类名就可看出,是基于 AspectJ 的注解适配自动代理生成器。
其工作原理是,<aop:aspectj-autoproxy/>通过扫描找到@Aspect 定义的切面类,再由切
面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。
E 、 Step5 :测试类中使的 用目标对象的 id
(2 ) @Before 前置通知- 方法有 JoinPoint
在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参
数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、
目标对象等。
不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该
参数。
(3 ) @AfterReturning 后置通知- 注解有 returning 属性
在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回
值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以,被注解为后
置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变
量最好为 Object 类型,因为目标方法的返回值可能是任何类型。
接口增加方法:
实现方法:
定义切面:
(4 ) @Around 环绕通知- 增强方法有 ProceedingJoinPoint 参数
在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,Object 类型。并
且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个
proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法
的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。
接口增加方法:
接口方法的实现:
定义切面:
(5 ) @AfterThrowing 异常通知- 注解中有 throwing
在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。
当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的
名称,表示发生的异常对象。
增加业务方法:
方法实现:
定义切面:
(6 ) @After 最终通知
无论目标方法是否抛出异常,该增强均会被执行。
增加方法:
方法实现:
定义切面:
(7 ) @Pointcut 定义切入点
当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。
AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。
其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均
可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用@Pointcut 注解
的方法一般使用 private 的标识方法,即没有实际作用的方法。
3.7.5 AspectJ 基于 XML 文件的 AOP
AspectJ 除了提供了基于注解的 AOP 的实现外,还提供了以 XML 方式的实现。
切面就是一个普通类,而用于增强的方法就是普通的方法。通过配置文件,将切面中的
功能增强织入到了目标类的目标方法中。
实现步骤
Step1: 定义业务接口与实现类
接口:
实现类:
Step2:定义切面类
定义列作为切面出现。其中定义了若干普通方法,将作为不同的通知方法。
Step3 :注册目标对象与 POJO 切面类
Step4 :在容器中定义 AOP
配置文件中,除了要定义目标类与切面的 Bean 外,最主要的是在<aop:config/>中进行
aop 的配置。而该标签的底层,会根据其子标签的配置,生成自动代理。
通过其子标签<aop:pointcut/>定义切入点,该标签有两个属性,id 与 expression。分别
用于指定该切入点的名称及切入点的值。expression 的值为 execution 表达式。
通过子标签<aop:aspect/>定义具体的织入规则:根据不同的通知类型,确定不同的织入
时间;将 method 指定的增强方法,按照指定织入时间,织入到切入点指定的目标方法中。
<aop:aspect/>的 ref 属性用于指定使用哪个切面。
<aop:aspect/>的子标签是各种不同的通知类型。不同的通知所包含的属性是不同的,但
也有共同的属性。
method:指定该通知使用的切面中的增强方法。
pointcut-ref:指定该通知要应用的切入点。
AspectJ 的 5 种通知的 XML 标签如下:
➢ <aop:before/>:前置通知
➢ <aop:after-returning/>:后置通知
➢ <aop:around/>:环绕通知
➢ <aop:after-throwing/>:异常通知
➢ <aop:after/>:最终通知
Step5 :测试类中使用目标对象的 id
3.8 Spring 实现 AOP
Spring 中 AOP 的可用接口:
前置通知:MethodBeforeAdvice
后置通知:AfterReturningAdvice
环绕通知:MethodInterceptor
异常通知:ThrowsAdvice
实现环绕通知:
Step1: 接口
Step2: 实现类
Step3: 环绕通知
Step4: Spring 配置文件
Step5: 测试类
03-spring框架—— AOP 面向切面编程的更多相关文章
- Spring框架 AOP面向切面编程(转)
一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...
- Spring框架——AOP面向切面编程
简介 AOP练习 使用动态代理解决问题 Spring AOP 用AspectJ注解声明切面 前置后置通知 利用方法签名编写AspectJ切入点表达式 指定切面的优先级 基于XML的配置声明切面 Spr ...
- 详解Spring框架AOP(面向切面编程)
最近在学习AOP,之前一直很不明白,什么是AOP?为什么要使用AOP,它有什么作用?学完之后有一点小小的感触和自己的理解,所以在这里呢就跟大家一起分享一下 AOP(Aspect-Oriented Pr ...
- Spring 08: AOP面向切面编程 + 手写AOP框架
核心解读 AOP:Aspect Oriented Programming,面向切面编程 核心1:将公共的,通用的,重复的代码单独开发,在需要时反织回去 核心2:面向接口编程,即设置接口类型的变量,传入 ...
- Spring:AOP面向切面编程
AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果. AOP是软件开发思想阶段性的产物,我们比较熟悉面向过程O ...
- spring:AOP面向切面编程02
参考: https://blog.csdn.net/jeffleo/article/details/54136904 一.AOP的核心概念AOP(Aspect Oriented Programming ...
- Spring的AOP面向切面编程
什么是AOP? 1.AOP概念介绍 所谓AOP,即Aspect orientied program,就是面向方面(切面)的编程. 功能: 让关注点代码与业务代码分离! 关注点: 重复代码就叫做关注点: ...
- Spring注解 - AOP 面向切面编程
基本概念: AOP:Aspect Oriented Programming,即面向切面编程 指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式 前置通知(@Before):在目标 ...
- JavaWeb_(Spring框架)SpringAOP面向切面编程
SpringAOP:面向切面编程(面向fifter编程) 通俗易懂术语:所有纵向重复的代码,我们提取成横向的代码 以下文章内容参考知乎:从0带你学习SpringAOP,彻底的理解AOP思想 传送门 1 ...
随机推荐
- JetBrain系列学生免费授权
1.访问网址:https://www.jetbrains.com/zh/student/ 2.往下滚动,点击立即申请 3.填写邮箱信息 4.确认后,跳转到Thank you页面,上面说已经给注册邮箱发 ...
- Eclipse使用git发布项目到github
因为一直都在使用svn,今天尝试了下git,记录下来既是方便自己以后查看,也是分享一些经验! 废话不多说,撸起袖子就是干!!! 1.选中要上传的项目右键 2.选中git 3.在图上打钩,点击所要上传的 ...
- 自在因梦 | 威爾伯的Fourth Turning所引發的聯想
2015-05-06 胡因梦 ...
- 【转】HBASE Region in Transition issue on Master UI
[From]https://community.hortonworks.com/content/supportkb/244808/hbase-region-in-transition-issue-on ...
- java:LeakFilling (Linux)
1.Nosql 列数据库,没有update,非关系型数据库: 为了解决高并发.高可扩展.高可用.大数据存储问题而产生的数据库解决方案,就是NoSql数据库. NoSQL,泛指非关系型的数据库,NoS ...
- 关于虚拟机docker 启动mysql 启动成功但未挂载到端口
首先排查了防火墙和其他权限相关问题 然后检查了mysql 用户权限问题 docker logs 查看日志 正常应该是到3306 问题是我的mysql my.cnf 文件是挂在在本地.当第二次启动容器时 ...
- run.sh
1.run.sh 文件 ./run.sh start启动 ./run.sh stop 停止 ./run.sh restart重启 ./run.sh install安装 ...
- 最简单MySQL教程详解(基础篇)之多表联合查询
在关系型数据库中,我们通常为了减少数据的冗余量将对数据表进行规范,将数据分割到不同的表中.当我们需要将这些数据重新合成一条时,就需要用到我们介绍来将要说到的表连接. 常用术语冗余(Redundancy ...
- 02-Zookeeper介绍及安装
1 Zookeeper介绍 ZooKeeper是为分布式应用所设计的高可用.高性能且一致的开源协调服务,它提供了一项基本服务:分布式锁服务.分布式应用可以基于它实现更高级的服务,实现诸如同步服务.配置 ...
- ABC136E Max GCD
Thinking about different ways of thinking. --- LzyRapx 题目 思路比较容易想到. Observations: 每次操作过后和不变. 枚举和的因子 ...