For a rich domain driven design I want to use Guice dependency injection on JPA/Hibernate entity beans. I am looking for a similar solution as the Spring @configurable annotation for non-Spring beans.
Does anybody know of a library? Any code examples?开发者_JAVA技巧
You can do this with AspectJ.
Create the @Configurable annotation:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Configurable {
}
Create an AspectJ @Aspect similar to this:
@Aspect
public class ConfigurableInjectionAspect {
private Logger log = Logger.getLogger(getClass().getName());
@Pointcut("@within(Configurable) && execution(*.new(..)) && target(instantiated)")
public void classToBeInjectedOnInstantiation(Object instantiated) {}
@After(value = "classToBeInjectedOnInstantiation(instantiated)",
argNames = "instantiated")
public void onInstantiation(Object instantiated) {
Injector injector = InjectorHolder.getInjector();
if (injector == null) {
log.log(Level.WARNING, "Injector not available at this time");
} else {
injector.injectMembers(instantiated);
}
}
}
Create (and use) a holding class for your injector:
public final class InjectorHolder {
private static Injector injector;
static void setInjector(Injector injector) {
InjectorHolder.injector = injector;
}
public static Injector getInjector() {
return injector;
}
}
Configure META-INF/aop.xml:
<aspectj>
<weaver options="-verbose">
<include within="baz.domain..*"/>
<include within="foo.bar.*"/>
</weaver>
<aspects>
<aspect name="foo.bar.ConfigurableInjectionAspect"/>
</aspects>
</aspectj>
Start your VM with aspectjweaver:
-javaagent:lib/aspectjweaver.jar
Annotate your domain classes:
@Entity
@Table(name = "Users")
@Configurable
public class User {
private String username;
private String nickname;
private String emailAddress;
@Inject
private transient UserRepository userRepository
public User() {}
}
I found a bit dirty workaround for this problem.
Assuming there are only two ways to create an entity object of type T
:
- Obtaining one from a
javax.inject.Provider<T>
- Quering it from the entity manager (which will call
@PostLoad
annotated methods).
Further assuming you have a infrastructural baseclass for all of your entities, you can just add an entity listener to this entity. In this example I use static injection - maybe there is a nicer way.
@MappedSuperclass
public abstract class PersistentDomainObject<K extends Serializable & Comparable<K>>
implements Comparable<PersistentDomainObject<K>>, Serializable {
private static transient Injector injector;
@PostLoad
private final void onAfterLoaded() {
injector.injectMembers(this);
}
@EmbeddedId
private K id;
public K getId() { return id; }
// ... compareTo(), equals(), hashCode(), maybe a @Version member ...
}
In your module setup you just need to call requestStaticInjection(PersistentDomainObject.class);
Now you simply can create entity classes like
@Entity
public class MyDomainEntity extends PersistentDomainObject<SomeEmbeddableIdType>
implements HatLegacyId {
@Inject
private transient MyDomainService myDomainService;
private String name;
// ... common stuff
}
Bad thing about it, you have to trust in that noone will create a MyDomainEntity
on his own but will ask a Provider<MyDomainEntity>
for it. This could be provided by hiding the constructor.
Kind regards,
avi
Since entities are created by the JPA provider, I fail to see when Guice will come in play. Maybe have a look at the approach of the Salve project though.
精彩评论