Component registration info - Lifecycle method finder rewritten

The method that looks for lifecycle methods now uses MemberFinder to
locate lifecycle methods in both interfaces and superclasses.
This commit is contained in:
Emmanuel BENOîT 2015-09-14 09:34:39 +02:00
parent aa3ee13f66
commit a95d71e770

View file

@ -7,6 +7,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
@ -28,12 +29,19 @@ public final class NewComponentInfo< T >
{
/** Member finder that looks for the default constructor */
private static final MemberFinder< Constructor< ? > > CONSTRUCTOR_FINDER;
/** Member finder that looks for lifecycle methods */
private static final MemberFinder< Method > LC_METHOD_FINDER;
static {
CONSTRUCTOR_FINDER = new MemberFinder< >( ( final Class< ? > c ) -> c.getDeclaredConstructors( ) , false );
NewComponentInfo.CONSTRUCTOR_FINDER
.setMemberFilter( ( final Constructor< ? > c ) -> c.getParameterCount( ) == 0 );
LC_METHOD_FINDER = new MemberFinder< >( MemberFinder.METHOD_EXTRACTOR , true );
NewComponentInfo.LC_METHOD_FINDER.setClassesFirst( true );
NewComponentInfo.LC_METHOD_FINDER
.setMemberFilter( m -> m.getDeclaredAnnotation( LifecycleMethod.class ) != null );
}
@ -78,7 +86,7 @@ public final class NewComponentInfo< T >
}
// Explicit dependencies
for ( Class< ? super T > c : Annotations.findParentsWith( klass , Dependencies.class ) ) {
for ( final Class< ? super T > c : Annotations.findParentsWith( klass , Dependencies.class ) ) {
final Dependencies dependencies = c.getDeclaredAnnotation( Dependencies.class );
for ( final String dependency : dependencies.value( ) ) {
info.addDependency( dependency );
@ -86,6 +94,7 @@ public final class NewComponentInfo< T >
}
// Find lifecycle methods and dependency injections
NewComponentInfo.findLifecycleActions( info , klass );
NewComponentInfo.findMemberAnnotations( info , klass , klass );
return info;
@ -118,6 +127,53 @@ public final class NewComponentInfo< T >
}
/**
* Finds lifecycle methods in all classes and interfaces a component class extends / implements and adds the
* corresponding actions to the information record.
*
* <p>
* FIXME: inefficient algorithm
*
* @param info
* the information record
* @param klass
* the component class
* @throws ComponentDefinitionException
* if a class or interface in the hierarchy declares more than one method for the same lifecycle stage
*/
private static < T > void findLifecycleActions( final NewComponentInfo< T > info , final Class< T > klass )
throws ComponentDefinitionException
{
final ArrayList< Method > output = new ArrayList< >( );
NewComponentInfo.LC_METHOD_FINDER.find( output , klass );
final EnumMap< LifecycleStage , Method > methods = new EnumMap< >( LifecycleStage.class );
final HashMap< Class< ? > , EnumSet< LifecycleStage > > checks = new HashMap< >( );
final int nFound = output.size( );
for ( int i = nFound - 1 ; i >= 0 ; i-- ) {
final Method m = output.get( i );
final Class< ? > mc = m.getDeclaringClass( );
EnumSet< LifecycleStage > present = checks.get( mc );
if ( present == null ) {
present = EnumSet.noneOf( LifecycleStage.class );
checks.put( mc , present );
}
final LifecycleStage lma = m.getDeclaredAnnotation( LifecycleMethod.class ).value( );
if ( !present.add( lma ) ) {
throw new ComponentDefinitionException( "in class " + mc + ": duplicate lifecycle method for " + lma );
}
methods.put( lma , m );
}
for ( final EnumMap.Entry< LifecycleStage , Method > entry : methods.entrySet( ) ) {
final Method m = entry.getValue( );
m.setAccessible( true );
info.setLifecycleAction( entry.getKey( ) , o -> m.invoke( o ) );
}
}
/**
* Finds annotations from the members of a component's class and of its ancestors
*
@ -139,58 +195,10 @@ public final class NewComponentInfo< T >
return;
}
NewComponentInfo.findMemberAnnotations( info , klass , current.getSuperclass( ) );
NewComponentInfo.addLifecycleActions( info , current );
NewComponentInfo.addDependencyInjectors( info , current );
}
/**
* Adds component lifecycle actions to an information record by looking for annotated methods
*
* @param info
* the information record to be updated
* @param current
* the class being examined
*/
private static < T > void addLifecycleActions( final NewComponentInfo< T > info , final Class< ? super T > current )
{
final boolean found[] = {
false , false , false , false
};
for ( final Method method : current.getDeclaredMethods( ) ) {
final LifecycleMethod lcm = method.getAnnotation( LifecycleMethod.class );
if ( lcm == null ) {
continue;
}
// Check for duplicates
final int order = lcm.value( ).ordinal( );
if ( found[ order ] ) {
throw new ComponentDefinitionException(
"in class " + current + ": duplicate lifecycle action for stage " + lcm.value( ) );
}
// Check method
if ( method.getParameterCount( ) != 0 ) {
throw new ComponentDefinitionException( "in class " + current + ": lifecycle action method for stage "
+ lcm.value( ) + " has arguments" );
}
if ( ( method.getModifiers( ) & Modifier.STATIC ) != 0 ) {
throw new ComponentDefinitionException(
"in class " + current + ": lifecycle action method for stage " + lcm.value( ) + " is static" );
}
// Add it
found[ order ] = true;
info.setLifecycleAction( lcm.value( ) , o -> {
method.setAccessible( true );
method.invoke( o );
method.setAccessible( false );
} );
}
}
/**
* Finds fields that require dependency injection in a component's class and adds them to the information record
*