Reflection - Method caching for multiple subclasses - java

I've got one abstract class which is extended by multiple concrete classes.
The abstract class initialization block fetch all the methods of the current class hierarchy searching for particular annotated methods.
ConcreteClass2 > ConcreteClass1 > AbstractClass
OtherConcrete1 > AbstractClass
{
...
final List<Method> methods = new ArrayList<>(16);
Clazz<?> clazz = getClass();
while (clazz != Object.class) {
for (final Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(MyAnnotation.class)) {
methods.add(method);
}
}
clazz = clazz.getSuperclass();
}
METHODS.put(getClass(), methods);
...
}
These concrete classes are instantiated in a web/application server and are used inside Servlets.
As per se Reflection is not the fastest, and as I do not want to repeat the scan every time a concrete type is instantiated (new), what is the best strategy to cache the annotated Method's.
I'm actually using ConcurrentHashMap as <Class<? extends AbstractClass>, List<Method>>, and In the initialization block I check if the Map already contains an entry for the uppermost concrete type.
However I do not think this is the best way to handle it. And by the way, I cannot have external dependencies.

If you are on Java 8, use computeIfAbsent to avoid recomputing the set of methods with annotations:
private static final ConcurrentMap<Class<?>,List<Method>> METHODS = new ConcurrentHashMap<>();
...
Clazz<?> clazz = getClass();
METHODS.computeIfAbsent(clazz, c -> findMethods(c));
...
private static List<Method> findMethods(Class<?> clazz) {
final List<Method> methods = new ArrayList<>(16);
while (clazz != Object.class) {
for (final Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(MyAnnotation.class)) {
methods.add(method);
}
}
clazz = clazz.getSuperclass();
}
return methods;
}
This would cache methods per class, and avoid recomputing List<Method> for the same class.

Related

FactoryPattern with Generics in Java

i'm new to java and im trying to use the Factory Pattern.
My App simply imports Data from an Excelsheet into an ArrayList by a given Pojo/Bean.
As i have more than one Bean i decided to create a Config object and use a factory pattern.
The cfg object configures the generic extractor and the factory method returns the concrete Extractor. In this case the MyDataExtracor.
The importExcel method simply runs through every line in the excel and adds the extracted data as object per line to a list.
But the line List<MyData> myDataList = e.importExcel();
throws the following error: Type mismatch: cannot convert from List<capture#1-of ?> to List<MyData>
anyone has a hint what im doing wrong?
Heres my code:
main
ExtractorConfig ec = new ExtractorConfig();
ec.setType(MyData.class);
ec.setFileName("MyData.xlsx");
ec.setStartRow(2d);
ec.setEndRow(3241d);
ec.setSheetName("Sheet1");
Extractor e = Extractor.createFromConfig(ec);
List<MyData> myDataList = e.importExcel();
Extractor.java
public abstract class Extractor {
public abstract List<?> importExcel();
public abstract ExtractorBean processRow(Row currentRow);
public static Extractor createFromConfig(ExtractorConfig ec) {
if (ec.getC() == MyData.class)
return new MyDataExtractor(ec);
return null;
}
}
MyDataExtractor.java
public class MyDataExtractor extends Extractor {
ExtractorConfig c;
public MyDataExtractor(ExtractorConfig c) {
super();
this.c = c;
}
#Override
public List<MyData> importExcel() {
List<MyData> aList = new ArrayList<MyData>();
// some code
// loop through worksheet
MyData aMyDataObject = processRow(currentRow);
aList.add(aMyDataObject);
// end of loop
return aList;
}
public MyData processRow(Row currentRow) {
MyData myDataObject = new MyData();
// do some stuff like setting data
return myDataObject;
}
}
In order to use Extractor like you want to itneeds to have a generic type of its own, e.g. Extractor<T> and then public abstract List<T> importExcel(). Then define the concrete implementation as MyDataExtractor implements Extractor<MyData>.
Additionally, you need to adjust the config and factory method, e.g. like this:
public class ExtractorConfig<T> {
public Extractor<T>(Class<T> type) { ... }
Class<T> getType();
}
//need to suppress the warning about the cast
//using E here in order to avoid confusion with <T> of Extractor<T>
#SuppressWarnings("unchecked")
public <E> static Extractor<E> createFromConfig(ExtractorConfig<E> ec) {
//this isn't that elegant though as Extractor would now need to know all implementations and their data classes
if (MyData.class.equals(ec.getType()))
//unfortunately this cast is necessary
return (Extractor<E>)new MyDataExtractor(ec);
return null;
}
And use it like this:
ExtractorConfig<MyData> ec = new ExtractorConfig(MyData.class);
...
Extractor<MyData> e = Extractor.createFromConfig(ec);
List<MyData> myDataList = e.importExcel();
Note that if you want to be more flexible with possible implementations, you might need to use a kind of registry (factory factory) with either contains singleton extractors, prototypes or metafactories.
Simple example using meta factories:
interface ExtractorFactory<T> {
Class<T> getType();
Extractor<T> buildExtractor(ExtractorConfig<T> ec);
}
class ExtractorRegistry {
Map<Class<?>, ExtractorFactory<?>> factories = ...;
void registerFactory(ExtractorFactory<?> factory) {
factories.put(factory.getType(), factory);
}
#SuppressWarnings("unchecked")
<T> Extractor<T> build(ExtractorConfig<T> ec) {
//same ugly but necessary cast, warning needs to be suppressed
ExtractorFactory<T> f = (ExtractorFactory)factories.get(ec.getType());
if( f != null ) return f.buildExtractor(ec);
return null;
}
}
And use it like this:
ExtractorRegistry registry = ...//get it from somewhere (or make it static, but that's not preferred)
registry.registerFactory(new MyDataExtractorFactory());
Extractor<MyData> e = registry .build(ec);
List<MyData> myDataList = e.importExcel();
Finally a word on those casts:
Since you'll want to deal with different and potentially unrelated types in your factory methods (or the registry method that returns factories) you need to cast from the concrete types you get (e.g. MyData defined by MyDataExtractor to the generic or wildcard type of the method.
However, this is ok if you make sure that those casts will always succeed. The warnings are there to tell you this is potentially unsafe and thus you need to be extra careful not to break it. If you are sure this can't break, supress the warnings :)

