[weld] weld终极武器-Portable extension

wuhaixing 2010-06-21
       CDI要成为框架的基础,能够扩展、集成其他技术。因此,CDI提供了一组SPI给开发人员构建portable extension。比如CDI的设计者已经考虑的下面这些扩展:
  • 与BMP(业务流程管理)引擎的集成;
  • 与第三方框架集成,比如spring,seam,GWT或wicket等
  • 与基于CDI编程模型的新技术集成

官方说法:
portable extension 可以通过如下方式与容器集成:
  • 向容器提供自己的beans,interceptors和decorators;
  • 使用依赖注入服务将依赖组件注入到自己的对象中;
  • 为定制的scope提供context的实现;
  • 修饰或重载基于注解的metadata
wuhaixing 2010-06-21
创建extension
两步:
1.创建一个实现了Extension接口(标,没有方法)的类
2.创建一个文件 META-INF/services/javax.enterprise.inject.spi.Extension,文件内容是你的Extension类名,比如:org.mydomain.extension.MyExtension

extension不是bean,但因为是container在初始化阶段创建的(在任何bean 或 context存在之前),所以初始化阶段一完成就可以被injected到其他的bean中。
extension可以和bean一样有observes方法,通常关注的都是container生命周期事件。
wuhaixing 2010-06-21
容器干的那些事
创世纪,容器说,要有事:
  • BeforeBeanDiscovery
  • ProcessAnnotatedType
  • ProcessInjectionTarget and ProcessProducer
  • ProcessBean and ProcessObserverMethod
  • AfterBeanDiscovery
  • AfterDeploymentValidation


Extensions 可以关注这些事件:
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");
   }
 
} 

实际上,extension可以不止是看看,他被允许修改container的metamodel,甚至更多,下面是个简单的例子:
class MyExtension implements Extension {
   <T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> pat) {
      //tell the container to ignore the type if it is annotated @Ignore
      if ( pat.getAnnotatedType().isAnnotionPresent(Ignore.class) ) pat.veto();   
   } 
}


observes方法还可以注入BeanManager
<T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> pat, BeanManager beanManager) { ... }
wuhaixing 2010-06-21
BeanManager对象
这个是CDI 扩展最关键的对象,可以在程序中通过这个接口获取beans, interceptors, decorators, observers 和contexts。
http://docs.jboss.org/cdi/api/1.0/javax/enterprise/inject/spi/BeanManager.html

可以在任何bean或支持injection 的java ee组件中injection这个对象:
@Inject BeanManager beanManager;

java ee组件也可以通过JNDI (java:comp/BeanManager)来取得该对象。
任何时候,都可以在程序中调用BeanManager的所有操作。
wuhaixing 2010-06-21
InjectionTarget
框架开发者在Portable extension SPI中首先要解决的就是如何把CDI中的bean注入到CDI控制之外的对象中。InjectionTarget 接口就是为此而生的。

最好是让CDI来接管framework-controlled对象的实例化工作。这些对象就可以使用constructor注入了。然而如果framework需要对constructor做特殊处理,那就只能自己初始化对象,因此只能用method和field injection。

//get the BeanManager from JNDI

BeanManager beanManager = (BeanManager) new InitialContext().lookup("java:comp/BeanManager");


//CDI uses an AnnotatedType object to read the annotations of a class

AnnotatedType<SomeFrameworkComponent> type = beanManager.createAnnotatedType(SomeFrameworkComponent.class);


//The extension uses an InjectionTarget to delegate instantiation, dependency injection 

//and lifecycle callbacks to the CDI container

InjectionTarget<SomeFrameworkComponent> it = beanManager.createInjectionTarget(type);


//each instance needs its own CDI CreationalContext

CreationalContext ctx = beanManager.createCreationalContext(null);


//instantiate the framework component and inject its dependencies

SomeFrameworkComponent instance = it.produce(ctx);  //call the constructor

it.inject(instance, ctx);  //call initializer methods and perform field injection

it.postConstruct(instance);  //call the @PostConstruct method


...


//destroy the framework component instance and clean up dependent objects

it.preDestroy(instance);  //call the @PreDestroy method

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

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

wuhaixing 2010-06-21
Bean接口
bean接口的实例就是beans,应用程序中的每个bean在BeanManager中都会有个Bean实例。甚至是表示interceptors, decorators 和 producer 方法的Bean对象。

Bean接口提供了访问在2.1 bean的组成元素中所有有趣东西的方法。

public interface Bean<T> extends Contextual<T> {

   public Set<Type> getTypes();

   public Set<Annotation> getQualifiers();

   public Class<? extends Annotation> getScope();

   public String getName();

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

   public Class<?> getBeanClass();

   public boolean isAlternative();

   public boolean isNullable();

   public Set<InjectionPoint> getInjectionPoints();

}


要得到程序中所有的bean,只需要一行代码

Set<Bean<?>> allBeans = beanManager.getBeans(Obect.class, new AnnotationLiteral<Any>() {});:


wuhaixing 2010-06-21
注册一个Bean
最常见的CDI Portable extension 就是向container注册bean。
在下面的例子中,我们将一个框架类 SecurityManager变成可以注入,更有趣的是我们要将SecurityManager的实例化和注入代理到InjectionTarget中。
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 SessionScoped.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();
            }
            
        } );
    }
}


但portable extension也可能和container自动发现的bean混淆。
wuhaixing 2010-06-21
封装AnnotatedType
       extension 类最有趣的特长之一就是可以在container构建bean的metamodel之前处理其annotation。
      下面这个extension支持package级别的@Named,用于标记package中所有bean的EL name。Portable extension用ProcessAnnotatedType事件封装AnnotatedType对象,并override@Named标记的value()。
    
 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);

    }

    

}

wuhaixing 2010-06-21
封装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进行赋值。

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.getClass().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().getField(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);

        

    }

    

}
wuhaixing 2010-06-21
Context接口
你得知道有这么个东西,但怎么用,自己研究研究吧。
Global site tag (gtag.js) - Google Analytics