I want to configure below type of bean initialization to be performed by annotation.
Below is sample bean configuration in xml type and want to configure this kind of bean using annotations.
<bean id="Animal" class="aaa.type.Animal">
<property name="Animal" value="${Animal}" />
<property name="AnimalFamily" >
<bean class="aaa.type.AnimalFamily">
<property name="AnimalCharactertitic">
<list>
<bean class="aaa.type.AnimalColor">
<property name="name" value="Color" />
<property name="value" value="${color}" />
</bean>
<bean class="aaa.type.AnimalType">
<property name="name" value="Animal Type" />
<property name="value" value="${AnimalType}" />
</bean>
</list>
</property>
</bean>
</property>
</bean>
You can do something like this:
#Configuration
public class MyConfigurationClass {
#Bean
public AnimalCharactertitic animalColor(#Value("${color}") String color) {
AnimalCharactertitic animalCharactertitic = new AnimalCharactertitic();
animalCharactertitic.setName("Color");
animalCharactertitic.setValue(color);
return animalCharactertitic;
}
#Bean
public AnimalCharactertitic animalType(#Value("${AnimalType}") String animalType) {
AnimalCharactertitic animalCharactertitic = new AnimalCharactertitic();
animalCharactertitic.setName("Animal Type");
animalCharactertitic.setValue(animalType);
return animalCharactertitic;
}
#Bean
public AnimalFamily animalFamily(#Autowired AnimalCharactertitic animalColor,
#Autowired AnimalCharactertitic animalType) {
AnimalFamily animalFamily = new AnimalFamily();
List<AnimalCharactertitic> animalCharactertitics = new ArrayList<>();
animalCharactertitics.add(animalColor);
animalCharactertitics.add(animalType);
animalFamily.setAnimalCharactertitic(animalCharactertitics);
return animalFamily;
}
#Bean
public Animal animal(#Value("${Animal}") String animal, #Autowired AnimalFamily animalFamily) {
Animal animal = new Animal();
animal.setAnimal(animal);
animal.setAnimalFamily(animalFamily);
return animal;
}
}
Related
I am trying to convert the spring batch configuration from xml based to annotation based.
Below is my xml based configuration.
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" />
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<!-- Step will need a transaction manager -->
<bean id="transactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<bean id="dbMapper" class="org.test.DBValueMapper">
</bean>
<bean id="dbMapperFlatfile" class="org.test.FlatFileRowMapper">
</bean>
<bean id="paramSetter" class="org.test.DBParamSetter">
</bean>
<bean id="dbReader" class="org.test.DBValueReader"
scope="step">
<property name="paramSetter" ref="paramSetter"/>
<property name="verifyCursorPosition" value="false" />
<property name="dataSource" ref="dataSource" />
<property name="sql" value="#{jobParameters['SQL_QUERY']}" />
<property name="rowMapper" ref="dbMapper" />
<property name="fetchSize" value="5000" />
</bean>
<bean id="dbWriterIO" class="org.test.TemplateWritterIO"
scope="step">
<property name="velocityEngine" ref="velocityEngine" />
<!-- <property name="rptConfig" value="#{jobParameters['RPT_CONFIGVAL']}" /> -->
<property name="headerCallback" ref="dbWriterIO" />
<property name="footerCallback" ref="dbWriterIO" />
</bean>
<batch:job id="fileGenJobNio">
<batch:step id="fileGenJobStempNio">
<batch:tasklet>
<batch:chunk reader="dbReader" writer="dbWriterNIO"
commit-interval="5000">
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
Below is the equivalent Java based configuration:
#EnableBatchProcessing
#Import({ServiceConfiguration.class})
public class SRBatchGenerator extends DefaultBatchConfigurer{
#Autowired
private JobBuilderFactory jobBuilders;
#Autowired
private StepBuilderFactory stepBuilders;
#Autowired
private VelocityEngine velocityEngine;
#Autowired
private DBValueMapper mapper;
#Autowired
private DbHelper dbhelper;
#Autowired
private DataSource datasource;
#Bean
public Step step(){
return stepBuilders.get("step")
.chunk(5000)
.reader(reader())
//.processor(processor())
.writer(writer())
//.listener(logProcessListener())
.faultTolerant()
//.skipLimit(10)
//.skip(UnknownGenderException.class)
//.listener(logSkipListener())
.build();
}
#Bean
public Job fileGeneratorJob(){
return jobBuilders.get("fileGeneratorJob")
//.listener(protocolListener())
.start(step())
.build();
}
#Bean
public DBValueMapper mapper(){
return new DBValueMapper();
}
#Bean
#StepScope
public DBValueReader3 reader(){
String query="Select Test1,Test2,test3,test4 from RPt_TEST";
DBValueReader3 dbread = new DBValueReader3();
dbread.setSql(query);
dbread.setRowMapper(mapper);
dbread.setDataSource(datasource);
return dbread;
}
#Bean
#StepScope
public TemplateWritterIO writer(){
TemplateWritterIO writer=new TemplateWritterIO();
writer.setVelocityEngine(velocityEngine);
return writer;
}
#Override
protected JobRepository createJobRepository() throws Exception {
MapJobRepositoryFactoryBean factory =
new MapJobRepositoryFactoryBean();
factory.afterPropertiesSet();
return (JobRepository) factory.getObject();
}
}
When I execute my Job using xml based it took 27sec to write 1 Million record into Flat file.
But to write same 1 million record, Java based job took about 2 hours to write.
I am not sure what I am missing here. Can anyone help me or guide me why it is slow in Java based configuration.
I am using a Date dob; field in my pojo class in spring 4.1.6 using maven
below are the files associated with my app.
// this is my pojo class.
package com.aamir;
import java.util.Date;
public class Student {
private String firstName;
private Date dob;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public Date getDob() {
return dob;
}
public void setDob(Date dob) {
this.dob = dob;
}
}
// this is my client class
package com.aamir;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ClientStud1 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("s1");
System.out.println(student.getFirstName());
System.out.println(student.getDob());
}
}
// and finally this is my beans.xml file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="s1" class="com.aamir.Student">
<property name="firstName" value="aamir"/>
<property name="dob" value="12-12-1996"/>
</bean>
<bean id="dateEditor"
class="org.springframework.beans.propertyeditors.CustomDateEditor">
<constructor-arg>
<bean class="java.text.SimpleDateFormat">
<constructor-arg value="dd-MM-yyyy"/>
</bean>
</constructor-arg>
<constructor-arg value="true"/>
</bean>
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date" value-ref="dateEditor"/>
</map>
</property>
</bean>
</beans>
and I get this exception every time I try to run the client
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.beans.factory.config.CustomEditorConfigurer#0' defined in class path resource [beans.xml]:
Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'java.util.LinkedHashMap' to required type 'java.util.Map' for property 'customEditors'; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [org.springframework.beans.propertyeditors.CustomDateEditor] to required type [java.lang.Class] for property 'customEditors[java.util.Date]': PropertyEditor [org.springframework.beans.propertyeditors.ClassEditor] returned inappropriate value of type [org.springframework.beans.propertyeditors.CustomDateEditor]
Note that PropertyEditor are stateful, so you should not register instances directly.
Register property editor classes via customEditor property
Add PropertyEditorRegistrars via propertyEditorRegistrars property in CustomEditorConfigurer
As you want to configure the property editor instance, use property editor registrar instead.
<bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<bean class="org.example.CustomDateEditorRegistrar"/>
</list>
</property>
</bean>
As per the official documentation , the recommended way of registering a PropertyEditor is to create a class that implements propertyEditorRegistrar interface. Each propertyEditorRegistrar can register any number of propertyEditors on a given registry.
Two such implementaions are descibed below:
Approach 1: create a generalized class that implements PropertyEditorRegistrar.
This class can be used for any propertyEditor(s), so you can keep this class in your utils.
Lets name it CustomEditorRegistrar and it looks like this:
public class CustomEditorRegistrar implements PropertyEditorRegistrar
{
Class<?> t;
PropertyEditor propertyEditor;
public CustomEditorRegistrar(Class<?> t, PropertyEditor propertyEditor)
{
this.t = t;
this.propertyEditor = propertyEditor;
}
#Override
public void registerCustomEditors(PropertyEditorRegistry registry)
{
registry.registerCustomEditor(t, propertyEditor);
}
}
. The bean definition to register a CustomDateEditor is as below:
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<bean class="mky.spring.property.editor.date.CustomEditorRegistrar">
<constructor-arg index="0">
<value type="java.lang.Class">java.util.Date</value>
</constructor-arg>
<constructor-arg index="1">
<bean class="org.springframework.beans.propertyeditors.CustomDateEditor">
<constructor-arg index="0">
<bean class="java.text.SimpleDateFormat">
<constructor-arg value="dd-MM-yyyy" />
</bean>
</constructor-arg>
<constructor-arg index="1" value="true" />
</bean>
</constructor-arg>
</bean>
</list>
</property>
</bean>
<bean
name="Customer"
class="mky.spring.property.editor.date.Customer"
p:name="Customer 1"
p:address="CounterHills LA"
p:deliveryDate="12-11-2016"
p:issueDate="12-10-2016" />
Sys-out client:
public class TestGenericClient
{
ApplicationContext ctx;
public TestGenericClient()
{
ctx = new ClassPathXmlApplicationContext("genericPropertyEditorRegistrar-beans.xml");
}
public static void main(String[] args)
{
System.out.println(new TestGenericClient().ctx.getBean("Customer"));
}
}
And the output is this:
Name=Customer 1
Address=CounterHills LA
Issue Date=Wed Oct 12 00:00:00 GST 2016
deliveryDate=Sat Nov 12 00:00:00 GST 2016
Approach2: create a specific propertyEditorRegistrar eg, for Date
public class CustomDateEditorRegistrar implements PropertyEditorRegistrar
{
#Override
public void registerCustomEditors(PropertyEditorRegistry registry)
{
registry.registerCustomEditor(java.util.Date.class, new CustomDateEditor(new SimpleDateFormat("dd-MM-yyyyy"),true));
}
}
And the bean declaration for this specific CustomDateEditor is :
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<bean class="mky.spring.property.editor.date.CustomDateEditorRegistrar" />
</list>
</property>
</bean>
Sys-out cleint:
public TestDateClient()
{
ctx = new ClassPathXmlApplicationContext("datePropertyEditorRegistrar-beans.xml");
}
public static void main(String[] args)
{
System.out.println(new TestDateClient().ctx.getBean("Customer"));
}
And the same output :
Name=Customer 1
Address=CounterHills LA
Issue Date=Wed Oct 12 00:00:00 GST 2016
deliveryDate=Sat Nov 12 00:00:00 GST 2016
I have such method
#JmsListener(containerFactory = "jmsListenerContainerFactory", destination = "myQName")
public void rceive(MySerializableObject message) {
log.info("received: {}", message);
}
and such config on xml
<jms:annotation-driven />
<bean id="jmsListenerContainerFactory" class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="connectionFactory" ref="pooledConnectionFactory" />
<property name="concurrency" value="3-10" />
</bean>
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="${brokerURL}" />
</bean>
<bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">
<property name="maxConnections" value="5" />
<property name="maximumActiveSessionPerConnection" value="500" />
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<bean id="jmsContainerFactory" class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="connectionFactory" ref="pooledConnectionFactory" />
<property name="concurrency" value="3-10" />
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" p:connectionFactory-ref="pooledConnectionFactory" />
Seems consumer was not created. I can send messages but cannot receive them.
Any idea whats wrong here?
Just tested your config and it works well. Only difference that I make a class with #JmsListener as a <bean> in that context:
<bean class="org.springframework.integration.jms.JmsListenerAnnotationTests$TestService"/>
#ContextConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#DirtiesContext
public class JmsListenerAnnotationTests {
#Autowired
private JmsTemplate jmsTemplate;
#Autowired
private TestService testService;
#Test
public void test() throws InterruptedException {
this.jmsTemplate.convertAndSend("myQName", "foo");
assertTrue(this.testService.receiveLatch.await(10, TimeUnit.SECONDS));
assertNotNull(this.testService.received);
assertEquals("foo", this.testService.received);
}
public static class TestService {
private CountDownLatch receiveLatch = new CountDownLatch(1);
private Object received;
#JmsListener(containerFactory = "jmsListenerContainerFactory", destination = "myQName")
public void receive(String message) {
this.received = message;
this.receiveLatch.countDown();
}
}
}
<jms:annotation-driven /> makes the #JmsListener infrastructure available, but to force Spring to see those methods your classes should be beans anyway.
For example <component-scan> for the package with #Service classes.
Cheers!
My config:
<bean parent="cache-template">
<property name="name" value="yagoLabel" />
<property name="cacheMode" value="PARTITIONED" />
<property name="atomicityMode" value="TRANSACTIONAL" />
<property name="distributionMode" value="PARTITIONED_ONLY" />
<property name="backups" value="1" />
<property name="store">
<bean class="id.ac.itb.ee.lskk.lumen.yago.YagoLabelCacheStore" autowire="byType" init-method="init" />
</property>
<property name="writeBehindEnabled" value="true" />
<property name="writeBehindFlushSize" value="102380" />
<property name="writeBehindFlushFrequency" value="30000" />
<property name="writeBehindBatchSize" value="10240" />
<property name="swapEnabled" value="false" />
<property name="evictionPolicy">
<bean class="org.gridgain.grid.cache.eviction.lru.GridCacheLruEvictionPolicy">
<property name="maxSize" value="102400" />
</bean>
</property>
</bean>
And I start GridGain as follows:
My GridCacheStore implementation:
public class YagoLabelCacheStore extends GridCacheStoreAdapter<String, YagoLabel> {
private static final Logger log = LoggerFactory
.getLogger(YagoLabelCacheStore.class);
private DBCollection labelColl;
#GridSpringResource(resourceName="mongoDb")
private DB db;
#Inject
private GridGainSpring grid;
#PostConstruct
public void init() {
log.info("Grid is {}", grid);
labelColl = db.getCollection("label");
}
I start GridGain as follows:
String entityId = "Muhammad";
try (AnnotationConfigApplicationContext appCtx
= new AnnotationConfigApplicationContext(LumenConfig.class)) {
Grid grid = appCtx.getBean(Grid.class);
GridCache<String, YagoLabel> labelCache = YagoLabel.cache(grid);
log.info("Label for {}: {}", entityId, labelCache.get(entityId));
}
LumenConfig Spring configuration contains a DB bean named mongoDb.
However this throws NullPointerException because db is not injected properly. I tried #Inject GridGainSpring just for testing, and even GridGainSpring itself is not injected.
I also tried setting <property name="db" ref="mongoDb"/> in the GridGain Config XML but Spring complains cannot find the bean.
My workaround is to put it inside a public static field but that's soo hacky: https://github.com/ceefour/lumen-kb/blob/b8445fbebd227fb7ac337c758a60badb7ecd3095/cli/src/main/java/id/ac/itb/ee/lskk/lumen/yago/YagoLabelCacheStore.java
The way is to load the GridConfiguration using Spring, then pass it to GridGainSpring.start() :
// "classpath:" is required, otherwise it won't be found in a WAR
#ImportResource("classpath:id/ac/itb/ee/lskk/lumen/core/lumen.gridgain.xml")
#Configuration
public static class GridGainConfig {
#Inject
private ApplicationContext appCtx;
#Inject
private GridConfiguration gridCfg;
#Bean(destroyMethod="close")
public Grid grid() throws GridException {
return GridGainSpring.start(gridCfg, appCtx);
}
}
:-)
I have two lists generated with a FactoryBean and I would like to initialize a bean with the merged version of this two. Is there a way doing this?
<bean id="listA" class="XFactoryBean" >
...
</bean>
<bean id="listB" class="YFactoryBean">
...
</bean>
<bean >
<property name="AwithB" ...>
</bean>
There is a solution that works with static lists (http://vikdor.blogspot.hu/2012/10/using-collection-merging-in-spring-to.html), but does not work with those generated lists.
Java #Configuration FTW:
#Configuration
public class Config {
#Resource
private List listA;
#Resource
private List listB;
#Bean
public List AwithB() {
List mergedList = new ArrayList(listA);
listB.addAll(listB);
return mergedList;
}
}
Much less boilerplate.
There is a sublte bug with the ListMergerFactoryBean solution above.
Config:
<util:list id="listA" value-type="java.lang.String">
<value>fooA</value>
<value>barA</value>
</util:list>
<util:list id="listB" value-type="java.lang.String">
<value>fooB</value>
<value>barB</value>
</util:list>
<util:list id="listC" value-type="java.lang.String">
<value>fooC</value>
<value>barC</value>
</util:list>
<bean id="AwithB" class="com.util.ListMergerFactoryBean">
<property name="listOfLists">
<list>
<ref bean="listA" />
<ref bean="listB" />
</list>
</property>
</bean>
<bean id="AwithC" class="com.util.ListMergerFactoryBean">
<property name="listOfLists">
<list>
<ref bean="listA" />
<ref bean="listC" />
</list>
</property>
</bean>
With this config you might be surprised when bean AwithB has the expected content of AwithC. ListA is a singleton and the getObject method alters it, therefore alters it for all users of ListMergerFactoryBean, even though the bean factory is not a singleton. The fix is to not re-use the first list in the listOfLists:
#Override
public List getObject() throws Exception {
List mergedList = new ArrayList();
for (List list : listOfLists) {
mergedList.addAll(list);
}
return mergedList;
}
After checking the SpEL-related solutions (how to extend a list in spring config) and extending the ListFactoryBean (http://ericlefevre.net/wordpress/2008/04/02/merging-lists-in-a-spring-configuration-file/) I came up with the following solution:
config:
<bean id="AwithB" class="com.util.ListMergerFactoryBean">
<property name="listOfLists">
<list>
<ref bean="listA" />
<ref bean="listB" />
</list>
</property>
</bean>
java:
public class ListMergerFactoryBean implements FactoryBean<List> {
private List<List> listOfLists;
#Override
public List getObject() throws Exception {
List mergedList = new ArrayList();
for (List list : listOfLists) {
mergedList.addAll(list);
}
return mergedList;
}
#Override
public Class<?> getObjectType() {
return (new ArrayList()).getClass();
}
#Override
public boolean isSingleton() {
return false;
}
public void setListOfLists(List<List> listOfLists) {
this.listOfLists = listOfLists;
}
}
UPDATE: I have eliminated a bug using Aron Bartle's solution. Thanks!