0

我按照此答案中的说明配置了 CommonsPool2TargetSource 。然后我添加了借用验证(因为在我的真实场景中,每个池化对象都代表一个可能不再存在的进程)。

它可以工作,但是在池对象上的每个方法调用都会调用一次验证。每个对我们服务的请求可能会多次(通常只有两次)。(如果我记录池对象,再加上一次toString。)但是,每个请求一次验证就足够了。

我怎样才能更好地控制它?例如,要验证

  • 仅在ApplicationContext.getBean(在链接答案中使用)或Provider.get(我在下面使用的更多 DI 方式)上,
  • 或仅当池对象的方法抛出“断管”IOException;然后用一个新的替换它并重试,透明的。

使用的版本:spring-aop-5.2.5.RELEASE、Apache commons-pool2-2.7.0

代码(主要从这个答案复制)

package test.commonspool.inject;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.target.AbstractPoolingTargetSource;
import org.springframework.aop.target.CommonsPool2TargetSource;
import org.springframework.beans.factory.annotation.Value;
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 PoolConfiguration {

    // The targetBeanName is mandatory for CommonsPool2TargetSource. Rather use a constant to avoid mistakes.
    private static final String FOO_TARGET_NAME = "fooTarget";

    /**
     * @return the pooled bean
     */
    @Bean(FOO_TARGET_NAME)
    // Remember to make this bean a prototype
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Foo fooTarget() {
        return new Foo();
    }

    /**
     * @return the pool
     */
    @Bean
    public TargetSource fooSource(
        // You probably would externalize this value to your application.properties
        @Value("2") int maxSize
    ) {
        final AbstractPoolingTargetSource poolingConfig = new CommonsPool2TargetSource() {
            @Override
            protected GenericObjectPool<?> createObjectPool()
            {
                GenericObjectPool<?> pool = (GenericObjectPool<?>) super.createObjectPool();
                pool.setTestOnBorrow(true);
                return pool;
            }

            @Override
            public boolean validateObject(PooledObject<Object> p)
            {
                return ((Foo) p.getObject()).validate();
            }
        };
        poolingConfig.setMaxSize(maxSize);
        // The targetBeanName is mandatory
        poolingConfig.setTargetBeanName(FOO_TARGET_NAME);
        return poolingConfig;
    }

    /**
     * Returns a ProxyFactoryBean that is correctly pooled.
     * @return the proxy we will call
     */
    @Bean
    public ProxyFactoryBean foo(TargetSource fooSource) {
        ProxyFactoryBean proxyBean = new ProxyFactoryBean();
        proxyBean.setTargetSource(fooSource);
        return proxyBean;
    }
}
package test.commonspool.inject;
import java.util.Optional;
import javax.inject.Provider;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = { PoolConfiguration.class })
public class PoolConfigurationTest {
    private static final Logger LOG = LoggerFactory.getLogger(PoolConfigurationTest.class);

    @Autowired
    @javax.inject.Named("foo")
    private Provider<Foo> fooProvider;

    @Test
    public void test() {
        final Runnable runnable = () -> {
            Foo foo = fooProvider.get();

            // Does not help - getSingletonTarget returns null
            // foo = Optional.ofNullable((Foo) AopProxyUtils.getSingletonTarget(foo)).orElse(foo);

            LOG.debug("foo == {}", foo.toString()); //always invoke toString irrespective of logging config
            foo.call();
        };
        new Thread(runnable).start();
        new Thread(runnable).start();
        new Thread(runnable).start();
        runnable.run();
    }
}
package test.commonspool.inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Foo {
    public static final long SLEEP_PERIOD = 1000L;
    private static final AtomicInteger COUNTER = new AtomicInteger();
    private static final Logger LOG = LoggerFactory.getLogger(Foo.class);

    private final int instanceNumber;

    public Foo() {
        this.instanceNumber = COUNTER.incrementAndGet();
    }

    public void call() {
        LOG.warn(">>>>>>>>>>> Called instance {}", instanceNumber);
        try {
            Thread.sleep(SLEEP_PERIOD);
        } catch (InterruptedException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    public boolean validate() {
        LOG.info("({}).validate() at {}", this, Stream.of(new Throwable().getStackTrace()).limit(9).map(s -> s.getMethodName()).collect(Collectors.joining(" < ")));
        return true;
    }
}

显示由 toString 引起的验证调用的输出

main     |INFO |(test.commonspool.inject.Foo@91da29b).validate() at validate < validateObject < borrowObject < borrowObject < getTarget < intercept < toString < lambda$0 < test
Thread-4 |INFO |(test.commonspool.inject.Foo@4624aeba).validate() at validate < validateObject < borrowObject < borrowObject < getTarget < intercept < toString < lambda$0 < run
Thread-2 |INFO |(test.commonspool.inject.Foo@91da29b).validate() at validate < validateObject < borrowObject < borrowObject < getTarget < intercept < toString < lambda$0 < run
Thread-4 |INFO |(test.commonspool.inject.Foo@4624aeba).validate() at validate < validateObject < borrowObject < borrowObject < getTarget < intercept < call < lambda$0 < run
Thread-2 |INFO |(test.commonspool.inject.Foo@91da29b).validate() at validate < validateObject < borrowObject < borrowObject < getTarget < intercept < call < lambda$0 < run
Thread-4 |WARN |>>>>>>>>>>> Called instance 1
Thread-2 |WARN |>>>>>>>>>>> Called instance 2
Thread-3 |INFO |(test.commonspool.inject.Foo@91da29b).validate() at validate < validateObject < borrowObject < borrowObject < getTarget < intercept < toString < lambda$0 < run
main     |INFO |(test.commonspool.inject.Foo@4624aeba).validate() at validate < validateObject < borrowObject < borrowObject < getTarget < intercept < call < lambda$0 < test
main     |WARN |>>>>>>>>>>> Called instance 1
Thread-3 |INFO |(test.commonspool.inject.Foo@91da29b).validate() at validate < validateObject < borrowObject < borrowObject < getTarget < intercept < call < lambda$0 < run
Thread-3 |WARN |>>>>>>>>>>> Called instance 2
4

0 回答 0