spring 从非 bean 访问代理 bean class
spring access proxied bean from a non bean class
我有一个 spring bean,它在其方法上使用 dropwizard-metrics 注释来测量某些指标(参见 #parse 方法):
@Component
public class Consumer extends AbstractConsumer {
@Autowired
private EventsService eventsService;
private ExecutorService executor;
@PostConstruct
void init() throws Exception {
executor = Executors.newFixedThreadPool(1);
ConsumerEventsHandler consumer = new ConsumerEventsHandler(Arrays.asList("topic1"), this);
executor.submit(consumer);
}
@Timed(name = CONSUMER_BATCH_PARSING_TIME, absolute = true)
public void parse(ConsumerRecord<String, String> record) {
String val = record.value();
eventsService.parseToDB(val);
}
}
如您所见,此 bean 向执行程序服务提交了一个新任务,该任务不是一个 bean,但它需要引用 Consumer bean 以重用其逻辑(请参阅consumer.parse(记录);调用:
public class ConsumerEventsHandler implements Runnable {
private Consumer consumer;
private List<String> topics;
protected final KafkaConsumer<String, String> kafkaConsumer;
public ConsumerEventsHandler(List<String> topics, Consumer consumer) {
this.topics = topics;
this.consumer = consumer;
this.kafkaConsumer = new KafkaConsumer<>();
}
@Override
public void run() {
try {
consumer.subscribe(topics);
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Long.MAX_VALUE);
for (ConsumerRecord<String, String> record : records) {
consumer.parse(record);
}
}
} finally {
consumer.close();
}
}
}
这是指标配置(我使用 'ryantenney/metrics-spring' 库):
@Configuration
@EnableMetrics(proxyTargetClass = true)
@ComponentScan(basePackages = "com.inq")
public class SsvpApiMetricsConfig extends MetricsConfigurerAdapter {
@Resource
private MetricRegistry registry;
@PostConstruct()
public void init() {
configureReporters(registry);
}
@Override
public void configureReporters(MetricRegistry metricRegistry) {
registerReporter(JmxReporter.forRegistry(metricRegistry).
inDomain("com.inq.metrics").build()).start();
}
}
结果,我看到 Consumer bean 被代理了,但是 ConsumerEventsHandler 包含对普通 bean 的引用,因为它在创建时在 Consumer 的 @PostConstruct 方法中,通过 'new ConsumerEventsHandler(..)' Consumer bean 尚未被代理,我假设在代理事物之前调用了 @PostConstruct。
我看到的唯一解决方法是每次在#运行 方法中通过 ApplicationContext#getBean 获取 Consumer bean ref,而不是将 ref 存储在 ConsumerEventsHandler class 变量。
还有其他解决方案吗?
只需在非 spring class
上创建静态
public class ConsumerEventsHandler implements Runnable {
private static Consumer consumer;
private List<String> topics;
protected final KafkaConsumer<String, String> kafkaConsumer;
public ConsumerEventsHandler(List<String> topics) {
this.topics = topics;
this.kafkaConsumer = new KafkaConsumer<>();
}
public static setConsumer(Consumer consumer) {
ConsumerEventsHandler.consumer = consumer;
}
那么在你的spring这边你可以有:
@PostConstruct()
public void init() {
configureReporters(registry);
ConsumerEventsHandler.setConsumer(consumer);
}
我有一个 spring bean,它在其方法上使用 dropwizard-metrics 注释来测量某些指标(参见 #parse 方法):
@Component
public class Consumer extends AbstractConsumer {
@Autowired
private EventsService eventsService;
private ExecutorService executor;
@PostConstruct
void init() throws Exception {
executor = Executors.newFixedThreadPool(1);
ConsumerEventsHandler consumer = new ConsumerEventsHandler(Arrays.asList("topic1"), this);
executor.submit(consumer);
}
@Timed(name = CONSUMER_BATCH_PARSING_TIME, absolute = true)
public void parse(ConsumerRecord<String, String> record) {
String val = record.value();
eventsService.parseToDB(val);
}
}
如您所见,此 bean 向执行程序服务提交了一个新任务,该任务不是一个 bean,但它需要引用 Consumer bean 以重用其逻辑(请参阅consumer.parse(记录);调用:
public class ConsumerEventsHandler implements Runnable {
private Consumer consumer;
private List<String> topics;
protected final KafkaConsumer<String, String> kafkaConsumer;
public ConsumerEventsHandler(List<String> topics, Consumer consumer) {
this.topics = topics;
this.consumer = consumer;
this.kafkaConsumer = new KafkaConsumer<>();
}
@Override
public void run() {
try {
consumer.subscribe(topics);
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Long.MAX_VALUE);
for (ConsumerRecord<String, String> record : records) {
consumer.parse(record);
}
}
} finally {
consumer.close();
}
}
}
这是指标配置(我使用 'ryantenney/metrics-spring' 库):
@Configuration
@EnableMetrics(proxyTargetClass = true)
@ComponentScan(basePackages = "com.inq")
public class SsvpApiMetricsConfig extends MetricsConfigurerAdapter {
@Resource
private MetricRegistry registry;
@PostConstruct()
public void init() {
configureReporters(registry);
}
@Override
public void configureReporters(MetricRegistry metricRegistry) {
registerReporter(JmxReporter.forRegistry(metricRegistry).
inDomain("com.inq.metrics").build()).start();
}
}
结果,我看到 Consumer bean 被代理了,但是 ConsumerEventsHandler 包含对普通 bean 的引用,因为它在创建时在 Consumer 的 @PostConstruct 方法中,通过 'new ConsumerEventsHandler(..)' Consumer bean 尚未被代理,我假设在代理事物之前调用了 @PostConstruct。
我看到的唯一解决方法是每次在#运行 方法中通过 ApplicationContext#getBean 获取 Consumer bean ref,而不是将 ref 存储在 ConsumerEventsHandler class 变量。 还有其他解决方案吗?
只需在非 spring class
上创建静态public class ConsumerEventsHandler implements Runnable {
private static Consumer consumer;
private List<String> topics;
protected final KafkaConsumer<String, String> kafkaConsumer;
public ConsumerEventsHandler(List<String> topics) {
this.topics = topics;
this.kafkaConsumer = new KafkaConsumer<>();
}
public static setConsumer(Consumer consumer) {
ConsumerEventsHandler.consumer = consumer;
}
那么在你的spring这边你可以有:
@PostConstruct()
public void init() {
configureReporters(registry);
ConsumerEventsHandler.setConsumer(consumer);
}