【Spring源码分析】18-CommonAnnotationBeanPostProcessor

本文介绍了CommonAnnotationBeanPostProcessor对注解的处理。它继承InitDestroyAnnotationBeanPostProcessor,支持@PostConstruct、@PreDestroy、@Resource等注解。分阶段完成@PostConstruct和@PreDestroy注解功能,查找生命周期方法并缓存。对@Resource注解,先获取标记的字段或方法,再在属性注入阶段完成对象注入,还涉及懒加载代理对象创建。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CommonAnnotationBeanPostProcessor这个BeanPostProcessor通过继承InitDestroyAnnotationBeanPostProcessor对@javax.annotation.PostConstruct和@javax.annotation.PreDestroy注解的支持。以及依据bean name依赖注入的@javax.annotation.Resource支持。也支持WebServiceRef注解,具有创建JAX-WS服务端点的能力。最后,处理器还支持EJB3(@EJB)。

1、对@PostConstruct和@PreDestroy注解的处理

CommonAnnotationBeanPostProcessor继承了InitDestroyAnnotationBeanPostProcessor,这两个注解是由这个父类完成解析的,在CommonAnnotationBeanPostProcessor构造函数中调用父类的方法setInitAnnotationType()和setDestroyAnnotationType()将这两个注解Class对象传递给父类。

public CommonAnnotationBeanPostProcessor() {
   setOrder(Ordered.LOWEST_PRECEDENCE - 3);
   setInitAnnotationType(PostConstruct.class);
   setDestroyAnnotationType(PreDestroy.class);
   ignoreResourceType("javax.xml.ws.WebServiceContext");
}

在InitDestroyAnnotationBeanPostProcessor中分三个阶段完成这两个注解功能,分别为:

  1. 实现了MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()方法缓存初始化后和销毁前执行的方法。
  2. 实现了BeanPostProcessor.postProcessBeforeInitialization()用来执行@PostConstruct标注的方法。
  3. 实现了DestructionAwareBeanPostProcessor.postProcessBeforeDestruction()用来执行@PreDestroy标注的方法。

1.1、查找生命周期方法

在实例化一个bean后此时还未进行依赖注入时,每个bean definition会被MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()方法执行一遍用来获取一些元数据来增加额外的功能,InitDestroyAnnotationBeanPostProcessor就是将bean定义了被@PostConstruct和@PreDestroy注解的方法缓存到一个Map中,供之后BeanPostProcessor.postProcessBeforeInitialization()和DestructionAwareBeanPostProcessor.postProcessBeforeDestruction()阶段可以直接获取这些方法来执行。

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
   super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
   InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
   metadata.checkConfigMembers(beanDefinition);
}

 先看父类查找bean的初始化和销毁的方法。

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
   LifecycleMetadata metadata = findLifecycleMetadata(beanType);
   metadata.checkConfigMembers(beanDefinition);
}

findLifecycleMetadata方法内部主要调用buildLifecycleMetadata方法完成生命周期方法的,其余部分是缓存的操作不关心。 

private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
   List<LifecycleElement> initMethods = new ArrayList<>();
   List<LifecycleElement> destroyMethods = new ArrayList<>();
   Class<?> targetClass = clazz;

   do {
      final List<LifecycleElement> currInitMethods = new ArrayList<>();
      final List<LifecycleElement> currDestroyMethods = new ArrayList<>();

      ReflectionUtils.doWithLocalMethods(targetClass, method -> {
         // 构造方法指定的@PostConstruct注解
         if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
            LifecycleElement element = new LifecycleElement(method);
            currInitMethods.add(element);
            if (logger.isTraceEnabled()) {
               logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
            }
         }
         // @PreDestroy方法
         if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
            currDestroyMethods.add(new LifecycleElement(method));
            if (logger.isTraceEnabled()) {
               logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
            }
         }
      });
      // 初始化方法父类早于子类
      initMethods.addAll(0, currInitMethods);
      // 销毁方法父类晚于子类
      destroyMethods.addAll(currDestroyMethods);
      targetClass = targetClass.getSuperclass();
   }
   while (targetClass != null && targetClass != Object.class);

   return new LifecycleMetadata(clazz, initMethods, destroyMethods);
}

LifecycleElement是一个内部类,保存了生命周期方法和方法的一个标识符,和一个invoke()方法用来调用被注解的方法。值得注意的是这个方法的可以是private的。 

