You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过依赖方注解控制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 MultiPublisher is being injected
  • Extract the @KafkaSettings annotations from the parameter
  • Convert those annotations to Target objects (plus pull in config values)
  • Create a fresh MultiPublisher instance (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 MultiPublisher as 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 Environment to pull in bootstrap server configs, so it works seamlessly with application.properties/application.yaml.
  • Annotation Data Passing: Unlike @Qualifier (which only selects existing beans), this approach directly passes annotation metadata to the MultiPublisher constructor/setter.

内容的提问来源于stack exchange,提问作者Jammer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:40:43