首页 > 其他分享 >Sentinel注解支持详述

Sentinel注解支持详述

时间:2023-01-14 22:02:36浏览次数:56  
标签:详述 return pjp ex Sentinel 注解 fallback 方法 Class


Sentinel注解支持详述


☞ ​​博客导航​​,​​带你有序的阅读和学习!​


官方文档:​​https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81​

这一节,我们首先做一个小的案例,然后把官方文档中的介绍过一遍,再把文档所述的特性在代码中找到。

案例

依赖

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>

属性文件

spring:
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080
port: 8719
application:
name: sentinel-annotaion
server:
port: 9999

说明

  • ​spring.application.name​​ 定义应用名,如图所示的名称
  • ​server.port​​ :应用端口
  • ​spring.cloud.sentinel.transport.dashboard​​ :sentinel的IP:端口
  • ​spring.cloud.sentinel.transport.port​​ :sentinel 与服务的通讯端口

切面

如果使用的是Spring Boot/Cloud ,即没有导入前面的依赖,需要自己将切面纳入到Spring容器中去:

@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}

由于我们导入了springcloud的依赖,所以会自动配置好这个切面,源码如下:

Sentinel注解支持详述_静态方法

EchoService

public interface EchoService {
String echoMessage(String message);
String hello();
}

EchoServiceImpl

@Service
public class EchoServiceImpl implements EchoService {

@Override
@SentinelResource(value = "echo.message",blockHandler = "handleException",
blockHandlerClass = ExceptionUtil.class)
public String echoMessage(String message) {
return "echo message:"+message;
}

@Override
@SentinelResource(value = "hello",blockHandler = "handleHello")
public String hello() {
return "echo hello";
}

public String handleHello(BlockException ex){
return "handle hello ; exception:"+ex;
}
}

ExceptionUtil

public class ExceptionUtil {
public static String handleException(String message, BlockException ex){
return "exception handle "+message + " exception:"+ex;
}
}

EchoController

@RestController
public class EchoController {

@Autowired
private EchoService echoService;

@GetMapping("/echo/message/{message}")
public String echoMessage(@PathVariable String message){
return echoService.echoMessage(message);
}

@GetMapping("/hello")
public String hello(){
return echoService.hello();
}
}

页面控制流控

流控限制如下,简单使用基于​​QPS​​​的流控规则(规则后续会详细说到),阈值为​​2​​​,流控效果选的是​​快速失败​​。

Sentinel注解支持详述_Sentinel_02

通过测试,当我们在页面快速刷新(达到一秒访问3次或3次以上)可以看到下面的效果。

Sentinel注解支持详述_SpringCloudAlibaba_03


Sentinel注解支持详述_Sentinel_04

@SentinelResource 注解

​@sentinelResource​​​ 注解用于定义资源,并提供可选的异常处理和fallback配置项,​​@sentinelResource​​ 注解包含以下属性:

注解源码

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SentinelResource {
//资源名
String value() default "";
//entry 类型
EntryType entryType() default EntryType.OUT;
//指定异常处理函数名
String blockHandler() default "";
//如果异常处理函数不与目标方法在同一个类,则需要指定类,并且异常处理函数需要声明为static
Class<?>[] blockHandlerClass() default {};

//fallback函数名,默认为空
String fallback() default "";

//指定默认fallback函数
String defaultFallback() default "";

//同样,fallback函数需要和目标方法在同一个类,如果不再,则需要指定,并且对应的函数需要声明为static
Class<?>[] fallbackClass() default {};

//
Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};

//指定排除的异常类型,不进入异常统计,也不会进入fallback函数处理,而是原样抛出。
Class<? extends Throwable>[] exceptionsToIgnore() default {};
}

文档原文

