Home / Class/ Processor Class — spring-boot Architecture

Processor Class — spring-boot Architecture

Architecture documentation for the Processor class in BindableRuntimeHintsRegistrar.java from the spring-boot codebase.

Entity Profile

Relationship Graph

Source Code

core/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/BindableRuntimeHintsRegistrar.java lines 149–334

	private static final class Processor {

		private final Class<?> type;

		private final @Nullable Constructor<?> bindConstructor;

		private final BeanProperties bean;

		private final Set<Class<?>> seen;

		Processor(Bindable<?> bindable) {
			this(bindable, false, new HashSet<>());
		}

		private Processor(Bindable<?> bindable, boolean nestedType, Set<Class<?>> seen) {
			this.type = getRawClass(bindable);
			this.bindConstructor = (bindable.getBindMethod() != BindMethod.JAVA_BEAN)
					? BindConstructorProvider.DEFAULT.getBindConstructor(getBindableType(bindable), nestedType) : null;
			this.bean = JavaBeanBinder.BeanProperties.of(bindable);
			this.seen = seen;
		}

		private static Class<?> getBindableType(Bindable<?> bindable) {
			Class<?> resolved = bindable.getType().resolve();
			Assert.state(resolved != null, "'resolved' must not be null");
			return resolved;
		}

		private static Class<?> getRawClass(Bindable<?> bindable) {
			Class<?> rawClass = bindable.getType().getRawClass();
			Assert.state(rawClass != null, "'rawClass' must not be null");
			return rawClass;
		}

		void process(ReflectionHints hints) {
			if (this.seen.contains(this.type)) {
				return;
			}
			this.seen.add(this.type);
			handleConstructor(hints);
			if (this.bindConstructor != null) {
				handleValueObjectProperties(hints);
			}
			else if (this.bean != null && !this.bean.getProperties().isEmpty()) {
				handleJavaBeanProperties(hints);
			}
		}

		private void handleConstructor(ReflectionHints hints) {
			if (this.bindConstructor != null) {
				if (KotlinDetector.isKotlinType(this.bindConstructor.getDeclaringClass())) {
					KotlinDelegate.handleConstructor(hints, this.bindConstructor);
				}
				else {
					hints.registerConstructor(this.bindConstructor, ExecutableMode.INVOKE);
				}
				return;
			}
			Arrays.stream(this.type.getDeclaredConstructors())
				.filter(this::hasNoParameters)
				.findFirst()
				.ifPresent((constructor) -> hints.registerConstructor(constructor, ExecutableMode.INVOKE));
		}

		private boolean hasNoParameters(Constructor<?> candidate) {
			return candidate.getParameterCount() == 0;
		}

		private void handleValueObjectProperties(ReflectionHints hints) {
			Assert.state(this.bindConstructor != null, "'bindConstructor' must not be null");
			for (int i = 0; i < this.bindConstructor.getParameterCount(); i++) {
				String propertyName = this.bindConstructor.getParameters()[i].getName();
				ResolvableType propertyType = ResolvableType.forConstructorParameter(this.bindConstructor, i);
				handleProperty(hints, propertyName, propertyType);
			}
		}

		private void handleJavaBeanProperties(ReflectionHints hints) {
			Map<String, BeanProperty> properties = this.bean.getProperties();
			properties.forEach((name, property) -> {
				Method getter = property.getGetter();
				if (getter != null) {
					hints.registerMethod(getter, ExecutableMode.INVOKE);
				}
				Method setter = property.getSetter();
				if (setter != null) {
					hints.registerMethod(setter, ExecutableMode.INVOKE);
				}
				Field field = property.getField();
				if (field != null) {
					hints.registerField(field);
				}
				handleProperty(hints, name, property.getType());
			});
		}

		private void handleProperty(ReflectionHints hints, String propertyName, ResolvableType propertyType) {
			Class<?> propertyClass = propertyType.resolve();
			if (propertyClass == null) {
				return;
			}
			if (propertyClass.equals(this.type)) {
				return; // Prevent infinite recursion
			}
			Class<?> componentType = getComponentClass(propertyType);
			if (componentType != null) {
				// Can be a list of simple types
				if (!isJavaType(componentType)) {
					processNested(componentType, hints);
				}
			}
			else if (isNestedType(propertyName, propertyClass)) {
				processNested(propertyClass, hints);
			}
		}

		private void processNested(Class<?> type, ReflectionHints hints) {
			new Processor(Bindable.of(type), true, this.seen).process(hints);
		}

		private @Nullable Class<?> getComponentClass(ResolvableType type) {
			ResolvableType componentType = getComponentType(type);
			if (componentType == null) {
				return null;
			}
			if (isContainer(componentType)) {
				// Resolve nested generics like Map<String, List<SomeType>>
				return getComponentClass(componentType);
			}
			return componentType.toClass();
		}

		private @Nullable ResolvableType getComponentType(ResolvableType type) {
			if (type.isArray()) {
				return type.getComponentType();
			}
			if (isCollection(type)) {
				return type.asCollection().getGeneric();
			}
			if (isMap(type)) {
				return type.asMap().getGeneric(1);
			}
			return null;
		}

		private boolean isContainer(ResolvableType type) {
			return type.isArray() || isCollection(type) || isMap(type);
		}

		private boolean isCollection(ResolvableType type) {
			return Collection.class.isAssignableFrom(type.toClass());
		}

		private boolean isMap(ResolvableType type) {
			return Map.class.isAssignableFrom(type.toClass());
		}

		/**
		 * Specify whether the specified property refer to a nested type. A nested type
		 * represents a sub-namespace that need to be fully resolved. Nested types are
		 * either inner classes or annotated with {@link NestedConfigurationProperty}.
		 * @param propertyName the name of the property
		 * @param propertyType the type of the property
		 * @return whether the specified {@code propertyType} is a nested type
		 */
		private boolean isNestedType(String propertyName, Class<?> propertyType) {
			Class<?> declaringClass = propertyType.getDeclaringClass();
			if (declaringClass != null && isNested(declaringClass, this.type)) {
				return true;
			}
			Field field = ReflectionUtils.findField(this.type, propertyName);
			return (field != null) && MergedAnnotations.from(field).isPresent(Nested.class);
		}

		private static boolean isNested(Class<?> type, Class<?> candidate) {
			if (type.isAssignableFrom(candidate)) {
				return true;
			}
			return (candidate.getDeclaringClass() != null && isNested(type, candidate.getDeclaringClass()));
		}

		private boolean isJavaType(Class<?> candidate) {
			return candidate.getPackageName().startsWith("java.");
		}

	}

Domain

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free