Missing field annotation in cglib proxy class

#Service
public class TestService{
#DynamicReference
private ITestProvider testProvider;
public void run() {
}
}
DynamicReference dynamicRefrence = filed.getAnnotation(DynamicReference.class);
-->NOT NULL
This code is fine in this case. But when I add #Transactional in method run, then #DynamicReference will lose
#Service
public class TestService{
#DynamicReference
private ITestProvider testProvider;
#Transactional
public void run() {
}
}
DynamicReference dynamicRefrence = filed.getAnnotation(DynamicReference.class);
-->NULL
How can I get field annotation #DynamicReference in cglib proxy class?
this is get Field code:
Object o = this.applicationContext.getBean(beanName);
Class<?> clazz = o.getClass();
for (Field filed : clazz.getDeclaredFields()) {
DynamicReference dynamicRefrence = filed.getAnnotation(DynamicReference.class);
}
From Class.getDeclaredFields():
Returns an array of Field objects reflecting all the fieldsdeclared by the class or interface represented by this Class object. This includes public, protected, default(package) access, and private fields, but excludes inherited fields.
In your case, once you have a subclass-based proxy from cglib, the field will only exist in the superclass. Depending on your use case, you might want to collect all fields up in the inheritance chain that have you custom annotation.
Example code:
Collection<Field> fieldsWithAnnotation = new ArrayList<>();
Class<?> clazz = // your class
while(clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
DynamicReference dynamicRefrence = field.getAnnotation(DynamicReference.class);
if(dynamicRefrence != null)
fieldsWithAnnotation.add(field);
}
clazz = clazz.getSuperclass();
}
EDIT: This approach works to find the annotated field. However, doing field.set(proxyInstance, value) will actually set the field in the proxy. This does not help you, as even though the proxy subclasses, it still uses delegation to forward method calls to a wrapped instance of your actual class. Since your goal is apparently to set the field in this wrapped instance, I would advise you to not use a custom field injection but rather setter injection. Your code would look roughly like this (untested):
// in TestService
private ITestProvider testProvider;
#DynamicReference
public void setTestProvider(ITestProvider testProvider) { ... }
// Getting the method
while(clazz != null) {
for (Method method : clazz.getDeclaredMethods()) {
DynamicReference dynamicRefrence = method.getAnnotation(DynamicReference.class);
if(dynamicRefrence != null)
methodsWithAnnotation.add(method);
}
clazz = clazz.getSuperclass();
}
// invoking it
method.invoke(proxyInstance, dependencyInstanceYouWantToSet);
The proxy should delegate the method call to your wrapped instance. Maybe you even want to make the method protected.
The alternative would be getting the callback-field of the proxy and setting the field on that instance, but the approach above seems much cleaner (some might say that magic field injection is evil and you should always use setter/constructor injection for a clean oop approach).
Edit 2: maybe you could also rethink if you want to actually reinvent the DI framework and leverage the underlying existing DI framework functionality. Using #Qualifier or some custom injection resolver comes to mind. See eg this tutorial