​@SentinelResource​​​ 用于定义资源,并提供可选的异常处理和 fallback 配置项。 ​​@SentinelResource​​ 注解包含以下属性:

  • ​value​​:资源名称,必需项(不能为空)
  • ​entryType​​​:entry 类型,可选项(默认为 ​​EntryType.OUT​​)
  • ​blockHandler​​​ / ​​blockHandlerClass​​​: ​​blockHandler​​​对应处理 ​​BlockException​​​ 的函数名称,可选项。blockHandler 函数访问范围需要是 ​​public​​​,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 ​​BlockException​​​。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 ​​blockHandlerClass​​​ 为对应的类的 ​​Class​​ 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • ​fallback​​​:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 ​​exceptionsToIgnore​​​ 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:返回值类型必须与原函数返回值类型一致;方法参数列表需要和原函数一致,或者可以额外多一个 ​​Throwable​​​ 类型的参数用于接收对应的异常。fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 ​​fallbackClass​​​ 为对应的类的 ​​Class​​ 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • ​defaultFallback​​​(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 ​​exceptionsToIgnore​​​ 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:返回值类型必须与原函数返回值类型一致;方法参数列表需要为空,或者可以额外多一个 ​​Throwable​​​ 类型的参数用于接收对应的异常。defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 ​​fallbackClass​​​ 为对应的类的 ​​Class​​ 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • ​exceptionsToIgnore​​(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

注:1.6.0 之前的版本 fallback 函数只针对降级异常(​​DegradeException​​)进行处理,不能针对业务异常进行处理

特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 ​​BlockException​​​ 时只会进入 ​​blockHandler​​​ 处理逻辑。若未配置 ​​blockHandler​​​、​​fallback​​​ 和 ​​defaultFallback​​​,则被限流降级时会将 ​​BlockException​直接抛出

@SentinelResource逻辑分析

源码分析

@Around("sentinelResourceAnnotationPointcut()")
public Object invokeResourceWithSentinel(ProceedingJoinPoint pjp) throws Throwable {
//标有注解(@SentinelResource)的目标原始方法
Method originMethod = resolveMethod(pjp);
//获取注解对象(@SentinelResource)
SentinelResource annotation = originMethod.getAnnotation(SentinelResource.class);
if (annotation == null) {
throw new IllegalStateException("Wrong state for SentinelResource annotation");
}
//资源名称
String resourceName = getResourceName(annotation.value(), originMethod);

EntryType entryType = annotation.entryType();
Entry entry = null;
try {
//sentinel 逻辑代码
entry = SphU.entry(resourceName, entryType, 1, pjp.getArgs());
//执行目标方法
Object result = pjp.proceed();
return result;
} catch (BlockException ex) {
// 处理BlockException
return handleBlockException(pjp, annotation, ex);
} catch (Throwable ex) {
// 处理非BlockException
// 获取忽略处理的异常
Class<? extends Throwable>[] exceptionsToIgnore = annotation.exceptionsToIgnore();
//判断当前异常是否在 忽略异常列表中,如果存在,则直接抛出
if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) {
throw ex;
}
//如果当前异常在exceptionsToTrace属性中定义了,就进行处理
if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) {
traceException(ex);
return handleFallback(pjp, annotation, ex);
}
//前面的条件都不符合,则直接抛出异常
throw ex;
} finally {
if (entry != null) {
entry.exit(1, pjp.getArgs());
}
}
}

我们主要看 catch 中的​​handleBlockException()​​ 方法。

第一步:执行 blockHandler所配置的方法

protected Object handleBlockException(ProceedingJoinPoint pjp, SentinelResource annotation, BlockException ex)
throws Throwable {

//第一步:处理blockHandler方法。
// 如果配置了blockHandler 处理函数,则进行执行处理,获取blockHandler处理方法
Method blockHandlerMethod = extractBlockHandlerMethod(pjp, annotation.blockHandler(),
annotation.blockHandlerClass());
//存在blockHandlerMethod的方法
if (blockHandlerMethod != null) {
//获取目标方法的参数
Object[] originArgs = pjp.getArgs();
// 构建参数
Object[] args = Arrays.copyOf(originArgs, originArgs.length + 1);
args[args.length - 1] = ex;
try {
//静态方法
if (isStatic(blockHandlerMethod)) {
//反射调用方法,静态方法不需要传入obj
return blockHandlerMethod.invoke(null, args);
}
//非静态方法。
return blockHandlerMethod.invoke(pjp.getTarget(), args);
} catch (InvocationTargetException e) {
// throw the actual exception
throw e.getTargetException();
}
}
//如果没有配置blockHandler 异常处理函数。则执行fallback
//第二步执行fallback 处理函数
return handleFallback(pjp, annotation, ex);
}


//========================进入extractBlockHandlerMethod方法========================
/**
* name:异常处理函数名,locationClass:异常处理函数所在类,如果没有配置,则说明不是静态方法
*/
//注意:这里可以看到,如果使用blockHandler函数,则先处理普通的,其次在去处理静态的。
private Method extractBlockHandlerMethod(ProceedingJoinPoint pjp, String name,
Class<?>[] locationClass) {
if (StringUtil.isBlank(name)) {
return null;
}
// locationClass 如果配置了类,说明异常处理方法是静态方法。
boolean mustStatic = locationClass != null && locationClass.length >= 1;
Class<?> clazz;
//是不是配置了方法所在的类
if (mustStatic) {
//如果传入了blockHandlerClass,则取第一个class
clazz = locationClass[0];
} else {
// 如果为空,就取当前类
clazz = pjp.getTarget().getClass();
}
//从缓存中取MethodWrapper
MethodWrapper m = ResourceMetadataRegistry.lookupBlockHandler(clazz, name);
//缓存中没有
if (m == null) {
// name是异常处理方法的名称,clazz是方法所在的类,mustStatic 是 是否为静态方法
// 获取 异常处理方法在当前类(及所有父类)的异常处理方法
Method method = resolveBlockHandlerInternal(pjp, name, clazz, mustStatic);
//缓存当前方法的MethodWrapper实例
ResourceMetadataRegistry.updateBlockHandlerFor(clazz, name, method);
return method;
}
if (!m.isPresent()) {
return null;
}
//从缓存中取
return m.getMethod();
}

//===============进入resolveBlockHandlerInternal()方法================================
//name: 异常处理方法;class:异常处理方法所在的类,mustStatic是否为静态方法
//注意:通过这个方法可以看到:blockHandler异常处理函数的参数列表是在原方法的基础上,在末尾添加一个
// BlockException类型的形参
private Method resolveBlockHandlerInternal(ProceedingJoinPoint pjp, /*@NonNull*/ String name,
Class<?> clazz,boolean mustStatic) {
//获取目标方法的Method对象
Method originMethod = resolveMethod(pjp);
//获取方法参数数组
Class<?>[] originList = originMethod.getParameterTypes();
//获取异常处理方法的参数列表
Class<?>[] parameterTypes = Arrays.copyOf(originList, originList.length + 1);
//添加了一个新的参数,所以,我们定义降级的方法时,在末尾需要添加一个BlockException。
parameterTypes[parameterTypes.length - 1] = BlockException.class;
//查找处理异常方法的Method对象(handleException)
return findMethod(mustStatic, clazz, name, originMethod.getReturnType(), parameterTypes);
}

//=================进入findMethod()方法==========================
//注意:这里可以发现,如果使用的是非静态的方法的处理方式,如果本类没有对应的异常处理函数,会递归追溯到父类,
// 父类的父类......
/*递归查询方法对象*/
private Method findMethod(boolean mustStatic, Class<?> clazz, String name, Class<?> returnType,
Class<?>... parameterTypes) {
//获取当前类对象的所有方法对象
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (name.equals(method.getName()) && checkStatic(mustStatic, method)
&& returnType.isAssignableFrom(method.getReturnType())
&& Arrays.equals(parameterTypes, method.getParameterTypes())) {

RecordLog.info("Resolved method [{0}] in class [{1}]", name, clazz.getCanonicalName());
return method;
}
}
// 本类没有,则向它的父类查找
Class<?> superClass = clazz.getSuperclass();
if (superClass != null && !Object.class.equals(superClass)) {
return findMethod(mustStatic, superClass, name, returnType, parameterTypes);
} else {
String methodType = mustStatic ? " static" : "";
RecordLog.warn("Cannot find{0} method [{1}] in class [{2}] with parameters {3}",
methodType, name, clazz.getCanonicalName(), Arrays.toString(parameterTypes));
return null;
}
}