private static class LifecycleElement {
   private final Method method;
   private final String identifier;
   public LifecycleElement(Method method) {
      if (method.getParameterCount() != 0) {
         throw new IllegalStateException("Lifecycle method annotation requires a no-arg method: " + method);
      }
      this.method = method;
      this.identifier = (Modifier.isPrivate(method.getModifiers()) ?
            ClassUtils.getQualifiedMethodName(method) : method.getName());
   }
   public void invoke(Object target) throws Throwable {
      ReflectionUtils.makeAccessible(this.method);
      this.method.invoke(target, (Object[]) null);
   }
}

在依托父类完成生命周期方法的查找后就是查找@Resource有关的字段或方法了。

1.2、@PostConstruct方法的处理

bean会经过BeanPostProcessor.postProcessBeforeInitialization()方法处理,这个时候就会调用@PostConstruct方法。

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
   LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
   try {
      metadata.invokeInitMethods(bean, beanName);
   }
   catch (InvocationTargetException ex) {
      throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
   }
   return bean;
}
public void invokeInitMethods(Object target, String beanName) throws Throwable {
   Collection<LifecycleElement> checkedInitMethods = this.checkedInitMethods;
   Collection<LifecycleElement> initMethodsToIterate =
         (checkedInitMethods != null ? checkedInitMethods : this.initMethods);
   if (!initMethodsToIterate.isEmpty()) {
      for (LifecycleElement element : initMethodsToIterate) {
         if (logger.isTraceEnabled()) {
            logger.trace("Invoking init method on bean '" + beanName + "': " + element.getMethod());
         }
         element.invoke(target);
      }
   }
}

1.3、@PreDestroy方法的处理

在bean销毁前会经过DestructionAwareBeanPostProcessor.postProcessBeforeDestruction()方法,在这个时候执行@PreDestroy方法。

@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
   LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
   try {
      metadata.invokeDestroyMethods(bean, beanName);
   }
   catch (InvocationTargetException ex) {
      String msg = "Destroy method on bean with name '" + beanName + "' threw an exception";
      if (logger.isDebugEnabled()) {
         logger.warn(msg, ex.getTargetException());
      }
      else {
         logger.warn(msg + ": " + ex.getTargetException());
      }
   }
   catch (Throwable ex) {
      logger.warn("Failed to invoke destroy method on bean with name '" + beanName + "'", ex);
   }
}
public void invokeDestroyMethods(Object target, String beanName) throws Throwable {
   Collection<LifecycleElement> checkedDestroyMethods = this.checkedDestroyMethods;
   Collection<LifecycleElement> destroyMethodsToUse =
         (checkedDestroyMethods != null ? checkedDestroyMethods : this.destroyMethods);
   if (!destroyMethodsToUse.isEmpty()) {
      for (LifecycleElement element : destroyMethodsToUse) {
         if (logger.isTraceEnabled()) {
            logger.trace("Invoking destroy method on bean '" + beanName + "': " + element.getMethod());
         }
         element.invoke(target);
      }
   }
}

2、对@Resource注解的处理

与@PostConstruct类似,先在postProcessMergedBeanDefinition中取得被@Resource标记的字段或方法,后再postProcessProperties()方法中完成对象的注入。

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
   super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
   //找出beanType所有被@Resource标记的字段和方法封装到InjectionMetadata中
   InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
   //将InjectionMetadata中每个被@Resource标记的字段和方法打标,防止重复计算
   metadata.checkConfigMembers(beanDefinition);
}

