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章。


返回顶部