第二步:执行fallback所配置的方法,进入​​handleFallback()​​方法

protected Object handleFallback(ProceedingJoinPoint pjp, SentinelResource annotation, Throwable ex)
throws Throwable {
return handleFallback(pjp, annotation.fallback(), annotation.defaultFallback(),
annotation.fallbackClass(), ex);
}

protected Object handleFallback(ProceedingJoinPoint pjp, String fallback, String defaultFallback,
Class<?>[] fallbackClass, Throwable ex) throws Throwable {
//目标方法的参数数组
Object[] originArgs = pjp.getArgs();

// 如果配置了fallback处理函数,则执行
Method fallbackMethod = extractFallbackMethod(pjp, fallback, fallbackClass);
if (fallbackMethod != null) {
// 构造参数列表
int paramCount = fallbackMethod.getParameterTypes().length;
Object[] args;
if (paramCount == originArgs.length) {
args = originArgs;
} else {
args = Arrays.copyOf(originArgs, originArgs.length + 1);
args[args.length - 1] = ex;
}

try {
if (isStatic(fallbackMethod)) {
//静态方法调用
return fallbackMethod.invoke(null, args);
}
//普通方法调用
return fallbackMethod.invoke(pjp.getTarget(), args);
} catch (InvocationTargetException e) {
// throw the actual exception
throw e.getTargetException();
}
}
// If fallback is absent, we'll try the defaultFallback if provided.
// 如果fallback 方法没有,则执行默认的fallback方法
//第三步处理默认的fallback配置的函数
return handleDefaultFallback(pjp, defaultFallback, fallbackClass, ex);
}


//==================进入extractFallbackMethod()方法==================
private Method extractFallbackMethod(ProceedingJoinPoint pjp, String fallbackName,
Class<?>[] locationClass) {
if (StringUtil.isBlank(fallbackName)) {
return null;
}
//判断配置为静态方法
boolean mustStatic = locationClass != null && locationClass.length >= 1;
//同样,如果是配置为静态的,直接取,如果不是,则使用目标方法所在类
Class<?> clazz = mustStatic ? locationClass[0] : pjp.getTarget().getClass();

//从缓存中取
MethodWrapper m = ResourceMetadataRegistry.lookupFallback(clazz, fallbackName);

if (m == null) {
// 获取fallback方法的Method对象
Method method = resolveFallbackInternal(pjp, fallbackName, clazz, mustStatic);
// 加入到缓存中
ResourceMetadataRegistry.updateFallbackFor(clazz, fallbackName, method);
return method;
}
if (!m.isPresent()) {
return null;
}
return m.getMethod();
}

//===========进入resolveFallbackInternal()方法中=================
private Method resolveFallbackInternal(ProceedingJoinPoint pjp, /*@NonNull*/ String name,
Class<?> clazz,boolean mustStatic) {
//获取原方法(目标增强方法)
Method originMethod = resolveMethod(pjp);
// Fallback function allows two kinds of parameter list.
// Fallback函数是支持两种类型的参数列表的,这里不像BlockHandler函数
// 第一种:与原参数列表一直;第二种:在原参数列表的最后添加一个Throwable类型
Class<?>[] defaultParamTypes = originMethod.getParameterTypes();
Class<?>[] paramTypesWithException = Arrays.copyOf(defaultParamTypes, defaultParamTypes.length + 1);
paramTypesWithException[paramTypesWithException.length - 1] = Throwable.class;
// We first find the fallback matching the signature of origin method.
// 先查找第一个参数列表的方法
Method method = findMethod(mustStatic, clazz, name, originMethod.getReturnType(),
defaultParamTypes);
// If fallback matching the origin method is absent, we then try to find the other one.
if (method == null) {
// 如果第一种没有找到,则查询第二个加了异常类型的方法列表的方法
method = findMethod(mustStatic, clazz, name, originMethod.getReturnType(),
paramTypesWithException);
}
return method;
}

第三步:执行defaultFallback属性配置的函数,进入​​handleDefaultFallback()​​方法

