Spring autowire priority with more than two layers - java

How am I able to override an autowired bean more than once? Consider this 3-layer construct:
Library 1 defines an interface and a default implementation.
public interface Foo {}
#Component
public class FooImpl1 implements Foo {}
Throughout all 3 layers the interface will be used for autowiring.
#Autowired
private Foo foo;
Library 2 depends on library 1 and provides its own implementation which has to override the first one, i.e. be autowired into existing code of library 1.
#Component
public class FooImpl2 implements Foo {}
The actual application depends on library 2 and also provides another implementation that has to override the former one.
#Component
public class FooImpl3 implements Foo {}
How do I configure this? If there were only two layers, I could use #Primary but this doesn't work with more than two layers because #Primary can't be overriden again. Is there a more fine-grained way of prioritizing beans that I'm overlooking or can I accomplish a second override using a #Configuration or do you simply can not overide a bean more than once and I have to resort to some kind of static helper like FooProvider.getFoo() instead of autowiring?

Maybe is not the case, however when you have more than one implementation and want to establish some kind of ordination, existe the annotation #order
#order(3)
#Component
public class FooImpl1 implements Foo {}
#order(1)
#Component
public class FooImpl2 implements Foo {}
#order(2)
#Component
public class FooImpl3 implements Foo {}
public class PrintFoo {
List<Foo> fooList;
#autowired
public PrintFoo() {
for(Foo fooImpl: fooList) {
System.out.println(fooImpl.getClass().getSimpleName());
}
}
the output should be:
FooImpl2
FooImpl3
FooImpl1

Related

Parameter 0 of constructor in ..... Spring Boot

I have a problem when launch my app. Could somebody help me to solve this issue?
Parameter 0 of constructor in com.journaldev.elasticsearch.service.BookServiceImpl required a bean of type 'com.journaldev.elasticsearch.dao.search.BookRepositorySearch' that could not be found.
Action:
Consider defining a bean of type 'com.journaldev.elasticsearch.dao.search.BookRepositorySearch' in your configuration.
GenericRepository
public interface GenericRepository<T, K> {
Map<String, Object> get(final K id);
}
GenericRepositoryImpl
public class GenericRepositoryImpl<T, K extends Serializable> implements GenericRepository<T, K> {
private RestHighLevelClient restHighLevelClient;
private ObjectMapper objectMapper;
public GenericRepositoryImpl(ObjectMapper objectMapper, RestHighLevelClient restHighLevelClient) {
this.objectMapper = objectMapper;
this.restHighLevelClient = restHighLevelClient;
}
#Override
public Map<String, Object> get(K id) {
return null;
}
}
BookRepositorySearch
#Component
public interface BookRepositorySearch extends GenericRepository<Book, Long> {}
BookService
public interface BookService {
Map<String, Object> get(final Long id);
}
BookServiceImpl
#Service
public class BookServiceImpl implements BookService {
private final BookRepositorySearch bookRepositorySearch;
public BookServiceImpl(BookRepositorySearch bookRepositorySearch) {
this.bookRepositorySearch = bookRepositorySearch;
}
#Override
public Map<String, Object> get(Long id) {
return null;
}
}
From your previous comments, looks like you want to keep BookRepositorySearch as an interface. If that's the case, you need to create a concrete instance of that interface and put #Component on that.
You don't need #Component on your interface declaration and you can't extend a class in an interface.
public interface BookRepositorySearch {}
Create a concrete type that implements the interface and extends extends GenericRepository<Book, Long> you want to autowire and put #Component on it:
#Component
public class BookRepositorySearchImpl
implements BookRepositorySearch
extends GenericRepository<Book, Long>
{}
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-class-ctor
Instantiation with a constructor
When you create a bean by the
constructor approach, all normal classes are usable by and compatible
with Spring. That is, the class being developed does not need to
implement any specific interfaces or to be coded in a specific
fashion. Simply specifying the bean class should suffice. However,
depending on what type of IoC you use for that specific bean, you may
need a default (empty) constructor.
The Spring IoC container can manage virtually any class you want it to
manage; it is not limited to managing true JavaBeans. Most Spring
users prefer actual JavaBeans with only a default (no-argument)
constructor and appropriate setters and getters modeled after the
properties in the container. You can also have more exotic
non-bean-style classes in your container. If, for example, you need to
use a legacy connection pool that absolutely does not adhere to the
JavaBean specification, Spring can manage it as well.
I solved it with this configuration.
Look for #EnableAutoConfiguration in your Configuration file.
#Configuration
#EnableJpaRepositories(basePackages = "com.akog02.repostories")
#EntityScan(basePackages = "com.akog02.domain")
#EnableTransactionManagement
#EnableAutoConfiguration
public class WebConfiguration {
}
First of, You need to "tell" spring what to pass as a parameter. The simplest option is the one mentioned by #Berger in a comment. If for some reason that is not a good approach for you (eg. BookRepositorySearch is not a spring managed bean), you can make a java config file with some more logic:
#Configuration
public class Config {
// you can use #Autowired here
#Bean
public BookService bookService() {
return new BookServiceImpl(--pass your parameter here, get it however you want--)
}
}
edit:
Apparently Spring doesn't require #Autowired anymore (thanks #Mark Rotteveel).
So the problem is that spring doesn't have an instance of your class. The reason for that is (I think) that you use a class parameter instead of an interface. If You just create a marker interface that BookRepositorySearch implements and use that as an argument instead of the actual inplementation, I would expect it to work.
Another solution is what I wrote above already, but for the BookRepositorySearch class.
#Configuration
public class Config {
// you can use #Autowired here
#Bean
public BookRepositorySearch bookRepositorySearch () {
return new BookRepositorySearch();
}
}
This way Spring will have it's beloved instance ;)
How do you inherit a class as an interface?
#Component
public interface BookRepositorySearch extends GenericRepository<Book, Long> {}
Change this interface with a class then try again.
#Component
public class BookRepositorySearch extends GenericRepository<Book, Long> {}
You must add #EnableJpaRepositories("org.tennis.Tennnis.dao") in Prin

#Autowired attribute that needs a parameter to be instantiated

I'm new to spring boot, and I need to know how to use #autowired in an attribute that needs parameters to be instantiated.
Please bear in mind the following illustrative situation. It would be something like this:
public class MyClassA{
public SpecificClass myMethod(){
//some logic
}
}
public class MyClassB extends MyClassA{
#Autowired
MyComponent myComponent (myMethod()); //here is my doubt, because my component needs a parameter to be built
}
#Component
public class MyComponent{
public MyComponent(SpecificClass foo){
this.foo=foo;
}
That's not really proper design if your intention is to work with dependency injection. There shouldn't be a direct dependency to the superclass' method like that. Injecting the dependencies indirectly as you're supposed to do would result in something like the following
public class MyClassB extends MyClassA {
#Autowired
private MyComponent myComponent;
}
#Configuration
public class SomeConfig {
#Bean
#Autowired
public MyComponent createComponent(SpecificClass foo) {
// SpecificClass is also injected, providing another layer of indirection
return new MyComponent(foo);
}
}
#Autowired only tells Spring to inject a component into a constructor, field, or method parameter. The injected component is instantiated by the bean container before that. I assume what you are looking for is a way to create MyComponent in such a way that it also receives a Spring Bean.
In your example you could achieve this with the following
#Configuration
public class MyClassA{
#Bean //the bean would have the name 'myMethod', so maybe change that
public SpecificClass myMethod(){
//some logic
}
}
//this needs to be a component, service, ...
#Component
public class MyClassB {
#Autowired
MyComponent myComponent;
}
#Component
public class MyComponent{
#Autowired //Spring wires the Bean 'myMethod' in here, autowired is not needed in the latest Spring Versions
public MyComponent(SpecificClass foo){
this.foo=foo;
}
}
This is a basic Spring question, and not specific to Spring Boot. To better understand wiring you can take a look at the Spring 4 Framework Reference Documentation.

Create instance of call with autowire

I have to create a instance of a class, that have autowired elements, for test.
public class MyClass extends SomeOtherClass {
#Autowired
public MyClass(OtherClass1 one, OtherClass2 two){
super(one, two)
}
}
How can i in code create instance of this class, with the arguments wired in though spring?
Your test can be made Spring-aware if you use the SpringJUnit4ClassRunner to read in your Spring Context to be used in the test. For instance:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"the-config.xml"})
public final class MyClassTests {
#Autowired
private MyClass testee;
#Test
public void testSomething() {
assertThat(testee).doesSomethingExpected();
}
}
Note that you should reuse as much of your production config as possible and not create a parallel Spring Context config that mirrors it.
Instead of passing the other elements in as constructor arguments, you Autowire them as properties. Spring will then inject the objects.
public class MyClass extends SomeOtherClass {
#Autowired
private OtherClass1 one;
#Autowired
private OtherClass2 two
public MyClass(){
super(one, two)
}
}
Edit: Based on http://www.mkyong.com/spring/spring-auto-wiring-beans-with-autowired-annotation/, adding #Autowired to the constructor is also valid.
If you want to Autowire MyClass, you must annotate it with #Component or a similar annotation such as #Service.
#Component
public class MyClass extends SomeOtherClass
Then, you can use it in other classes
public class ClassThatUsesMyClass {
#Autowire
private MyClass myClass;
}

