CDI(Weld)整合<2>便携式扩展(Portable extensions)

综述:便携式扩展(Portable extensions)

CDI的目的是成为一个基础框架、扩展和与集成其余技术。
所以,CDI暴露一组为开发人员使用的便携式CDI的扩展的spi。
例如,如下类型的扩展是由CDI的设计者设想:
  • 与业务流程管理引擎的集成,
  • 集成第三方框架如Spring,Seam,GWT或Wicket,
  • 在CDI编程模型基础上的新技术集成
根据规范:一个便携式的扩展能够经过集成容器来使用:
  • 提供本身的bean,拦截器和修饰符的容器
  • 使用依赖注入将依赖项注入本身的对象服务
  • 提供一个上下文实现一个自定义的范围
  • 增长或覆盖基于注解的元数据

1. Creating an Extension

建立一个便携扩展的第一步是编写一个类,实现Extension.这个标记接口不定义任何方法.
import javax.enterprise.inject.spi.Extension;

class MyExtension implements Extension 
{ ... }
接下来,咱们须要注册扩展做为服务提供者经过建立一个文件META-INF/services/javax.enterprise.inject.spi.Extension,其中包含咱们的扩展类的完整名称:
org.mydomain.extension.MyExtension
extension不是一个bean,确切地说,由于它是由容器在初始化过程当中实例化,在任何bean或上下文以前就存在。然而,它一旦初始化过程完成,能够被注入其余bean。
@Inject 
MyBean(MyExtension myExtension) {

   myExtension.doSomething();
}
像Bean同样,extension能够有Observer方法。一般,观察者方法观察容器生命周期事件的方法。

2. Container lifecycle events

在初始化过程当中,容器触发一系列的事件,包括:
  1. BeforeBeanDiscovery
  2. ProcessAnnotatedType and ProcessSyntheticAnnotatedType
  3. AfterTypeDiscovery
  4. ProcessInjectionTarget and ProcessProducer
  5. ProcessInjectionPoint
  6. ProcessBeanAttributes
  7. ProcessBean, ProcessManagedBean, ProcessSessionBean, ProcessProducerMethod and ProcessProducerField
  8. ProcessObserverMethod
  9. AfterBeanDiscovery
  10. AfterDeploymentValidation
Extensions能够观察这些事件
import javax.enterprise.inject.spi.Extension;

class MyExtension implements Extension {

   void beforeBeanDiscovery(@Observes BeforeBeanDiscovery bbd) {
      Logger.global.debug("beginning the scanning process");
   }
      
   <T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> pat) {
      Logger.global.debug("scanning type: " + pat.getAnnotatedType().getJavaClass().getName());
   } 

   void afterBeanDiscovery(@Observes AfterBeanDiscovery abd) {
      Logger.global.debug("finished the scanning process");
   }
}
事实上,扩展能够作不少事情不只仅是观察。扩展容许修改容器的元模型等等。这里有一个很简单的例子:
import javax.enterprise.inject.spi.Extension;

class MyExtension implements Extension {
     
   <T> void processAnnotatedType(@Observes @WithAnnotations({Ignore.class}) ProcessAnnotatedType<T> pat) {

      /* tell the container to ignore the type if it is annotated @Ignore */
      if ( pat.getAnnotatedType().isAnnotationPresent(Ignore.class) ) pat.veto();   
   } 
}
New in CDI 1.1
@WithAnnotations注解致使容器在传递ProcessAnnotatedType的event事件的时候只包含指定注解的类型.

Observer method 能够注入一个BeanManager,( Observer method 不容许注入其余对象)
<T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> pat, BeanManager beanManager) { ... }

3. The BeanManager object

CDI有个重要的对象,那就是BeanManager Object.
BeanManager接口让咱们以编程方式获取bean,拦截器,修饰符、observers 和上下文。
public interface BeanManager {

   public Object getReference(Bean<?> bean, Type beanType, CreationalContext<?> ctx);

   public Object getInjectableReference(InjectionPoint ij, CreationalContext<?> ctx);

   public <T> CreationalContext<T> createCreationalContext(Contextual<T> contextual);

   public Set<Bean<?>> getBeans(Type beanType, Annotation... qualifiers);

   public Set<Bean<?>> getBeans(String name);

   public Bean<?> getPassivationCapableBean(String id);

   public <X> Bean<? extends X> resolve(Set<Bean<? extends X>> beans);

   public void validate(InjectionPoint injectionPoint);

   public void fireEvent(Object event, Annotation... qualifiers);

   public <T> Set<ObserverMethod<? super T>> resolveObserverMethods(T event, Annotation... qualifiers);

   public List<Decorator<?>> resolveDecorators(Set<Type> types, Annotation... qualifiers);

   public List<Interceptor<?>> resolveInterceptors(InterceptionType type, Annotation... interceptorBindings);

   public boolean isScope(Class<? extends Annotation> annotationType);

   public boolean isNormalScope(Class<? extends Annotation> annotationType);

   public boolean isPassivatingScope(Class<? extends Annotation> annotationType);

   public boolean isQualifier(Class<? extends Annotation> annotationType);

   public boolean isInterceptorBinding(Class<? extends Annotation> annotationType);

   public boolean isStereotype(Class<? extends Annotation> annotationType);

   public Set<Annotation> getInterceptorBindingDefinition(Class<? extends Annotation> bindingType);

   public Set<Annotation> getStereotypeDefinition(Class<? extends Annotation> stereotype);

   public boolean areQualifiersEquivalent(Annotation qualifier1, Annotation qualifier2);

   public boolean areInterceptorBindingsEquivalent(Annotation interceptorBinding1, Annotation interceptorBinding2);

   public int getQualifierHashCode(Annotation qualifier);

   public int getInterceptorBindingHashCode(Annotation interceptorBinding);

   public Context getContext(Class<? extends Annotation> scopeType);

   public ELResolver getELResolver();

   public ExpressionFactory wrapExpressionFactory(ExpressionFactory expressionFactory);

   public <T> AnnotatedType<T> createAnnotatedType(Class<T> type);

   public <T> InjectionTarget<T> createInjectionTarget(AnnotatedType<T> type);

   public <T> InjectionTargetFactory<T> getInjectionTargetFactory(AnnotatedType<T> annotatedType);

   public <X> ProducerFactory<X> getProducerFactory(AnnotatedField<? super X> field, Bean<X> declaringBean);

   public <X> ProducerFactory<X> getProducerFactory(AnnotatedMethod<? super X> method, Bean<X> declaringBean);

   public <T> BeanAttributes<T> createBeanAttributes(AnnotatedType<T> type);

   public BeanAttributes<?> createBeanAttributes(AnnotatedMember<?> type);

