Как настроить CommonsPool2TargetSource весной?

Это стало болью в моей шее! У меня три запроса.

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

Мой класс POJO внутри пакета "com.redirect.model":

@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]: ожидается один соответствующий компонент, но найден 2: jioTUURL,proxyFactoryBean

К вашему сведению, я не определил ни одного компонента для JioTUURL явно. Это до @ComponentScan весны

Если я прокомментирую следующую строку, внутри метода proxyFactoryBean() класса JioTUConfig.java

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

тогда он работает нормально с информацией журнала, как показано ниже

09-08-2016 16:28:13.866|INFO |localhost-startStop-1| Бин 'poolTargetSource' типа [class org.springframework.aop.target.CommonsPool2TargetSource] не подходит для обработки всеми 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-запроса?

0 ответов

Я столкнулся с той же проблемой и воспроизвел простой случай, который, похоже, работает. Полагаю, уже слишком поздно помогать вам, но я надеюсь, что это поможет будущим читателям.

Настройка пула

Вам необходимо настроить три элемента:

  • Исходный 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;
    }
}

Быстрый тест

Мой Foobean довольно просто. Он связывается с идентификатором при создании и просто регистрирует его при регистрации.

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

Если вы посмотрите журнал ниже, вы увидите, что мы используем только два экземпляра (соответствующие maxSizeСтавлю). Когда используются оба экземпляра, следующие потоки должны ждать завершения предыдущей обработки, следовательно, пауза в 1 с (время ожиданияFoo) в журналах.

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
Другие вопросы по тегам