在大型项目开发中,多个开发者并行工作时,确保所有必需的依赖项都已正确设置是至关重要的。理想情况下,这种检查应该在编译时进行,如果不可能,那么至少在应用启动时尽早进行,以避免在缺少值时出现NullPointerException
。Spring框架提供了多种在启动时进行依赖检查的机制。本文将探讨Spring框架在依赖检查方面的一些方面和选项。
构造器注入与Setter注入
在我们之前的教程中,我们讨论了依赖注入的不同方式,我们总是应该使用基于构造器的注入来处理必需的属性(并进行程序化的参数验证),而使用基于Setter的注入来处理可选属性。尽管如此,我们可能仍有许多理由选择使用Setter而不是构造器来注入必需的属性。可能是因为我们的构造器变得过于复杂,也可能是我们稍后想要重新配置某些属性(当然,在这种情况下它们不能是final的,但在连接时仍然是必需的)。
通过构造器进行依赖注入
如果缺少目标构造器所需的依赖项,Spring会抛出UnsatisfiedDependencyException
。
package com.logicbig.example;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@ComponentScan(useDefaultFilters = false,
includeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "ConstructorDiExample"))
public class ConstructorDiExample {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(ConstructorDiExample.class);
ClientBean bean = context.getBean(ClientBean.class);
bean.doSomething();
}
@Component
private static class ClientBean {
private final ServiceBean serviceBean;
private ClientBean(ServiceBean serviceBean) {
this.serviceBean = serviceBean;
}
void doSomething() {
System.out.println("doing something with: " + serviceBean);
}
}
//uncomment @service to get rid of UnsatisfiedDependencyException
// private class ServiceBean {
// }
}
输出
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.logicbig.example.ConstructorDiExample$ServiceBean' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
在Setter注入中检查必需属性
如果我们是通过Setter进行必需依赖的注入,那么有以下几种依赖检查的选项:
使用Setter注入
对于Setter注入,我们不需要使用@Autowired
。但对于依赖检查,我们需要明确使用这个注解。@Autowired
注解的required
属性默认为true。因此,如果Setter没有提供依赖项,Spring会抛出UnsatisfiedDependencyException
。
package com.logicbig.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Configuration
@ComponentScan(useDefaultFilters = false, includeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "SetterAutowiredExample"))
public class SetterAutowiredExample {
public static void main(String... strings) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
SetterAutowiredExample.class);
ClientBean bean = context.getBean(ClientBean.class);
bean.doSomething();
}
@Component
public static class ClientBean { private ServiceBean serviceBean;
public void setServiceBean(ServiceBean serviceBean) {
this.serviceBean = serviceBean;
}
public void doSomething() {
System.out.println("doing something with : " + serviceBean);
}
}
//uncomment following to get rid of UnsatisfiedDependencyException
//@Service
public static class ServiceBean {
}
}
输出
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.logicbig.example.SetterAutowiredExample$ClientBean': Unsatisfied dependency expressed through method 'setServiceBean' parameter 0: No qualifying bean of type 'com.logicbig.example.SetterAutowiredExample$ServiceBean' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
在PostConstruct
方法中手动执行验证
在PostConstruct
方法中手动执行验证:
package com.logicbig.example;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import jakarta.annotation.PostConstruct;
public class PostConstructExample {
public ClientBean clientBean (ServiceBean serviceBean) {
ClientBean clientBean = new ClientBean();
//uncomment following to get rid of IllegalArgumentException
//clientBean.setServiceBean(serviceBean);
return clientBean;
}
public ServiceBean serviceBean () {
return new ServiceBean();
}
public static void main (String... strings) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(
PostConstructExample.class);
ClientBean bean = context.getBean(ClientBean.class);
bean.doSomething();
}
public static class ClientBean {
private ServiceBean serviceBean;
public void myPostConstructMethod () throws Exception {
// we can do more validation than just checking null values here
if (serviceBean == null) {
throw new IllegalArgumentException("ServiceBean not set");
}
}
public void setServiceBean (ServiceBean serviceBean) {
this.serviceBean = serviceBean;
}
public void doSomething () {
System.out.println("doing something with: " + serviceBean);
}
}
public static class ServiceBean {
}
}
输出
Caused by: java.lang.IllegalArgumentException: ServiceBean not set
使用InitializingBean接口
InitializingBean定义
(版本:spring-framework 6.1.2)
package org.springframework.beans.factory;
........
public interface InitializingBean {
void afterPropertiesSet() throws Exception; 1
}
| 1| Invoked by the Spring container after it has set all bean properties.
This method allows the bean instance to perform validation of its overall
configuration and final initialization when all bean properties have been set.
示例
package com.logicbig.example;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class InitializingBeanExample {
public ClientBean clientBean() {
return new ClientBean();
}
//remove following comment to fix java.lang.IllegalArgumentException: ServiceBean not set
// public ServiceBean serviceBean() {
return new ServiceBean();
}
public static void main(String... strings) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(
InitializingBeanExample.class);
ClientBean bean = context.getBean(ClientBean.class);
bean.doSomething();
}
private static class ClientBean implements InitializingBean {
private ServiceBean serviceBean;
public void setServiceBean(ServiceBean serviceBean) {
this.serviceBean = serviceBean;
}
public void doSomething() {
System.out.println("doing something with: " + serviceBean);
}
public void afterPropertiesSet() throws Exception {
if (serviceBean == null) {
throw new IllegalArgumentException("ServiceBean not set");
}
}
}
private static class ServiceBean {
}
}
输出
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'clientBean' defined in com.logicbig.example.InitializingBeanExample: ServiceBean not set
Caused by: java.lang.IllegalArgumentException: ServiceBean not set
示例项目
使用的依赖和技术:
- spring-context 6.1.2 (Spring Context)
版本兼容性:4.3.0.RELEASE - 6.1.2版本列表
×
spring-context的版本兼容性与此示例:- 4.3.0.RELEASE
- 4.3.1.RELEASE
- …(省略中间版本)
- 6.1.2
绿色版本已测试。
- jakarta.jakartaee-api 10.0.0 (Eclipse Foundation)
- JDK 17
- Maven 3.8.1
兼容的Java版本:17+
此博客文章详细介绍了如何在Spring框架中进行依赖检查,包括构造器注入、Setter注入以及通过PostConstruct
方法和InitializingBean
接口进行手动验证。通过这些机制,可以确保在应用启动时或编译时就发现并处理依赖问题,从而避免运行时错误。