Begin by creating a new Maven project. The pom.xml should define the Spring Boot parent and include the web sttarter dependency.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>custom-logging-starter</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
Next, implement a servlet filter that logs incoming HTTP requests. This serves as the functional component of the starter.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class RequestLoggingFilter implements Filter {
private static final Logger LOGGER = LoggerFactory.getLogger(RequestLoggingFilter.class);
@Override
public void init(FilterConfig filterConfig) {
LOGGER.info("Initializing RequestLoggingFilter.");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
LOGGER.info("Processing request for URI: {}", httpRequest.getRequestURI());
chain.doFilter(request, response);
}
@Override
public void destroy() {
LOGGER.info("Destroying RequestLoggingFilter.");
}
}
Register this filter as a Spring bean using a FilterRegistrationBean. This bean configures the URL patterns and order.
import org.springframework.boot.web.servlet.FilterRegistrationBean;
public class RequestLoggingFilterRegistration extends FilterRegistrationBean<RequestLoggingFilter> {
public RequestLoggingFilterRegistration() {
super();
this.setFilter(new RequestLoggingFilter());
this.addUrlPatterns("/*");
this.setName("requestLoggingFilter");
this.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
}
Create a auto-configuration class that conditionally registers the filter bean. This class uses @ConditionalOnClass to ensure it only loads when the required classes are present.
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnClass({RequestLoggingFilter.class, RequestLoggingFilterRegistration.class})
public class RequestLoggingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RequestLoggingFilterRegistration requestLoggingFilterRegistration() {
return new RequestLoggingFilterRegistration();
}
}
Define a custom annotation to enable this starter's functionality. This annotation will trigger the import of the auto-configuration.
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(RequestLoggingSelector.class)
public @interface EnableRequestLogging {
}
Implement a DeferredImportSelector to load the auto-configuration class from the spring.factories file.
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
public class RequestLoggingSelector implements DeferredImportSelector, BeanClassLoaderAware {
private ClassLoader classLoader;
private final Class<?> annotationType = EnableRequestLogging.class;
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
List<String> configurations = new ArrayList<>(
new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(this.annotationType, this.classLoader))
);
return configurations.toArray(new String[0]);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
}
Create a spring.factories file in the src/main/resources/META-INF/ directory. This file maps the enabling annotation to the auto-configuration class.
com.example.annotation.EnableRequestLogging=com.example.config.RequestLoggingAutoConfiguration
To use the starter in another project, add it as a dependency.
<dependency>
<groupId>com.example</groupId>
<artifactId>custom-logging-starter</artifactId>
<version>1.0.0</version>
</dependency>
Final, annotate the main application class with @EnableRequestLogging to activate the auto-configured filter.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableRequestLogging
public class SampleApplication {
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
}