Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I have an abstract class with some configuration properties value which are set using #Value. I want to reuse the abstract class but with another set of configuration properties. The issue is that, those properties value have already been set in the abstract class and all of the concrete classes have inherited it.
I have thought about:
creating another set of config values in the abstract class, but that seems to be creating duplicate, but this is not quite extensible when in future there is yet a third set of config values.
changing the accessibly of the config value in the abstract class from private to protected and have the concrete class to override it. But I'm not sure this kind of overriding is good as it seems create confusion as to what is the actual config value.
create another abstract class which is similar as "AbstractService" but injecting the different set of config value using #Value. This also seems to create duplication.
public abstract class AbstractService {
#Value("${config1}")
private String config1;
#Value("${config2}")
private String config2;
public void serviceMethod() {
//using config1 and config 2 values
}
}
public class concreteServiceA extends AbstractService {
public void serviceA() {
// using serviceMethod in the abstract class
}
}
public class concreteServiceB extends AbstractService {
public void serviceB() {
// using serviceMethod in the abstract class
}
}
Would it be a good way if using constructor to pass the required parameters in the abstract class, and let the concrete classes to use constructor injection and #Value to set those values ? though if there are long list of config values this may not scale well.
public abstract class AbstractService {
private String config1;
private String config2;
public AbstractService(String config1, String config2) {
this.config1 = config1;
this.config2 = config2;
}
public void serviceMethod() {
//using config1 and config2 values
}
}
public concreteServiceA extends AbstractService {
public concreteServiceA(#Value("${config1}") String config1,
#Value("${config2}") String config2) {
super(config1, config2);
}
public void serviceA() {
//using serviceMethod in the abstract class
}
}
public concreteServiceB extends AbstractService {
public concreteServiceB(#Value("${configB1}") String config1,
#Value("${configB2}") String config2) {
super(config1, config2);
}
public void serviceB() {
//using serviceMethod in the abstract class
}
}
You can go like following:
public abstract class AbstractService {
public void serviceMethod() {
String config1 = getConfig1();
String config2 = getConfig2();
//using config1 and config 2 values
}
public abstract String getConfig1();
public abstract String getConfig2();
}
public class concreteServiceA extends AbstractService {
#Value("${config1}") private String config1;
#Value("${config2}") private String config2;
public String getConfig1(){
return config1;
}
public String getConfig2(){
return config2;
}
public void serviceA() { // using serviceMethod in the abstract class }
}
public class concreteServiceB extends AbstractService {
#Value("${config1.1}") private String config1;
#Value("${config2.1}") private String config2;
public String getConfig1(){
return config1;
}
public String getConfig2(){
return config2;
}
public void serviceB() { // using serviceMethod in the abstract class }
}
You could either use setter injection or (probably more elegant) constructor injection like this:
public abstract class AbstractService {
protected AbstractService(String config1, String config2) {
this.config1 = config1;
this.config2 = config2;
}
private String config1;
private String config2;
public void serviceMethod() {
//using config1 and config 2 values
}
}
public class ConcreteServiceA extends AbstractService {
public ConcreteServiceA(#Value("${config1a}") String config1, #Value("${config2a}") String config2) {
super(config1, config2);
}
public void serviceA() {
// using serviceMethod in the abstract class
}
}
public class ConcreteServiceB extends AbstractService {
public ConcreteServiceB(#Value("${config1b}") String config1, #Value("${config2b}") String config2) {
super(config1, config2);
}
public void serviceB() {
// using serviceMethod in the abstract class
}
}
But if you have lots of values you can also use setter injection and override the setters in each subclass. Or you can still use constructor injection but pass a container class holding the config like this:
public class ServiceConfig {
private String config1;
private String config2;
// getters, setters and more properties
}
Then pass it like this
public abstract class AbstractService {
private ServiceConfig config;
protected AbstractService(ServiceConfig config) {
this.config = config;
}
}
public class ConcreteServiceA extends AbstractService {
public ConcreteServiceA(#Value("${configA}") ServiceConfig config) {
super(config);
}
}
You can externalize your properties to specific beans which will be autowired to the concrete classes.
Spring annotation #ConfigurationProperties allows you to initialise simple POJO properties based on properties prefix.
First create your POJO which we will inject in the concrete services :
public class ServiceProperties {
private String config1;
private String config2;
//getters and setters
}
Then create a configuration class in a package scanned by spring :
#Configuration
public class ServicePropertiesConfiguration {
#Bean
#ConfigurationProperties(prefix = "service-a")
public ServiceProperties serviceAProperties() {
return new ServiceProperties();
}
#Bean
#ConfigurationProperties(prefix = "service-b")
public ServiceProperties serviceBProperties() {
return new ServiceProperties();
}
}
As you can see, prefix tells to spring where he has to search the properties. Your application.properties will look like this :
service-a.config1=serviceAConfig1
service-a.config2=serviceAConfig2
service-b.config1=serviceBConfig1
service-b.config2=serviceBConfig2
At this stage, you will have two beans of type ServiceProperties with specific values inside
The abstract service looks like this :
public abstract class AbstractService {
private final ServiceProperties serviceProperties;
protected AbstractService(ServiceProperties serviceProperties) {
this.serviceProperties = serviceProperties;
}
public void serviceMethod() {
//using config1 and config 2 values
// serviceProperties.getConfig1();
// serviceProperties.getConfig2();
}
}
In the concrete service, you have to use #Qualifier annotation with name of created bean
#Service
public class ConcreteServiceA extends AbstractService{
public ConcreteServiceA(#Qualifier("serviceAProperties") ServiceProperties serviceProperties) {
super(serviceProperties);
}
}
#Service
public class ConcreteServiceB extends AbstractService{
protected ConcreteServiceB(#Qualifier("serviceBProperties") ServiceProperties serviceProperties) {
super(serviceProperties);
}
}
Related
This question already has answers here:
How to programmatically inject a Java CDI managed bean into a local variable in a (static) method
(6 answers)
Closed 4 months ago.
I have this abstract class
public abstract class GenericScheduleController implements Serializable {
#Inject
private Service service;
#PostConstruct
private void init() {
service.doSomething(getLabel());
}
protected abstract String getLabel();
}
and I would like programmatically inject a new one dynamically.
public <T extends GenericScheduleController> T getScheduleController(String chaine) {
//TODO
//get new CDI instance programmatically with abstract getLabel() return chaine
}
Is it possible ?
Thx
I think what you're looking for is Instance:
#Inject
Instance<GenericScheduleController> instances;
public <T extends GenericScheduleController> T getScheduleController(String chaine) {
return instances.stream()
.filter(s -> s.getLabel().equals(chaine))
.findAny()
.orElse(null);
}
I do this and it's ok
public abstract class GenericScheduleController implements Serializable {
#Inject
private Service service;
#PostConstruct
private void init() {
if (getLabel() != null) {
service.doSomething(getLabel());
}
}
protected abstract String getLabel();
}
#Named
public class FooScheduleController extends GenericScheduleController {
private String label;
#Override
public String getLabel() {
return label;
}
public void refreshInit(String label) {
this.label = label;
super.init();
}
}
#Named
#ViewScoped
public class ReportingSchedulerMenuController implements Serializable {
#Inject
private FooScheduleController fooScheduleController;
public GenericScheduleController getScheduleController(String chaine) throws Exception {
if (fooScheduleController.getValue() == null) {
fooScheduleController.refreshInit(chaine);
}
return fooScheduleController;
}
}
I have a interface which cntaining two implementations.
public interface IEncryptDecryptService {
String encrypt(String text);
String decrypt(String text);
}
one class is
#Service("SunJks")
public class GeneralEncryptDecryptServiceImpl implements
IEncryptDecryptService {
public String encrypt{
}
public String decrypt{
}
}
another class is
#Service("SafenetHsm")
public class SafenetHsmEncryptDecryptServiceImpl implements
IEncryptDecryptService {
public String encrypt{
}
public String decrypt{
}
}
I want to inject one of two classes in another class.
#Component
public class LogService implements ILogService {
#Resource(name = "${vault.encryptdecrypt.provider}")
private IEncryptDecryptService edservice;
public display{
edservice.encrypt("***");
}
This is the class where i need two inject the one of the two beans.
In application.properties i have configured that
#Provider Configurer
vault.encryptdecrypt.provider=SunJks
then "GeneralEncryptDecryptServiceImpl"is injected.
#Provider Configurer
vault.encryptdecrypt.provider=SafenetHsm
then SafenetHsmEncryptDecryptServiceImpl is injected into the "LogService" class.
It works fine.
and if i implement same thing in Custom JsonSerializer class it is not working,bean is not injected.
#Component
public class MaskSerializer extends JsonSerializer<Xclass> {
#Resource(name = "${vault.encryptdecrypt.provider}")
private IEncryptDecryptService edservice;
#Override
public void serialize(Xclass value, JsonGenerator gen,
SerializerProvider provider) throws IOException {
String str = value.getPersistenceValue();
String strr = edservice.encrypt(str);
gen.writeStartObject();
gen.writeFieldName(strr);
gen.writeEndObject();
}
i am getting nullpointer exception at edservice.encrypt(str) in above class.
Bean is not injected????
I need to get the bean from the factory by name.
I wonder if there is a more elegant way to deal with this problem?
My working code now looks like this. This is my interface service and "factory"
public interface GreetingService {
void getGreeting(String name);
}
public interface GreetingServiceFactory {
GreetingService getGreetingService(String region);
}
Implementation greetingService:
#Service
public class EnglishGreetingServiceImpl implements GreetingService {
#Override
public void getGreeting(String name) {
System.out.println("Hello " + name);
}
}
#Service
public class GermanGreetingServiceImpl implements GreetingService {
#Override
public void getGreeting(String name) {
System.out.println("Willkommen " + name);
}
}
Implementation factory:
#Service
public class GreetingServiceFactoryImpl implements GreetingServiceFactory {
private Map<String, GreetingService> greetingBeanMap;
#Autowired
#Qualifier("germanGreetingServiceImpl")
private GreetingService germanGreetingService;
#Autowired
#Qualifier("englishGreetingServiceImpl")
private GreetingService englishGreetingService;
#PostConstruct
public void init () {
greetingBeanMap = new HashMap<>();
greetingBeanMap.put("en", englishGreetingService);
greetingBeanMap.put("de", germanGreetingService);
}
#Override
public GreetingService getGreetingService(String region) {
return greetingBeanMap.get(region);
}
}
Main class with example code where I receive bean after some name
#SpringBootApplication
public class SpringFactoryApplication implements CommandLineRunner {
#Autowired
private GreetingServiceFactory greetingServiceFactory;
public static void main(String[] args) {
SpringApplication.run(SpringFactoryApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
String config1 = "en";
GreetingService english = greetingServiceFactory.getGreetingService(config1);
english.getGreeting("John");
String config2 = "de";
GreetingService deutsch = greetingServiceFactory.getGreetingService(config2);
deutsch.getGreeting("Hans");
}
}
In your above code, this piece of code is completely redundant
#Autowired
#Qualifier("germanGreetingServiceImpl")
private GreetingService germanGreetingService;
#Autowired
#Qualifier("englishGreetingServiceImpl")
private GreetingService englishGreetingService;
#PostConstruct
public void init () {
greetingBeanMap = new HashMap<>();
greetingBeanMap.put("en", englishGreetingService);
greetingBeanMap.put("de", germanGreetingService);
}
this piece of code can be replaced by
#Autowired
private Map<String, GreetingService> greetingBeanMap;
When you declare like this, spring will search for all implementations of GreetingService interface and inject into your map, with the key as the bean name. i.e. the greetingBeanMap will have key's as germanGreetingServiceImpl and englishGreetingServiceImpl and value as the bean's itself.
If you want to make the key's as "en" and "de" instead of bean names, then you can name the beans as "en" and "de". Like this
#Service("en")
public class EnglishGreetingServiceImpl implements GreetingService {
......
}
#Service("de")
public class GermanGreetingServiceImpl implements GreetingService {
......
}
I'm trying to inject several datastax Mappers but the Provider creation code is always the same and writing a provider for each type is redundant.
The provider code is
public class FooMapperProvider extends Provider<Mapper<Foo>> () {
private final MappingManager mappingManager
#Inject
FooMapperProvider(MappingManager) {
this.mappingManager = mappingManager;
}
#Override
public Mapper<Foo> get() {
mappingManager.mapper(Foo.class);
}
}
Is it possible to bind or create the provider for
bind(Foo.class).toProvider(GenericMapperProvider.class)
bind(Bar.class).toProvider(GenericMapperProvider.class)
so that get is called in a way mappingManager.mapper can create a mapper based on the class for that specific binding?
I thought about trying something like
public class MapperProvider<T> implements Provider<Mapper<T>> {
private final MappingManager mappingManager;
private final Class klass;
#Inject
public MapperProvider(MappingManager mappingManager, Class klass) {
this.mappingManager = mappingManager;
this.klass = klass;
}
#Override
public Mapper<T> get() {
return mappingManager.mapper(klass);
}
}
but I can't figure out how to specify the class and inject the dependency
public class MapperProvider<T> implements Provider<Mapper<T>> {
private final MappingManager mappingManager;
private final TypeLiteral<T> type;
#Inject
public MapperProvider(MappingManager mappingManager, TypeLiteral<T> type) {
this.mappingManager = mappingManager;
this.type = type;
}
#Override
public Mapper<T> get() {
return mappingManager.mapper(type.getRawType());
}
}
bind(new TypeLiteral<Mapper<Foo>>(){})
.toProvider(new TypeLiteral<MapperProvider<Foo>>(){});
let imagine I have per entity a repository class (spring data jpa) for database access and a service class. The dependencies are managed by spring framework. Every service method does in most cases the same, so there is mainly code duplication:
public class NewsService {
#Inject
private NewsRepository newsRepository;
public void add(News news) {
// do some validation
newsRepository.save(news);
}
}
public class UserService {
#Inject
private UserRepository userRepository;
public void add(User user) {
// do some validation
userRepository.save(user);
}
}
Now i thought about creating an abstract class like this:
public abstract class AbstractService<T> {
private UnknownRepository unknownRepository;
public void add(T entity) {
// do some validation
unknownRepository.save(entity);
}
}
public class NewsService extends AbstractService<News> {
}
public class UserService extends AbstractService<User> {
}
My problem: How can i overwrite the repository used inside the abstract class based on my entities?
You can replace the UnknownRepository field with an abstract method and a type parameter:
// R is the type of the repository
public abstract class AbstractService<T,R extends BaseRepository> {
protected abstract R getRepository();
public void add(T entity) {
getRepository().save(entity);
}
}
And inject the specific repository to the implementations of this class:
public class NewsService extends AbstractService<News, NewsRepository> {
#Inject private NewsRepository newsRepository;
#Override
public NewsRepository getRepository() {
return newsRepository;
}
// the inherited add() method works now
}