百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

注解@Autowired和@Resource的区别总结

yuyutoo 2025-04-09 22:23 11 浏览 0 评论

零、前言

@Autowired和@Resource注解都可以在Spring应用中进行声明式的依赖注入。以前都是看的网上关于两者的区别,但是实际和网上说的有出入,故从源码角度进行分析、验证。

以下源码基于spring 5.3.20(通过springboot 2.7.0引入)

一、结论

1、@Autowired注解总结

  • 可用于构造函数,成员变量以及set方法
  • Spring 4.3开始,如果目标Bean只有一个构造函数,则在该构造函数上可以省略@Autowired注解;如果目标Bean有多个构造函数则不可省略

@Autowired注入方式:

  • 按照type查找bean,如果使用@Qualifier注解声明了name,则从结果集中取出与该name相匹配的bean返回(此时可以视为通过name和type获取bean,但实质是先通过type获取所有bean,然后通过name筛选,详情见后文findAutowireCandidates()方法源码分析)
  • 如果没有使用@Qualifier注解,且找到多个bean,则判断这些bean中是否有使用@Primary注解和@Priority注解,有就返回优先级最高的哪一个bean,没有就按照字段名称去匹配bean,匹配成功返回,不成功抛出异常。(详情见后文determineAutowireCandidate()方法源码解析)

整体注入流程如下所示:

2、@Resource注解总结

  • 可用于成员变量以及set方法
  • 若不指定name属性,则会把name属性值处理为字段名set方法标识的字段名称
  • 若指定type属性,则type属性值必须与字段类型或set方法返回值类型为父子关系(type属性值可以是子类,也可以是超类),否则会抛出异常
  • @Resource先按照name属性值注入,若未找到,则按type属性值注入。即默认的name或指定的name找不到 bean ,就会按 type 注入

整体注入流程如下所示:

二、@Autowired注入过程源码分析

1、AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata():切入点

首先定位到
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata()方法,代码如下。可以看到,如果是在属性上声明@Autowired,则构造AutowiredFieldElement对象,如果是在方法上声明@Autowired,则构造AutowiredMethodElement对象。

 private InjectionMetadata buildAutowiringMetadata(Class clazz) {
     if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
         return InjectionMetadata.EMPTY;
     }
 
     List elements = new ArrayList<>();
     Class targetClass = clazz;
 
     do {
         final List currElements = new ArrayList<>();
 
         // 字段调用
         ReflectionUtils.doWithLocalFields(targetClass, field -> {
             MergedAnnotation ann = findAutowiredAnnotation(field);
             if (ann != null) {
                 if (Modifier.isStatic(field.getModifiers())) {
                     if (logger.isInfoEnabled()) {
                         logger.info("Autowired annotation is not supported on static fields: " + field);
                     }
                     return;
                 }
                 boolean required = determineRequiredStatus(ann);
                 // 构造AutowiredFieldElement对象
                 currElements.add(new AutowiredFieldElement(field, required));
             }
         });
 
         // 方法调用
         ReflectionUtils.doWithLocalMethods(targetClass, method -> {
             Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
             if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                 return;
             }
             MergedAnnotation ann = findAutowiredAnnotation(bridgedMethod);
             if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                 if (Modifier.isStatic(method.getModifiers())) {
                     if (logger.isInfoEnabled()) {
                         logger.info("Autowired annotation is not supported on static methods: " + method);
                     }
                     return;
                 }
                 if (method.getParameterCount() == 0) {
                     if (logger.isInfoEnabled()) {
                         logger.info("Autowired annotation should only be used on methods with parameters: " +
                                     method);
                     }
                 }
                 boolean required = determineRequiredStatus(ann);
                 PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                 // 构造AutowiredMethodElement对象
                 currElements.add(new AutowiredMethodElement(method, required, pd));
             }
         });
 
         elements.addAll(0, currElements);
         targetClass = targetClass.getSuperclass();
     }
     while (targetClass != null && targetClass != Object.class);
 
     return InjectionMetadata.forElements(elements, clazz);
 }