private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs) {
   // Fall back to class name as cache key, for backwards compatibility with custom callers.
   String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
   // Quick check on the concurrent map first, with minimal locking.
   InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
   if (InjectionMetadata.needsRefresh(metadata, clazz)) {
      synchronized (this.injectionMetadataCache) {
         metadata = this.injectionMetadataCache.get(cacheKey);
         if (InjectionMetadata.needsRefresh(metadata, clazz)) {
            if (metadata != null) {
               metadata.clear(pvs);
            }
            metadata = buildResourceMetadata(clazz);
            this.injectionMetadataCache.put(cacheKey, metadata);
         }
      }
   }
   return metadata;
}
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
   List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
   Class<?> targetClass = clazz;

   do {
      final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

      ReflectionUtils.doWithLocalFields(targetClass, field -> {
         if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
            if (Modifier.isStatic(field.getModifiers())) {
               throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
            }
            currElements.add(new WebServiceRefElement(field, field, null));
         }
         else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
            if (Modifier.isStatic(field.getModifiers())) {
               throw new IllegalStateException("@EJB annotation is not supported on static fields");
            }
            currElements.add(new EjbRefElement(field, field, null));
         }
         else if (field.isAnnotationPresent(Resource.class)) {
            //注意静态字段不支持
            if (Modifier.isStatic(field.getModifiers())) {
               throw new IllegalStateException("@Resource annotation is not supported on static fields");
            }
            //如果不想注入某一类型对象 可以将其加入ignoredResourceTypes中
            if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
               //字段会封装到ResourceElement
               currElements.add(new ResourceElement(field, field, null));
            }
         }
      });

      ReflectionUtils.doWithLocalMethods(targetClass, method -> {
         //找出我们在代码中定义的方法而非编译器为我们生成的方法
         Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
         if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
            return;
         }
         //如果重写了父类的方法,则使用子类的
         if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
            if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
               if (Modifier.isStatic(method.getModifiers())) {
                  throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
               }
               if (method.getParameterCount() != 1) {
                  throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
               }
               PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
               currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
            }
            else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
               if (Modifier.isStatic(method.getModifiers())) {
                  throw new IllegalStateException("@EJB annotation is not supported on static methods");
               }
               if (method.getParameterCount() != 1) {
                  throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
               }
               PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
               currElements.add(new EjbRefElement(method, bridgedMethod, pd));
            }
            else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
               //同样不支持静态方法
               if (Modifier.isStatic(method.getModifiers())) {
                  throw new IllegalStateException("@Resource annotation is not supported on static methods");
               }
               Class<?>[] paramTypes = method.getParameterTypes();
               if (paramTypes.length != 1) {
                  throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
               }
               if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
                  //bean的getter或setter
                  PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                  currElements.add(new ResourceElement(method, bridgedMethod, pd));
               }
            }
         }
      });
      //先父后子
      elements.addAll(0, currElements);
      targetClass = targetClass.getSuperclass();
   }
   while (targetClass != null && targetClass != Object.class);

   return new InjectionMetadata(clazz, elements);

上述代码是将@Resource注解的字段和方法以bean name为key,InjectedElement为value封装一个Map中,在属性注入阶段取出InjectedElement通过反射为目标字段或方法设置@Resource name指定的bean。InjectionMetadata封装的是一组InjectionMetadata.InjectedElement,这个InjectedElement会使用inject方法完成依赖注入,具体下面会讲到。

下面看postProcessProperties()方法如何进行属性注入的。

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
   InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
   try {
      metadata.inject(bean, beanName, pvs);
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
   }
   return pvs;
}

将缓存中的InjectionMetadata 取出调用它的inject方法。

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
   Collection<InjectedElement> checkedElements = this.checkedElements;
   Collection<InjectedElement> elementsToIterate =
         (checkedElements != null ? checkedElements : this.injectedElements);
   if (!elementsToIterate.isEmpty()) {
      for (InjectedElement element : elementsToIterate) {
         if (logger.isTraceEnabled()) {
            logger.trace("Processing injected element of bean '" + beanName + "': " + element);
         }
         element.inject(target, beanName, pvs);
      }
   }
}

 依次调用之前保存的ResouceElement对象的inject方法。下面的重点就是放到了ResouceElement身上。

private class ResourceElement extends LookupElement {

   private final boolean lazyLookup;

