Skip to content

Commit

Permalink
Injector: Separate @Inject into two annotations for clarity
Browse files Browse the repository at this point in the history
  • Loading branch information
Emil Forslund committed Jun 21, 2016
1 parent 4471fd4 commit ad7f097
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 90 deletions.
Expand Up @@ -16,7 +16,6 @@
*/ */
package com.speedment.common.injector.annotation; package com.speedment.common.injector.annotation;


import com.speedment.common.injector.State;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited; import java.lang.annotation.Inherited;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
Expand All @@ -25,36 +24,11 @@


/** /**
* Annotes a field that should be set automatically using dependency injection. * Annotes a field that should be set automatically using dependency injection.
* Each injected field has a specified state that it is required to be in. It
* is up to the injector to make sure that a type has been properly setup.
* *
* @author Emil Forslund * @author Emil Forslund
* @since 1.0.0 * @since 1.0.0
*/ */
@Inherited @Inherited
@Target({ElementType.FIELD, ElementType.PARAMETER}) @Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface Inject { public @interface Inject {}

/**
* The {@link State} that the injected value must be in before
* it can be injected.
* <p>
* The default state is {@link State#STARTED}.
*
* @return the expected phase of the injected component
*/
State value() default State.STARTED;

/**
* If the injector should throw an exception if no implementation
* for this value could be found. If {@code required} is false, then
* the value will simply be set to null.
* <p>
* The default is that every injected value is required.
*
* @return if this value is required
*/
boolean required() default true;

}
@@ -0,0 +1,47 @@
/**
*
* Copyright (c) 2006-2016, Speedment, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); You may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.speedment.common.injector.annotation;

import com.speedment.common.injector.State;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Annotes that the method parameter should be set to
* something with a certain state.
*
* @author Emil Forslund
* @since 1.0.0
*/
@Inherited
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface WithState {

/**
* The {@link State} that the injected value must be in before
* it can be injected.
* <p>
* The default state is {@link State#CREATED}.
*
* @return the expected phase of the injected component
*/
State value() default State.CREATED;
}
Expand Up @@ -53,6 +53,7 @@
import java.util.Optional; import java.util.Optional;
import java.util.Properties; import java.util.Properties;
import com.speedment.common.injector.annotation.IncludeInjectable; import com.speedment.common.injector.annotation.IncludeInjectable;
import com.speedment.common.injector.annotation.WithState;
import com.speedment.common.logger.Logger; import com.speedment.common.logger.Logger;
import com.speedment.common.logger.LoggerManager; import com.speedment.common.logger.LoggerManager;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
Expand Down Expand Up @@ -128,7 +129,7 @@ public void stop() {
.map(Execution::getMethod) .map(Execution::getMethod)
.forEach(m -> { .forEach(m -> {
final Object[] params = Stream.of(m.getParameters()) final Object[] params = Stream.of(m.getParameters())
.map(p -> findIn(p.getType(), p.getAnnotation(Inject.class).required())) .map(p -> findIn(p.getType(), p.getAnnotation(WithState.class) != null))
.toArray(Object[]::new); .toArray(Object[]::new);


m.setAccessible(true); m.setAccessible(true);
Expand Down Expand Up @@ -222,7 +223,7 @@ private <T> void injectFields(T instance) {
if (Injector.class.isAssignableFrom(field.getType())) { if (Injector.class.isAssignableFrom(field.getType())) {
value = this; value = this;
} else { } else {
value = findIn(field.getType(), field.getAnnotation(Inject.class).required()); value = findIn(field.getType(), field.getAnnotation(WithState.class) != null);
} }


field.setAccessible(true); field.setAccessible(true);
Expand Down Expand Up @@ -332,7 +333,7 @@ public Injector build() throws InstantiationException, NoDefaultConstructorExcep
if (Inject.class.isAssignableFrom(field.getType())) { if (Inject.class.isAssignableFrom(field.getType())) {
value = injector; value = injector;
} else { } else {
value = findIn(injector, field.getType(), instances, field.getAnnotation(Inject.class).required()); value = findIn(injector, field.getType(), instances, field.getAnnotation(WithState.class) != null);
} }


field.setAccessible(true); field.setAccessible(true);
Expand Down Expand Up @@ -379,7 +380,7 @@ public Injector build() throws InstantiationException, NoDefaultConstructorExcep
.map(Execution::getMethod) .map(Execution::getMethod)
.forEach(m -> { .forEach(m -> {
final Object[] params = Stream.of(m.getParameters()) final Object[] params = Stream.of(m.getParameters())
.map(p -> findIn(injector, p.getType(), instances, p.getAnnotation(Inject.class).required())) .map(p -> findIn(injector, p.getType(), instances, p.getAnnotation(WithState.class) != null))
.toArray(Object[]::new); .toArray(Object[]::new);


m.setAccessible(true); m.setAccessible(true);
Expand Down
Expand Up @@ -18,15 +18,14 @@


import com.speedment.common.injector.annotation.Execute; import com.speedment.common.injector.annotation.Execute;
import com.speedment.common.injector.annotation.ExecuteBefore; import com.speedment.common.injector.annotation.ExecuteBefore;
import com.speedment.common.injector.annotation.Inject;
import com.speedment.common.injector.exception.CyclicReferenceException; import com.speedment.common.injector.exception.CyclicReferenceException;
import com.speedment.common.injector.internal.dependency.Dependency; import com.speedment.common.injector.internal.dependency.Dependency;
import com.speedment.common.injector.internal.dependency.DependencyGraph; import com.speedment.common.injector.internal.dependency.DependencyGraph;
import com.speedment.common.injector.internal.dependency.DependencyNode; import com.speedment.common.injector.internal.dependency.DependencyNode;
import com.speedment.common.injector.internal.dependency.Execution; import com.speedment.common.injector.internal.dependency.Execution;
import static com.speedment.common.injector.internal.util.ReflectionUtil.traverseFields;
import static com.speedment.common.injector.internal.util.ReflectionUtil.traverseMethods; import static com.speedment.common.injector.internal.util.ReflectionUtil.traverseMethods;
import com.speedment.common.injector.State; import com.speedment.common.injector.State;
import com.speedment.common.injector.annotation.WithState;
import com.speedment.common.injector.internal.InjectorImpl; import com.speedment.common.injector.internal.InjectorImpl;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Parameter; import java.lang.reflect.Parameter;
Expand Down Expand Up @@ -81,10 +80,6 @@ public DependencyNode getOrCreate(Class<?> clazz) {
@Override @Override
public DependencyGraph inject() { public DependencyGraph inject() {
nodes.forEach((clazz, node) -> { nodes.forEach((clazz, node) -> {
// Go through all the member variables with the '@Inject'-annotation in the
// class and add edges to them in the dependency graph.
addDependenciesTo(clazz, node.getDependencies());

// Go through all the methods with the '@Execute'-annotation in the class and // Go through all the methods with the '@Execute'-annotation in the class and
// add them to the executors set. // add them to the executors set.
traverseMethods(clazz) traverseMethods(clazz)
Expand Down Expand Up @@ -120,55 +115,26 @@ private static String methodName(Method m) {
")"; ")";
} }


private void addDependenciesTo(Class<?> clazz, Set<Dependency> dependencies) {
traverseFields(clazz)
.filter(f -> f.isAnnotationPresent(Inject.class))
.forEach(f -> {

final Inject inject = f.getAnnotation(Inject.class);
final Class<?> type = f.getType();
final State state = inject.value();

try {
dependencies.add(
new DependencyImpl(get(type), state)
);
} catch (final IllegalArgumentException ex) {
throw new IllegalStateException(
"Class " + clazz.getSimpleName() +
" requested field " + type.getSimpleName() +
" to be injected, but it was not found. Available components: [" +
nodes.keySet().stream()
.map(Class::getSimpleName)
.collect(joining(",\n ", "\n ", "\n")) +
"].",
ex
);
}
});
}

private Execution createExecution(Class<?> clazz, Method m, State executeBefore) { private Execution createExecution(Class<?> clazz, Method m, State executeBefore) {


// Make sure all the methods parameters are annoted with the // Make sure all the methods parameters are annoted with the
// '@Inject'-annotation. // '@WithState'-annotation.
if (Stream.of(m.getParameters()) if (Stream.of(m.getParameters())
.anyMatch(p -> !p.isAnnotationPresent(Inject.class))) { .anyMatch(p -> !p.isAnnotationPresent(WithState.class))) {
throw new RuntimeException( throw new RuntimeException(
"Method '" + methodName(m) + "Method '" + methodName(m) +
"' has a parameter that is missing the @" + "' has a parameter that is missing the @" +
Inject.class.getSimpleName() + " annotation." WithState.class.getSimpleName() + " annotation."
); );
} }



final Set<Dependency> dependencies = new HashSet<>(); final Set<Dependency> dependencies = new HashSet<>();
try { try {
for (int i = 0; i < m.getParameterCount(); i++) { for (int i = 0; i < m.getParameterCount(); i++) {
final Parameter p = m.getParameters()[i]; final Parameter p = m.getParameters()[i];
final Inject inject = p.getAnnotation(Inject.class); final WithState ws = p.getAnnotation(WithState.class);
final Class<?> type = p.getType(); final Class<?> type = p.getType();
final State state = inject.value(); final State state = ws.value();


try { try {
dependencies.add( dependencies.add(
Expand All @@ -185,8 +151,6 @@ private Execution createExecution(Class<?> clazz, Method m, State executeBefore)
ex ex
); );
} }

addDependenciesTo(clazz, dependencies);


return new ExecutionImpl(executeBefore, m, dependencies); return new ExecutionImpl(executeBefore, m, dependencies);
} }
Expand Down
Expand Up @@ -18,7 +18,7 @@


import static com.speedment.common.injector.State.RESOLVED; import static com.speedment.common.injector.State.RESOLVED;
import com.speedment.common.injector.annotation.ExecuteBefore; import com.speedment.common.injector.annotation.ExecuteBefore;
import com.speedment.common.injector.annotation.Inject; import com.speedment.common.injector.annotation.WithState;


/** /**
* *
Expand All @@ -28,7 +28,7 @@
public final class StringIdentityMapper implements TypeMapper<String, String> { public final class StringIdentityMapper implements TypeMapper<String, String> {


@ExecuteBefore(RESOLVED) @ExecuteBefore(RESOLVED)
protected void install(@Inject(RESOLVED) TypeMapperComponent mappers) { protected void install(@WithState(RESOLVED) TypeMapperComponent mappers) {
mappers.install(String.class, String.class, this); mappers.install(String.class, String.class, this);
} }


Expand Down
Expand Up @@ -16,7 +16,6 @@
*/ */
package com.speedment.common.injector.test_b; package com.speedment.common.injector.test_b;


import com.speedment.common.injector.State;
import com.speedment.common.injector.annotation.Inject; import com.speedment.common.injector.annotation.Inject;


/** /**
Expand All @@ -26,7 +25,7 @@
*/ */
public final class A { public final class A {


public @Inject(State.INITIALIZED) B b; public @Inject B b;
public @Inject(State.RESOLVED) C c; public @Inject C c;


} }
Expand Up @@ -17,9 +17,9 @@
package com.speedment.common.injector.test_b; package com.speedment.common.injector.test_b;


import com.speedment.common.injector.State; import com.speedment.common.injector.State;
import com.speedment.common.injector.annotation.Execute;
import com.speedment.common.injector.annotation.ExecuteBefore; import com.speedment.common.injector.annotation.ExecuteBefore;
import com.speedment.common.injector.annotation.Inject; import com.speedment.common.injector.annotation.Inject;
import com.speedment.common.injector.annotation.WithState;


/** /**
* *
Expand All @@ -28,12 +28,9 @@
*/ */
public final class B { public final class B {


public @Inject(State.INITIALIZED) A a; public @Inject A a;
public @Inject(State.RESOLVED) C c; public @Inject C c;


@Execute
protected void anytime() {}

@ExecuteBefore(State.RESOLVED) @ExecuteBefore(State.RESOLVED)
protected void beforeResolve() {} protected void beforeResolve(@WithState(State.INITIALIZED) A a, @WithState(State.RESOLVED) C c) {}
} }
Expand Up @@ -16,7 +16,6 @@
*/ */
package com.speedment.common.injector.test_b; package com.speedment.common.injector.test_b;


import com.speedment.common.injector.State;
import com.speedment.common.injector.annotation.Inject; import com.speedment.common.injector.annotation.Inject;


/** /**
Expand All @@ -26,7 +25,7 @@
*/ */
public final class C { public final class C {


public @Inject(State.INITIALIZED) A a; public @Inject A a;
public @Inject(State.RESOLVED) B b; public @Inject B b;


} }

0 comments on commit ad7f097

Please sign in to comment.