   public <T> Bean<T> createBean(BeanAttributes<T> attributes, Class<T> beanClass,

   public <T, X> Bean<T> createBean(BeanAttributes<T> attributes, Class<X> beanClass, ProducerFactory<X> producerFactory);

   public InjectionPoint createInjectionPoint(AnnotatedField<?> field);

   public InjectionPoint createInjectionPoint(AnnotatedParameter<?> parameter);

   public <T extends Extension> T getExtension(Class<T> extensionClass);

}

任何bean或其余Java EE组件,可经过@Inject得到BeanManager的一个实例: java

@Inject BeanManager beanManager;
另外,一个BeanManager参考可用CDI经过静态方法调用得到的。
CDI.current().getBeanManager()
Java EE组件可能得到的实例BeanManager从JNDI查找名称Java:comp / BeanManager。
让咱们研究一些BeanManager公开的接口。

4. The CDI class

当应用程序组件不能经过@Inject和javax.enterprise.inject.spi JNDI查找得到BeanManager引用。CDI类能够经过静态方法调用来获取
BeanManager manager = CDI.current().getBeanManager();
那上面代码的CDI从哪里得到?能够直接使用CDI类以编程方式查找CDI bean
CDI.select(Foo.class).get()

5. The InjectionTarget interface

一个框架开发人员的第一件事就是去寻找在便携扩展SPI方式注入不在CDI的控制下的CDI bean对象。
InjectionTarget接口使得这个很容易。

请注意
咱们建议框架让CDI接手的工做其实是初始化framework-controlled对象。
这样,能够利用构造函数注入framework-controlled对象。
然而,若是构造函数的框架须要使用特殊的签名,该框架须要实例化对象自己,因此只有用方法和字段来Inject。
import javax.enterprise.inject.spi.CDI;

...

//获取一个BeanManager
BeanManager beanManager = CDI.current().getBeanManager();

//CDI采用AnnotatedType对象来读取一个类的注解
AnnotatedType<SomeFrameworkComponent> type = beanManager.createAnnotatedType(SomeFrameworkComponent.class);

//extensions使用的InjectionTarget在CDI容器中委托实例化,依赖注入 和生命周期回调
InjectionTarget<SomeFrameworkComponent> it = beanManager.createInjectionTarget(type);

//每一个实例都须要它本身的CDI CreationalContext(CDI创造上下文)
CreationalContext ctx = beanManager.createCreationalContext(null);


//实例化的框架组件,并注入其依赖关系
SomeFrameworkComponent instance = it.produce(ctx);  //调用构造函数
it.inject(instance, ctx);  //调用初始化方法,并完成字段方式的注入
it.postConstruct(instance);  //call the @PostConstruct method
...

//摧毁的框架组件实例和清理依赖对象
it.preDestroy(instance);  //call the @PreDestroy method

it.dispose(instance);  //it is now safe to discard the instance

ctx.release();  //clean up dependent objects

6. The Bean interface

咱们能够经过如下的方式获取BeanAttributes .
public interface BeanAttributes<T> {

   public Set<Type> getTypes();

   public Set<Annotation> getQualifiers();

   public Class<? extends Annotation> getScope();

   public String getName();

   public Set<Class<? extends Annotation>> getStereotypes();

   public boolean isAlternative();
}
Bean接口扩展了BeanAttributes接口并定义全部容器须要管理的Bean实例。
public interface Bean<T> extends Contextual<T>, BeanAttributes<T> {
public interface Bean<T> extends Contextual<T>, BeanAttributes<T> {

   public Class<?> getBeanClass();

   public Set<InjectionPoint> getInjectionPoints();

   public boolean isNullable();

}
有一个简单的方法在应用程序来找出bean:
Set<Bean<?>> allBeans = beanManager.getBeans(Obect.class, new AnnotationLiteral<Any>() {});
Bean接口用便携扩展为新的Bean提供支持,除了那些CDI规范定义的.例如咱们可使用Bean接口容许对象管理由另外一个框架注入bean。

7. Registering a Bean

最多见的事情是在容器中CDI便携扩展注册bean。
在这个例子中,咱们作一个框架类,让SecurityManager可供注入。
为了让事情更有趣,咱们将委托回容器的InjectionTarget执行实例化和注入SecurityManager实例。
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.event.Observes;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.enterprise.inject.spi.InjectionPoint;
...

public class SecurityManagerExtension implements Extension {
   
