Skip to content

Commit

Permalink
Feature/replace componentscan (#386)
Browse files Browse the repository at this point in the history
* # 291 replace componentscan based context configuration with explicit configuration file and @beans declarations

* #291 replace explicit constructors with lombok annotation.

* #291 @conditional on springwolf.enabled for all autoconfigurations.

* #291 spotless format

* chore(kafka): move SpringwolfKafkaTemplateFactoryTest to producer package

* refactor(core): remove unused `@Component` and `@Service` annotation

As suggested in the PR

Co-authored-by: sam0r040 <[email protected]>

* #291 moved @order annotataions from bean classes to spring configurations

---------

Co-authored-by: Timon Back <[email protected]>
Co-authored-by: sam0r040 <[email protected]>
  • Loading branch information
3 people authored Oct 13, 2023
1 parent 65b49bd commit c23bcf0
Show file tree
Hide file tree
Showing 78 changed files with 723 additions and 365 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CommonModelConvertersConfiguration {
/**
* Spring AutoConfiguration adding an {@link MonetaryAmountConverter} Bean to the spring context.
*/
@Configuration(proxyBeanMethods = false)
public class CommonModelConvertersAutoConfiguration {

@Bean
public MonetaryAmountConverter monetaryAmountConverter() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.github.stavshamir.springwolf.common_converters.configuration.CommonModelConvertersAutoConfiguration
Original file line number Diff line number Diff line change
@@ -1,29 +1,81 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf;

import io.github.stavshamir.springwolf.asyncapi.AsyncApiCustomizer;
import io.github.stavshamir.springwolf.asyncapi.AsyncApiService;
import io.github.stavshamir.springwolf.asyncapi.ChannelsService;
import io.github.stavshamir.springwolf.asyncapi.DefaultAsyncApiService;
import io.github.stavshamir.springwolf.asyncapi.DefaultChannelsService;
import io.github.stavshamir.springwolf.asyncapi.SpringwolfInitApplicationListener;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.ChannelsScanner;
import io.github.stavshamir.springwolf.configuration.AsyncApiDocket;
import io.github.stavshamir.springwolf.configuration.AsyncApiDocketService;
import io.github.stavshamir.springwolf.configuration.DefaultAsyncApiDocketService;
import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigConstants;
import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigProperties;
import io.github.stavshamir.springwolf.schemas.DefaultSchemasService;
import io.github.stavshamir.springwolf.schemas.SchemasService;
import io.github.stavshamir.springwolf.schemas.example.ExampleGenerator;
import io.github.stavshamir.springwolf.schemas.example.ExampleJsonGenerator;
import io.swagger.v3.core.converter.ModelConverter;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;

import java.util.List;
import java.util.Optional;

/**
* Spring Boot auto-configuration which loads all spring-beans for springwolf core and eventually loaded plugins.
* <p>
* This configuration uses the spring @{@link ComponentScan} mechanism to detect and load the necessary beans. Both
* the core components as well as all plugin components are located underneath the base package 'io.github.stavshamir.springwolf'
* so existing springwolf plugins are automatically loaded together with the springwolf core beans.
* Spring Boot auto-configuration which loads all spring-beans for springwolf core module.
* <p>
* To disable this auto-configuration, set the environment property {@code springwolf.enabled=false}.
* To disable springwolf support, set the environment property {@code springwolf.enabled=false}.
*/
@AutoConfiguration
@ComponentScan(
basePackages = {"io.github.stavshamir.springwolf"},
excludeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
})
@Import({SpringwolfWebConfiguration.class, SpringwolfScannerConfiguration.class})
@ConditionalOnProperty(name = SpringwolfConfigConstants.SPRINGWOLF_ENABLED, matchIfMissing = true)
public class SpringwolfAutoConfiguration {}
public class SpringwolfAutoConfiguration {

@Bean
public SpringwolfConfigProperties springwolfConfigProperties() {
return new SpringwolfConfigProperties();
}

@Bean
public SpringwolfInitApplicationListener springwolfInitApplicationListener(
AsyncApiService asyncApiService, SpringwolfConfigProperties configProperties) {
return new SpringwolfInitApplicationListener(asyncApiService, configProperties);
}

@Bean
public AsyncApiService asyncApiService(
AsyncApiDocketService asyncApiDocketService,
ChannelsService channelsService,
SchemasService schemasService,
List<AsyncApiCustomizer> customizers) {
return new DefaultAsyncApiService(asyncApiDocketService, channelsService, schemasService, customizers);
}

@Bean
public ChannelsService channelsService(List<? extends ChannelsScanner> channelsScanners) {
return new DefaultChannelsService(channelsScanners);
}

@Bean
public SchemasService schemasService(List<ModelConverter> modelConverters, ExampleGenerator exampleGenerator) {
return new DefaultSchemasService(modelConverters, exampleGenerator);
}

@Bean
public AsyncApiDocketService asyncApiDocketService(
Optional<AsyncApiDocket> optionalAsyncApiDocket, SpringwolfConfigProperties springwolfConfigProperties) {
return new DefaultAsyncApiDocketService(optionalAsyncApiDocket, springwolfConfigProperties);
}

@Bean
@ConditionalOnMissingBean
public ExampleGenerator exampleGenerator() {
return new ExampleJsonGenerator();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf;

import io.github.stavshamir.springwolf.asyncapi.scanners.beans.BeanMethodsScanner;
import io.github.stavshamir.springwolf.asyncapi.scanners.beans.DefaultBeanMethodsScanner;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.ChannelPriority;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.ConsumerOperationDataScanner;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.ProducerOperationDataScanner;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.annotation.AsyncListenerAnnotationScanner;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.annotation.AsyncPublisherAnnotationScanner;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.annotation.MessageBindingProcessor;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.annotation.OperationBindingProcessor;
import io.github.stavshamir.springwolf.asyncapi.scanners.classes.ComponentClassScanner;
import io.github.stavshamir.springwolf.asyncapi.scanners.classes.ConfigurationClassScanner;
import io.github.stavshamir.springwolf.configuration.AsyncApiDocketService;
import io.github.stavshamir.springwolf.schemas.SchemasService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;

import java.util.List;

import static io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigConstants.SPRINGWOLF_SCANNER_ASYNC_LISTENER_ENABLED;
import static io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigConstants.SPRINGWOLF_SCANNER_ASYNC_PUBLISHER_ENABLED;
import static io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigConstants.SPRINGWOLF_SCANNER_CONSUMER_DATA_ENABLED;
import static io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigConstants.SPRINGWOLF_SCANNER_PRODUCER_DATA_ENABLED;

/**
* Spring configuration defining the core scanner beans.
*/
@Configuration(proxyBeanMethods = false)
public class SpringwolfScannerConfiguration {

@Bean
public ComponentClassScanner componentClassScanner(
AsyncApiDocketService asyncApiDocketService, Environment environment) {
return new ComponentClassScanner(asyncApiDocketService, environment);
}

@Bean
public ConfigurationClassScanner configurationClassScanner(
AsyncApiDocketService asyncApiDocketService, Environment environment) {
return new ConfigurationClassScanner(asyncApiDocketService, environment);
}

@Bean
public BeanMethodsScanner beanMethodsScanner(ConfigurationClassScanner configurationClassScanner) {
return new DefaultBeanMethodsScanner(configurationClassScanner);
}

@Bean
@ConditionalOnProperty(name = SPRINGWOLF_SCANNER_CONSUMER_DATA_ENABLED, matchIfMissing = true)
@Order(value = ChannelPriority.MANUAL_DEFINED)
public ConsumerOperationDataScanner consumerOperationDataScanner(
AsyncApiDocketService asyncApiDocketService, SchemasService schemasService) {
return new ConsumerOperationDataScanner(asyncApiDocketService, schemasService);
}

@Bean
@ConditionalOnProperty(name = SPRINGWOLF_SCANNER_PRODUCER_DATA_ENABLED, matchIfMissing = true)
@Order(value = ChannelPriority.MANUAL_DEFINED)
public ProducerOperationDataScanner producerOperationDataScanner(
AsyncApiDocketService asyncApiDocketService, SchemasService schemasService) {
return new ProducerOperationDataScanner(asyncApiDocketService, schemasService);
}

@Bean
@ConditionalOnProperty(name = SPRINGWOLF_SCANNER_ASYNC_LISTENER_ENABLED, matchIfMissing = true)
@Order(value = ChannelPriority.ASYNC_ANNOTATION)
public AsyncListenerAnnotationScanner asyncListenerAnnotationScanner(
ComponentClassScanner componentClassScanner,
SchemasService schemasService,
List<OperationBindingProcessor> operationBindingProcessors,
List<MessageBindingProcessor> messageBindingProcessors) {
return new AsyncListenerAnnotationScanner(
componentClassScanner, schemasService, operationBindingProcessors, messageBindingProcessors);
}

@Bean
@ConditionalOnProperty(name = SPRINGWOLF_SCANNER_ASYNC_PUBLISHER_ENABLED, matchIfMissing = true)
@Order(value = ChannelPriority.ASYNC_ANNOTATION)
public AsyncPublisherAnnotationScanner asyncPublisherAnnotationScanner(
ComponentClassScanner componentClassScanner,
SchemasService schemasService,
List<OperationBindingProcessor> operationBindingProcessors,
List<MessageBindingProcessor> messageBindingProcessors) {
return new AsyncPublisherAnnotationScanner(
componentClassScanner, schemasService, operationBindingProcessors, messageBindingProcessors);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf;

import io.github.stavshamir.springwolf.asyncapi.AsyncApiSerializerService;
import io.github.stavshamir.springwolf.asyncapi.AsyncApiService;
import io.github.stavshamir.springwolf.asyncapi.DefaultAsyncApiSerializerService;
import io.github.stavshamir.springwolf.asyncapi.controller.ActuatorAsyncApiController;
import io.github.stavshamir.springwolf.asyncapi.controller.AsyncApiController;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import static io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigConstants.SPRINGWOLF_ENDPOINT_ACTUATOR_ENABLED;

/**
* Spring-Configuration defining the web controller beans.
*/
@Configuration(proxyBeanMethods = false)
public class SpringwolfWebConfiguration {

@Bean
@ConditionalOnProperty(name = SPRINGWOLF_ENDPOINT_ACTUATOR_ENABLED, havingValue = "false", matchIfMissing = true)
public AsyncApiController asyncApiController(
AsyncApiService asyncApiService, AsyncApiSerializerService asyncApiSerializerService) {
return new AsyncApiController(asyncApiService, asyncApiSerializerService);
}

@Bean
@ConditionalOnProperty(name = SPRINGWOLF_ENDPOINT_ACTUATOR_ENABLED, havingValue = "true")
public ActuatorAsyncApiController actuatorAsyncApiController(
AsyncApiService asyncApiService, AsyncApiSerializerService asyncApiSerializerService) {
return new ActuatorAsyncApiController(asyncApiService, asyncApiSerializerService);
}

@Bean
public AsyncApiSerializerService asyncApiSerializerService() {
return new DefaultAsyncApiSerializerService();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
import io.swagger.v3.core.util.Json;
import io.swagger.v3.core.util.Yaml;
import jakarta.annotation.PostConstruct;
import org.springframework.stereotype.Service;

@Service
public class DefaultAsyncApiSerializerService implements AsyncApiSerializerService {

private ObjectMapper jsonMapper = Json.mapper();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@
import io.github.stavshamir.springwolf.schemas.SchemasService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

@Slf4j
@Service
@RequiredArgsConstructor
public class DefaultAsyncApiService implements AsyncApiService {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.ChannelsScanner;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -16,7 +15,6 @@
* Service to detect AsyncAPI channels in the current spring context.
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class DefaultChannelsService implements ChannelsService {

Expand All @@ -36,7 +34,7 @@ public Map<String, ChannelItem> findChannels() {
Map<String, ChannelItem> channels = scanner.scan();
foundChannelItems.addAll(channels.entrySet());
} catch (Exception e) {
log.error("An error was encountered during channel scanning with {}: {}", scanner, e.getMessage());
log.error("An error was encountered during channel scanning with {}: {}", scanner, e.getMessage(), e);
}
}
return ChannelMerger.merge(foundChannelItems);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Service;

/**
* Spring ApplicationListener listening on {@link ApplicationReadyEvent}. Triggers the AsyncAPI creation.
Expand All @@ -17,7 +16,6 @@
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class SpringwolfInitApplicationListener implements ApplicationListener<ApplicationReadyEvent>, InitializingBean {

private final AsyncApiService asyncApiService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,12 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;

import static io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigConstants.SPRINGWOLF_ENDPOINT_ACTUATOR_ENABLED;

@Component
@Slf4j
@RestControllerEndpoint(id = "springwolf")
@RequiredArgsConstructor
@ConditionalOnProperty(name = SPRINGWOLF_ENDPOINT_ACTUATOR_ENABLED, havingValue = "true")
public class ActuatorAsyncApiController {

private final AsyncApiService asyncApiService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,13 @@
import io.github.stavshamir.springwolf.asyncapi.types.AsyncAPI;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import static io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigConstants.SPRINGWOLF_ENDPOINT_ACTUATOR_ENABLED;

@Slf4j
@RestController
@RequiredArgsConstructor
@ConditionalOnProperty(name = SPRINGWOLF_ENDPOINT_ACTUATOR_ENABLED, havingValue = "false", matchIfMissing = true)
public class AsyncApiController {

private final AsyncApiService asyncApiService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import io.github.stavshamir.springwolf.asyncapi.scanners.classes.ConfigurationClassScanner;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.util.Arrays;
Expand All @@ -14,7 +13,6 @@

import static java.util.stream.Collectors.toSet;

@Service
@RequiredArgsConstructor
public class DefaultBeanMethodsScanner implements BeanMethodsScanner {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.AsyncHeaders;
import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.HeaderReference;
import io.github.stavshamir.springwolf.schemas.SchemasService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
Expand All @@ -35,15 +35,14 @@
import static java.util.stream.Collectors.toSet;

@Slf4j
@RequiredArgsConstructor
public abstract class AbstractClassLevelListenerScanner<
ClassAnnotation extends Annotation, MethodAnnotation extends Annotation>
implements ChannelsScanner {

@Autowired
private ComponentClassScanner componentClassScanner;
private final ComponentClassScanner componentClassScanner;

@Autowired
private SchemasService schemasService;
private final SchemasService schemasService;

private static final Comparator<Map.Entry<String, ChannelItem>> byPublishOperationName =
Comparator.comparing(it -> it.getValue().getPublish().getOperationId());
Expand Down
Loading

0 comments on commit c23bcf0

Please sign in to comment.