CDI最令人喜欢的功能之一是其便携式扩展SPI。根据规范
便携式扩展可以通过以下方式与容器集成
- 向容器提供自己的bean、拦截器和装饰器
- 使用依赖注入服务将其依赖项注入其自己的对象中
- 为自定义范围提供上下文实现
- 使用来自其他来源的元数据增强或覆盖基于注解的元数据
我们有很多CDI应用程序代码的示例,但没有太多针对希望集成CDI或增强内置功能的框架开发者的示例。所以这里有一些CDI便携式扩展的示例。
让我们从一个示例开始,这个示例提供了一个对在包级别使用@Named的支持。包级别的名称用于限定该包中定义的所有bean的EL名称。
便携式扩展实现标记接口扩展并观察容器生命周期事件,在这种情况下ProcessAnnotatedType,这是一个由容器在发现bean存档中的类或接口时触发的事件。便携式扩展包装AnnotatedType对象并覆盖bean的value()注解的@Named值。
public class QualifiedNameExtension implements Extension {
<X> void processAnnotatedType(@Observes ProcessAnnotatedType<X> pat) {
//wrap this to override the annotations of the class
final AnnotatedType<X> at = pat.getAnnotatedType();
AnnotatedType<X> wrapped = new AnnotatedType<X>() {
@Override
public Set<AnnotatedConstructor<X>> getConstructors() {
return at.getConstructors();
}
@Override
public Set<AnnotatedField<? super X>> getFields() {
return at.getFields();
}
@Override
public Class<X> getJavaClass() {
return at.getJavaClass();
}
@Override
public Set<AnnotatedMethod<? super X>> getMethods() {
return at.getMethods();
}
@Override
public <T extends Annotation> T getAnnotation(final Class<T> annType) {
if ( Named.class.equals(annType) ) {
class NamedLiteral
extends AnnotationLiteral<Named>
implements Named {
@Override
public String value() {
Package pkg = at.getClass().getPackage();
String unqualifiedName = at.getAnnotation(Named.class).value();
final String qualifiedName;
if ( pkg.isAnnotationPresent(Named.class) ) {
qualifiedName = pkg.getAnnotation(Named.class).value()
+ '.' + unqualifiedName;
}
else {
qualifiedName = unqualifiedName;
}
return qualifiedName;
}
}
return (T) new NamedLiteral();
}
else {
return at.getAnnotation(annType);
}
}
@Override
public Set<Annotation> getAnnotations() {
return at.getAnnotations();
}
@Override
public Type getBaseType() {
return at.getBaseType();
}
@Override
public Set<Type> getTypeClosure() {
return at.getTypeClosure();
}
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annType) {
return at.isAnnotationPresent(annType);
}
};
pat.setAnnotatedType(wrapped);
}
}
我们需要将这个便携式扩展部署到一个jar中,该jar包含一个名为javax.enterprise.inject.spi.Extension的文件,位于META-INF/services目录下。此文件应包含我们QualifiedNameExtension类的全名。
这是第二个示例,是一个创建Bean对象并为每个类注册bean的便携式扩展。这与容器为bean存档中发现的类所做的类似。
public class RegisterExtension implements Extension {
List<Class<?>> getBeanClasses() {
//get some classes from somewhere
}
void afterBeanDiscovery(@Observes AfterBeanDiscovery abd, BeanManager bm) {
for ( final Class c: getBeanClasses() ) {
//use this to read annotations of the class
AnnotatedType at = bm.createAnnotatedType(c);
//use this to create the class and inject dependencies
final InjectionTarget it = bm.createInjectionTarget(at);
abd.addBean( new Bean() {
@Override
public Class<?> getBeanClass() {
return c;
}
@Override
public Set<InjectionPoint> getInjectionPoints() {
return it.getInjectionPoints();
}
@Override
public String getName() {
return null;
}
@Override
public Set<Annotation> getQualifiers() {
Set<Annotation> qualifiers = new HashSet<Annotation>();
qualifiers.add( new AnnotationLiteral<Default>() {} );
qualifiers.add( new AnnotationLiteral<Any>() {} );
return qualifiers;
}
@Override
public Class<? extends Annotation> getScope() {
return Dependent.class;
}
@Override
public Set<Class<? extends Annotation>> getStereotypes() {
return Collections.emptySet();
}
@Override
public Set<Type> getTypes() {
Set<Type> types = new HashSet<Type>();
types.add(c);
types.add(Object.class);
return types;
}
@Override
public boolean isAlternative() {
return false;
}
@Override
public boolean isNullable() {
return false;
}
@Override
public Object create(CreationalContext ctx) {
Object instance = it.produce(ctx);
it.inject(instance, ctx);
it.postConstruct(instance);
return instance;
}
@Override
public void destroy(Object instance, CreationalContext ctx) {
it.preDestroy(instance);
it.dispose(instance);
ctx.release();
}
} );
}
}
}
更新:我在Weld参考文档中终于写了一个关于便携式扩展的适当章节。如果您对这个帖子感兴趣,请查看这个版本的第16章。