Component registration info - Rewrote dependency injector finder
Use the MemberFinder. Dependencies can be injected using methods.
This commit is contained in:
parent
a1ddf5f63a
commit
9f5fbcbf3d
3 changed files with 102 additions and 63 deletions
|
@ -11,7 +11,7 @@ import java.lang.annotation.Target;
|
||||||
/**
|
/**
|
||||||
* This annotation lists dependencies for a component. If some components are injected as dependencies through the
|
* This annotation lists dependencies for a component. If some components are injected as dependencies through the
|
||||||
* UseComponent annotation, they don't need to be listed here (same goes for the component indicated by
|
* UseComponent annotation, they don't need to be listed here (same goes for the component indicated by
|
||||||
* {@link IsDriverFor}).
|
* {@link DriverFor}).
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
|
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -31,6 +31,10 @@ public final class NewComponentInfo< T >
|
||||||
private static final MemberFinder< Constructor< ? > > CONSTRUCTOR_FINDER;
|
private static final MemberFinder< Constructor< ? > > CONSTRUCTOR_FINDER;
|
||||||
/** Member finder that looks for lifecycle methods */
|
/** Member finder that looks for lifecycle methods */
|
||||||
private static final MemberFinder< Method > LC_METHOD_FINDER;
|
private static final MemberFinder< Method > LC_METHOD_FINDER;
|
||||||
|
/** Member finder that looks for dependency injections using fields */
|
||||||
|
private static final MemberFinder< Field > DI_FIELD_FINDER;
|
||||||
|
/** Member finder that looks for dependency injections using methods */
|
||||||
|
private static final MemberFinder< Method > DI_METHOD_FINDER;
|
||||||
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
@ -42,6 +46,12 @@ public final class NewComponentInfo< T >
|
||||||
NewComponentInfo.LC_METHOD_FINDER.setClassesFirst( true );
|
NewComponentInfo.LC_METHOD_FINDER.setClassesFirst( true );
|
||||||
NewComponentInfo.LC_METHOD_FINDER
|
NewComponentInfo.LC_METHOD_FINDER
|
||||||
.setMemberFilter( m -> m.getDeclaredAnnotation( LifecycleMethod.class ) != null );
|
.setMemberFilter( m -> m.getDeclaredAnnotation( LifecycleMethod.class ) != null );
|
||||||
|
|
||||||
|
DI_FIELD_FINDER = new MemberFinder< >( MemberFinder.FIELD_EXTRACTOR , false );
|
||||||
|
NewComponentInfo.DI_FIELD_FINDER.setMemberFilter( f -> f.getDeclaredAnnotation( UseComponent.class ) != null );
|
||||||
|
|
||||||
|
DI_METHOD_FINDER = new MemberFinder< >( MemberFinder.METHOD_EXTRACTOR , true );
|
||||||
|
NewComponentInfo.DI_METHOD_FINDER.setMemberFilter( m -> m.getDeclaredAnnotation( UseComponent.class ) != null );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,7 +105,8 @@ public final class NewComponentInfo< T >
|
||||||
|
|
||||||
// Find lifecycle methods and dependency injections
|
// Find lifecycle methods and dependency injections
|
||||||
NewComponentInfo.findLifecycleActions( info , klass );
|
NewComponentInfo.findLifecycleActions( info , klass );
|
||||||
NewComponentInfo.findMemberAnnotations( info , klass , klass );
|
NewComponentInfo.findDependencyInjectorFields( info , klass );
|
||||||
|
NewComponentInfo.findDependencyInjectorMethods( info , klass );
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
@ -175,81 +186,107 @@ public final class NewComponentInfo< T >
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds annotations from the members of a component's class and of its ancestors
|
* Finds fields annotated with {@link UseComponent} and add dependency injection actions setting these fields to the
|
||||||
|
* information structure.
|
||||||
*
|
*
|
||||||
* @param info
|
* @param info
|
||||||
* the information record being built
|
* the information structure
|
||||||
* @param klass
|
* @param klass
|
||||||
* the class of the component
|
* the component class
|
||||||
* @param current
|
|
||||||
* the class being examined
|
|
||||||
*
|
|
||||||
* @throws ComponentDefinitionException
|
* @throws ComponentDefinitionException
|
||||||
* if errors are found while reading the annotations
|
* if a static or final field is annotated with {@link UseComponent}, or if the field's type is invalid
|
||||||
|
* (primitive or array type)
|
||||||
*/
|
*/
|
||||||
private static < T > void findMemberAnnotations( final NewComponentInfo< T > info , final Class< T > klass ,
|
private static < T > void findDependencyInjectorFields( final NewComponentInfo< T > info , final Class< T > klass )
|
||||||
final Class< ? super T > current )
|
throws ComponentDefinitionException
|
||||||
throws ComponentDefinitionException
|
|
||||||
{
|
{
|
||||||
if ( current == Object.class ) {
|
final ArrayList< Field > fields = new ArrayList< >( );
|
||||||
return;
|
NewComponentInfo.DI_FIELD_FINDER.find( fields , klass );
|
||||||
|
for ( final Field f : fields ) {
|
||||||
|
if ( ( f.getModifiers( ) & Modifier.STATIC ) != 0 ) {
|
||||||
|
throw new ComponentDefinitionException( "in class " + f.getDeclaringClass( ) + ": static field "
|
||||||
|
+ f.getName( ) + " annotated with UseComponent" );
|
||||||
|
}
|
||||||
|
if ( ( f.getModifiers( ) & Modifier.FINAL ) != 0 ) {
|
||||||
|
throw new ComponentDefinitionException( "in class " + f.getDeclaringClass( ) + ": final field "
|
||||||
|
+ f.getName( ) + " annotated with UseComponent" );
|
||||||
|
}
|
||||||
|
|
||||||
|
DependencyInfo dep;
|
||||||
|
try {
|
||||||
|
dep = NewComponentInfo.depFrom( f.getType( ) , f.getDeclaredAnnotation( UseComponent.class ) );
|
||||||
|
} catch ( final ComponentDefinitionException e ) {
|
||||||
|
throw new ComponentDefinitionException(
|
||||||
|
"in class " + f.getDeclaringClass( ) + ", field " + f.getName( ) + ": " + e.getMessage( ) );
|
||||||
|
}
|
||||||
|
f.setAccessible( true );
|
||||||
|
info.addDependencyInjector( dep , ( o , d ) -> f.set( o , d ) );
|
||||||
}
|
}
|
||||||
NewComponentInfo.findMemberAnnotations( info , klass , current.getSuperclass( ) );
|
|
||||||
NewComponentInfo.addDependencyInjectors( info , current );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds fields that require dependency injection in a component's class and adds them to the information record
|
* Finds methods annotated with {@link UseComponent} and add dependency injection actions calling these methods to
|
||||||
|
* the information structure.
|
||||||
*
|
*
|
||||||
* @param info
|
* @param info
|
||||||
* the information record being updated
|
* the information structure
|
||||||
* @param current
|
* @param klass
|
||||||
* the class being examined
|
* the component class
|
||||||
|
* @throws ComponentDefinitionException
|
||||||
|
* if a static method or a method with more than one parameter is annotated with {@link UseComponent},
|
||||||
|
* or if the method parameter's type is invalid (primitive or array type)
|
||||||
*/
|
*/
|
||||||
private static < T > void addDependencyInjectors( final NewComponentInfo< T > info ,
|
private static < T > void findDependencyInjectorMethods( final NewComponentInfo< T > info , final Class< T > klass )
|
||||||
final Class< ? super T > current )
|
throws ComponentDefinitionException
|
||||||
{
|
{
|
||||||
for ( final Field field : current.getDeclaredFields( ) ) {
|
final ArrayList< Method > methods = new ArrayList< >( );
|
||||||
final UseComponent uc = field.getAnnotation( UseComponent.class );
|
NewComponentInfo.DI_METHOD_FINDER.find( methods , klass );
|
||||||
if ( uc == null ) {
|
for ( final Method m : methods ) {
|
||||||
continue;
|
if ( ( m.getModifiers( ) & Modifier.STATIC ) != 0 ) {
|
||||||
|
throw new ComponentDefinitionException( "in class " + m.getDeclaringClass( ) + ": static method "
|
||||||
|
+ m.getName( ) + " annotated with UseComponent" );
|
||||||
|
}
|
||||||
|
if ( m.getParameterCount( ) != 1 ) {
|
||||||
|
throw new ComponentDefinitionException( "in class " + m.getDeclaringClass( ) + ": method "
|
||||||
|
+ m.getName( ) + " has more than 1 parameter" );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the field
|
DependencyInfo dep;
|
||||||
final int mod = field.getModifiers( );
|
try {
|
||||||
if ( ( mod & Modifier.STATIC ) != 0 ) {
|
dep = NewComponentInfo.depFrom( m.getParameterTypes( )[ 0 ] ,
|
||||||
|
m.getDeclaredAnnotation( UseComponent.class ) );
|
||||||
|
} catch ( final ComponentDefinitionException e ) {
|
||||||
throw new ComponentDefinitionException(
|
throw new ComponentDefinitionException(
|
||||||
"in class " + current + ": field " + field.getName( ) + " is static" );
|
"in class " + m.getDeclaringClass( ) + ", field " + m.getName( ) + ": " + e.getMessage( ) );
|
||||||
}
|
}
|
||||||
if ( ( mod & Modifier.FINAL ) != 0 ) {
|
m.setAccessible( true );
|
||||||
throw new ComponentDefinitionException(
|
info.addDependencyInjector( dep , ( o , d ) -> m.invoke( o , d ) );
|
||||||
"in class " + current + ": field " + field.getName( ) + " is final" );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the dependency
|
|
||||||
final String ucn = uc.value( );
|
|
||||||
final DependencyInfo di;
|
|
||||||
if ( "".equals( ucn ) ) {
|
|
||||||
try {
|
|
||||||
di = new DependencyInfo( field.getType( ) );
|
|
||||||
} catch ( final ComponentDefinitionException e ) {
|
|
||||||
throw new ComponentDefinitionException(
|
|
||||||
"in class " + current + ", field " + field.getName( ) + ": " + e.getMessage( ) );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
di = new DependencyInfo( ucn );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the injector
|
|
||||||
info.addDependencyInjector( di , ( c , d ) -> {
|
|
||||||
field.setAccessible( true );
|
|
||||||
field.set( c , d );
|
|
||||||
field.setAccessible( false );
|
|
||||||
} );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a dependency from an {@link UseComponent}-annotated field or method
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* the type of the field or of the method's parameter
|
||||||
|
* @param annotation
|
||||||
|
* the {@link UseComponent} annotation
|
||||||
|
* @return the dependency information record
|
||||||
|
* @throws ComponentDefinitionException
|
||||||
|
* if the type is invalid (primitive or array type)
|
||||||
|
*/
|
||||||
|
private static DependencyInfo depFrom( final Class< ? > type , final UseComponent annotation )
|
||||||
|
throws ComponentDefinitionException
|
||||||
|
{
|
||||||
|
if ( "".equals( annotation.value( ) ) ) {
|
||||||
|
return new DependencyInfo( type );
|
||||||
|
}
|
||||||
|
DependencyInfo.checkClassValidity( type );
|
||||||
|
return new DependencyInfo( annotation.value( ) );
|
||||||
|
}
|
||||||
|
|
||||||
/** The component's instance, or <code>null</code> if a supplier is being used */
|
/** The component's instance, or <code>null</code> if a supplier is being used */
|
||||||
private final T component;
|
private final T component;
|
||||||
/** The component's supplier, or <code>null</code> if an instance has been supplied */
|
/** The component's supplier, or <code>null</code> if an instance has been supplied */
|
||||||
|
@ -268,7 +305,7 @@ public final class NewComponentInfo< T >
|
||||||
/** Lifecylce actions for the new component */
|
/** Lifecylce actions for the new component */
|
||||||
private final EnumMap< LifecycleStage , ThrowingConsumer< ? super T > > lcActions;
|
private final EnumMap< LifecycleStage , ThrowingConsumer< ? super T > > lcActions;
|
||||||
/** Dependency injectors */
|
/** Dependency injectors */
|
||||||
private final HashMap< DependencyInfo , ArrayList< ThrowingBiConsumer< ? super T , ? > > > injectors;
|
private final HashMap< DependencyInfo , ArrayList< ThrowingBiConsumer< ? super T , Object > > > injectors;
|
||||||
|
|
||||||
/** Whether the component should be activated automatically */
|
/** Whether the component should be activated automatically */
|
||||||
private boolean autostart;
|
private boolean autostart;
|
||||||
|
@ -468,7 +505,7 @@ public final class NewComponentInfo< T >
|
||||||
* @return the modifiable map of dependency injectors, with the dependency information as the key and a list of
|
* @return the modifiable map of dependency injectors, with the dependency information as the key and a list of
|
||||||
* functions that inject the dependency into the target component as the value.
|
* functions that inject the dependency into the target component as the value.
|
||||||
*/
|
*/
|
||||||
public HashMap< DependencyInfo , ArrayList< ThrowingBiConsumer< ? super T , ? > > > getInjectors( )
|
public HashMap< DependencyInfo , ArrayList< ThrowingBiConsumer< ? super T , Object > > > getInjectors( )
|
||||||
{
|
{
|
||||||
return this.injectors;
|
return this.injectors;
|
||||||
}
|
}
|
||||||
|
@ -485,9 +522,9 @@ public final class NewComponentInfo< T >
|
||||||
* @return the current object
|
* @return the current object
|
||||||
*/
|
*/
|
||||||
public NewComponentInfo< T > addDependencyInjector( final DependencyInfo dependency ,
|
public NewComponentInfo< T > addDependencyInjector( final DependencyInfo dependency ,
|
||||||
final ThrowingBiConsumer< ? super T , ? > injector )
|
final ThrowingBiConsumer< ? super T , Object > injector )
|
||||||
{
|
{
|
||||||
ArrayList< ThrowingBiConsumer< ? super T , ? > > injectors = this.injectors.get( dependency );
|
ArrayList< ThrowingBiConsumer< ? super T , Object > > injectors = this.injectors.get( dependency );
|
||||||
if ( injectors == null ) {
|
if ( injectors == null ) {
|
||||||
injectors = new ArrayList< >( );
|
injectors = new ArrayList< >( );
|
||||||
this.injectors.put( dependency , injectors );
|
this.injectors.put( dependency , injectors );
|
||||||
|
|
|
@ -9,14 +9,16 @@ import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This annotation indicates that a component's field is meant to receive the instance of another component (creating a
|
* This annotation indicates that a component's field or method is meant to receive the instance of another component
|
||||||
* dependency implicitly). If no name is specified, the component to inject will be determined based on the type of the
|
* (creating a dependency implicitly). If no name is specified, the component to inject will be determined based on the
|
||||||
* field.
|
* type of the field.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
|
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
|
||||||
*/
|
*/
|
||||||
@Retention( RetentionPolicy.RUNTIME )
|
@Retention( RetentionPolicy.RUNTIME )
|
||||||
@Target( ElementType.FIELD )
|
@Target( {
|
||||||
|
ElementType.FIELD , ElementType.METHOD
|
||||||
|
} )
|
||||||
public @interface UseComponent
|
public @interface UseComponent
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue