[weld] weld终极武器-Portable extension
wuhaixing
2010-06-21
CDI要成为框架的基础,能够扩展、集成其他技术。因此,CDI提供了一组SPI给开发人员构建portable extension。比如CDI的设计者已经考虑的下面这些扩展:
官方说法: portable extension 可以通过如下方式与容器集成:
|
|
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
容器干的那些事
创世纪,容器说,要有事:
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接口
你得知道有这么个东西,但怎么用,自己研究研究吧。 |
相关讨论
相关资源推荐
- weld-parent-6.pom
- sting流:istringstream,ostringstream,stringstream。教会你str()和clear()的区别
- 初级 Java I/O 综述(三)
- weld-se-core-2.2.14.Final.zip
- weld-ANSYS-apdl.zip_ANSYS热源加载_apdl-weld_cooling heat_weld-_焊接APD
- weld-porting-package-tck11-2.2.0.Final.zip
- weld-spi-1.0-SP1.jar
- weld-api-1.0-SP1.jar
- weld-core-1.0.1-SP3.jar
- weld-se-core-2.0.1.Final.zip