Can I qualify an injection based on an Enum?

We have some legacy code that I'm trying to figure out a way to clean up. One solution I've thought of is that perhaps I can inject a custom handler based on the enum value I'm given. Can I qualify an Injection based on an enum? I'm thinking something like this maybe (pseudocode)
#Service(MyEnum.MYVALUE, MyEnum.MYOTHERVALUE) // produces a handler given these enums
public class MyHandler { ... }
#Service(MyEnum.ANOTHERVALUE)
public class AnotherHandler {... }
// .... some mystical way of telling spring what my current enum context is so I can get the right handler
I don't think this will work.
First of all, the value for a #Service is a String, not an Enum[]. And, it just suggests a name for the bean that gets registered for that service class.
Instead, what I think you may want is to use is #Qualifier. So, you can either have something like:
#Service
#Qualifier("foo")
public class FooHandler implements IHandler { ... }
#Service
#Qualifier("bar")
public class BarHandler implements IHandler { ... }
#Component
public class MyThing {
#Autowired #Qualifier("foo")
private IHandler handler;
...
}
Or, you can create your own custom qualifier annotation, like:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
#Qualifier
public #interface MyQualifier { ... }
#Service
#MyQualifier
public class FooHandler implements IHandler { ... }
#Component
public class MyClass {
#Autowired #MyQualifier
private IHandler handler;
...
}
See Fine-tuning annotation-based autowiring with qualifiers for more details.
Well, you can try to create your own validation (yes, even with enums) and then provide your own BeanPostProcessor that will do the job and inject values to your annotated fields.
There are a lot of Spring BeanPostProcessors so you can see how it is done just by browsing Spring's sources.

