Inherit #Component in Spring - java

I have a hierarchy of classes. I want mark them with #Component. I'm trying to mark only the parent class. I expect Spring will mean childs as components too. But it doesn't happen.
I tried to use custom #InheritedComponent annotation like described here. It doesn't work.
I wrote an unit test. It fails with: "No qualifying bean of type 'bean.inherit.component.Child' available". Spring version is 4.3.7.
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Component
#Inherited
public #interface InheritedComponent {}
#InheritedComponent
class Parent { }
class Child extends Parent {
}
#Configuration
#ComponentScan(basePackages = "bean.inherit.component")
class Config {
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Config.class)
public class InheritComponentTest {
#Autowired
private Child child;
#Test
public void name() throws Exception {
assertNotNull(child);
}
}

You can use a ComponentScan.Filter for this.
#ComponentScan(basePackages = "bean.inherit.component",
includeFilters = #ComponentScan.Filter(InheritedComponent.class))
This will allow Spring to autowire your Child, without you having to attach an annotation to Child directly.

To answer the question posed under the accepted answers "How do do in XML"
This is taken from Spring in Action with a trivial example by me.
Annotate the base class with component, add a qualifier
#Component
#InstrumentQualifier("TUBA")
public class Instrument {}
Qualify the subclass - NOTE- No need for #component
#InstrumentQualifier("GUITAR")
public class Guitar extends Instrument {}
Inject it like so
#Autowired
#InstrumentQualifier("GUITAR")
public void setInstrument(Instrument instrument) {this.instrument =instrument;}
In context.xml, add an include filter for assignable type
<context:component-scan base-package="some.pkg">
<context:include-filter type="assignable" expression="some.pkg.Instrument"/>
</context:component-scan>
The type and expression work as define the strategy here.
You could also use the type as "annotation" and the expression would be the path to where the custom qualifier is defined to achieve the same goal.

Related

Is Controller inheritance in Spring Boot good practice? [duplicate]

Can I somehow group a set of annotations on an abstract class, and every class that extends this class has automatically assigned these annotations?
At least the following does not work:
#Service
#Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
class AbstractService
class PersonService extends AbstractService {
#Autowired //will not work due to missing qualifier annotation
private PersonDao dao;
}
The answer is: no
Java annotations are not inherited unless the annotation type has the #Inherited meta-annotation on it: https://docs.oracle.com/javase/7/docs/api/java/lang/annotation/Inherited.html.
Spring's #Component annotation does not have #Inherited on it, so you will need to put the annotation on each component class. #Service, #Controller and #Repository neither.
The short answer is: with the annotations that you mentioned in your example, no.
The long answer: there is a meta-annotation called java.lang.annotation.Inherited. If an annotation itself is annotated with this annotation, then when a class is annotated with it, its subclasses are also automatically annotated with it by implication.
However, as you can see in the spring source code, the #Service and #Scope annotation are not themselves annotated with #Inherited, so the presence of #Service and #Scope on a class is not inherited by its subclasses.
Maybe this is something that can be fixed in Spring.
I have this piece of code in my project and it works just fine, although it's not annotated as Service:
public abstract class AbstractDataAccessService {
#Autowired
protected GenericDao genericDao;
}
and
#Component
public class ActorService extends AbstractDataAccessService {
// you can use genericDao here
}
So you don't need to put annotation on your abstract class but even if you do you will still have to put annotation on all subclass as #Component or #Service annotations are not inherited.

Is it possible to exclude a class when specifying a scan package in spring boot? [duplicate]

