Can I have Spring's #Component on enum? - java

I'm using Spring 3.0.x and following the enum singleton pattern for one of my implementatons.
public enum Person implements Nameable {
INSTANCE;
public String getName(){
// return name somehow (Having a variable but omitted for brevity)
}
}
Recently we started to collecting those types via Spring so I need to add #Component to my class.
#Component
public enum Person implements Nameable {
INSTANCE;
public String getName(){
// return name somehow (Having a variable but omitted for brevity)
}
}
and collecting method is
#Autowired
public void collectNameables(List<Nameable> all){
// do something
}
After doing this I observed failures and cause was Spring cannot intialize enum classes (which is understandable).
My question is -
Is there any other way usign which I can mark my enum classes as a bean ?
Or i need to change my implementation?

If you really need to use enum-based singleton (despite the fact that Spring beans are singletons by default), you need to use some other way to register that bean in the Spring context. For example, you can use XML configuration:
<util:constant static-field="...Person.INSTANCE"/>
or implement a FactoryBean:
#Component
public class PersonFactory implements FactoryBean<Person> {
public Person getObject() throws Exception {
return Person.INSTANCE;
}
public Class<?> getObjectType() {
return Person.class;
}
public boolean isSingleton() {
return true;
}
}

You won't need to use the enum singleton pattern if you're using Spring to manage dependency injection. You can change your Person to a normal class. Spring will use the default scope of singleton, so all Spring-injected objects will get the same instance.

Related

Autowire Spring Bean into interface for default method

I need to add a default method to an interface some classes implement, but my IDE complains (bean may not have been initialized).
Code would be something like this:
public interface IValidator {
MyValidationBean beanToBeAutowired;
...
default Boolean doSomeNewValidations(){
return beanToBeAutowired.doSomeNewValidations();
}
}
Is it just that autowiring into interfaces is not allowed or there's something wrong with the code?
Using #Component on the interface doesn't make any difference.
I'd rather keep this design instead of using an abstract class.
Adding a Variable into interface is not possible in Java. It will be by default a public static final constant. So you have to do either the following:
MyValidationBean beanToBeAutowired = new MyValidationBeanImpl();
or the following:
MyValidationBean beanToBeAutowired();
default Boolean doSomeNewValidations(){
return beanToBeAutowired().doSomeNewValidations();
}
And you can override the beanToBeAutowired method in the implementation class.
i can think of solution as below -
public interface IValidator {
public Service getBeanToBeAutowired();
default Boolean doSomeNewValidations(){
return getBeanToBeAutowired().doSomeNewValidations();
}
}
public class ValidatorClass implements IValidator {
#Autowire private Service service;
#Override
public Service getBeanToBeAutowired() {
return service;
}
}
Just an idea, send validation bean to interface as parameter;
public interface IValidator {
default Boolean doSomeNewValidations(MyValidationBean beanToBeAutowired){
return beanToBeAutowired.doSomeNewValidations();
}
}
Your callerClass;
public class CallerClass implements IValidator{
#Autowired
MyValidationBean beanToBeAutowired;
...
doSomeNewValidations(beanToBeAutowired);
}

enum returning spring beans dynamically

Is it possible for an enum to return different spring beans for different values?
If possible, we can create an enum holding different values, and we can return different type of spring component for a different value.
I wanted to achieve something like this:
public enum MyFactory {
BEAN1 {
#Autowired
Bean1 bean1;
#Override
public MyBean getMyBean() {
return bean1;
}
},
BEAN2 {
#Autowired
Bean1 bean2;
#Override
public MyBean getMyBean() {
return bean2;
}
};
public abstract MyBean getMyBean();
}
Thanks,
Java enums are designed to be constant values.
But to achieve you requirement, you should set the enum states after their creation or passing as parameter the ApplicationContext to the enum method or to an enum method initialization.
It defeats the enum purpose : constant values.
I think that it is make more sense to define a bean class that provides beans defined once and without way to change them.
#Component
public class MyEnumClass{
#Autowired
private Value valueA;
#Autowired
private Value valueB;
public Value getValueA(){
return valueA;
}
public Value getValueB(){
return valueB;
}
...
}
Where Value class is preferably not mutable.
Do not use enums to conditionally inject.
Spring is more than capable of doing that on its own.
This runs somewhat counter to the whole benefit of dependency injection; so long as your components define a common interface between them, you can inject whichever one you want at will without the need for (self-managed) conditions.
Supposing that you had components AComponent and BComponent. They are related and share a common-enough interface that it makes sense to codify it as an interface. You can then define this.
public interface Component {
Integer generateValue(String foo, List<Integer> bar);
}
public class AComponent implements Component {}
public class BComponent implements Component {}
Then, you can inject it in at will:
private Component component;
#Autowired
public MyService(#Qualifier("bComponent") Component component) {
this.component = component;
}
Spring is perfectly capable of managing the dynamic wiring for you here.
Alternatively, if these components are completely unrelated to one another, then you'll have to wire them in individually anyway as opposed to dynamically selecting one based on your needs.