protected Object handleDefaultFallback(ProceedingJoinPoint pjp, String defaultFallback,
Class<?>[] fallbackClass, Throwable ex) throws Throwable {
// 如果配置了默认的fallback方法,如果配置了的话
Method fallbackMethod = extractDefaultFallbackMethod(pjp, defaultFallback, fallbackClass);
if (fallbackMethod != null) {
// 构造参数
Object[] args = fallbackMethod.getParameterTypes().length == 0 ? new Object[0] :
new Object[] {ex};
try {
if (isStatic(fallbackMethod)) {
//静态方法调用
return fallbackMethod.invoke(null, args);
}
//普通方法调用
return fallbackMethod.invoke(pjp.getTarget(), args);
} catch (InvocationTargetException e) {
// 抛出实际异常
throw e.getTargetException();
}
}
// 如果没有配置任何的fallback函数(fallback函数和默认的fallback函数,则直接抛出异常 BlockException)
throw ex;
}

//====================进入extractDefaultFallbackMethod()方法====================
private Method extractDefaultFallbackMethod(ProceedingJoinPoint pjp, String defaultFallback,
Class<?>[] locationClass) {
if (StringUtil.isBlank(defaultFallback)) {
return null;
}
//判断配置的是否是其他类的静态方法
boolean mustStatic = locationClass != null && locationClass.length >= 1;
//获取方法所在的类名
Class<?> clazz = mustStatic ? locationClass[0] : pjp.getTarget().getClass();
//查询缓存
MethodWrapper m = ResourceMetadataRegistry.lookupDefaultFallback(clazz, defaultFallback);
if (m == null) {

Class<?> originReturnType = resolveMethod(pjp).getReturnType();
// Default fallback allows two kinds of parameter list.
// 默认的fallback方法支持两种参数列表
// One is empty parameter list.
// 第一种是空参数
Class<?>[] defaultParamTypes = new Class<?>[0];
// The other is a single parameter {@link Throwable} to get relevant exception info.
// 第二种是只有一个Throwable类型的参数
Class<?>[] paramTypeWithException = new Class<?>[] {Throwable.class};
// We first find the default fallback with empty parameter list.
// 先查找空参数
Method method = findMethod(mustStatic, clazz, defaultFallback, originReturnType, defaultParamTypes);
// If default fallback with empty params is absent, we then try to find the other one.
if (method == null) {
// 再查询有参数的方法
method = findMethod(mustStatic, clazz, defaultFallback, originReturnType,
paramTypeWithException);
}
// 缓存方法
ResourceMetadataRegistry.updateDefaultFallbackFor(clazz, defaultFallback, method);
return method;
}
if (!m.isPresent()) {
return null;
}
return m.getMethod();
}

如果把上面的代码过一遍的话,前面文档中总结的要点基本都理解了。

执行方法顺序: blockHandler --> fallback --> defaultFallback

处理BlockException

属性方法形参:

  • blockHandler:在原方法的形参列表基础上,需要在最后添加一个​​BlockException​​ 类型的参数。
//获取方法参数数组
Class<?>[] originList = originMethod.getParameterTypes();
//获取异常处理方法的参数列表
Class<?>[] parameterTypes = Arrays.copyOf(originList, originList.length + 1);
//添加了一个新的参数,所以,我们定义降级的方法时,在末尾需要添加一个BlockException。
parameterTypes[parameterTypes.length - 1] = BlockException.class;
  • fallback:有两种:1:方法参数列表与原方法参数列表一致,2:在原方法列表后添加一个​​Throwable​​ 类型的参数
// Fallback函数是支持两种类型的参数列表的,这里不像BlockHandler函数
// 第一种:与原参数列表一致;第二种:在原参数列表的最后添加一个Throwable类型
Class<?>[] defaultParamTypes = originMethod.getParameterTypes();
Class<?>[] paramTypesWithException = Arrays.copyOf(defaultParamTypes,
defaultParamTypes.length + 1);
paramTypesWithException[paramTypesWithException.length - 1] = Throwable.class;
  • defaultFallback:有两种,1:方法参数列表为空,2:仅有一个​​Thowable​​的参数列表