复制代码

AutowiredFieldElement和AutowiredMethodElement对象很类似,都是构造器初始化数据,inject()获取bean,现以AutowiredFieldElement为例进行说明。

2、AutowiredFieldElement对象

 private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {
 
     private final boolean required;
 
     private volatile boolean cached;
 
     @Nullable
     private volatile Object cachedFieldValue;
 
     // 1、构造器初始化数据,由于@Autowired就一个required属性,故非常见到那
     public AutowiredFieldElement(Field field, boolean required) {
         super(field, null);
         this.required = required;
     }
 
     // 2、inject()方法获取bean
     @Override
     protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
         Field field = (Field) this.member;
         Object value;
         if (this.cached) {
             try {
                 // 2.1 开启缓存,调用resolvedCachedArgument获取bean
                 value = resolvedCachedArgument(beanName, this.cachedFieldValue);
             }
             catch (NoSuchBeanDefinitionException ex) {
                 // Unexpected removal of target bean for cached argument -> re-resolve
                 // 调用resolvedCachedArgument获取bean,则调用resolveFieldValue获取bean
                 value = resolveFieldValue(field, bean, beanName);
             }
         }
         else {
             // 2.2 调用resolveFieldValue获取bean
             value = resolveFieldValue(field, bean, beanName);
         }
         if (value != null) {
             ReflectionUtils.makeAccessible(field);
             field.set(bean, value);
         }
     }
 
     // 3、调用resolveFieldValue()获取bean
     @Nullable
     private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
         DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
         desc.setContainingClass(bean.getClass());
         Set autowiredBeanNames = new LinkedHashSet<>(1);
         Assert.state(beanFactory != null, "No BeanFactory available");
         TypeConverter typeConverter = beanFactory.getTypeConverter();
         Object value;
         try {
             // 3.1 实际上就是调用beanFactory.resolveDependency方法
             value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
         }
         catch (BeansException ex) {
             throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
         }
         synchronized (this) {
             if (!this.cached) {
                 Object cachedFieldValue = null;
                 if (value != null || this.required) {
                     cachedFieldValue = desc;
                     registerDependentBeans(beanName, autowiredBeanNames);
                     if (autowiredBeanNames.size() == 1) {
                         String autowiredBeanName = autowiredBeanNames.iterator().next();
                         if (beanFactory.containsBean(autowiredBeanName) &&
                             beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                             cachedFieldValue = new ShortcutDependencyDescriptor(
                                 desc, autowiredBeanName, field.getType());
                         }
                     }
                 }
                 this.cachedFieldValue = cachedFieldValue;
                 this.cached = true;
             }
         }
         return value;
     }
 }
复制代码

如果开启缓存,则inject()方法直接调用resolvedCachedArgument()方法获取bean,否则调用resolveFieldValue()方法,而resolveFieldValue()方法又会去调用
beanFactory.resolveDependency()方法,即调用
org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency()方法,进而调用doResolveDependency()方法

3、DefaultListableBeanFactory对象

3.1 resolveDependency():@Lazy懒加载处理

总结:如果使用了@Lazy,则生成cglib代理对象返回;否则调用doResolveDependency()方法获取bean

 @Override
 @Nullable
 public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
                @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
 
     descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
     if (Optional.class == descriptor.getDependencyType()) {
         return createOptionalDependency(descriptor, requestingBeanName);
     }
     else if (ObjectFactory.class == descriptor.getDependencyType() ||
              ObjectProvider.class == descriptor.getDependencyType()) {
         return new DependencyObjectProvider(descriptor, requestingBeanName);
     }
     else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
         return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
     }
     // 上面是一些特殊逻辑处理,下面才是主流程
     else {
         // 如果使用@Autowired的同时使用了@Lazy,则这里返回一个cglib代理对象,否则返回null
         Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
             descriptor, requestingBeanName);
         if (result == null) {
             // 也就是说只有单独使用@Autowired才会进这里
             result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
         }
         return result;
     }
 }