EJB3 instantiation

I got some propably trivial question.
If I got defined EJB3+ interface, lets say it's remote like that:
#Remote
public class FooServiceRemote {
void foo();
}
and one implementation
#Stateless
public class FooService implements FooServiceRemote {
void foo() { ... }
}
How does the application server by default resolves what implementation to use (and call via proxy) if he knows only #EJB annotation for dependency injection like on interface:
public class SomeClass {
#EJB
private FooServiceRemote fooService;
}
Is it done by reflection (shortening name of interface)? Or he scans possible implementations of such interface, choosing one. Or.. ? And what if I want to create more implementations of one interface, is it possible and how to specify what implementation should be instantiated (maybe it is possible via some argument of annotation).
Thanks:-)
In the rare case that you need to have two beans implementing the same interface (not a good practice), you can name them and choose which one you want by name.
#Stateless(name="FooService1")
public class FooService1 implements FooService { }
#Stateless(name="FooService2")
public class FooService2 implements FooService { }
public class SomeClass {
#EJB(beanName="FooService1")
private FooService fooService;
}
Other possible approaches are looking it up using JNDI or the mappedName property.
See the javadoc for EJB annotation here: http://download.oracle.com/javaee/6/api/javax/ejb/EJB.html
Just a fix,
#Remote
interface FooServiceRemote {
void foo();
}
#Stateless
public class FooService implements FooServiceRemote {
void foo() { ... }
}
With this, application server knows which classes implements the specified interface.
If you have two classes, you must specifify which class do you need.

Categories

Resources