springboot yml map property how to ref bean - spring

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.

Related

Spring filter beans based on annotation ignoring property values

Here is a simple example:
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface Type {
public String name();
}
#Configuration
public class Configuration {
#Bean
#Type(name = "type1")
public String getType1() {...}
#Bean
#Type(name = "type2")
public String getType2() {...}
...
#Bean
#Type(name = "typeN")
public String getTypeN() {...}
}
public class Test {
#Autowired
#Type // Here I'm asked to specify a name, but I want to get all the beans with #Type annotation regardless of the name property.
private List<String> types;
}
As mentioned in the comment, I want to find all the beans with annotation #Type regardless of the name property. How can achieve it?
A not good workaround to achieve it:
#Retention(RetentionPolicy.RUNTIME)
#Qualifier(value = "Type") // --> add this line
public #interface Type {
public String name();
}
and autowire it like this:
#Component
public class Test {
#Autowired
#Qualifier(value = "Type")
private List<String> types;
public void printAll(){
System.out.println(types);
}
}

How to speicify list inside map in spring boot application.properties

I need to have list of POJO(ServiceMetadata) inside a map, which will read configuration
from application.properties
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "props")
#Data
#Component
public class ApplicationConfig {
private Map<String, List<ServiceMetadata>> markets = new HashMap<>();
}
#Data
public class ServiceMetadata {
private String applicationName;
private String backendURL;
}
I tried the below, it is not working.
props.markets.UK.serviceEndpoints[0].applicationName=abc
props.markets.UK.serviceEndpoints[1].backendURL=http://localhost:8080/api/v1/markets/{marketId}
props.markets.ES.serviceEndpoints[0].applicationName=xyz
props.markets.ES.serviceEndpoints[1].backendURL=http://localhost:8080/api/v2/markets/{marketId}
Try bellow:
props.markets.UK[0].applicationName=abc
props.markets.UK[0].backendURL=http://localhost:8080/api/v1/markets/{marketId}
props.markets.UK[1].applicationName=def
props.markets.UK[1].backendURL=http://localhost:8080/api/v1/markets/{marketId}
props.markets.ES[0].applicationName=xyz
props.markets.ES[0].backendURL=http://localhost:8080/api/v2/markets/{marketId}

How to #Autwired MessageSource in spring into Entity class correctly?

I have the following entity in spring boot application:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Audited
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
#Table(name = "currency", catalog = "currency_db")
public class Currency implements java.io.Serializable {
#Autowired
Messages messages;
As for message, it just a container of spring MessageSource here it is:
#ApplicationScope
#Component
#Slf4j
public class Messages {
#Autowired
private MessageSource messageSource;
private MessageSourceAccessor accessor;
#PostConstruct
private void init() {
accessor = new MessageSourceAccessor(messageSource, Locale.ENGLISH);
log.info("Messages initialized");
}
public String get(String code) {
return accessor.getMessage(code);
}
}
I'm getting the following error when run mvn clean install. Any idea what I'm missing here?
org.hibernate.MappingException: Could not determine type for: com.company.currencyservice.Messages, at table: currency, for columns: [org.hibernate.mapping.Column(messages)]
It's looks like hibernate think it's a column. Thanks.
Entities are not Spring beans and therefor you cannot use dependency injection in entities.
If you want to access a Spring bean from within an entity you can use a helper class like this:
#Service
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public static <T> T bean(Class<T> beanType) {
return context.getBean(beanType);
}
public static Object bean(String name) {
return context.getBean(name);
}
#Override
public void setApplicationContext(#SuppressWarnings("NullableProblems") ApplicationContext ac) {
context = ac;
}
}
Then you can use ApplicationContextProvider.getBean(Messages.class) to get access to the Messages.

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

#Value returning null

I have a couple #Value annotations within my Entity Class. I am not sure why but they are both returning null. I also have "ShaPasswordEncoder" Object Autowired, which too is throwing a NullPointer Exception. I have no idea why. Please advise.
#Repository
#Configurable
#Entity
#Table(name="user")
#NamedQueries({...})
public class User implements Serializable{
#Transient private static final Logger logger = Logger.getLogger(User.class);
#Transient private static final AppUtil appUtil = new AppUtil();
#Transient #Value("some value") private String extDir;
#Transient #Value("100x100") private String imageSize;
#Transient private static byte[] salt = "FBar".getBytes();
#Transient #Autowired private ShaPasswordEncoder passwordEncoder;
....
//Default Constructor
public User(){
logger.info("TEST IMAGE => "+imageSize);
}
public String passwordEncoder(String password) {
return passwordEncoder.encodePassword(password,salt);
}
Making a JPA entity as a Spring bean is a bad design.
You should keep your entity simple: only getters and setters.
// Only JPA annotations
#Entity
#Table(name="user")
#NamedQueries({...})
public class User {
// Getters & Setters
}
Then you should delegate the business logic to service classes:
#Service
public class UserService {
#Autowired
private ShaPasswordEncoder passwordEncoder;
#Value("${conf.extDir}")
private String dir;
// Some operations ...
public void createUser(User user) {
// ...
}
public void updateUser(User user) {
// ...
}
}
Are you passing valid value expressions? For properties placeholder you can use something like:
#Value("${directory.extDirectory}")
You can also use Spring EL and get all the goodness from it using the #{value} check the docs here
Is also possible to assign a default value in case the property is not found
#Value("${directory.extDirectory:defaultValue}")
Using Spring annotations on a POJO means you are delegating the creation and the configuration of this bean to the Spring IoC Container !!!
The Spring container will supply all the required dependencies.
For example:
#Component
public class MyBean {
#Value("${data}")
private String data;
#Autowired
private MyService service;
// ...
}
When you try to instantiate a bean using the new operator, you will get null values.
MyBean bean = new MyBean();
In order to have a fully-configured bean you MUST get it from the applicationContext. The Spring container will provide all the requested dependencies.
MyBean bean = (MyBean) applicationContext.getBean(MyBean.class);
Or
#Component
public class AnotherBean {
// You are sure that Spring will create and inject the bean for you.
#Autowired
private MyBean bean;
}
Although the bean is managed by Spring it is also possible that you make a new bean yourself instead of getting it from spring container.
So below code will make a new user but it is not get from Spring context.
User user = new User()
If you use above code the #value is not applied to your bean.
If you want you must get the User from Spring by #Autowired
public class SampleService{
#Autowired
private User user;
public void Sample(){
user.getExtDir(); //here user.extDir is not null
}
}

Resources