I have a component that I want to exclude from a #ComponentScan in a particular #Configuration:
#Component("foo") class Foo {
...
}
Otherwise, it seems to clash with some other class in my project. I don't fully understand the collision, but if I comment out the #Component annotation, things work like I want them to. But other projects that rely on this library expect this class to be managed by Spring, so I want to skip it only in my project.
I tried using #ComponentScan.Filter:
#Configuration
#EnableSpringConfigured
#ComponentScan(basePackages = {"com.example"}, excludeFilters={
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class)})
public class MySpringConfiguration {}
but it doesn't appear to work. If I try using FilterType.ASSIGNABLE_TYPE, I get a strange error about being unable to load some seemingly random class:
Caused by: java.io.FileNotFoundException: class path resource [junit/framework/TestCase.class] cannot be opened because it does not exist
I also tried using type=FilterType.CUSTOM as following:
class ExcludeFooFilter implements TypeFilter {
#Override
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
return metadataReader.getClass() == Foo.class;
}
}
#Configuration #EnableSpringConfigured
#ComponentScan(basePackages = {"com.example"}, excludeFilters={
#ComponentScan.Filter(type=FilterType.CUSTOM, value=ExcludeFooFilter.class)})
public class MySpringConfiguration {}
But that doesn't seem to exclude the component from the scan like I want.
How do I exclude it?
The configuration seem alright, except that you should use excludeFilters instead of excludes:
#Configuration #EnableSpringConfigured
#ComponentScan(basePackages = {"com.example"}, excludeFilters={
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class)})
public class MySpringConfiguration {}
Using explicit types in scan filters is ugly for me. I believe more elegant approach is to create own marker annotation:
#Retention(RetentionPolicy.RUNTIME)
public #interface IgnoreDuringScan {
}
Mark component that should be excluded with it:
#Component("foo")
#IgnoreDuringScan
class Foo {
...
}
And exclude this annotation from your component scan:
#ComponentScan(excludeFilters = #Filter(IgnoreDuringScan.class))
public class MySpringConfiguration {}
Another approach is to use new conditional annotations.
Since plain Spring 4 you can use #Conditional annotation:
#Component("foo")
#Conditional(FooCondition.class)
class Foo {
...
}
and define conditional logic for registering Foo component:
public class FooCondition implements Condition{
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// return [your conditional logic]
}
}
Conditional logic can be based on context, because you have access to bean factory. For Example when "Bar" component is not registered as bean:
return !context.getBeanFactory().containsBean(Bar.class.getSimpleName());
With Spring Boot (should be used for EVERY new Spring project), you can use these conditional annotations:
#ConditionalOnBean
#ConditionalOnClass
#ConditionalOnExpression
#ConditionalOnJava
#ConditionalOnMissingBean
#ConditionalOnMissingClass
#ConditionalOnNotWebApplication
#ConditionalOnProperty
#ConditionalOnResource
#ConditionalOnWebApplication
You can avoid Condition class creation this way. Refer to Spring Boot docs for more detail.
In case you need to define two or more excludeFilters criteria, you have to use the array.
For instances in this section of code I want to exclude all the classes in the org.xxx.yyy package and another specific class, MyClassToExclude
#ComponentScan(
excludeFilters = {
#ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.xxx.yyy.*"),
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = MyClassToExclude.class) })
I had an issue when using #Configuration, #EnableAutoConfiguration and #ComponentScan while trying to exclude specific configuration classes, the thing is it didn't work!
Eventually I solved the problem by using #SpringBootApplication, which according to Spring documentation does the same functionality as the three above in one annotation.
Another Tip is to try first without refining your package scan (without the basePackages filter).
#SpringBootApplication(exclude= {Foo.class})
public class MySpringConfiguration {}
In case of excluding test component or test configuration, Spring Boot 1.4 introduced new testing annotations #TestComponent and #TestConfiguration.
I needed to exclude an auditing #Aspect #Component from the app context but only for a few test classes. I ended up using #Profile("audit") on the aspect class; including the profile for normal operations but excluding it (don't put it in #ActiveProfiles) on the specific test classes.
Your custom filer has wrong logic.
Because metadataReader.getClass() is always not equal to Foo.class, metadataReader.getClass() return "org.springframework.core.type.classreading.MetadataReader". It'd use className for comparation.
public class CustomFilter implements TypeFilter {
#Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return metadataReader.getClassMetadata().getClassName().equals(Foo.class.getName());
}
}
#ComponentScan.Filter is belong every #ComponentScan and only filter compononets from the specific scan path.
If you find it doesn't work, you could check whether there's another #ComponentScan or <context:component-scan base-package="xxx"/> in your code

How to properly inject #Autowired between Spring Boot classes?

