How inject #Any Singletons - java

I have problem with inject singletons to Instance. I want to inject all singletons which implements interface Inter, I want to take value from class annotation. How I can do that. My solution not working :(
Interface:
public interface Inter {
String getString();
}
Annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface IConf {
String fileName();
}
Class which implements interface
#Singleton
#IConf(fileName = "Config.xml")
public class ConfObjt implements Inter{
public String getString() {
// TODO Auto-generated method stub
return "";
}
}
Main class
#Singleton
#Startup
public class Sing {
#Inject #Any
Instance<Inter> beans;
#PostConstruct
public void init(){
System.out.println("metoda init");
for(Inter inter : beans) {
Class<?> clazz = inter.getClass();
IConf ano = clazz.getAnnotation(IConf.class);
System.out.println(ano.fileName());
}
}
}

Related

How can I create different spring beans depending on the input

All the class are implemented from the same interface. What is the best way to create beans depending on the input value we are receiving.
If the value is a it need to invoke one class vs different class if the value is b.
You cloud try something like this:
#Component
public class SomeServiceFactory {
#Autowired
private Someservice someserviceA;
#Autowired
private Someservice someserviceB;
#Autowired
private MyServiceThree SomeserviceC;
public SomeService getSomeService(String serviceType) {
if (serviceType.equals("A")) {
return someserviceA;
} else if (serviceType.equals("B")) {
return someserviceB;
} else {
return someserviceC;
}
}
}
First the interface:
public interface MyService {
void doSomething();
}
Then defining two implementation:
#Service
public class MyServiceA implements MyService {
#Override
public void doSomething() {
// do your business A
}
}
#Service
public class MyServiceB implements MyService {
#Override
public void doSomething() {
// do your business B
}
}
The context:
#Service
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class MyServiceContext {
private final Map<String, MyService> strategyMap;
public MyService getMyService(String key) {
// the key is the bean name
return strategyMap.get(key);
}
}
Usage
#Autowired
private MyServiceContext context;
...
// your input key must be the bean name.
context.getMyService(yourInputValue).doSmething();

How to inject two instances of two different classes which implement the same interface?

When dealing with CDI in java, I want to inject two instances of two different classes, implementing the same interface.
As I understand, I can inject an instance of a class which does not implement an interface, e.g.:
class MyClass {
// ...
}
class XY {
#Inject MyClass myClass;
}
When my class implements an interface I have to declare the member by the interface name (and specify the concrete implementation):
class MyClass implements MyInterface {
// ...
}
class XY {
#Inject MyInterface myClass;
}
But as soon as I want to inject different implementations, I get the "Api type [...] is not found with the qualifiers" exception:
class MyClassOne implements MyInterface {
// ...
}
class MyClassTwo implements MyInterface {
// ...
}
class XY {
#Inject MyClassOne myClassOne;
#Inject MyClassTwo myClassTwo;
}
I appreciate any ideas what to try or where to continue reading (the obvious keywords for a search on this topic give very unspecific results).
Thanks in advance!
In order to inject different instances, there are different ways to construct and inject beans.
Approach 1:
#Qualifier
#Retention(RUNTIME)
#Target({FIELD, TYPE, METHOD})
public #interface ClassifierOne {
}
#Qualifier
#Retention(RUNTIME)
#Target({FIELD, TYPE, METHOD})
public #interface ClassifierTwo {
}
These qualifiers can be used in your class part of construction parameter injection or setter injection level.
#ClassifierOne
public class MyClassOne implements MyInterface {
// ...
}
#ClassifierTwo
public class MyClassTwo implements MyInterface {
// ...
}
public class XY {
private final MyInterface myClassOne;
private final MyInterface myClassTwo;
#Inject
public XY ( #ClassifierOne MyInterface myClassOne, #ClassifierTwo MyInterface myClassTwo ) {
this.myClassOne = myClassOne;
this.myClassTwo = myClassTwo;
}
}
Approach 2: Use of #Produces
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
public #interface MyClassType {
ClassImplName value();
}
public enum ClassImplName {
CLASS_ONE(MyClassOne.class),
CLASS_TWO(MyClassTwo.class);
private Class<? extends MyInterface> classType;
private ClassImplName(Class<? extends MyInterface> clazz) {
this.classType = clazz;
}
public Class<? extends MyInterface> getClassType(){
return classType;
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
public #interface ClassType {
ClassImplName value();
}
Above custom qualifiers will allow you to chose type of implementation by removing abibuaty in producer method.
And, you can use below mentioned MyClassFactory to produce interfaces. This mechanism would be efficient as it uses InjectionPoint where the bean is injected.
public class MyInterfaceFactory {
#Produces
#MyClassType
public MyInterface createMyClasses(#Any Instance<MyInterface> instance, InjectionPoint injectionPoint) {
Annotated annotated = injectionPoint.getAnnotated();
ClassType classTypeAnnotation = annotated.getAnnotation(ClassType.class);
Class<? extends MyInterface> classType = classTypeAnnotation.value().getClassType();
return instance.select(classType).get();
}
}
Finally, you can use these generated instances in your class.
public class XY {
#Inject
#ClassType(ClassImplName.CLASS_ONE)
#MyClassType
private MyInterface myClassOne;
#Inject
#ClassType(ClassImplName.CLASS_TWO)
#MyClassType
private MyInterface myClassTwo;
// Other methods using injected beans ...
}

Spring choose bean implementation at runtime

I'm using Spring Beans with annotations and I need to choose different implementation at runtime.
#Service
public class MyService {
public void test(){...}
}
For example for windows's platform I need MyServiceWin extending MyService, for linux platform I need MyServiceLnx extending MyService.
For now I know only one horrible solution:
#Service
public class MyService {
private MyService impl;
#PostInit
public void init(){
if(windows) impl=new MyServiceWin();
else impl=new MyServiceLnx();
}
public void test(){
impl.test();
}
}
Please consider that I'm using annotation only and not XML config.
1. Implement a custom Condition
public class LinuxCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Linux"); }
}
Same for Windows.
2. Use #Conditional in your Configuration class
#Configuration
public class MyConfiguration {
#Bean
#Conditional(LinuxCondition.class)
public MyService getMyLinuxService() {
return new LinuxService();
}
#Bean
#Conditional(WindowsCondition.class)
public MyService getMyWindowsService() {
return new WindowsService();
}
}
3. Use #Autowired as usual
#Service
public class SomeOtherServiceUsingMyService {
#Autowired
private MyService impl;
// ...
}
Let's create beautiful config.
Imagine that we have Animal interface and we have Dog and Cat implementation. We want to write write:
#Autowired
Animal animal;
but which implementation should we return?
So what is solution? There are many ways to solve problem. I will write how to use #Qualifier and Custom Conditions together.
So First off all let's create our custom annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
public #interface AnimalType {
String value() default "";
}
and config:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class AnimalFactoryConfig {
#Bean(name = "AnimalBean")
#AnimalType("Dog")
#Conditional(AnimalCondition.class)
public Animal getDog() {
return new Dog();
}
#Bean(name = "AnimalBean")
#AnimalType("Cat")
#Conditional(AnimalCondition.class)
public Animal getCat() {
return new Cat();
}
}
Note our bean name is AnimalBean. why do we need this bean? because when we inject Animal interface we will write just #Qualifier("AnimalBean")
Also we crated custom annotation to pass the value to our custom Condition.
Now our conditions look like this (imagine that "Dog" name comes from config file or JVM parameter or...)
public class AnimalCondition implements Condition {
#Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
if (annotatedTypeMetadata.isAnnotated(AnimalType.class.getCanonicalName())){
return annotatedTypeMetadata.getAnnotationAttributes(AnimalType.class.getCanonicalName())
.entrySet().stream().anyMatch(f -> f.getValue().equals("Dog"));
}
return false;
}
}
and finally injection:
#Qualifier("AnimalBean")
#Autowired
Animal animal;
You can move the bean injection into the configuration, as:
#Configuration
public class AppConfig {
#Bean
public MyService getMyService() {
if(windows) return new MyServiceWin();
else return new MyServiceLnx();
}
}
Alternatively, you may use profiles windows and linux, then annotate your service implementations with the #Profile annotation, like #Profile("linux") or #Profile("windows"), and provide one of this profiles for your application.
Autowire all your implementations into a factory with #Qualifier annotations, then return the service class you need from the factory.
public class MyService {
private void doStuff();
}
My Windows Service:
#Service("myWindowsService")
public class MyWindowsService implements MyService {
#Override
private void doStuff() {
//Windows specific stuff happens here.
}
}
My Mac Service:
#Service("myMacService")
public class MyMacService implements MyService {
#Override
private void doStuff() {
//Mac specific stuff happens here
}
}
My factory:
#Component
public class MyFactory {
#Autowired
#Qualifier("myWindowsService")
private MyService windowsService;
#Autowired
#Qualifier("myMacService")
private MyService macService;
public MyService getService(String serviceNeeded){
//This logic is ugly
if(serviceNeeded == "Windows"){
return windowsService;
} else {
return macService;
}
}
}
If you want to get really tricky you can use an enum to store your implementation class types, and then use the enum value to choose which implementation you want to return.
public enum ServiceStore {
MAC("myMacService", MyMacService.class),
WINDOWS("myWindowsService", MyWindowsService.class);
private String serviceName;
private Class<?> clazz;
private static final Map<Class<?>, ServiceStore> mapOfClassTypes = new HashMap<Class<?>, ServiceStore>();
static {
//This little bit of black magic, basically sets up your
//static map and allows you to get an enum value based on a classtype
ServiceStore[] namesArray = ServiceStore.values();
for(ServiceStore name : namesArray){
mapOfClassTypes.put(name.getClassType, name);
}
}
private ServiceStore(String serviceName, Class<?> clazz){
this.serviceName = serviceName;
this.clazz = clazz;
}
public String getServiceBeanName() {
return serviceName;
}
public static <T> ServiceStore getOrdinalFromValue(Class<?> clazz) {
return mapOfClassTypes.get(clazz);
}
}
Then your factory can tap into the Application context and pull instances into it's own map. When you add a new service class, just add another entry to the enum, and that's all you have to do.
public class ServiceFactory implements ApplicationContextAware {
private final Map<String, MyService> myServices = new Hashmap<String, MyService>();
public MyService getInstance(Class<?> clazz) {
return myServices.get(ServiceStore.getOrdinalFromValue(clazz).getServiceName());
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
myServices.putAll(applicationContext.getBeansofType(MyService.class));
}
}
Now you can just pass the class type you want into the factory, and it will provide you back the instance you need. Very helpful especially if you want to the make the services generic.
Simply make the #Service annotated classes conditional:
That's all. No need for other explicit #Bean methods.
public enum Implementation {
FOO, BAR
}
#Configuration
public class FooCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Implementation implementation = Implementation.valueOf(context.getEnvironment().getProperty("implementation"));
return Implementation.FOO == implementation;
}
}
#Configuration
public class BarCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Implementation implementation = Implementation.valueOf(context.getEnvironment().getProperty("implementation"));
return Implementation.BAR == implementation;
}
}
Here happens the magic.
The condition is right where it belongs: At the implementating classes.
#Conditional(FooCondition.class)
#Service
class MyServiceFooImpl implements MyService {
// ...
}
#Conditional(BarCondition.class)
#Service
class MyServiceBarImpl implements MyService {
// ...
}
You can then use Dependency Injection as usual, e.g. via Lombok's #RequiredArgsConstructor or #Autowired.
#Service
#RequiredArgsConstructor
public class MyApp {
private final MyService myService;
// ...
}
Put this in your application.yml:
implementation: FOO
👍 Only the implementations annotated with the FooCondition will be instantiated. No phantom instantiations. 👍
Just adding my 2 cents to this question. Note that one doesn't have to implement so many java classes as the other answers are showing. One can simply use the #ConditionalOnProperty. Example:
#Service
#ConditionalOnProperty(
value="property.my.service",
havingValue = "foo",
matchIfMissing = true)
class MyServiceFooImpl implements MyService {
// ...
}
#ConditionalOnProperty(
value="property.my.service",
havingValue = "bar")
class MyServiceBarImpl implements MyService {
// ...
}
Put this in your application.yml:
property.my.service: foo
MyService.java:
public interface MyService {
String message();
}
MyServiceConfig.java:
#Configuration
public class MyServiceConfig {
#Value("${service-type}")
MyServiceTypes myServiceType;
#Bean
public MyService getMyService() {
if (myServiceType == MyServiceTypes.One) {
return new MyServiceImp1();
} else {
return new MyServiceImp2();
}
}
}
application.properties:
service-type=one
MyServiceTypes.java
public enum MyServiceTypes {
One,
Two
}
Use in any Bean/Component/Service/etc. like:
#Autowired
MyService myService;
...
String message = myService.message()

