Backend Development 8 min read

Why Your Custom Spring Advisor Fails and How to Fix It in Spring Boot 3.2

This article explores why a custom Advisor aspect may not work in Spring Boot 3.2, demonstrates how enabling @EnableAspectJAutoProxy and adjusting bean roles resolves the issue, and also covers advanced topics such as FactoryBean type conversion, version retrieval, SpringProperties, and the Spring SPI mechanism.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Why Your Custom Spring Advisor Fails and How to Fix It in Spring Boot 3.2

Environment: Spring Boot 3.2.5

Spring typically defines aspects using the @Aspect annotation, but you can also create low‑level Advisor beans. Even when using @Aspect, Spring ultimately converts it to an Advisor. Defining an Advisor bean alone may not take effect when @EnableTransactionManagement is active because its proxy processor is limited by the @Role annotation.

<code>// Define an Advisor
public class LogAdvisor implements PointcutAdvisor {
    // TODO: output log before and after business method execution
    // matches any class and method
}

// Enable transaction management
@Configuration
@EnableTransactionManagement
public class TxConfig {
    @Bean
    public LogAdvisor logAdvisor() {
        return new LogAdvisor();
    }
}

// Business method
@Transactional
public void save() {
    System.out.printf("save%n");
}</code>

Running the application shows that the custom LogAdvisor does not intercept the method.

Adding @Role(BeanDefinition.ROLE_INFRASTRUCTURE) to the Advisor bean and then enabling @EnableAspectJAutoProxy resolves the problem because the AspectJ proxy processor replaces the transaction manager’s proxy processor and has no @Role restriction.

<code>@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public LogAdvisor logAdvisor() {}
</code>

After adding @EnableAspectJAutoProxy and removing the @Role annotation, the LogAdvisor works as expected.

<code>@Bean
public LogAdvisor logAdvisor() {}

@EnableAspectJAutoProxy
public class ProxyConfig {}

@EnableTransactionManagement
public class TxConfig {}
</code>

Result: the custom LogAdvisor is now effective.

Spring Boot also provides convenient type‑conversion support via ConversionServiceFactoryBean, allowing you to register custom converters.

<code>@Bean
public ConversionServiceFactoryBean csfb() {
    ConversionServiceFactoryBean bean = new ConversionServiceFactoryBean() {
        @Override
        protected GenericConversionService createConversionService() {
            return new FormattingConversionService();
        }
    };
    Set<?> converters = new HashSet<>();
    converters.add(...);
    bean.setConverters(converters);
    return bean;
}
</code>

You can retrieve the current Spring and Spring Boot versions programmatically:

<code>public void getVersion() {
    String springVersion = SpringVersion.getVersion();
    String springBootVersion = SpringBootVersion.getVersion();
}
</code>

SpringVersion reads the Implementation‑Version attribute from META‑INF/MANIFEST.MF, while SpringBootVersion#getVersion() returns the version string directly.

Spring also exposes a global configuration file via SpringProperties , which reads a spring.properties file from the classpath root. Common properties include spring.beaninfo.ignore , spring.getenv.ignore , and newer ones such as spring.context.checkpoint and spring.context.exit .

<code># Enable faster startup
spring.beaninfo.ignore
# Ignore system environment variables
spring.getenv.ignore
# Default compiler mode for SpEL expressions
spring.expression.compiler.mode
# ... other properties ...
</code>

The Spring SPI mechanism uses SpringFactoriesLoader to load factory implementations from META-INF/spring.factories files on the classpath. The file maps an interface or abstract class name to a comma‑separated list of implementation class names.

<code>com.pack.CommonService=com.pack.MyCommonService1,com.pack.MyCommonService2</code>

Implementation classes must have an instantiable constructor (single, public, or default). If the constructor requires arguments, an ArgumentResolver should be provided; a FailureHandler can customize error handling. You can also load factories from a custom location:

<code>List<CommonService> services = SpringFactoriesLoader
    .forResourceLocation("com/pack/spi/pack.factories")
    .load(CommonService.class);
System.out.println(services);
</code>
aopSpring BootFactoryBeanAdvisorSpring SPIVersion Retrieval
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.