复制代码

3.2 doResolveDependency():获取bean的处理流程

 // 注入依赖时未使用@Lazy注解,调用此方法
 @Nullable
 public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
                @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
 
         ......
             
         // 1、按类型查找所有的bean,未通过@Qualifier指定name时,查到几个就是几个;
         //  若通过@Qualifier指定name,则将查到的bean与name进行匹配,没有匹配的就返回空集合
         // findAutowireCandidates()方法详解见下文
         Map matchingBeans = findAutowireCandidates(beanName, type, descriptor);
         if (matchingBeans.isEmpty()) {  // 没有找到
             if (isRequired(descriptor)) {   // required值为true,必须有值,抛出异常
                 raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
             }
             return null;    //required值为false,返回null
         }
 
         String autowiredBeanName;
         Object instanceCandidate;
 
         // 2、按类型找到多个bean
         if (matchingBeans.size() > 1) {
             // 从多个bean中找到那唯一一个,没有找到返回null
             // 寻找规则:@Primary > @Priority (若有多个,返回优先级最高的那个,值越小优先级越高) > 按照字段名匹配
             // determineAutowireCandidate()方法详解见下文
             autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
             if (autowiredBeanName == null) {
                 // 没有从多个bean中选出唯一一个,而且还是必须的,则抛出异常
                 if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
                     return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
                 }
                 else {
                     return null;
                 }
             }
             instanceCandidate = matchingBeans.get(autowiredBeanName);
         }
         else {
             // We have exactly one match.
             // 3、按类型找到一个bean,直接返回
             Map.Entry entry = matchingBeans.entrySet().iterator().next();
             autowiredBeanName = entry.getKey();
             instanceCandidate = entry.getValue();
         }
 
      ......
 }
复制代码

3.3 findAutowireCandidates():通过类型查找bean的处理流程

方法路径:
org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates()

总结:

  1. 获取所有类型的bean
  2. 如果使用@Qualifier声明name,则将指定name的bean放入结果集中,若没有则返回空
  3. 如果未使用@Qualifier声明name,则将所有bean放入结果集中,返回
 protected Map findAutowireCandidates(
     @Nullable String beanName, Class requiredType, DependencyDescriptor descriptor) {
 
     // 1、获取该类型的所有bean
     String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
         this, requiredType, true, descriptor.isEager());
     
     // 2、将resolvableDependencies集合中类型相同的bean放到result中
     Map result = CollectionUtils.newLinkedHashMap(candidateNames.length);
     for (Map.Entry<Class, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
         Class autowiringType = classObjectEntry.getKey();
         if (autowiringType.isAssignableFrom(requiredType)) {
             Object autowiringValue = classObjectEntry.getValue();
             autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
             if (requiredType.isInstance(autowiringValue)) {
                 result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
                 break;
             }
         }
     }
     
     // 3、循环所有bean数组,将满足条件的bean放入result中
     for (String candidate : candidateNames) {
         // isSelfReference:判断是否为自引用
         // isAutowireCandidate:判断指定的 bean 是否有资格作为自动装配候选者。
         //  如果没有使用@Qualifier声明name,则都有资格;若声明了name,则只有beanName等于name时才有资格
         //  具体调用路径见后文
         if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
             addCandidateEntry(result, candidate, descriptor, requiredType);
         }
     }
     // 后备匹配选项,暂时忽略
     ......
     return result;
 }
复制代码

