问题
This become pain in my neck!!! I have three queries.
1)I want to configure CommonsPool2TargetSource in my project for pooling of my custom POJO class.
What I have done so far :
MySpringBeanConfig class :
@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;
}
}
My POJO class inside "com.redirect.model" package:
@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
}
Exception I am getting :
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.redirect.model.JioTUURL] is defined: expected single matching bean but found 2: jioTUURL,proxyFactoryBean
FYI I have not define any bean for JioTUURL explicitly. It is up to the @ComponentScan of spring
If I comment the following line, inside proxyFactoryBean() method of JioTUConfig.java class
@Bean
public ProxyFactoryBean proxyFactoryBean() {
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
// proxyFactoryBean.setTargetSource(poolTargetSource());
return proxyFactoryBean;
}
then it is running fine with log information as below
09-08-2016 16:28:13.866|INFO |localhost-startStop-1|Bean 'poolTargetSource' of type [class org.springframework.aop.target.CommonsPool2TargetSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)|[PostProcessorRegistrationDelegate.java:328]
2)How to fetch objects from pool?
@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)Is those objects in pool recycled or is going to re-instantiate after each HTTP request served?
回答1:
I've been confronted to the same problem and have reproduced a simple case that seems to be working. I suppose it is too late to help you, but I hope it might help future readers.
Configuring the pool
You have to configure three elements:
- The original bean, that you are pooling. Naturally, you will need to specify the
prototype
scope. - The
CommonsPool2TargetSource
, which will require the name of the prototype bean you just configured. - A
ProxyFactoryBean
that will use the just configuredTargetSource
.
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;
}
}
Quick test
My Foo
bean is quite simple. It is associated to an ID on creation and simply logs it on logging.
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);
}
}
}
Here is a (dirty) class to run this bean several times, in parallel threads.
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();
}
}
If you have a look at the log below, you'll see we use only two instances (corresponding to the maxSize
I set). When both instances are in use, the next threads have to wait for the previous processings to be over, hence a pause of 1 s (the sleep time of Foo
) in the logs.
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
来源:https://stackoverflow.com/questions/38850992/how-to-configure-commonspool2targetsource-in-spring