Custom #Qualifier with Spring autoconfig

I'm not sure how to use custom qualifier interface with component scanning and autowire in Spring. I have an interface:
#Target({ElementType.FIELD,ElementType.PARAMETER,ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface BigBean {
}
a bean I want to injected to:
#Component
public class Bean {
#Autowired
#BigBean("A")
private SomeBean sb;
public SomeBean getSb() {
return sb;
}
public void setSb(SomeBean sb) {
this.sb = sb;
}
}
and beans of the same type to be distinguished by custom qualifier:
#Component
#BigBean("A") //<-????
public class SmallBeanA implements SomeBean{
}
#Component
public class SmallBeanB implements SomeBean{
}
What I found in spring documentation doesn't compile in my case. How to use this custom qualifier I have?
You will need to add attribute value to BigBean annotation as
#Target({ElementType.FIELD,ElementType.PARAMETER,ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface BigBean {
String value() default "";
}
This a bit old thread now, not sure if my answer will be of any help or not but let me try. Below is code to inject a selected instance of SomeBean.
#Qualifier
#Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
public #interface BigBean {
String value() default "";
}
public interface SomeBean {
}
#Component
#BigBean("A")
public class SmallBeanA implements SomeBean {
private SmallBeanA() {
System.out.println("SmallBeanA constructed");
}
#Override
public String toString() {
return "SmallBeanA";
}
}
#Component
#BigBean("B")
public class SmallBeanB implements SomeBean{
private SmallBeanB() {
System.out.println("SmallBeanB constructed");
}
#Override
public String toString() {
return "SmallBeanB";
}
}
#Component
public class Bean {
#Autowired
#BigBean("A")
private SomeBean sb;
public SomeBean getSb() {
return sb;
}
#Override
public String toString() {
return "Bean [sb=" + sb + "]";
}
}
public class QualifierTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Spring-Module.xml");
Bean myBean = context.getBean(Bean.class);
System.out.println(myBean.getSb().toString());
//OR
System.out.println(myBean.toString());
}
}