isAutowireCandidate()方法调用流程简单说明(通过debug可以快速走出来)

 // 第一步:重载方法
 @Override
 public boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor)
     throws NoSuchBeanDefinitionException {
 
     return isAutowireCandidate(beanName, descriptor, getAutowireCandidateResolver());
 }
 
 // 第二步:重载方法
 protected boolean isAutowireCandidate(
     String beanName, DependencyDescriptor descriptor, AutowireCandidateResolver resolver)
     throws NoSuchBeanDefinitionException {
 
     String bdName = BeanFactoryUtils.transformedBeanName(beanName);
     if (containsBeanDefinition(bdName)) {
         // 2、走这里
         return isAutowireCandidate(beanName, getMergedLocalBeanDefinition(bdName), descriptor, resolver);
     }
     else if (containsSingleton(beanName)) {
         return isAutowireCandidate(beanName, new RootBeanDefinition(getType(beanName)), descriptor, resolver);
     }
     // 代码省略
     ......
 }
 
 // 第三步:
 protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd,
             DependencyDescriptor descriptor, AutowireCandidateResolver resolver) {
     // 代码省略
     ......
     // 3、走这里
     return resolver.isAutowireCandidate(holder, descriptor);
 }
 
 // 第四步:org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#isAutowireCandidate()
 @Override
 public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
     boolean match = super.isAutowireCandidate(bdHolder, descriptor);
     if (match) {
         // 4、判断Qualifier注解声明的name是否与当前bean匹配
         match = checkQualifiers(bdHolder, descriptor.getAnnotations());
         if (match) {
             MethodParameter methodParam = descriptor.getMethodParameter();
             if (methodParam != null) {
                 Method method = methodParam.getMethod();
                 if (method == null || void.class == method.getReturnType()) {
                     match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
                 }
             }
         }
     }
     return match;
 }
 
 // 第五步:将给定的限定符(即Qualifier声明的name)与候选 bean 的beanName匹配
 protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
     ......
 }
复制代码

3.4 determineAutowireCandidate():找到多个bean的处理流程

方法路径:
org.springframework.beans.factory.support.DefaultListableBeanFactory#
determineAutowireCandidate()

总结:

  1. 判断是否有使用@Primary注解的bean,有直接返回,没有返回null
  2. 判断是否有使用@Priority注解的bean,没有返回null;有一个直接返回;有多个,返回优先级最高的那个(@Priority值越小优先级越高)
  3. 既没有使用@Primary,也没有使用@Priority,用被@Autowired注解字段名称当做beanName去匹配,匹配成功直接返回,否则返回null
 @Nullable
 protected String determineAutowireCandidate(Map candidates, DependencyDescriptor descriptor) {
     Class requiredType = descriptor.getDependencyType();
     // 1、判断是否有使用@Primary注解的bean,有直接返回,没有返回null
     String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
     if (primaryCandidate != null) {
         return primaryCandidate;
     }
     // 2、判断是否有使用@Priority注解的bean,没有返回null;有一个直接返回;有多个,返回优先级最高的那个(@Priority值越小优先级越高)
     String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
     if (priorityCandidate != null) {
         return priorityCandidate;
     }
     // Fallback
     // 3、既没有使用@Primary,也没有使用@Priority,用被@Autowired注解字段名称当做beanName去匹配,匹配成功直接返回,否则返回null
     for (Map.Entry entry : candidates.entrySet()) {
         String candidateName = entry.getKey();
         Object beanInstance = entry.getValue();
         // matchesBeanName():判断候选bean集合中是否存在与字段名称相同的bean
         if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
             matchesBeanName(candidateName, descriptor.getDependencyName())) {
             return candidateName;
         }
     }
     return null;
 }
复制代码

三、@Resource注入过程源码分析

1、CommonAnnotationBeanPostProcessor#buildResourceMetadata():切入点

首先定位到
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#buildResourceMetadata()方法,里面有这么一段代码:判断字段是否使用@Resource注解:先判断是否为静态字段,然后判断字段类型是否为忽略的类型,若不是则构建一个ResourceElement对象。

 ......
 else if (field.isAnnotationPresent(Resource.class)) {
     // 判断是否为静态字段
     if (Modifier.isStatic(field.getModifiers())) {
         throw new IllegalStateException("@Resource annotation is not supported on static fields");
     }
     // 判断字段类型是否为忽略的类型,若不是则构建一个ResourceElement对象
     if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
         currElements.add(new ResourceElement(field, field, null));
     }
 }
 ......