   //根据上面分析有两种调用构造方法的情况
   //1、member是字段ae也是字段,pd==null
   //2、member是桥接方法(或原始方法)ae是原始方法,pd是原始方法的getter或setter
   public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
      super(member, pd);
      Resource resource = ae.getAnnotation(Resource.class);
      String resourceName = resource.name();
      Class<?> resourceType = resource.type();
      this.isDefaultName = !StringUtils.hasLength(resourceName);
      if (this.isDefaultName) {
         ///如果没有设置@Resource name属性就用字段名称作为bean name
         resourceName = this.member.getName();
         //如果member是setter方法方法 则取setXXX得XXX部分为bean name
         if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
            resourceName = Introspector.decapitalize(resourceName.substring(3));
         }
      }
      else if (embeddedValueResolver != null) {
         //如果设置了@Resource name的属性,则使用EmbeddedValueResolver对象先做一次SpringEL解析得到真正的bean name
         resourceName = embeddedValueResolver.resolveStringValue(resourceName);
      }
      if (Object.class != resourceType) {
         //确保字段或setter方法类型与resourceType一致
         checkResourceType(resourceType);
      }
      else {
         // No resource type specified... check field/method.
         resourceType = getResourceType();
      }
      this.name = (resourceName != null ? resourceName : "");
      this.lookupType = resourceType;
      String lookupValue = resource.lookup();
      //如果使用jndi查找的名字
      this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
      Lazy lazy = ae.getAnnotation(Lazy.class);
      //是否延迟注入
      this.lazyLookup = (lazy != null && lazy.value());
   }

   @Override
   protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
      //如果懒加载则使用一个代理对象,往下看
      return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
            getResource(this, requestingBeanName));
   }
}

从上面CommonAnnotationBeanPostProcessor的postProcessProperties()方法可知,属性的注入是调用ResourceElement的inject()方法,ResourceElement本身没有这个方法而是调用父类InjectedElement的inject方法。

protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
      throws Throwable {

   if (this.isField) {
      Field field = (Field) this.member;
      ReflectionUtils.makeAccessible(field);
      //如果是使用字段形式的注入,getResourceToInject由子类@ResourceElement实现,下面再看
      field.set(target, getResourceToInject(target, requestingBeanName));
   }
   else {
      //此步骤检测如果bean已经显示的设置一个对象依赖引用则跳过使用setter方法再次赋值。
      if (checkPropertySkipping(pvs)) {
         return;
      }
      try {
         Method method = (Method) this.member;
         //支持私有方法
         ReflectionUtils.makeAccessible(method);
         method.invoke(target, getResourceToInject(target, requestingBeanName));
      }
      catch (InvocationTargetException ex) {
         throw ex.getTargetException();
      }
   }
}

可以看到需要注入对象的来源方法getResourceToInject(),上面已经看到如果懒加载则使用buildLazyResourceProxy()方法将返回一个代理对象,等到使用到这个代理对象的方法时才会调用getResource()返回requestingBeanName指定的bean。下面先看getResource()方法。

protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
      throws NoSuchBeanDefinitionException {
    
   if (StringUtils.hasLength(element.mappedName)) {
      return this.jndiFactory.getBean(element.mappedName, element.lookupType);
   }
   if (this.alwaysUseJndiLookup) {
      return this.jndiFactory.getBean(element.name, element.lookupType);
   }
   if (this.resourceFactory == null) {
      throw new NoSuchBeanDefinitionException(element.lookupType,
            "No resource factory configured - specify the 'resourceFactory' property");
   }
   return autowireResource(this.resourceFactory, element, requestingBeanName);
}

getResource()方法又直接调用了autowireResource()方法。

protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
      throws NoSuchBeanDefinitionException {

   Object resource;
   Set<String> autowiredBeanNames;
   String name = element.name;

   if (this.fallbackToDefaultTypeMatch && element.isDefaultName &&
         factory instanceof AutowireCapableBeanFactory && !factory.containsBean(name)) {
      //如果容器中还没有此bean,则会使用resolveDependency()方法将符合bean type的bean definetion调用一次getBean()从这些bean选出符合requestingBeanName的bean
      autowiredBeanNames = new LinkedHashSet<>();
      resource = ((AutowireCapableBeanFactory) factory).resolveDependency(
            element.getDependencyDescriptor(), requestingBeanName, autowiredBeanNames, null);
      if (resource == null) {
         throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
      }
   }
   else {
      //如果容器中有此bean则取出这个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;
}

下面再看需要懒加载的代理对象创建方法buildLazyResourceProxy()。

protected Object buildLazyResourceProxy(final LookupElement element, final @Nullable String requestingBeanName) {
   TargetSource ts = new TargetSource() {
      @Override
      public Class<?> getTargetClass() {
         return element.lookupType;
      }
      @Override
      public boolean isStatic() {
         return false;
      }
      @Override
      public Object getTarget() {
         //这个方法里还是会调用getResource(),什么时候调用的getTarget()方法呢,往下看
         return getResource(element, requestingBeanName);
      }
      @Override
      public void releaseTarget(Object target) {
      }
   };
   //代理对象工厂
   ProxyFactory pf = new ProxyFactory();
   pf.setTargetSource(ts);
   if (element.lookupType.isInterface()) {
      pf.addInterface(element.lookupType);
   }
   ClassLoader classLoader = (this.beanFactory instanceof ConfigurableBeanFactory ?
         ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() : null);
   return pf.getProxy(classLoader);
}

代理工厂首先将TargetSource对象赋值到自身的成员变量中,然后调用getProxy()创建代理对象。

public Object getProxy(@Nullable ClassLoader classLoader) {
   return createAopProxy().getProxy(classLoader);
}
protected final synchronized AopProxy createAopProxy() {
   if (!this.active) {
      activate();
   }
   //getAopProxyFactory()默认返回DefaultAopProxyFactory
   return getAopProxyFactory().createAopProxy(this);
}
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
   if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
         throw new AopConfigException("TargetSource cannot determine target class: " +
               "Either an interface or a target is required for proxy creation.");
      }
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
   }
   else {
      return new JdkDynamicAopProxy(config);
   }
}