Spring: Is it possible to have a duplicate constructor with different qualifier while autowiring?

I am trying to autowire a member in a class using the constructor.
#Component
public class MyClass {
private ClassA myMember;
#Autowire
public MyClass(ClassA objectA) {
myMember = objectA;
}
}
If I have multiple sources that create beans of ClassA, is it possible to have a duplicate constructor definition that instantiates based on the bean that was autowired into this class?
I want to do something like this:
#Component
public class MyClass {
private ClassA myMember;
#Autowire
public MyClass(#Qualifier ("qualifierA") ClassA objectA) {
myMember = objectA;
}
#Autowire
public MyClass(#Qualifier ("qualifierB") ClassA objectB) {
myMember = objectB;
}
}
I tried using #Qualifier this way, but it didn't work.
Is it possible to do what I'm trying to do, with Spring? How can I disambiguate based on the name (qualifierA) or (qualifierB), if the bean definition is like:
#Bean (name = "qualifierA")
public ClassA getQualifierA() {
...
}
#Bean (name = "qualifierB")
public ClassA getQualifierB() {
...
}
You can't have two constructors with the exact same signature in a single class in Java. Nor any other programming language I've ever encountered. You might use method-injection instead, with two methods (named differently, of course), mark them as #Autowired(required = false) and use the proper #Qualifier(...) to specify the instance you want to inject. You might want to handle the case when both instances are present in the spring context, so no unexpected things happen.
The short answer is: no, that is not possible. In Java you cannot have two constructors with exactly the same signature. And also, you can assign only one value to your "myMember".
However, what are you trying to accomplish here? It seems that in some occasions MyClass needs to use "objectA" and in other occasions, you need "objectB".
For these scenarios, you should not use autowiring (you can't), but simply use explicit wiring:
#Bean
MyClass myObject() {
return new MyClass(qualifierA());
}

Guice inject based on annotation value

I would like to use goolge/guice inject a value based on a class i provide with the annotation.
AutoConfig annotation
#BindingAnnotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.PARAMETER, ElementType.FIELD })
public #interface AutoConfig {
// default null not possible
Class<? extends Provider<? extends ConfigLoader<?>>> provider() default XMLAutoConfigProvider.class;
}
This is my annotation which allows configuring the type of config, that should be used for the annotated fields.
Usecase:
#AutoConfig()
ConfigLoader<?> defaultConfig;
#AutoConfig(provider = JsonConfigProvider)
ConfigLoader<?> jsonConfig;
I want to have two configs, one default/xml one and a json one. They will probably never occur in the same class at the same time. But i don't know when the one or the other is used. I used the approach with a class because they are provided by some dependencies/libs and this annotation will be used for some (plugable) submodules.
MyGuiceModule
public class MyGuiceModule extends AbstractModule {
#Override
protected void configure() {
bind(new TypeLiteral<ConfigLoader<?>>() {})
.annotatedWith(AutoConfig.class)
.toProvider(autoConfig.provider());
}
}
This the critical part, i just cannot imagine how to implement it.
So basically i just want to use the provider class specified in the annotation.
Its not necessary to use the provider class here too. Because autoConfig.provider().newInstance() is basically all i need. (I need to use a setter on the new instance but thats all i want to do at this place)
To sum it up all i really want to do is push the annotation (or its values to the provider) either using the get(AutoConfig autoConfig) or in the constructor.
Currently i only use the constructor to inject the configFile value i want to set on the newly generated config instance.
If you know that #AutoConfig(provider = JsonConfigProvider) ConfigLoader<?> jsonConfig is going to return you exactly the results of jsonConfigProvider.get(), and JsonConfigProvider obviously has a public parameterless constructor for newInstance to work, why wouldn't you just ask for a JsonConfigProvider in the first place?
Fundamentally Guice is just a Map<Key, Provider> with fancy wrapping. The bad news is that this makes variable bindings like "bind Foo<T> for all T" impossible to express concisely, and that includes your "bind #Annotation(T) Foo for all T". The good news is that you still have two options.
Bind each provider separately
Though you can't inspect annotations during provision (or tell Guice to do so for you), Guice will compare annotations using their equals methods if you bind an annotation instance rather than an annotation class (the way you would with Names.named("some-name")). This means that you can bind a ConfigLoader<?> with each expected annotation in a Module. Of course, this also means you'll have to have a list of possible ConfigLoader Providers available at configuration time, but they have to be compile-time constants anyway if you're using them as annotation parameters.
This solution works with constructor injection as well, but for fields you'll need both #Inject and #AutoConfig(...), and AutoConfig will need to keep its #BindingAnnotation meta-annotation.
To do this, you're going to have to write an implementation of your annotation, the way Guice does with NamedImpl. Note that the implementations of equals and hashCode must match the ones Java provides in java.lang.Annotation. Then it's just a matter of (redundantly) binding like this:
for(Class<ConfigLoader<?>> clazz : loaders) {
bind(ConfigLoader.class).annotatedWith(new AutoConfigImpl(clazz))
.toProvider(clazz);
}
The definition of equals is up to you, which means you can (and should) bind #AutoConfig(ConfigEnum.JSON) and keep the Guice bindings in your modules rather than specifying your requested implementation all over your codebase.
Use custom injections
You can also use custom injections to search your injected types for custom annotations like #AutoConfig. At this point, you'd be using Guice as a platform to interpret #AutoConfig instead of #Inject, which means that constructor injection won't work but that you can control your injection based on the injected instance, field name, field annotation, annotation parameters, or any combination thereof. If you choose this style, you can drop #BindingAnnotation from AutoConfig.
Use the example in the wiki article linked above as your template, but at minimum you'll need to:
Use bindListener on Binder or AbstractModule to match types that need this custom injection.
In the TypeListener you bind, search injected types for #AutoConfig-annotated fields, and if they have any matching methods then bind those matching methods to a MembersInjector or InjectionListener. You'll probably want to tease the class literal out of the annotation instance here, and pass in the Field and Class as constructor arguments to the MembersInjector/InjectionListener.
In the MembersInjector or InjectionListener you write, instantiate the provider and set the field to the instance the provider provides.
This is a very powerful feature, which would futher allow you to--for instance--automatically provide the configuration based on which instance you're injecting into or based on the name of the field. However, use it carefully and document it heavily, because it may be counter-intuitive to your coworkers that Guice is providing for an annotation other than #Inject. Also bear in mind that this won't work for constructor injection, so refactoring from field injection to constructor injection will cause Guice to complain that it's missing a required binding to instantiate the class.
I had a similar problem. I wanted to use a custom annotation that receives a enum param to choose the implementation. After a lot of research, debug and testing, I came to the following solution:
//enum to define authentication types
public enum AuthType {
Ldap, Saml
}
//custom annotation to be used in injection
#Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
#BindingAnnotation
public #interface Auth {
AuthType value();
}
//defintion of authenticator
public interface Authenticator {
public void doSomehting();
}
//Authenticator implementations
public class LdapAuthenticator implements Authenticator {
#Override
public void doSomehting() {
// doing ldap stuff
}
}
public class SamlAuthenticator implements Authenticator {
#Override
public void doSomehting() {
// doing saml stuff
}
}
public class MyModule extends AbstractModule {
// annotate fields to bind to implementations
private #Auth(AuthType.Ldap) Authenticator ldap;
private #Auth(AuthType.Saml) Authenticator saml;
#Override
protected void configure() {
//bind the implementation to the annotation from field
bindAnnotated("ldap", LdapAuthenticator.class);
bindAnnotated("saml", SamlAuthenticator.class);
}
private void bindAnnotated(String fieldName, Class<? extends Authenticator> implementation) {
try {
//get the annotation from fields, then bind it to implementation
Annotation ann = MyModule.class.getDeclaredField(fieldName).getAnnotation(Auth.class);
bind(Authenticator.class).annotatedWith(ann).to(implementation);
} catch (NoSuchFieldException | SecurityException e) {
throw new RuntimeException(e);
}
}
}
//usage: add #Auth(<AuthType>) to the dependency
public class ClientClass {
private Authenticator authenticator;
#Inject
public ClientClass(#Auth(AuthType.Ldap) Authenticator authenticator) {
this.authenticator = authenticator;
}
}
Check the documentation of Binder
I tested the Jeff Bowman solution, but it apparently works only binding to providers
As a BindingAnnotations#binding-annotations-with-attributes states equals() and hashCode() should be properly implemented. So given that there is MyAnnotation
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER})
public #interface MyAnnotation {
SomeEnum value() default SomeEnum.A;
}
which is used to specify SomeInterface implementation(SomeDefault and SomeOther), SomeModule class could look like
public class SomeModule extends AbstractModule {
#Override
protected void configure() {
bind(Key.get(SomeInterface.class, createAnnotationClass(A))).to(SomeDefault.class);
// more common binding expresion
bind(SomeInterface.class).annotatedWith(createAnnotationClass(B)).to(SomeDefault.class);
}
private Annotation createAnnotationClass(SomeEnum someEnum) {
return new MyAnnotation() {
#Override
public SomeEnum value() {
return someEnum;
}
#Override
public Class<? extends Annotation> annotationType() {
return MyAnnotation.class;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyAnnotationCl myAnnoCl = (MyAnnotationCl) o;
return A == myAnnoCl.getValue();
}
#Override
public int hashCode() {
// from java annotation documentation
return (127 * "value".hashCode()) ^ value().hashCode();
}
};
}
}
Then annotation could be used as follows:
public class DoSomethingWithSomething {
private final SomeInterface someImplementation;
#Inject
public DoSomethingWithSomething(
#MyAnnotation SomeInterface someDefault
// #MyAnnotation(A) SomeInterface someDefault
// #MyAnnotation(B) SomeInterface someOther
) {
this.someImplementation = someDefault;
}
}

