Can't get #Component to be inherited in Spring? - java

In my project there's a common base class that all client classes extend. This has an #Autowired field that needs to be injected by Hibernate. These are all grouped together in another class that has an #Autowired collection of the base class.
In order to reduce boilerplate for client code I'm trying to get #Component inherited. With #Component not doing this by default (apparently it used to though), I created this workaround annotation
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Component
#Inherited
public #interface InheritedComponent {
}
... and annotated the base class with it. Its not pretty but I hoped it would work. Unfortunately it didn't, which really confuses me as #Inherited should make it work
Is there any other way to get #Component inherited? Or do I just have to say that any class that extends the base class needs this boilerplate?

The problem is that the Component annotation type itself needs to be marked with #Inherited.
Your #InheritedComponent annotation type is correctly inherited by any classes that extend a superclass which is marked with #InheritedComponent - but it does not inherit #Component. This is because you have #Component on the annotation, not the parent type.
An example:
public class InheritedAnnotationTest {
#InheritedComponent
public static class BaseComponent {
}
public static class SubClass extends BaseComponent {
}
public static void main(String[] args) {
SubClass s = new SubClass();
for (Annotation a : s.getClass().getAnnotations()) {
System.out.printf("%s has annotation %s\n", s.getClass(), a);
}
}
}
Output:
class brown.annotations.InheritedAnnotationTest$SubClass has annotation #brown.annotations.InheritedComponent()
In other words, when resolving what annotations a class has, the annotations of the annotations are not resolved - they do not apply to the class, only the annotation (if that makes sense).

I've dealt with this issue by creating my own annotation (heritable) and then customizing classpath scanning:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Component
#Inherited
public #interface BusinessService {
}
Spring configuration look likes this:
<context:component-scan base-package="io.bar">
<context:include-filter type="annotation"
expression="io.bar.core.annotations.BusinessService" />
</context:component-scan>
from Spring doc 5.10.3 Using filters to customize scanning

Related

Java annotations logic implementation

I'm creating a java library that can be imported in multiple projects and creates a graphQL schema file with classes and queries annotated with my custom annotations.
I want that when I compile the project, the classes marked by the annotation will be added (with all their fields) into a file that will be the "schema.graphqls" file.
My question is: where I have to insert my logic implementation? Creating a method into the annotation class?
I created two custom annotations, as in the following files:
package annotations;
import java.lang.annotation.*;
#Documented
#Retention(RetentionPolicy.SOURCE)
#Target(ElementType.TYPE) //with TYPE, you can annotate class, interfaces, annotation, types or enum
public #interface GraphQLQuery {
}
GraphQLQuery.java
package annotations;
import java.lang.annotation.*;
#Documented
#Retention(RetentionPolicy.SOURCE)
#Target(ElementType.TYPE) //with TYPE, you can annotate class, interfaces, annotation, types or enum
public #interface GraphQLModel {
}
GraphQLModel.java

Java config instead of #ComponentScan

Is there a way to create Java-based configuration class that does exactly same as #ComponentScan(basePackageClasses = {X.class}) ?
I have to create generic configuration class (for extending in tests) doing something like that:
#Configuration
#ComponentScan(basePackageClasses = T.class)
public class EndpointTestConfig<T> {
}
It's impossible to use generics with annotations so I would need the same effect using Java.
I'm not sure what exactly you are trying to achieve. If I understand you correctly you have a base class T and a lot of derived classes? And you want to have a base config that will create common beans and every extender will create a bean of class that extends T? So you can do something like that:
#Configuration
public class BaseTestConfig {
//common beans
}
#Configuration
#Import(BaseTestConfig.class)
public class ConcreteEndpointConfig {
#Bean
public YourDerivedClass bean() {
return new YourDerivedClass();
}
}
I hope that I got your idea. If this is not what you need please elaborate.

How annotation internally works in java

I know how to create custom annotation. But i am unable to understand how does it work internally. If i take example of spring annontation.
#PropertySource(value = { "classpath:database.properties" }).
if we see internal details of #PropertySource annotation
#Target({ java.lang.annotation.ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Repeatable(PropertySources.class)
public #interface PropertySource {
public abstract String name();
public abstract String[] value();
public abstract boolean ignoreResourceNotFound();
}
We do not have provided any implementation here for loading property file.
Then How is it loading property file from classpath. Who is working behind the scene ?
Really simple: framework. That is. All 'custom' annotations processed by frameworks using reflection. Only small scope of annotations are processed by compiler, such as #Override, #SuppressWarnings, #Retention and so on

Java: process annotation inherited from .class file

I have an annotation that is inherited and has class-retention:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.CLASS)
#Inherited
public #interface MyAnnotation {}
It is used like this:
#MyAnnotation
public class Parent {}
public class Child extends Parent {}
If these classes are within the same project, my annotation processor is executed on both classes because the annotation is inherited correctly.
If Parent is in a separate project the .jar of which is included in the classpath of the project of Child, it doesn't work. The annotation processor is not executed on Child.
Is this by design or is there a way to fix this?

#Transactional in super classes not weaved when using load time weaving

The project I am working on has a similar structure for the DAOs to the one bellow:
/**
* Base DAO class
*/
#Transactional
public class JPABase {
#PersistenceContext
private EntityManager entityManager;
public void persist(Object entity) {
entityManager.persist(entity);
}
//some more methods in here
}
and
/**
* Generic DAO class implementation
*/
#Transactional
public abstract class GenericDao extends JpaBase {
//some methods in here
}
and
/**
* Specialized DAO class
*/
#Repository
#Transactional
public class PersonDao extends GenericDao {
//some methods in here
}
Until now, the project used compile time weaving, but the configuration has changed to use <context:load-time-weaver/> with -javaagent:/opt/tomcat7-1/lib/spring-instrument.jar.
Since this change has been applied, the JpaBase's and GenericDao's #Transactional annotations are not weaved anymore. Each time a service class calls the persist method on a PersonDao object, no transaction is started.
Noteworthy:
this used to work in the past, when using compile time weaving.
all the methods that are defined in the PersonDao are weaved correctly, but the ones inherited (e.g. persist(Object entity)) are NOT weaved.
Compile time weaving and load time weaving are supposed to do the same thing, just at different moments in time. Why has the weaving behaviour changed?
Tomcat default classlLoader is WebappClassLoader, but you need `TomcatInstrumentableClassLoader.
There are two solutions:
Modify WebappLoader.class
Change WebappLoader.java
private String loaderClass = "org.apache.catalina.loader.WebappClassLoader";
Replaceļ¼š
private String loaderClass = "org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader";
Compile it and replaced the class file(catalina.jar), then it works.
Here is required dependency jars: catalina.jar,tomcat-coyote.jar,tomcat-util.jar(/bin),tomcat-juli.jar
Modify context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
</Context>
You probably missing the public constructor chaining inside these classes, #Transactional will work only if classes are having public constructors and public methods.

Categories

Resources