【问题标题】:Spring Boot datasource doesn't go to postProcessAfterInitialization method of BeanPostProcessor implementationSpring Boot 数据源不去 BeanPostProcessor 实现的 postProcessAfterInitialization 方法
【发布时间】:2021-09-13 10:28:00
【问题描述】:

我正在尝试为我的数据源创建一个代理来监督数据库查询,但是从未使用 bean 数据源对象调用以下内容,并且 bean instanceof DataSource 始终为 false:

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.Clock;
import java.util.List;

import javax.sql.DataSource;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.actuate.metrics.MetricsEndpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ReflectionUtils;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.talan.dbmonitor.config.RabbitMQProperties;
import com.talan.dbmonitor.listeners.QueryListener;
import com.talan.dbmonitor.service.MapperService;

import lombok.extern.slf4j.Slf4j;
import net.ttddyy.dsproxy.support.ProxyDataSource;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;

@Configuration
@ConditionalOnProperty(value="enable.dbmonitor", havingValue="true", matchIfMissing= false)
@Slf4j
public class DatasourceProxyBeanPostProcessor implements BeanPostProcessor {

    @Autowired
    private AmqpTemplate amqpTemplate;
    
    @Autowired
    private  Clock clock;

    @Autowired
    private RabbitMQProperties rabbitMQProperties;
    
    @Autowired
    private  MetricsEndpoint metricsendpoint;
    @Autowired
    private  ObjectMapper objectMapper;
    @Autowired
    private  MapperService mapperService;
    
    @Value("#{T(java.util.Arrays).asList('${dbmonitor.tables.ignore.list:}')}")
    List<String> tablesIgnoreList;
    

    @Override
    public Object postProcessBeforeInitialization(final Object bean, final String beanName)  {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(final Object bean, final String beanName) {
        
        System.out.println(beanName+" : "+bean.getClass().toString());
        System.out.println((bean instanceof DataSource));
        if (bean instanceof DataSource && !(bean instanceof ProxyDataSource)) {
            ProxyFactory factory = new ProxyFactory(bean);
            factory.setProxyTargetClass(true);
            factory.addAdvice(new ProxyDataSourceInterceptor((DataSource) bean, amqpTemplate, 
            rabbitMQProperties, metricsendpoint, objectMapper, mapperService, tablesIgnoreList ,clock
                    ));
            return factory.getProxy() == null ? factory.getProxy() : bean;
        }
        return bean;
    }
private static class ProxyDataSourceInterceptor implements MethodInterceptor {
        private final DataSource dataSource;

        public ProxyDataSourceInterceptor(final DataSource dataSource, AmqpTemplate amqpTemplate,
                RabbitMQProperties rabbitMQProperties,MetricsEndpoint metricsendpoint,ObjectMapper objectMapper,MapperService mapperService,
                List<String> tablesIgnoreList ,Clock clock
                ) {
            
            super();
            QueryListener qListener = new QueryListener(amqpTemplate, rabbitMQProperties,metricsendpoint,objectMapper, mapperService, tablesIgnoreList , clock
                    );
            String datasourceUrl = null;
            
            try (Connection datasourceConnection = dataSource.getConnection()){
                datasourceUrl = datasourceConnection.getMetaData().getURL();
        
            } catch (SQLException e) {
                log.warn(e.getMessage());
            }
            finally {
            this.dataSource = ProxyDataSourceBuilder.create(dataSource).name(datasourceUrl).countQuery()
                    .listener(qListener).build();
            }
        }

        @Override
        public Object invoke(final MethodInvocation invocation) throws Throwable {
            Method proxyMethod = ReflectionUtils.findMethod(dataSource.getClass(), invocation.getMethod().getName());
            if (proxyMethod != null) {
                return proxyMethod.invoke(dataSource, invocation.getArguments());
            }
            return invocation.proceed();
        }

    }
}

数据源设置:

spring.datasource.platform = postgres
spring.datasource.url= jdbc:postgresql://${DB_SERVER_HOST:localhost}:${DB_SERVER_PORT:5432}/${DB_NAME:polaris_db}
spring.datasource.username= ${DB_USER:postgres}
spring.datasource.password= ${DB_PASSWORD:yosra}
spring.datasource.driverClassName= org.postgresql.Driver
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.PostgreSQLDialect

Spring boot 版本:2.3.7.RELEASE

问题是 postProcessAfterInitialization 中的这条指令永远不会被验证,所以我的代理永远不会被调用 bean instanceof DataSource &amp;&amp; !(bean instanceof ProxyDataSource)

【问题讨论】:

    标签: java spring-boot hibernate proxy spring-data-jpa


    【解决方案1】:

    有很多ready-to-use solutions 可以检查数据库查询。你不必自己写。

    我不知道您的域模型中的ProxyDatasource 是什么。但我认为您误解了BeanPostProcessor 的概念。 postProcessAfterInitialization 可能会收到一个已经被另一个 BeanPostProccessor 代理的 bean。通常你应该记住postProcessBeforeInitialization 阶段的bean 名称。然后你可以将它们包装在postProcessAfterInitialization中。

    【讨论】:

      猜你喜欢
      • 2019-02-12
      • 2019-05-30
      • 1970-01-01
      • 1970-01-01
      • 2019-10-13
      • 1970-01-01
      • 2016-08-09
      • 2014-07-17
      相关资源
      最近更新 更多