复制代码

2、ResourceElement对象

对象路径:
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.ResourceElement()

ResourceElement类包含2个方法:一个构造器,一个getResourceToInject()方法。构造器只要功能就是初始化数据,代码如下:

 public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
     super(member, pd);
     Resource resource = ae.getAnnotation(Resource.class);
     // 1、获取注解的name属性值
     String resourceName = resource.name();
     // 2、获取注解的type属性值
     Class resourceType = resource.type();
     // 3、判断name属性值是否为默认值""
     this.isDefaultName = !StringUtils.hasLength(resourceName);
     if (this.isDefaultName) {
         // 3.1 获取被注解原始的名称:用在字段上,则拿到的就是字段名称,否则是set方法
         resourceName = this.member.getName();
         // 3.2 如果是方法,则截取set后的元素,并将首字母改为小写
         if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
             resourceName = Introspector.decapitalize(resourceName.substring(3));
         }
     }
     else if (embeddedValueResolver != null) {
         // 4、指定了name属性,则进行解析(占位符的处理等)
         resourceName = embeddedValueResolver.resolveStringValue(resourceName);
     }
     // 5、若显示指定了type属性值,则判断指定的值与字段的类型或set方法返回值类型是否为父子关系,若不是则抛出异常
     if (Object.class != resourceType) {
         checkResourceType(resourceType);
     }
     else {
         // 未指定type属性值,使用默认值Object.class,则获取字段类型或set方法的返回值类型
         resourceType = getResourceType();
     }
     // 6、将name属性值和type属性值赋值给成员变量
     this.name = (resourceName != null ? resourceName : "");
     this.lookupType = resourceType;
     String lookupValue = resource.lookup();
     this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
     // 7、@Lazy懒加载处理
     Lazy lazy = ae.getAnnotation(Lazy.class);
     this.lazyLookup = (lazy != null && lazy.value());
 }
 
 @Override
 protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
     // this.lazyLookup = true,表示使用了@Lazy注解,此时调用buildLazyResourceProxy返回cglib代理对象,否则调用getResource方法
     return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
             getResource(this, requestingBeanName));
 }
复制代码

总结:

  1. 从这里可以看出,只要你不指定name属性,则都会把name属性值处理未字段名或set方法声明的字段名称
  2. 除非你指定的type属性值与字段类型或set方法返回值类型为父子关系(指定值可以是子类,也可以是超类) ,否则都会抛出异常
  3. @Resource 在不指定 name 的情况下,默认的name是字段名或set方法标识的字段名称;如果默认的name 或指定的name找不到 bean ,就会按 type 注入。

3、autowireResource():获取bean流程

调用链:getResourceToInject() -> getResource() -> autowireResource

方法路径:
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource()

 protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
       throws NoSuchBeanDefinitionException {
 
    Object resource;
    Set autowiredBeanNames;
    String name = element.name;
 
     // 1、判断beanFactory类型
    if (factory instanceof AutowireCapableBeanFactory) {
       AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
       DependencyDescriptor descriptor = element.getDependencyDescriptor();
       // 1.1 根据 name 属性值获取不到bean
       if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
          autowiredBeanNames = new LinkedHashSet<>();
          // 1.1.1 根据 type 类型获取bean
          resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
          // 1.1.2 获取不到抛出异常
          if (resource == null) {
             throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
          }
       }
        // 1.2 根据name属性获取到bean,判断类型是否相同,不同抛出异常
       else {
          resource = beanFactory.resolveBeanByName(name, descriptor);
          autowiredBeanNames = Collections.singleton(name);
       }
    }
    else {   // 2、根据name属性获取到bean,判断类型是否相同,不同抛出异常
       resource = factory.getBean(name, element.lookupType);
       autowiredBeanNames = Collections.singleton(name);
    }
 
    if (factory instanceof ConfigurableBeanFactory) {
       ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
       for (String autowiredBeanName : autowiredBeanNames) {
          if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {
             beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
          }
       }
    }
 
    return resource;
 }