Inject cached instances using cdi

I want expose instances managed by an external framework to CDI applications using #Inject. These instances must be provided this other framework since their lifecycle is based on various caching strategies.
Ex: same instance is visible within same thread scope, might live across many request scopes, session scope is not applicable. Seems I need to define a new scope targeting these kind of instances?
What is the best way to do this? An extension, is it possible with producer methods?
I almost got it to work with producer methods using the following:
#Inject
#CustomInject
FwObject obj;
#Produces
#CustomInject
FwObject createConfig(InjectionPoint p) {
return (FwObject) ctx.get((Class<?>) p.getType());
}
But this force me to be explicit about the type produced which is not possible since there is no common framework interface.
Any help appreciated.
Maybe with producer methods, all depends on what you need, but an extension is probably the best way to go. If you need to go with a new scope (if you're using JSF the Conversation scope may work) you will certainly need to create an extension.
I think I solved it by creating a custom scope. The following article was really helpful:
http://www.verborgh.be/articles/2010/01/06/porting-the-viewscoped-jsf-annotation-to-cdi/
This is a very brief description of how I solved it.
Create custom scope annotation.
import javax.enterprise.context.NormalScope;
#Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD })
#NormalScope
public #interface CustomScope {
}
Create custom context.
import javax.enterprise.context.spi.Context;
public class CustomContext implements Context {
private MyFw myFw = .... ;
#Override
public Class<? extends Annotation> getScope() {
return CustomScope.class;
}
#Override
public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext) {
Bean bean = (Bean) contextual;
return (T) myFw.get(bean.getBeanClass());
}
#Override
public <T> T get(Contextual<T> contextual) {
Bean bean = (Bean) contextual;
return (T) myFw.get(bean.getBeanClass());
}
#Override
public boolean isActive() {
return true;
}
}
Create extension and register context.
import javax.enterprise.inject.spi.Extension;
public class CustomContextExtension implements Extension {
public void afterBeanDiscovery(#Observes AfterBeanDiscovery event, BeanManager manager) {
event.addContext(new CustomContext());
}
}
Register extension.
Add CustomContextExtension to META-INF/javax.enterprise.inject.spi.Extension
Add CustomScope to framework object.
#CustomScope
public class FwObject { ... }
Inject FwObject using #Inject where needed.
public class MyService {
#Inject
FwObject obj;
}

Categories

Resources