8

这成了我脖子上的痛!!!我有三个疑问。

1)我想在我的项目中配置 CommonsPool2TargetSource 以汇集我的自定义 POJO 类。

到目前为止我做了什么:

MySpringBeanConfig 类:

    @Configuration
    @EnableWebMvc
    @ComponentScan(basePackages = {"com.redirect.controller","com.redirect.business","com.redirect.dao.impl","com.redirect.model"})
    @EnableTransactionManagement
    @PropertySource("classpath:" + JioTUConstant.SYSTEM_PROPERTY_FILE_NAME + ".properties")
    @Import({JioTUCouchbaseConfig.class,JioTUJmsConfig.class})
    public class JioTUBeanConfig extends WebMvcConfigurerAdapter {
          private static final Logger LOGGER = Logger.getLogger(JioTUConfig.class);

          @Bean
          public CommonsPool2TargetSource poolTargetSource() {
               CommonsPool2TargetSource commonsPool2TargetSource = new CommonsPool2TargetSource();
               commonsPool2TargetSource.setTargetBeanName("jioTUURL");
               commonsPool2TargetSource.setMinIdle(5);
               commonsPool2TargetSource.setMaxIdle(5);
               commonsPool2TargetSource.setMaxSize(10);
               return commonsPool2TargetSource;
           }

           @Bean
           public ProxyFactoryBean proxyFactoryBean() {
               ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
               proxyFactoryBean.setTargetSource(poolTargetSource());
               return proxyFactoryBean;
           }

           @Bean
           public MethodInvokingFactoryBean poolConfigAdvisor() {
               MethodInvokingFactoryBean poolConfigAdvisor = new MethodInvokingFactoryBean();
               poolConfigAdvisor.setTargetObject(poolTargetSource());
               poolConfigAdvisor.setTargetMethod("getPoolingConfigMixin");
               return poolConfigAdvisor;
           }
   }

我在“com.redirect.model”包中的 POJO 类:

@Repository
@Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Document
public class JioTUURL{

    @Id
    private String keyword;

    @Field
    private String url;

    @Field
    private String title;

    @Field
    private String timestamp;

    @Field
    private String ip;

    @Field
    private Integer clicks;

    @Field
    private String user;

    //Getter/Setter

}

我得到的例外:

org.springframework.beans.factory.NoUniqueBeanDefinitionException:没有定义 [com.redirect.model.JioTUURL] 类型的合格 bean:预期的单个匹配 bean 但找到了 2:jioTUURL,proxyFactoryBean

仅供参考,我没有为 JioTUURL 明确定义任何 bean。由spring的@ComponentScan决定

如果我在JioTUConfig.java类的proxyFactoryBean()方法中注释以下行

    @Bean
    public ProxyFactoryBean proxyFactoryBean() {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
//        proxyFactoryBean.setTargetSource(poolTargetSource());
        return proxyFactoryBean;
    }

然后它运行良好,日志信息如下

09-08-2016 16:28:13.866|INFO |localhost-startStop-1|[class org.springframework.aop.target.CommonsPool2TargetSource] 类型的 Bean 'poolTargetSource' 不符合所有 BeanPostProcessors 处理的条件(例如:不符合自动代理条件)|[PostProcessorRegistrationDelegate.java:328]

2)如何从池中获取对象?

@Controller
public class JioTUController {

    private static final Logger LOGGER = Logger.getLogger(JioTUController.class);
    @Autowired
    private JioTUCommonBusiness jioTUCommonBusiness;
    @Autowired
    private ObjectFactory<JioTUURL> jioTUURLObjectFactory;//Need to replace I guess

    public JioTUController() {
         LOGGER.info("Loading JioTUController complete");
    }
    @RequestMapping(method = RequestMethod.GET, value="/*")
    public ModelAndView postDataAsJSON(HttpServletRequest request,HttpServletResponse response, ModelAndView modelAndView) {

            //Should be replace with code that fetch object for me from the pool
            JioTUURL jioTUURL = jioTUURLObjectFactory.getObject();

            //App Business
    }
}

3) 池中的那些对象是回收的还是在每个 HTTP 请求服务后重新实例化?

4

1 回答 1

4

我遇到了同样的问题,并复制了一个似乎有效的简单案例。我想现在帮助你为时已晚,但我希望它可以帮助未来的读者。

配置池

您必须配置三个元素:

  • 原始 bean,您正在汇集。当然,您需要指定prototype范围。
  • CommonsPool2TargetSource它将需要您刚刚配置的原型 bean 的名称。
  • ProxyFactoryBean使用刚刚配置的TargetSource.
import org.keyboardplaying.bean.Foo;
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";

    /**
     * Returns the pooled bean.
     * 
     * @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();
    }

    /**
     * Returns the pool.
     * 
     * @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();
        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;
    }
}

快速测试

我的Foo豆子很简单。它与创建时的 ID 相关联,并在日志记录中简单地记录它。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.atomic.AtomicInteger;

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);
        }
    }
}

这是一个(脏)类,用于在并行线程中多次运行此 bean。

import org.keyboardplaying.bean.Foo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class PoolConfigurationTest {

    @Autowired
    private ApplicationContext context;

    @Test
    public void test() {
        final Runnable runnable = () -> {
            // Note: the name is required as both "foo" and "fooTarget" will match class Foo.
            final Foo foo = (Foo) context.getBean("foo");
            foo.call();
        };
        new Thread(runnable).start();
        new Thread(runnable).start();
        new Thread(runnable).start();
        runnable.run();
    }
}

如果您查看下面的日志,您会看到我们只使用了两个实例(对应于maxSizeI 集)。当两个实例都在使用时,下一个线程必须等待前面的处理结束,因此Foo日志中会暂停 1 秒(的睡眠时间)。

14:30:59.624  WARN [    main] Foo: >>>>>>>>>>> Called instance 1
14:30:59.624  WARN [Thread-4] Foo: >>>>>>>>>>> Called instance 2
14:31:00.626  WARN [Thread-5] Foo: >>>>>>>>>>> Called instance 2
14:31:00.626  WARN [Thread-3] Foo: >>>>>>>>>>> Called instance 1
于 2019-07-25T12:43:44.740 回答