// Default fallback allows two kinds of parameter list.
// 默认的fallback方法支持两种参数列表
// One is empty parameter list.
// 第一种是空参数
Class<?>[] defaultParamTypes = new Class<?>[0];
// The other is a single parameter {@link Throwable} to get relevant exception info.
// 第二种是只有一个Throwable类型的参数
Class<?>[] paramTypeWithException = new Class<?>[] {Throwable.class};

处理非BlockException

如果配置了在忽略异常列表中,则直接抛出原始异常,否则使用​​exceptionToTrace​​ 配置进行处理,如果没有配置,则直接抛出原始异常。

// 处理非BlockException
// 获取忽略处理的异常
Class<? extends Throwable>[] exceptionsToIgnore = annotation.exceptionsToIgnore();
//判断当前异常是否在 忽略异常列表中,如果存在,则直接抛出
if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) {
throw ex;
}
//如果当前异常在exceptionsToTrace属性中定义了,就进行fallback和defaultFallback处理
if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) {
traceException(ex);
return handleFallback(pjp, annotation, ex);
}
//前面的条件都不符合,则直接抛出异常
throw ex;

​annotation.exceptionsToTrace()​​​ 默认是​​Throwable.class​​类型的,所以,理论上fallback是可以处理任何异常,排序在忽略异常列表中的异常。

这一节通过阅读源码的方式来学习sentinel的注解支持,后面我们看一下sentinel的几种控制规则。


标签:详述,return,pjp,ex,Sentinel,注解,fallback,方法,Class
From: https://blog.51cto.com/u_12131813/6007931

相关文章

  • 连接sentinel控制台并实现限流
    连接sentinel控制台并实现限流☞​​博客导航​​,​​带你有序的阅读和学习!​​文章目录​​连接sentinel控制台并实现限流​​​​连接sentinel控制台​​​​依赖​​​......
  • Sentinel规则之熔断降级规则
    Sentinel规则之熔断降级规则☞​​博客导航​​,​​带你有序的阅读和学习!​​文章目录​​Sentinel规则之熔断降级规则​​​​概述​​​​降级策略​​​​降级演示​​......
  • Sentinel规则之黑白名单规则
    Sentinel规则之黑白名单规则☞​​博客导航​​,​​带你有序的阅读和学习!​​概述很多时候,我们需要根据调用方来限制资源是否通过,这时候可以使用Sentinel的黑白名单控制......
  • byteTCC框架--@Compensable注解问题
    在使用byteTCC框架0.5.0-BETA2版本时,发现一个关于@Compensable注解的问题,这里记录下:1.@Compensable注解问题若try的实现逻辑写在serviceImpl中,而不是controller中,那么,@Compe......
  • SpringMVC轻松学习-注解的使用(三)
    我们采用sprngMVC开发项目时,通常都会采用注解的方式,这样可以大大提高我们的开发效率。实现零配置。下面我们从零开始重新做一个springMVC的配置。这个项目完全采用注解的......
  • 注解和反射
    注解和反射注解Java注解(Annotation)又称Java标注,是JDK5.0引入的一种注释机制。Java语言中的类、方法、变量、参数和包等都可以被标注。和Javadoc不同,Java标注可......
  • 注解和反射
    注解和反射注解:1.内置注解:Annotation注解,Override重写方法,Deprecated已经过时,SupperssWarnings正压警告2.元注解:负责注解其他注解Target表示我们的注解可以用在那些地方......
  • 注解@EnableAsync使用注意项
    注解@EnableAsync使用注意项以异步性能优化为目的,反而带来的整体业务不可用的结果,是不可取的一种优化。SpringBoot默认用于异步任务的配置参数其中两项如下:queueCapaci......
  • @Data注解使用/注解getset不起作用
    讲个小工具Idea创建对象时不用写getset方法导入maven坐标<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency......
  • Redis-多机数据库-Sentinel
    SentinelSentinel(哨岗、哨兵)是Redis的高可用性(highavailability)解决方案:由一个或多个Sentinel实例(instance)组成的Sentinel系统(system)可以监视任意多个主服务器,以及这些主......