学习更多JAVA知识与技巧,内容涵盖:Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、Redis、MySQL、Spring、SpringBoot、SpringCloud、RabbitMQ、Kafka、Linux等技术栈,关注博主!

相关推荐

ETCD 故障恢复(etc常见故障)

概述Kubernetes集群外部ETCD节点故障,导致kube-apiserver无法启动。...

在Ubuntu 16.04 LTS服务器上安装FreeRADIUS和Daloradius的方法

FreeRADIUS为AAARadiusLinux下开源解决方案,DaloRadius为图形化web管理工具。...

如何排查服务器被黑客入侵的迹象(黑客 抓取服务器数据)

---排查服务器是否被黑客入侵需要系统性地检查多个关键点,以下是一份详细的排查指南,包含具体命令、工具和应对策略:---###**一、快速初步检查**####1.**检查异常登录记录**...

使用 Fail Ban 日志分析 SSH 攻击行为

通过分析`fail2ban`日志可以识别和应对SSH暴力破解等攻击行为。以下是详细的操作流程和关键分析方法:---###**一、Fail2ban日志位置**Fail2ban的日志路径因系统配置...

《5 个实用技巧,提升你的服务器安全性,避免被黑客盯上!》

服务器的安全性至关重要,特别是在如今网络攻击频繁的情况下。如果你的服务器存在漏洞,黑客可能会利用这些漏洞进行攻击,甚至窃取数据。今天我们就来聊聊5个实用技巧,帮助你提升服务器的安全性,让你的系统更...

聊聊Spring AI Alibaba的YuQueDocumentReader

序本文主要研究一下SpringAIAlibaba的YuQueDocumentReaderYuQueDocumentReader...

Mac Docker环境,利用Canal实现MySQL同步ES

Canal的使用使用docker环境安装mysql、canal、elasticsearch,基于binlog利用canal实现mysql的数据同步到elasticsearch中,并在springboo...

RustDesk:开源远程控制工具的技术架构与全场景部署实战

一、开源远程控制领域的革新者1.1行业痛点与解决方案...

长安汽车一代CS75Plus2020款安装高德地图7.5

不用破解原车机,一代CS75Plus2020款,安装车机版高德地图7.5,有红绿灯读秒!废话不多讲,安装步骤如下:一、在拨号状态输入:在电话拨号界面,输入:*#518200#*(进入安卓设置界面,...

Zookeeper使用详解之常见操作篇(zookeeper ui)

一、Zookeeper的数据结构对于ZooKeeper而言,其存储结构类似于文件系统,也是一个树形目录服务,并通过Key-Value键值对的形式进行数据存储。其中,Key由斜线间隔的路径元素构成。对...

zk源码—4.会话的实现原理一(会话层的基本功能是什么)

大纲1.创建会话...

Zookeeper 可观测性最佳实践(zookeeper能够确保)

Zookeeper介绍ZooKeeper是一个开源的分布式协调服务,用于管理和协调分布式系统中的节点。它提供了一种高效、可靠的方式来解决分布式系统中的常见问题,如数据同步、配置管理、命名服务和集群...

服务器密码错误被锁定怎么解决(服务器密码错几次锁)

#服务器密码错误被锁定解决方案当服务器因多次密码错误导致账户被锁定时,可以按照以下步骤进行排查和解决:##一、确认锁定状态###1.检查账户锁定状态(Linux)```bash#查看账户锁定...

zk基础—4.zk实现分布式功能(分布式zk的使用)

大纲1.zk实现数据发布订阅...

《死神魂魄觉醒》卡死问题终极解决方案:从原理到实战的深度解析

在《死神魂魄觉醒》的斩魄刀交锋中,游戏卡死犹如突现的虚圈屏障,阻断玩家与尸魂界的连接。本文将从技术架构、解决方案、预防策略三个维度,深度剖析卡死问题的成因与应对之策,助力玩家突破次元壁障,畅享灵魂共鸣...

取消回复欢迎 发表评论: