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

相關文章
相關標籤/搜索