How to ignore modifiers when invoking methods on run-time - JAVA

I want to look for a specific annotation of every method in a class and if a method with that annotation is found, i would like to invoke it.
Also, if it's not found in the current class, a check on all inheriting classes should be made.
My problem is that there might be some methods that are protected, private etc. and I would like to ignore those modifiers and to gain access to all methods (i.e even if it's private etc.)
So this is how I invoke (given is the name of the annotation that I'm looking for:
if (m.isAnnotationPresent(Given.class)) {
m.invoke(instObj, intArgument);
}
(And this is how I check the rest of the class hierarchy - if i made a mistake somewhere, enlighten me please:
Class<?> superC = c.getSuperclass();
while (!(superC.equals(Object.class))) {
handleGiven(instObj, superC, methods, currentSentence,
methodArgument);
when handleGiven is a recursive call.
You need to use getDeclaredMethods to get all the methods (public, protected, etc), like this:
public Method findMethodWithAnnotation(Class<?> clazz,
Class<? extends Annotation> annotation) {
for (Method m : clazz.getDeclaredMethods()) {
if (m.isAnnotationPresent(annotation)) {
return m;
}
}
return null;
}
And check like this:
Class<?> clazz = ..; //get the class
Method m = null;
do {
m = findMethodWithAnnotation(clazz, DesiredAnnotation.class);
clazz = clazz.getSuperclass();
} while (m == null && clazz != null);
System.out.println(m);
Also make sure that your annotation has the following annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
If you need the annotation for fields, check the getDeclaredFields, and similars methods.
You need to make the method accessible, before the invocation
m.setAccessible(true);
If you want a more compact and recursive implementation, you can change to:
public Method findMethodWithAnnotation(Class<?> clazz,
Class<? extends Annotation> annotation) {
if (clazz == Object.class || clazz == null) return null;
for (Method m : clazz.getDeclaredMethods()) {
if (m.isAnnotationPresent(annotation)) {
return m;
}
}
return findMethodWithAnnotation(clazz.getSuperClass(), annotation);
}
And the use is:
Method m = findMethodWithAnnotation(clazz, DesiredAnnotation.class)
if (m == null) log("Nor the class, or any superclass have the desired annotation")
else {
m.setAccessitble(true);
m.invoke(obj, arguments);
}
Considerations:
this don't cover interfaces, if you need to cover interfaces, check getInterfaces() (this method return the interfaces in the order of declaration).
If a a class A has a override method desiredMethod, with no annotation, and extends a class SuperA, witch has a method desiredMethod, with the desired annotation, this return SuperA#desiredMethod, but when you invoke it, it will invoke in the A class (like a normal invocation)
My problem is that there might be some methods that are protected, private etc. and I would like to ignore those modifiers and to gain access to all methods (i.e even if it's private etc.)
You need to call Method.setAccessible(true) before invoking it, not forgetting to restore its original value afterwards, in a finally block.

Is it possible to get a class by qualifier annotation using reflection?

I have an interface with many possible implementations. The right implementation should be chosen at the runtime. And so Reflection sounds to be the solution.
I have annotated these classes by a qualifier that has as argument an enumeration.
So, is it possible to get at runtime using reflection the right implementatoin class by passing the right enumeration to the annotation?
But, reflection is not mandatory if there is another way..
First, here it is the enumeration :
public enum CATEGORY {
A,B,C;
}
Then, here it the interface :
public interface GenericI{
void method1(CATEGORY arg);
// some other methods
}
And now, here there are the annotated implementations :
#MyAnnotation(CATEGORY.A)
public class impl1 implements GenericI{
void method1(CATEGORY arg){
// some work here
}
}
#MyAnnotation(CATEGORY.B)
public class impl2 implements GenericI{
void method1(CATEGORY arg){
// some work here
}
}
Finally, the proxy that at a way, select dynamically the right implementation using annotation and enumeration (probably it shouldn't implement GenericI ??):
public class MyProxy implements GenericI {
// Here we must be able to select the right implementation
}
Reflexion is an answer, but you need to get all the classes from the classpath, and examinate it to find the implementation of your interface.
You can use this reflection library and get all the implementations like this (if your interface name is MyInterface):
Reflections reflections = new Reflections("your.base.package", new SubTypesScanner(), new TypeAnnotationsScanner());
Set<Class<T extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class);
for (Class<T extends MyInterface> c : classes) {
check if c is the rigth implementation!.
}
If you don't want to use a external library, you can use the Java Reflection API, and scan all packages, somethis like (see this answers to use instrumentation):
Instrumentation inst = InstrumentHook.getInstrumentation();
for (Class<?> c: inst.getAllLoadedClasses()) {
if (MyInterface.class.isAssignableFrom(c)) {
check if c is the rigth implementation!.
}
}
The first option allow you to save the Reflections object as a xml, so the component scan is saved and it's done only one time.
To check if the clazz have a Qualifier you can use:
if (c.isAnnotationPresent(Qualifier.class)) {
bingo!.
}
or is a property of the annotation:
if (c.isAnnotationPresent(Qualifier.class)) {
Qualifier q = c.getAnnotation(Qualifier.class);
if (q.theRight()) {
bingo!
}
}
I recommend you to see if the FactoryProblem is applycable to your problem, choose always Factory instead of Reflection.
An example "proxy":
public class MyProxy implements GenericI {
Map<Category, GenericI> generics;
public MyProxy() {
Reflections reflections = new Reflections("your.base.package", new SubTypesScanner(), new TypeAnnotationsScanner());
Set<Class<T extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class);
generics = new HashMap<Category, GenericI>();
for (Class<T extends MyInterface> c : classes) {
map.put(c.getAnnotation(MyAnnotation.class).value(), c.newInstance());
}
}
void method1(CATEGORY arg){
map.get(arg).method1(arg);
}
}
This is extremely heavy and overcomplicated, if you use this, please add extensive test, and make MyProxy a Singleton.
If you use a IOC framework:
#Component
public class MyProxy implements GenericI {
#Autoriwed // If spring
List<GenericI> generics;
#Inject #Any // If CDI
private Instance<GenericI> services;
Map<Category, GenericI> generics;
#PostConstruct
void makeMap() {
generics = new HashMap<>();
for (GenericI component : generics) {
generics.put(
component.getClass().getAnnotation(MyAnnotation.class).value(),
component);
}
}
void method1(CATEGORY arg){
map.get(arg).method1(arg);
}
}
I assume you don't know al possible subclasses.

Illegal access exception when trying to access attibrute from parent class by introspection

I am currently playing with introspection and annotations in Java 1.5.
The have a parent abstract class AbstractClass.
The inherited classes can have attributes (of type ChildClass) annotated with a custom #ChildAttribute annotation.
I wanted to write a generic method that would list all #ChildAttribute attributes of an instance.
Here is my code so far.
The parent class :
public abstract class AbstractClass {
/** List child attributes (via introspection) */
public final Collection<ChildrenClass> getChildren() {
// Init result
ArrayList<ChildrenClass> result = new ArrayList<ChildrenClass>();
// Loop on fields of current instance
for (Field field : this.getClass().getDeclaredFields()) {
// Is it annotated with #ChildAttribute ?
if (field.getAnnotation(ChildAttribute.class) != null) {
result.add((ChildClass) field.get(this));
}
} // End of loop on fields
return result;
}
}
A test implementation, with some child attributes
public class TestClass extends AbstractClass {
#ChildAttribute protected ChildClass child1 = new ChildClass();
#ChildAttribute protected ChildClass child2 = new ChildClass();
#ChildAttribute protected ChildClass child3 = new ChildClass();
protected String another_attribute = "foo";
}
The test itself:
TestClass test = new TestClass();
test.getChildren()
I get the following error :
IllegalAccessException: Class AbstractClass can not access a member of class TestClass with modifiers "protected"
I tought that introspection access did not care about modifiers, and could read / write even private members.It seems that it is not the case.
How can I access the values of these attributes ?
Thanks in advance for your help,
Raphael
Add field.setAccessible(true) before you get the value:
field.setAccessible(true);
result.add((ChildClass) field.get(this));
Try field.setAccessible(true) before calling field.get(this). By default, modifiers are honored, but that can be switched off.

Categories