最终是使用了AopProxy 的具体子类的getProxy()方法完成代理对象的创建。以JdkDynamicAopProxy为例。

public Object getProxy(@Nullable ClassLoader classLoader) {
   if (logger.isTraceEnabled()) {
      logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
   }
   Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
   findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
   return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

使用了JDK的动态代理,自身也实现了InvocationHandler接口,来看invoke()实现。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   MethodInvocation invocation;
   Object oldProxy = null;
   boolean setProxyContext = false;

   TargetSource targetSource = this.advised.targetSource;
   Object target = null;

   try {
      if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
         // The target does not implement the equals(Object) method itself.
         return equals(args[0]);
      }
      else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
         // The target does not implement the hashCode() method itself.
         return hashCode();
      }
      else if (method.getDeclaringClass() == DecoratingProxy.class) {
         // There is only getDecoratedClass() declared -> dispatch to proxy config.
         return AopProxyUtils.ultimateTargetClass(this.advised);
      }
      else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
            method.getDeclaringClass().isAssignableFrom(Advised.class)) {
         // Service invocations on ProxyConfig with the proxy config...
         return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
      }

      Object retVal;

      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }

      // Get as late as possible to minimize the time we "own" the target,
      // in case it comes from a pool.
      target = targetSource.getTarget();
      Class<?> targetClass = (target != null ? target.getClass() : null);

      // Get the interception chain for this method.
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

      // Check whether we have any advice. If we don't, we can fallback on direct
      // reflective invocation of the target, and avoid creating a MethodInvocation.
      if (chain.isEmpty()) {
         // We can skip creating a MethodInvocation: just invoke the target directly
         // Note that the final invoker must be an InvokerInterceptor so we know it does
         // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
      }
      else {
         // We need to create a method invocation...
         invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
         // Proceed to the joinpoint through the interceptor chain.
         retVal = invocation.proceed();
      }

      // Massage return value if necessary.
      Class<?> returnType = method.getReturnType();
      if (retVal != null && retVal == target &&
            returnType != Object.class && returnType.isInstance(proxy) &&
            !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
         // Special case: it returned "this" and the return type of the method
         // is type-compatible. Note that we can't help if the target sets
         // a reference to itself in another returned object.
         retVal = proxy;
      }
      else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
         throw new AopInvocationException(
               "Null return value from advice does not match primitive return type for: " + method);
      }
      return retVal;
   }
   finally {
      if (target != null && !targetSource.isStatic()) {
         // Must have come from TargetSource.
         targetSource.releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}

这个方法大段代码是为了实现AOP,但还是会调用target = targetSource.getTarget();获取实际对象的,可以看到懒加载机制的代理对象只有调用其方法才会去容器中获取实际的bean。

2025-06-06 16:15:10.725 ERROR 40332 --- [ main] o.s.boot.SpringApplication : Application run failed org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'applyEntranceController': Unsatisfied dependency expressed through field 'applyEntranceService'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'applyEntranceServiceImpl': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'parkUserServiceImpl': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'feignLoggerFactory' defined in org.springframework.cloud.openfeign.FeignClientsConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.cloud.openfeign.FeignLoggerFactory]: Factory method 'feignLoggerFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: feign/slf4j/Slf4jLogger at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:660) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:920) ~[spring-context-5.3.27.jar:5.3.27] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.27.jar:5.3.27] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.12.jar:2.7.12] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) ~[spring-boot-2.7.12.jar:2.7.12] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.12.jar:2.7.12] at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.7.12.jar:2.7.12] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.12.jar:2.7.12] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.12.jar:2.7.12] at com.park.oa.ParkOaServerApplication.main(ParkOaServerApplication.java:16) ~[classes/:na] Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'applyEntranceServiceImpl': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'parkUserServiceImpl': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'feignLoggerFactory' defined in org.springframework.cloud.openfeign.FeignClientsConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.cloud.openfeign.FeignLoggerFactory]: Factory method 'feignLoggerFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: feign/slf4j/Slf4jLogger at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:332) ~[spring-context-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1391) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:657) ~[spring-beans-5.3.27.jar:5.3.27] ... 20 common frames omitted Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'parkUserServiceImpl': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'feignLoggerFactory' defined in org.springframework.cloud.openfeign.FeignClientsConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.cloud.openfeign.FeignLoggerFactory]: Factory method 'feignLoggerFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: feign/slf4j/Slf4jLogger at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:332) ~[spring-context-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1391) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:544) ~[spring-context-5.3.27.jar:5.3.27] at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:520) ~[spring-context-5.3.27.jar:5.3.27] at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:673) ~[spring-context-5.3.27.jar:5.3.27] at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:228) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:329) ~[spring-context-5.3.27.jar:5.3.27] ... 31 common frames omitted Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'feignLoggerFactory' defined in org.springframework.cloud.openfeign.FeignClientsConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.cloud.openfeign.FeignLoggerFactory]: Factory method 'feignLoggerFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: feign/slf4j/Slf4jLogger at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:658) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:486) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:920) ~[spring-context-5.3.27.jar:5.3.27] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.27.jar:5.3.27] at org.springframework.cloud.context.named.NamedContextFactory.createContext(NamedContextFactory.java:137) ~[spring-cloud-context-3.1.1.jar:3.1.1] at org.springframework.cloud.context.named.NamedContextFactory.getContext(NamedContextFactory.java:105) ~[spring-cloud-context-3.1.1.jar:3.1.1] at org.springframework.cloud.context.named.NamedContextFactory.getInstance(NamedContextFactory.java:146) ~[spring-cloud-context-3.1.1.jar:3.1.1] at org.springframework.cloud.openfeign.FeignClientFactoryBean.get(FeignClientFactoryBean.java:339) ~[spring-cloud-openfeign-core-3.1.0.jar:3.1.0] at org.springframework.cloud.openfeign.FeignClientFactoryBean.feign(FeignClientFactoryBean.java:121) ~[spring-cloud-openfeign-core-3.1.0.jar:3.1.0] at org.springframework.cloud.openfeign.FeignClientFactoryBean.getTarget(FeignClientFactoryBean.java:407) ~[spring-cloud-openfeign-core-3.1.0.jar:3.1.0] at org.springframework.cloud.openfeign.FeignClientFactoryBean.getObject(FeignClientFactoryBean.java:396) ~[spring-cloud-openfeign-core-3.1.0.jar:3.1.0] at org.springframework.cloud.openfeign.FeignClientsRegistrar.lambda$registerFeignClient$0(FeignClientsRegistrar.java:235) ~[spring-cloud-openfeign-core-3.1.0.jar:3.1.0] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1249) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1191) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1391) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:544) ~[spring-context-5.3.27.jar:5.3.27] at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:520) ~[spring-context-5.3.27.jar:5.3.27] at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:673) ~[spring-context-5.3.27.jar:5.3.27] at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:228) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:329) ~[spring-context-5.3.27.jar:5.3.27] ... 47 common frames omitted Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.cloud.openfeign.FeignLoggerFactory]: Factory method 'feignLoggerFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: feign/slf4j/Slf4jLogger at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-5.3.27.jar:5.3.27] ... 84 common frames omitted Caused by: java.lang.NoClassDefFoundError: feign/slf4j/Slf4jLogger at org.springframework.cloud.openfeign.FeignClientsConfiguration.feignLoggerFactory(FeignClientsConfiguration.java:168) ~[spring-cloud-openfeign-core-3.1.0.jar:3.1.0] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na] at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.27.jar:5.3.27] ... 85 common frames omitted Caused by: java.lang.ClassNotFoundException: feign.slf4j.Slf4jLogger at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[na:na] at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na] at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) ~[na:na] ... 91 common frames omitted
最新发布
06-07
<think>嗯,用户遇到的这个问题很典型,关于SpringBoot中feignLoggerFactory的NoClassDefFoundError错误。从用户描述看,错误涉及到feign.Logger和Slf4j的日志组件,这通常是依赖冲突或缺失导致的。用户上次提问时提到了多个引用信息,特别是引用[2]和[4]很有参考价值。引用[2]提到了SLF4J的绑定机制,而引用[4]则指出即使Maven中存在依赖,编译时也可能未被正确引入。用户现在明确想解决SpringBoot应用程序启动失败的问题,具体错误与Feign的LoggerFactory和Slf4j的Logger有关。这个错误通常发生在SpringCloud项目整合Feign和SLF4J时。我准备从三个主要方面来解答:首先是Maven依赖问题,需要检查feign-slf4j是否引入。用户可能漏了这个专用桥接包。接着要处理版本冲突,特别是SpringCloud和Feign的版本兼容性。最后要注意依赖排除,避免多个日志实现打架。用户之前提到过Maven依赖问题,所以解决方案会包含具体的dependencytree检查和版本推荐表格。考虑到用户可能不太熟悉Maven层次结构,在回答中需要解释如何查看依赖树并识别冲突。另外注意到用户提到"feignLoggerFactory"这个具体类名,说明错误日志已经定位到Feign的日志工厂类,这进步确认了是Feign-SLF4J桥接的问题。解决方案中会特别强调这个包的关键作用。最后准备用表格形式总结版本兼容关系,这样用户看起来更直观。相关问题设计也会围绕Feign日志配置和依赖验证的后续操作展开。</think> **Spring Boot Feign `feign.LoggerFactory` `NoClassDefFoundError` 解决方案** 这个问题通常源于 **Feign 所需的 SLF4J 日志实现缺失** 或 **jar包冲突**。请参考以下结构化步骤解决: --- ### **1. 添加关键依赖:`feign-slf4j`** Feign 默认日志输出需要 `feign-slf4j` 作为 SLF4J 的桥接模块: ```xml <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-slf4j</artifactId> <version>与Spring Cloud版本匹配</version> </dependency> ``` > ✅ **关键点**:仅 `spring-cloud-starter-openfeign` 提供基础 Feign 功能,**不含日志实现**[^2][^4]。 --- ### **2. 验证 SLF4J 实现包** 确保项目中存在 *至少一个* SLF4J 实现(如 Logback、Log4j2)。Spring Boot 默认依赖 Logback,检查 `pom.xml`: ```xml <!-- 默认包含在 spring-boot-starter-web 中 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </dependency> ``` > ⚠️ 若手动排除 Logback,需添加其他实现包(如 `log4j-slf4j-impl`)。 --- ### **3. 解决依赖冲突** 使用 Maven 检查依赖树,排除重复的 SLF4J 绑定: ```bash mvn dependency:tree -Dincludes=org.slf4j ``` 若发现多个 SLF4J 实现(如同时存在 Logback 和 Log4j),需排除冲突项: ```xml <dependency> <groupId>冲突的依赖组</groupId> <artifactId>冲突的依赖项</artifactId> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <!-- 或其他冗余绑定 --> </exclusion> </exclusions> </dependency> ``` --- ### **4. 检查版本兼容性** 确保版本兼容: | 组件 | 兼容性要求 | |-------------------|-----------------------------------| | Spring Cloud | 需兼容 Spring Boot 主版本 | | `feign-slf4j` | 需匹配 Spring Cloud Feign 版本 | | SLF4J 实现 | 需兼容 `slf4j-api` 版本 | --- ### **5. 检查编译打包结果** 确认依赖包已正确打入最终制品: * **IDE中**:检查 `target/` 下的 `lib/` 目录 * **JAR/WAR包**:解压后检查 `BOOT-INF/lib/`(Spring Boot可执行JAR)或 `WEB-INF/lib/`(WAR包) > ❗ 若依赖缺失,尝试 `mvn clean package` 重新构建 [^4]。 --- ### **总结流程图** ```mermaid graph TD A[出现 NoClassDefFoundError] --> B{添加 feign-slf4j} B --> C[检查SLF4J实现] C --> D[检查版本兼容] D --> E[排除冲突依赖] E --> F[验证最终打包结果] F --> G[问题解决] ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值