Как настроить 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;
}
}
Быстрый тест
Мой Foo
bean довольно просто. Он связывается с идентификатором при создании и просто регистрирует его при регистрации.
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