如何通过依赖方注解控制Bean创建并传递注解参数?
Absolutely, this is totally achievable using Spring's core extension points! Let's walk through how to build this as a clean, reusable library that lets your users write code exactly like your example—no extra factory setup or bean definitions required.
1. Define Your Annotation & Model Classes
First, formalize the @KafkaSettings annotation and Target model to capture the metadata your MultiPublisher needs:
import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface KafkaSettings { // Shortcut for the topic name String value(); String site() default ""; } public class Target { public String topic; public String site; public String bootstrapServers; // For config-derived values public Target(String topic, String site) { this.topic = topic; this.site = site; } }
2. Build a Custom Dependency Resolver
The key here is extending Spring's AutowiredAnnotationBeanPostProcessor—the component that handles @Autowired injections. We'll override its resolveDependency method to:
- Detect when a
MultiPublisheris being injected - Extract the
@KafkaSettingsannotations from the parameter - Convert those annotations to
Targetobjects (plus pull in config values) - Create a fresh
MultiPublisherinstance (non-singleton) with these targets
Here's the implementation:
import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; import org.springframework.core.type.TypeDescriptor; import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.config.DependencyDescriptor; import java.util.Arrays; import java.util.Set; public class KafkaPublisherAutowiredProcessor extends AutowiredAnnotationBeanPostProcessor implements EnvironmentAware, BeanFactoryAware { private Environment environment; private BeanFactory beanFactory; @Override public Object resolveDependency(DependencyDescriptor descriptor, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException { // Check if we're dealing with a MultiPublisher injection request if (MultiPublisher.class.isAssignableFrom(descriptor.getDependencyType())) { // Pull all @KafkaSettings annotations from the parameter KafkaSettings[] annotations = descriptor.getAnnotations().getAnnotationsByType(KafkaSettings.class); if (annotations.length == 0) { throw new IllegalArgumentException("MultiPublisher injection requires at least one @KafkaSettings annotation"); } // Convert annotations to Target objects, and fetch bootstrap config for each site Target[] targets = Arrays.stream(annotations) .map(anno -> { Target target = new Target(anno.value(), anno.site()); // Fetch bootstrap servers from application config String bootstrapKey = String.format("kafka.regions.%s.bootstrap", target.site); target.bootstrapServers = environment.getProperty(bootstrapKey); return target; }) .toArray(Target[]::new); // Create a fresh MultiPublisher instance (non-singleton) // If MultiPublisher has its own Spring-managed dependencies, use the BeanFactory to create it MultiPublisher publisher = beanFactory.getBean(MultiPublisher.class); publisher.setTargets(targets); // Or pass targets via constructor if preferred return publisher; } // Delegate to the parent class for all other dependency resolutions return super.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter); } @Override public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } }
3. Auto-Register the Processor & Configure MultiPublisher Scope
As a reusable library, you want this setup to happen automatically for users. Use Spring's auto-configuration (for Spring Boot) or a @Configuration class to register components:
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; @Configuration public class KafkaPublisherAutoConfiguration { // Register our custom processor to handle @Autowired injections for MultiPublisher @Bean public static KafkaPublisherAutowiredProcessor kafkaPublisherAutowiredProcessor() { return new KafkaPublisherAutowiredProcessor(); } // Define MultiPublisher as a prototype bean so we get a fresh instance every time @Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public MultiPublisher multiPublisher() { return new MultiPublisher(); } }
For Spring Boot users, add a file named META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports to your library's resources, containing:
com.yourpackage.KafkaPublisherAutoConfiguration
This ensures the auto-config is picked up automatically when users add your library dependency.
4. Verify User-Facing Usage
Your users can now use the code exactly as you outlined—no extra work required:
import org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Autowired; @Component public class MyClass { private final MultiPublisher multiPublisher; @Autowired public MyClass( @KafkaSettings(value = "topic1", site = "aws") @KafkaSettings(value = "topic7", site = "gcp") MultiPublisher multiPublisher ) { this.multiPublisher = multiPublisher; } }
Key Details to Note
- Non-Singleton Guarantee: By marking
MultiPublisheras prototype and creating a fresh instance on each injection, we avoid singleton conflicts entirely. - No User Boilerplate: Users only need to add your library and write the injection code—no factories or extra bean definitions.
- Config Integration: We use Spring's
Environmentto pull in bootstrap server configs, so it works seamlessly withapplication.properties/application.yaml. - Annotation Data Passing: Unlike
@Qualifier(which only selects existing beans), this approach directly passes annotation metadata to theMultiPublisherconstructor/setter.
内容的提问来源于stack exchange,提问作者Jammer