Unable to resolve any beans for Type: xxx; Qualifiers: [#javax.enterprise.inject.Any()]

I have a LoginProvider interface:
public interface LoginProvider {
boolean login(String username, String password);
}
And 2 different implementations:
public class LoginProvider1Impl implements LoginProvider {
#Override
public boolean login(String username, String password) {
...
}
}
public class LoginProvider2Impl implements LoginProvider {
#Override
public boolean login(String username, String password) {
...
}
}
Then a producer annotation:
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({FIELD, METHOD, PARAMETER, TYPE, CONSTRUCTOR})
public #interface LoginProviderProducer {
}
An annotation to specify the Login Provider implementation:
#Retention(RetentionPolicy.RUNTIME)
#Target({FIELD, METHOD, TYPE})
public #interface LoginProviderType {
LoginProviderName value();
public enum LoginProviderName {
PROVIDER1(LoginProvider1Impl.class),
PROVIDER2(LoginProvider2Impl.class);
private Class<? extends LoginProvider> loginProviderType;
private LoginProviderName(Class<? extends LoginProvider> loginProviderType) {
this.loginProviderType = loginProviderType;
}
public Class<? extends LoginProvider> getLoginProviderType() {
return loginProviderType;
}
}
}
And a factory:
#ApplicationScoped
public class LoginProviderFactory {
#Produces
#LoginProviderProducer
public LoginProvider createLoginProvider(#Any Instance<LoginProvider> instance, InjectionPoint injectionPoint) {
Annotated annotated = injectionPoint.getAnnotated();
LoginProviderType loginProviderTypeAnnotation = annotated.getAnnotation(LoginProviderType.class);
Class<? extends LoginProvider> loginProviderType = loginProviderTypeAnnotation.value().getLoginProviderType();
return instance.select(loginProviderType).get();
}
}
Finally I have a helper in which the login providers are injected:
#ApplicationScoped
public class LoginProviderHelperImpl implements LoginProviderHelper {
#Inject
#LoginProviderProducer
#LoginProviderType(LoginProviderName.PROVIDER1)
private LoginProvider provider1;
#Inject
#LoginProviderProducer
#LoginProviderType(LoginProviderName.PROVIDER2)
private LoginProvider provider2;
...
}
I get this error when the helper is used:
org.jboss.weld.exceptions.UnsatisfiedResolutionException:
WELD-001308: Unable to resolve any beans for Type:
class com.xxx.LoginProvider1Impl; Qualifiers: [#javax.enterprise.inject.Any()]
Thanks!
I added the annotation #ApplicationScoped to LoginProvider1Impl and LoginProvider2Impl.
Another way to make it work is to modify the factory method:
#ApplicationScoped
public class LoginProviderFactory {
#Produces
#LoginProviderProducer
public LoginProvider createLoginProvider(#Any Instance<LoginProvider> instance, InjectionPoint injectionPoint) {
Annotated annotated = injectionPoint.getAnnotated();
LoginProviderType loginProviderTypeAnnotation = annotated.getAnnotation(LoginProviderType.class);
Class<? extends LoginProvider> loginProviderType = loginProviderTypeAnnotation.value().getLoginProviderType();
//return instance.select(loginProviderType).get();
if (loginProviderType == LoginProvider1Impl.class) {
return new LoginProvider1Impl();
} else if (loginProviderType == LoginProvider2Impl.class) {
return new LoginProvider2Impl();
} else
return null;
}
}

Categories

Resources