Autowire the configuration class into another configuration class field - spring-boot

I have a configuration class RetryConfig , which uses configuration properties of class RetryConfiguration.
In class RetryConfig , I am creating one object SimpleRetryPolicy field, which needs one property of class RetryConfiguration as below:
Ex:
public class RetryConfig {
//private static final int MAX_RETRY_ATTEMPTS = 5;
#Autowired
RetryConfiguration retryConfiguration;
private final SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy(retryConfiguration.getcount()); }
But with the above code, it fails.
whats i am missing here?

Field initialization (SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy...) happens before #Autowired is evaluated. Try one of those:
make SimpleRetryPolicy also a bean (e.g., #Component) and autowire RetryConfiguration there
create a constructor of RetryConfig with RetryConfiguration parameter and initialize simpleRetryPolicy in this constructor. The class then can look like this:
public class RetryConfig {
//private static final int MAX_RETRY_ATTEMPTS = 5;
RetryConfiguration retryConfiguration;
private final SimpleRetryPolicy simpleRetryPolicy;
#Autowired
public RetryConfig (RetryConfiguration c) {
simpleRetryPolicy = new SimpleRetryPolicy(c.getcount());}
}

Related

springboot yml map property how to ref bean

I want to inject a map in yml file like that ;
abc-identify:
test:
51L: anhuiAbcIdentifyRule
my config class like that
#ConfigurationProperties(prefix = "abc-identify")
#Component
#Data
public class AbcIdentifyConfig {
private Map<Long, IdentifyRule> test;
anhuiAbcIdentifyRule is a existing bean in container
#Component
public class AnhuiAbcIdentifyRule implements IdentifyRule
I tried above setting which not work,how can I resolve this?
Spring does not support this type of string to bean conversion yet. Your code need to be changed to
private Map<Long, String> test;
If you want to get bean by rule from properties, there is a workaround.
#Data
#Component
#ConfigurationProperties(prefix = "abc-identify")
public class AbcProperties {
private Map<Long, String> test;
#Autowired
private Map<String, IdentifyRule> identifyRuleMap;
public IdentifyRule getRule(Long rule){
String beanName = test.get(rule);
if(beanName != null){
return identifyRuleMap.get(beanName);
}
return null;
}
}
Here Map<String, IdentifyRule> identifyRuleMap will contain all beans of IdentityRule with keys as beanName and values as bean.

Null pointer while trying to access Bean

I have a configuration class which creates multiple beans:
#Configuration
public class TopLevelConfig {
#Bean
public MyMapper myMapper() {
MyMapper mapper = new MyMapper();
mapper.registerModule(new MetadataModule());
return new MyMapper();
}
}
Now in MetadataModule:
#Override
public void setupModule(final SetupContext setupContext) {
final SimpleDeserializers deserializers = new SimpleDeserializers();
deserializers.addDeserializer(Payload.class, new PayloadDeserializer());
setupContext.addDeserializers(deserializers);
}
In PayloadDeserializer I'm not able to autowire the MyMapper class. I'm thinking this is because when the new Object of PayloadDeserializer is created, the bean of MyMapper hasn't been created by then. How do I allow PayloadDeserializer to get access to the bean object?
You are creating PayloadDeserializer object by yourself by calling new PayloadDeserializer(), this is the reason why MyMapper is not injected to it. To inject/autowire to work, your bean should be spring managed. To do that, you can use #Component on top of your PayloadDeserializer class like below.
#Component
public class PayloadDeserializer {
private final MyMapper mapper;
#Autowired
public PayloadDeserializer(MyMapper mapper) {
this.mapper = mapper;
}
}
#Configuration
public class TopLevelConfig {
#Bean
public MyMapper myMapper(PayloadDeserializer payloadDeserializer) {
MyMapper mapper = new MyMapper();
mapper.registerModule(metadataModule(payloadDeserializer));
return mapper;
}
#Bean
public MetadataModule metadataModule(PayloadDeserializer payloadDeserializer) {
return new MetadataModule(payloadDeserializer);
}
}
public class MetadataModule {
private final PayloadDeserializer payloadDeserializer;
public MetadataModule(PayloadDeserializer payloadDeserializer) {
this.payloadDeserializer = payloadDeserializer;
}
#Override
public void setupModule(final SetupContext setupContext) {
final SimpleDeserializers deserializers = new SimpleDeserializers();
deserializers.addDeserializer(Payload.class, payloadDeserializer);
setupContext.addDeserializers(deserializers);
}
}

how to set spring annotation bean in custom spring xml schema parser?

I write a spring xml schema like this
<route:urls id="urlHandlerMap">
<route:url pattern="user/api/**" beforeHandlers="defaultBeforeHandler"/>
</route:urls>
this is my schema parser
public class ApiRouteParser extends AbstractSingleBeanDefinitionParser {
private static String DEFAULT_BEFORE_HANDLER = "defaultBeforeHandler";
private static String ROUTE_URL_FIELD = "route:url";
private static String PATTERN_FIELD = "pattern";
private static String BEFORE_HANDLER_FIELD = "beforeHandlers";
#Override
protected Class getBeanClass(Element element) {
return UrlHandlerMap.class;
}
#Override
protected void doParse(Element element,ParserContext parserContext, BeanDefinitionBuilder builder) {
NodeList urlList = element.getElementsByTagName(ROUTE_URL_FIELD);
ManagedMap<String, BeanMetadataElement> urlHandlerMapperDefinition = new ManagedMap<String, BeanMetadataElement>();
for (int i=0; i < urlList.getLength(); i++){
Element urlNode = (Element)urlList.item(i);
String pattern = urlNode.getAttribute(PATTERN_FIELD);
String beforeHandlers = urlNode.getAttribute(BEFORE_HANDLER_FIELD);
if (StringUtils.isEmpty(beforeHandlers)){
beforeHandlers = DEFAULT_BEFORE_HANDLER;
}
BeanDefinitionBuilder urlHandlerBuilder = BeanDefinitionBuilder.genericBeanDefinition(UrlHandler.class);
urlHandlerBuilder.addPropertyValue("beforeHandlerList", parseList(beforeHandlers));
urlHandlerMapperDefinition.put(pattern, urlHandlerBuilder.getBeanDefinition());
}
builder.addPropertyValue("urlHandlerMapper", urlHandlerMapperDefinition);
}
private List<BeanMetadataElement> parseList(String handlers){
List<BeanMetadataElement> definitionList = new ManagedList<BeanMetadataElement>();
String[] handlerArray = handlers.split(",");
for (String handler : handlerArray){
// this handler is inject with #Component
definitionList.add(new RuntimeBeanReference(handler));
}
return definitionList;
}
}
but when I run it,throws:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'defaultBeforeHandler' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:680)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1183)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:275)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
this is the defaultBeforeHandler
#Component
public class DefaultBeforeHandler extends BaseController implements BeforeHandler {
private static Logger logger = LoggerFactory.getLogger(DefaultBeforeHandler.class);
#Override
public void execute(RouteRequest routeRequest, HttpServletRequest request) throws Exception{
logger.debug("defaultBeforeHandler");
}
}
but when I define defaultBeforeHandler in xml,it will be ok.
how can I use the #Component annotation in my xml parser?
I have already config <context:component-scan base-package="com.my.app" /> in applicationContext.xml
spring version: 4.3
You need to enable component scanning through configuration. Either by annotation or XML. E.g.
#Configuration
#ComponentScan("com.acme.app.services")
public class AppConfig {
// various #Bean definitions ... }
That example will enable annotation configuration and scan the packages under com.acme.app.services for any annotated classes and register them as beans. Update that string to match the package with your annotated class in it.
See docs for more detail.

Spring 4 bean autowiring with generics

I am using Spring 4 via Spring Boot 1.1.8 and have created a class to cache some data. The class relies on generics to work but I'm having trouble with Spring and autowiring this class as a bean in another service.
I get errors like this:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [orm.repository.BaseRepository] is defined: expected single matching bean but found 2: dataTypeRepository,propertyNameRepository
The class in question:
/**
* The purpose of this class is to get data from a cache backed by a database
* and if it does not exist to create it and insert into the database.
*/
#Service
public class CacheByName<TRepo extends BaseRepository, TItem extends BaseWithName> {
private final TRepo repo;
private final Class<TItem> itemClass;
private final Map<String, TItem> itemsCache; // TODO: change to better caching strategy
#Autowired
public CacheByName(TRepo repo, Class<TItem> itemClass) {
this.repo = repo;
this.itemClass = itemClass;
itemsCache = new HashMap();
}
public TItem getCreateItem(String name) {
TItem item = null;
if(itemsCache.containsKey(name)) {
item = itemsCache.get(name);
} else {
// try and load from db
item = (TItem) repo.findByName(name);
if(item == null) {
try {
item = itemClass.newInstance();
item.setName(name);
repo.saveAndFlush(item);
} catch (InstantiationException | IllegalAccessException ex) {
// TODO: log and handle better
return null;
}
}
itemsCache.put(name, item);
}
return item;
}
}
The class BaseRepository extends JpaRepository as follows. Other actual repositories extend this one.
#NoRepositoryBean
public interface BaseRepository<T extends Object, ID extends Serializable> extends JpaRepository<T, ID> {
public T findByName(String name);
}
The class BaseWithName is a MappedSuperclass that defines a name property and getters/setters for it. Other more concrete entity classes extend this.
I am trying to inject the CacheByName class into another service like the following. Note that I am defining the actual repository and entity class as generics in the constructor:
#Service
public class DataImporter extends BaseImporter {
private static final Logger log = LoggerFactory.getLogger(PropertyImporter.class);
private final PropertyNameRepository propertyNameRepo;
private final CacheByName<DataTypeRepository, DataType> dataTypeCache;
#Autowired
public PropertyImporter(RestTemplate restTemplateD5,
CacheByName<DataTypeRepository, DataType> dataTypeCache) {
super(restTemplateD5);
this.dataTypeCache = dataTypeCache;
}
.
.
.
}
My AppConfig.java looks like the following:
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class AppConfig {
#Value("${username}")
private String username;
#Value("${password}")
private String password;
#Bean
public RestTemplate restTemplateD5() {
return RestTemplateFactory.createWithHttpBasicAuth(username, password);
}
}
I haven't been able to find much information about creating beans that use generics. I suspect I need to create another #Bean definition in my AppConfig but I wasn't able to implement anything that worked.
As BaseRepository is also a generic type, I think you missed to add the generic type there. That should help Spring to find a proper bean to inject:
public class CacheByName<TRepo extends BaseRepository<TItem, ? extends Serializable>, TItem extends BaseWithName>
This should also make the cast no longer needed:
item = repo.findByName(name);

spring injection issues with spring data

I am getting the following error
Error creating bean with name 'genericRepository': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: Not an managed type: class java.lang.Object
I am new to generics, if there are any generics issue please let me know as well
my Contact.java is in com.merc.template.managelistofobjects.domain package
All other classes are in com.merc.template.managelistofobjects package
ContactCollectionManagerImpl
#Component
public class ContactCollectionManagerImpl extends CollectionManagerImpl<Contact> implements CollectionManager<Contact>{
#Autowired
private GenericRepository<Contact,Long> genericRepository;
public ContactCollectionManagerImpl() {
setGenericRepository(genericRepository);
}
#Override
public void addToCollection(Contact contact, boolean reload){
super.addToCollection(contact, entityDataMap, reload);
}
}
CollectionManagerImpl
public abstract class CollectionManagerImpl<T extends EntityBean> implements CollectionManager<T>{
private GenericRepository objectManager;
public void setGenericRepository(GenericRepository genericRepository) {
this.objectManager = genericRepository;
}
protected void addToCollection(T entity, Map<Long,T> entityDataMap, boolean reload) {
//reload is set to false when the static map needs not be updated
if(reload){
//loads all the existing collection objects from db
loadCollection(entityDataMap, false);
//check if the obect to be inserted already exists in collection
if(entityDataMap.containsKey(entity.getId())){
return;
}
}
//TODO save to database
objectManager.save(entity);
if(reload){
syncCollectionWithDB(entityDataMap);
}
}
}
CollectionManager
public interface CollectionManager<T> {
public void addToCollection(T object, boolean reload);
}
GenericRepository
public interface GenericRepository<T, ID extends Long> extends JpaRepository<T, ID>{
}
MyApplicationContext
#Configuration
#EnableJpaRepositories
#ComponentScan("com.merc.template.managelistofobjects")
#ImportResource("classpath:spring/app-context.xml")
#PropertySource("classpath:application.properties")
public class MyApplicationContext {
private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_FORMAT_SQL = "hibernate.format_sql";
private static final String PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY = "hibernate.ejb.naming_strategy";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";
#Resource
private Environment environment;
#Bean
public DataSource dataSource() {
BoneCPDataSource dataSource = new BoneCPDataSource();
dataSource.setDriverClass(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
dataSource.setJdbcUrl(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
dataSource.setUsername(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
dataSource.setPassword(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
return dataSource;
}
#Bean
public JpaTransactionManager transactionManager() throws ClassNotFoundException {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactoryBean().getObject());
return transactionManager;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() throws ClassNotFoundException {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
//setPackagesToScan = com.merc.template.managelistofobjects.domain
entityManagerFactoryBean.setPackagesToScan(environment.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistence.class);
Properties jpaProterties = new Properties();
jpaProterties.put(PROPERTY_NAME_HIBERNATE_DIALECT, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
jpaProterties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
jpaProterties.put(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY));
jpaProterties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
entityManagerFactoryBean.setJpaProperties(jpaProterties);
return entityManagerFactoryBean;
}
#Bean
public CollectionManager contactCollectionManager(){
return new ContactCollectionManagerImpl();
}
}
My main class contains the following code
ApplicationContext context = new AnnotationConfigApplicationContext(MyApplicationContext.class);
CollectionManager collMgr = context.getBean("contactCollectionManager",CollectionManager.class);
Contact contact = new Contact(2L,"xyz","abc");
collMgr.addToCollection(contact, true);
entitymanager.packages.to.scan=com.merc.template.managelistofobjects.domain
my spring xml file contains just one line
<jpa:repositories base-package="com.merc.template.managelistofobjects"/>
When i run the code I get the following error
java.lang.IllegalArgumentException: Not an managed type: class java.lang.Object
You cannot autowire an object that takes an generic type, You will have to define a strongly typed sub interface of GenericRepository and then autowire it inside your clases
public interface ContactGenericRepository extends GenericRepository<Contact,Long> {}
Then autowire the new interface
#Autowired
private ContactGenericRepository contractGenericRepository;
P.S: you cannot use the autowired object inside the constructor of the class that wrap it, as you are doing inside the ContactCollectionManagerImpl constructor, as the object is not instantiated yet
You could easily use #PostConstruct on any other method that does that behaviour you want, like this
#PostConstruct
public void populateContactCollectionManagerImpl() {
setGenericRepository(genericRepository);
}

Resources