    void afterBeanDiscovery(@Observes AfterBeanDiscovery abd, BeanManager bm) {
           
        //use this to read annotations of the class
        AnnotatedType<SecurityManager> at = bm.createAnnotatedType(SecurityManager.class); 

        //use this to instantiate the class and inject dependencies
        final InjectionTarget<SecurityManager> it = bm.createInjectionTarget(at); 

        abd.addBean( new Bean<SecurityManager>() {

            @Override
            public Class<?> getBeanClass() {

                return SecurityManager.class;
            }

            @Override
            public Set<InjectionPoint> getInjectionPoints() {

                return it.getInjectionPoints();
            }

            @Override
            public String getName() {

                return "securityManager";
            }

            @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 ApplicationScoped.class;
            }

            @Override
            public Set<Class<? extends Annotation>> getStereotypes() {

                return Collections.emptySet();
            }

            @Override
            public Set<Type> getTypes() {

                Set<Type> types = new HashSet<Type>();
                types.add(SecurityManager.class);
                types.add(Object.class);
                return types;
            }

            @Override
            public boolean isAlternative() {

                return false;
            }

            @Override
            public boolean isNullable() {

                return false;
            }

            @Override
            public SecurityManager create(CreationalContext<SecurityManager> ctx) {

                SecurityManager instance = it.produce(ctx);
                it.inject(instance, ctx);
                it.postConstruct(instance);
                return instance;
            }

            @Override
            public void destroy(SecurityManager instance,CreationalContext<SecurityManager> ctx) {

                it.preDestroy(instance);
                it.dispose(instance);
                ctx.release();
            }
        } );
    }
}

8. Wrapping an AnnotatedType

最有趣的事情,是一个扩展类能够在容器创建本身的元模型前处理一个bean类的注解。 
下面是2个例子,一个是关于@Named扩展的一个例子,它提供了支持使用@Named在包级别,包级别名称是在这个包中用来限定全部bean定义的EL名称。便携扩展使用ProcessAnnotatedType事件包装AnnotatedType对象和重写 @Named 注解的 value ()。
import java.lang.reflect.Type;
import javax.enterprise.inject.spi.Extension;
import java.lang.annotation.Annotation;
...

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();

        /* Only wrap AnnotatedTypes for classes with @Named packages */
        Package pkg = at.getJavaClass().getPackage();

        if ( !pkg.isAnnotationPresent(Named.class) ) {

            return;
        }

        AnnotatedType<X> wrapped = new AnnotatedType<X>() {

            class NamedLiteral extends AnnotationLiteral<Named>implements Named 
            {

                @Override
                public String value() {

                    Package pkg = at.getJavaClass().getPackage();

                    String unqualifiedName = "";

                    if (at.isAnnotationPresent(Named.class)) {

                        unqualifiedName = at.getAnnotation(Named.class).value();
                    }

                    if (unqualifiedName.isEmpty()) {

                        unqualifiedName = Introspector.decapitalize(at.getJavaClass().getSimpleName());
                    }

                    final String qualifiedName;

                    if ( pkg.isAnnotationPresent(Named.class) ) {

                        qualifiedName = pkg.getAnnotation(Named.class).value() + '.' + unqualifiedName;
                    }
                    else {
                        qualifiedName = unqualifiedName;
                    }

                    return qualifiedName;
                }
            }
           
            private final NamedLiteral namedLiteral = new NamedLiteral();

            @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)) {

                    return (T) namedLiteral;
                }
                else {

                    return at.getAnnotation(annType);
                }

            }

            @Override
            public Set<Annotation> getAnnotations() {

                Set<Annotation> original = at.getAnnotations();

                Set<Annotation> annotations = new HashSet<Annotation>();

                boolean hasNamed = false;

                for (Annotation annotation : original) {

                    if (annotation.annotationType().equals(Named.class)) {

                        annotations.add(getAnnotation(Named.class));
                        hasNamed = true;
                    }
                    else {
                        annotations.add(annotation);
                    }
                }

                if (!hasNamed) {
                    Package pkg = at.getJavaClass().getPackage();
                    if (pkg.isAnnotationPresent(Named.class)) {
                        annotations.add(getAnnotation(Named.class));
                    }
                }
                return annotations;
            }

            @Override
            public Type getBaseType() {

                return at.getBaseType();
            }

            @Override
            public Set<Type> getTypeClosure() {

                return at.getTypeClosure();
            }

            @Override
            public boolean isAnnotationPresent(Class<? extends Annotation> annType) {

                if (Named.class.equals(annType)) {
                    return true;
                }
                return at.isAnnotationPresent(annType);
            }
        };
        pat.setAnnotatedType(wrapped);
    }
}

这是第二个例子,将@Alternative注释添加到任何类,实现一个特定的服务接口。 express

import javax.enterprise.inject.spi.Extension;
import java.lang.annotation.Annotation;
...

class ServiceAlternativeExtension implements Extension {
      
   <T extends Service> void processAnnotatedType(@Observes ProcessAnnotatedType<T> pat) {

      final AnnotatedType<T> type = pat.getAnnotatedType();

      /* if the class implements Service, make it an @Alternative */
      AnnotatedType<T> wrapped = new AnnotatedType<T>() {

         class AlternativeLiteral extends AnnotationLiteral<Alternative> implements Alternative {}
       
         private final AlternativeLiteral alternativeLiteral = new AlternativeLiteral();

         @Override
         public <X extends Annotation> X getAnnotation(final Class<X> annType) {

            return (X) (annType.equals(Alternative.class) ?  alternativeLiteral : type.getAnnotation(annType));
         }

         @Override
         public Set<Annotation> getAnnotations() {

            Set<Annotation> annotations = new HashSet<Annotation>(type.getAnnotations());
            annotations.add(alternativeLiteral);
            return annotations;
         }
         
         @Override
         public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {

            return annotationType.equals(Alternative.class) ? true : type.isAnnotationPresent(annotationType);
         }
            
         /* remaining methods of AnnotatedType */
         ...
      }

      pat.setAnnotatedType(wrapped);
   } 
}

AnnotatedType 不是惟一一个能被extension.包装的. 编程


9. Overriding attributes of a bean by wrapping BeanAttributes

包装一个AnnotatedType用来对CDI元数据进行添加、删除或替换注解,是一种低级的方法,效率也不高.代码也写的多.CDI 1.1提供了更好的办法.
public interface BeanAttributes<T> {

   public Set<Type> getTypes();

   public Set<Annotation> getQualifiers();

   public Class<? extends Annotation> getScope();

   public String getName();

   public Set<Class<? extends Annotation>> getStereotypes();

   public boolean isAlternative();
}

BeanAttributes接口暴露bean的属性。对于每一个已经启用的bean容器都会Fire ProcessBeanAttributes event,这个事件容许修改bean的属性或彻底veto(否决)bean。 api

public interface ProcessBeanAttributes<T> {

    public Annotated getAnnotated();

    public BeanAttributes<T> getBeanAttributes();

    public void setBeanAttributes(BeanAttributes<T> beanAttributes);

    public void addDefinitionError(Throwable t);

    public void veto();
}

BeanManager提供了两建立BeanAttributes对象的方法: app

public <T> BeanAttributes<T> createBeanAttributes(AnnotatedType<T> type);

public BeanAttributes<?> createBeanAttributes(AnnotatedMember<?> type);

10. Wrapping an InjectionTarget

InjectionTarget接口exposes了建立、销毁组件实例的操做,注入其依赖并调用其生命周期的方法。portable extension能够将支持injection任何Java EE组件的InjectionTarget封装起来,当container调用这些操做的时候,就能够进行干预。 
      这个CDI portable extension 从properties 文件中读取键值,而后对Java EE组件(servlets, EJBs, managed beans, interceptors 之类的)进行配置。在这个例子中,为class org.mydomain.blog.Blogger 准备的配置文件是资源目录下的 org/mydomain/blog/Blogger.properties, 而且property的名称必须和field的名称一致, 因此Blogger.properties的内容以下: 
firstName=Gavin

lastName=King 框架

portable extension 的工做方式就是封装InjectionTarget,而后在 inject() 方法中对field进行赋值。 
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.InjectionPoint;

public class ConfigExtension implements Extension {

    <X> void processInjectionTarget(@Observes ProcessInjectionTarget<X> pit) {        

          /* wrap this to intercept the component lifecycle */
        final InjectionTarget<X> it = pit.getInjectionTarget();        

        final Map<Field, Object> configuredValues = new HashMap<Field, Object>();        

        /* use this to read annotations of the class and its members */
        AnnotatedType<X> at = pit.getAnnotatedType();       

        /* read the properties file */
        String propsFileName = at.getJavaClass().getSimpleName() + ".properties";

        InputStream stream = at.getJavaClass().getResourceAsStream(propsFileName);

        if (stream!=null) {

            try {
                Properties props = new Properties();
                props.load(stream);

                for (Map.Entry<Object, Object> property : props.entrySet()) {

                    String fieldName = property.getKey().toString();
                    Object value = property.getValue();

                    try {
                        Field field = at.getJavaClass().getDeclaredField(fieldName);
                        field.setAccessible(true);
                        if ( field.getType().isAssignableFrom( value.getClass() ) ) {

                            configuredValues.put(field, value);
                        }
                        else {
                            /* TODO: do type conversion automatically */
                            pit.addDefinitionError( new InjectionException("field is not of type String: " + field ) );
                        }
                    }
                    catch (NoSuchFieldException nsfe) {

                        pit.addDefinitionError(nsfe);
                    }
                    finally {
                        stream.close();
                    }
                }
            }
            catch (IOException ioe) {
                pit.addDefinitionError(ioe);
            }
        }
        

        InjectionTarget<X> wrapped = new InjectionTarget<X>() {

            @Override
            public void inject(X instance, CreationalContext<X> ctx) {

                it.inject(instance, ctx);

                /* set the values onto the new instance of the component */
                for (Map.Entry<Field, Object> configuredValue: configuredValues.entrySet()) {

                    try {
                        configuredValue.getKey().set(instance, configuredValue.getValue());
                    }
                    catch (Exception e) {
                        throw new InjectionException(e);
                    }
                }
            }

            @Override
            public void postConstruct(X instance) {
                it.postConstruct(instance);
            }


            @Override
            public void preDestroy(X instance) {
                it.dispose(instance);
            }


            @Override
            public void dispose(X instance) {

                it.dispose(instance);
            }

            @Override
            public Set<InjectionPoint> getInjectionPoints() {

                return it.getInjectionPoints();
            }

            @Override
            public X produce(CreationalContext<X> ctx) {
                return it.produce(ctx);
            }
        };

        pit.setInjectionTarget(wrapped);
    }    
}

11. Overriding InjectionPoint

CDI provides a way to override the metadata of an InjectionPoint. This works similarly to how metadata of a bean may be overridden using BeanAttributes.
For every injection point of each component supporting injection Weld fires an event of type javax.enterprise.inject.spi.ProcessInjectionPoint
public interface ProcessInjectionPoint<T, X> {

    public InjectionPoint getInjectionPoint();

    public void setInjectionPoint(InjectionPoint injectionPoint);

    public void addDefinitionError(Throwable t);
}
An extension may either completely override the injection point metadata or alter it by wrapping the InjectionPoint object obtained from ProcessInjectionPoint.getInjectionPoint()
There's a lot more to the portable extension SPI than what we've discussed here. Check out the CDI spec or Javadoc for more information. For now, we'll just mention one more extension point.

12 Manipulating interceptors, decorators and alternatives enabled for an application

An event of type javax.enterprise.inject.spi.AfterTypeDiscovery is fired when the container has fully completed the type discovery process and before it begins the bean discovery process.
public interface AfterTypeDiscovery {

    public List<Class<?>> getAlternatives();

    public List<Class<?>> getInterceptors();

    public List<Class<?>> getDecorators();

    public void addAnnotatedType(AnnotatedType<?> type, String id);
}
This event exposes a list of enabled alternatives, interceptors and decorators. Extensions may manipulate these collections directly to add, remove or change the order of the enabled records.
In addition, an AnnotatedType can be added to the types which will be scanned during bean discovery, with an identifier, which allows multiple annotated types, based on the same underlying type, to be defined.

13. The Context and AlterableContext interfaces

上下文和AlterableContext接口支持添加新的CDI范围,或扩展内置的范围。
public interface Context {

   public Class<? extends Annotation> getScope();

   public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext);

   public <T> T get(Contextual<T> contextual);

   boolean isActive();

}
例如,咱们可能会实现上下文添加一个业务流程范围CDI,或将支持conversation做用域添加到一个应用程序,该应用程序使用Wicket。
import javax.enterprise.context.spi.Context;

public interface AlterableContext extends Context {

    public void destroy(Contextual<?> contextual);

}
AlterableContext CDI 1.1中引入的。销毁方法容许应用程序从一个上下文删除上下文对象的实例。
通常人也不会用这个功能,除非特殊的需求,
具体的建立请看 http://in.relation.to/Bloggers/CreatingACustomScope

Over. dom

相关文章
相关标签/搜索