一、问题和详细异常
今天一个同事在开发时给一个类(AClassImpl
)加了@AllArgsConstructor
,希望通过private final XxClass xxClass的方式将XxClass
注入到当前类:
@Service
@AllArgsConstructor
public class AClassImpl implements AClass {
private final XsClass xxClass;
运行时报错如下:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'io.xxx.class' defined in URL [jar:file:/app/app.jar!/BOOT-INF/lib/xxx-SNAPSHOT.jar!xxxxx.class]: Unsatisfied dependency expressed through constructor parameter 2; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {
}
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1356)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1206)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:571)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:531)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:923)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:588)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:144)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:767)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:426)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:326)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1309)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1298)
at io.xxx.Application.main(Application.java:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:107)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {
}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1777)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1333)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1287)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
... 28 common frames omitted
二、原因分析
从报错提示可以看出是没有找到NoSuchBeanDefinition;根本原因是因为springioc容器加载bean默认使用无参构造进行初始化。
三、解决方案
给AClassImpl提供一个默认的构造方法。使用@RequiredArgsConstructor
注解来替换@AllArgsConstructor
注解;
@RequiredArgsConstructor 和 @AllArgsConstructor的区别?
- @RequiredArgsConstructor会将类的每一个final字段或者non-null字段生成一个构造方法
- @AllArgsConstructor 生成一个包含所有字段的构造方法;
- @NoArgsConstructor 生成无参的构造方法;
使用@RequiredArgsConstructor
可以代替@Autowrited注解;而@RequiredArgsConstructor
是根据构造器注入的,所以会有循坏依赖的问题。
@RequiredArgsConstructor循环依赖问题解决措施
有三种方式可以解决:
- 改为使用@Autowired注解做属性方法注入Bean;
不过,Spring 从 4.0 开始, 就不推荐使用属性注入模式了,原因在于它可以让我们忽略掉一些代码可能变坏的隐患。 - 使用@RequiredArgsConstructor(onConstructor =@_(@Autowired) ),这样默认都是通过@Autowired注入Bean的;
- 使用@RequiredArgsConstructor(onConstructor_={@Lazy} ),做懒加载处理。