I have a classA which implements an interfaceA, with a methodA, then I have a classB in which I call classA with an #Autowired to be able to use methodA, but it gives me a warning that I must create a method for classA. Why is this happening? Doesn't #Autowired work like this in this case? Should I just instantiate classA? Thank you very much for your answers.
ClassA
#RequiredArgsConstructor
public class RepositoryImpl implements IRepository {
#Autowired
private final TransactionDataMapper transactionDataMapper;
#Autowired
private SpringDataColminvoice springDataColminvoice;
#Override
public <S extends TransactionDto> S save(S s) {
Colm colm = transactionDataMapper.toEntity(s);
//methodA
springDataColminvoice.save(colm);
return null;
}
}
InterfaceA
public interface IRepository extends IRepository<TransactionDto, Integer> {}
ClassB
#Service
#RequiredArgsConstructor
public class ServiceImpl implements IInvoiceService {
#Autowired
private RepositoryImpl repositoryImpl;
#Override
public void save(CMessage cMessage) throws HandlerException {
try {
TransactionDto transactionDto = cMessage.getTransaction();
// methodA
repositoryImpl.save(transactionDto);
} catch (Exception e) {
throw new HandlerException(e.getMessage());
}
}
}
Exception
Action:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field RepositoryImpl in com.st.ms.yyu.d.binvoce.infraestructure.rest.spring.services.impl.InvoiceServiceImpl required a bean of type 'com.st.ms.yyu.d.binvoce.infraestructure.db.springdata.repository.impl.ServiceImpl' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.st.ms.yyu.d.binvoce.infraestructure.db.springdata.repository.impl.RepositoryImpl' in your configuration.
(posting this as an answer since I do not have enough reputation to comment)
As others have pointed out already, a code sample would help tremendously.
That being said, though, it sounds like you're missing implementation for "ClassA".
If you have an interface that "ClassA" implements, you have to implement the interface's methods in "ClassA" before you can use them.
I assume your code currently looks somewhat like this?
public interface InterfaceA {
void methodA();
}
public class ClassA implements InterfaceA {
}
public class ClassB {
#Autowired
ClassA classA; // Cannot use {#link InterfaceA#methodA} because the class does not implement the function
}
If this is your code, make sure you add an implementation for your "methodA()" function in "ClassA". Somewhat like so:
public class ClassA implements InterfaceA {
#Override
public void methodA() {
}
}
Additionally, in order to autowire in Spring (Boot), you need to ensure that the class you'd like to autowire is marked as such. You can autowire beans.
To make "ClassA" in the example eligible for autowiring, make sure to instantiate it either as:
A bean (using the #Bean annotation).
A component (using the #Component annotation).
A service (using the #Service annotation).
Any of the other annotations that may match your use case the best.
In our example, this would look somewhat like this:
#Component // Or #Service / whatever you may need
public class ClassA implements InterfaceA {
#Override
public void methodA() {
}
}
Hope you've found any of this helpful. All the best!
-T
As what I have understood, #Autowire means injecting the value/instance of the specific property where you put the annotation #Autowire. In this case, #Autowire only happens when there is defined/created Bean within your basePackage of your Spring Boot project that can match it, i.e. where your #Autowire referred to (meaning there is no conflict issue like ambiguity, etc. and the DataType(Class) can be implicitly casted). In your example, first you treat the IRepository and/or RepositoryImpl as Repository without using the #Repository annotation to inform the Spring Boot default configuration that this is a Repository bean. As you didn't put the POM.xml or posted the related code, I presumed you are creating your own repository class. I think it's much better to post your dependencies here.
But as what others pointed out. You need to create a bean that can match the #Autowired you've put on TransactDataManager & SpringDataColminvoice. You need also to inform the Spring Boot or register it that your class A is a Bean by annotating
#Bean - defining a regular bean,
#Component - a Component in the Project,
#Service - a Service in the Project,
#Repository - a Repository (if you're using Spring JPA), etc.
#<Other Annotations depending of what other Spring Project/Dependencies your using>
Since newer versions of Spring is moving to annotation based from XML mapping, we need to put proper annotation for each class/object that we want to be auto injected/instantiated from #Autowired using the above sample annotations depending on the role/purpose of your class/object is.
I suggest if you're not sure. Then create a typical bean using common annotation #Bean. So your class A might be
#Component //Insert proper Annotation for your class if necessary. This is just a sample
#RequiredArgsConstructor
public class RepositoryImpl implements IRepository {
#Autowired
private final TransactionDataMapper transactionDataMapper;
#Autowired
private SpringDataColminvoice
springDataColminvoice;//segunda
#Override
public <S extends TransactionDto> S save(S s) {
//Some implementation
}
#Bean
private TransactionDataMapper getTransactionDataMapper(<Some parameters if needed>){
return <an instance for TransactionDataManager>;
}
#Bean
private SpringDataColminvoice getSpringDataColmInvoice(<Some parameters if needed>){
return <an instance for SpringDataColminvoice>;
}
}
Note that 2 beans definition are optional if there are already a Beans define on outside class or if it was marked by other annotation like #Service, #Component or other proper annotations and the other one bean is just a reference parameter for the other bean in order to properly instantiated.
In your class B is the following:
public class ClassB {
#Autowired
ClassA classA;
/*Note: ignore this Bean definition if Class A is annotated with #Component,
#Service, or other proper #Annotation for Class A.
*/
#Bean
private ClassA getClassA(<Some Parameters if Needed>){
return <Instance of Class A>
}
}
Take note that, you don't need to put a Bean definition inside the Class B if you put a proper annotation for your Class A like #Component, #Service, etc.

Inherit annotations from abstract class?

Can I somehow group a set of annotations on an abstract class, and every class that extends this class has automatically assigned these annotations?
At least the following does not work:
#Service
#Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
class AbstractService
class PersonService extends AbstractService {
#Autowired //will not work due to missing qualifier annotation
private PersonDao dao;
}
The answer is: no
Java annotations are not inherited unless the annotation type has the #Inherited meta-annotation on it: https://docs.oracle.com/javase/7/docs/api/java/lang/annotation/Inherited.html.
Spring's #Component annotation does not have #Inherited on it, so you will need to put the annotation on each component class. #Service, #Controller and #Repository neither.
The short answer is: with the annotations that you mentioned in your example, no.
The long answer: there is a meta-annotation called java.lang.annotation.Inherited. If an annotation itself is annotated with this annotation, then when a class is annotated with it, its subclasses are also automatically annotated with it by implication.
However, as you can see in the spring source code, the #Service and #Scope annotation are not themselves annotated with #Inherited, so the presence of #Service and #Scope on a class is not inherited by its subclasses.
Maybe this is something that can be fixed in Spring.
I have this piece of code in my project and it works just fine, although it's not annotated as Service:
public abstract class AbstractDataAccessService {
#Autowired
protected GenericDao genericDao;
}
and
#Component
public class ActorService extends AbstractDataAccessService {
// you can use genericDao here
}
So you don't need to put annotation on your abstract class but even if you do you will still have to put annotation on all subclass as #Component or #Service annotations are not inherited.

#Component with parent?

Is there any way to use <bean parent="someParent"> with #Component annotation (creating spring beans using annotation)?
I would like to create spring bean that has "spring parent" using #Component annotation.
is it possible?
Following my comment, this piece of XML
<bean id="base" abstract="true">
<property name="foo" ref="bar"/>
</bean>
<bean class="Wallace" parent="base"/>
<bean class="Gromit" parent="base"/>
is more or less eqivalent to this code (note that I created artificial Base class since abstract beans in Spring don't need a class):
public abstract class Base {
#Autowired
protected Foo foo;
}
#Component
public class Wallace extends Base {}
#Component
public class Gromit extends Base {}
Wallace and Gromit now have access to common Foo property. Also you can override it, e.g. in #PostConstruct.
BTW I really liked parent feature in XML which allowed to keep beans DRY, but Java approach seems even cleaner.
Just came across the same Construct.. (a general abstract parent Class which used Beans - with context.xml-declaration for an potential Implementation that was injected by Component-scan)
This is what I could have done in the #Component Class:
#Autowired
public void setBeanUsedInParent(BeanUsedInParent bean) {
super.setBeanUsedInParent(bean);
}
..what I ended up doing (better, since it makes clear that the injection works on the object not on classes) - and if you are able to modify the parent Class:
// abstract getter in parent Class
public abstract BeanUsedInParent getBeanUsedInParent();
..leave the actual Beans as well as their injection up to the actual implementation (the #Component Class):
#Autowired
private BeanUsedInParent beanUsedInParent;
#Override
public BeanUsedInParent getBeanUsedInParent() {
return this.beanUsedInParent;
}

Categories