php cdi
今天,我将向您展示如何编写CDI扩展。
CDI提供了一种扩展功能的简便方法,例如
- 添加自己的范围,
- 启用Java核心类进行扩展,
- 使用注释元数据进行扩充或修改,
- 以及更多。
在本教程中,我们将实现一个扩展,该扩展将从属性文件中注入属性,就像往常一样,我将在github [更新: sources @ github ]中提供源代码 。
目标
提供扩展,使我们能够执行以下操作
@PropertyFile("myProps.txt")
public class MyProperties {@Property("version")Integer version;@Property("appname")String appname;
}
其中版本和应用程序的名字在文件myProps.txt定义。
制备
首先,我们需要CDI API的依赖项
dependencies.compile "javax.enterprise:cdi-api:1.1-20130918"
现在我们可以开始了。 那么我们
弄湿
基础
每个CDI扩展的入口点都是一个实现javax.enterprise.inject.spi.Extension的类
package com.coderskitchen.propertyloader;import javax.enterprise.inject.spi.Extension;
public class PropertyLoaderExtension implements Extension {
// More code later
}
另外,我们必须将此类的全限定名添加到META-INF / services目录中名为javax.enterprise.inject.spi.Extension的文件中。
javax.enterprise.inject.spi.Extension
com.coderskitchen.propertyloader.PropertyLoaderExtension
这些是编写CDI扩展的基本步骤。
背景资料
CDI使用Java SE的服务提供商体系结构,这就是为什么我们需要实现标记接口并将文件与实现类的FQN添加在一起的原因。
潜水更深
现在,我们必须选择正确的事件来收听。
背景资料
CDI规范定义了几个在应用程序初始化期间由容器触发的事件。
例如,在容器以bean发现开始之前,将触发BeforeBeanDiscovery 。
对于本教程,我们需要监听ProcessInjectionTarget事件。 对于发现的每个Java类,接口或枚举,都会触发此事件,并且可能在运行时由容器实例化该事件。
因此,让我们添加此事件的观察者:
public <T> void initializePropertyLoading(final @Observes ProcessInjectionTarget<T> pit) {
}
ProcessInjectionTarget通过getAnnotatedType方法授予对基础类的访问权限,并通过getInjectionTarget授予创建中的实例的访问权限。 我们使用annotatedType获取类的注释,以检查@PropertyFile是否可用。 如果没有,我们将直接作为短路返回。
随后, InjectionTarget用于覆盖当前行为并从属性文件中设置值。
public <T> void initializePropertyLoading(final @Observes ProcessInjectionTarget<T> pit) {AnnotatedType<T> at = pit.getAnnotatedType();if(!at.isAnnotationPresent(PropertyFile.class)) {return;}}
在本教程中,我们假定属性文件直接位于类路径的根目录中。 基于此假设,我们可以添加以下代码以从文件中加载属性
PropertyFile propertyFile = at.getAnnotation(PropertyFile.class);
String filename = propertyFile.value();
InputStream propertiesStream = getClass().getResourceAsStream("/" + filename);
Properties properties = new Properties();
try {properties.load(propertiesStream);assignPropertiesToFields(at.getFields, properties); // Implementation follows
} catch (IOException e) {e.printStackTrace();
}
现在,我们可以将属性值分配给字段。 但是对于CDI,我们必须以略有不同的方式执行此操作。 我们应该使用InjectionTarget并覆盖当前的AnnotatedType。 这使CDI可以确保所有事情都可以按正确的顺序进行。
为了实现这一点,我们使用了最终的Map <Field,Object> ,我们可以在其中存储当前分配以供以后在InjectionTarget中使用 。 映射是在方法AssignPropertiesToFields中完成的。
private <T> void assignPropertiesToFields(Set<AnnotatedField<? super T>> fields, Properties properties) {for (AnnotatedField<? super T> field : fields) {if(field.isAnnotationPresent(Property.class)) {Property property = field.getAnnotation(Property.class);String value = properties.getProperty(property.value());Type baseType = field.getBaseType();fieldValues.put(memberField, value);}}
最后,我们现在将创建一个新的InjectionTarget,以将字段值分配给所有新创建的基础类实例。
final InjectionTarget<T> it = pit.getInjectionTarget();InjectionTarget<T> wrapped = new InjectionTarget<T>() {@Overridepublic void inject(T instance, CreationalContext<T> ctx) {it.inject(instance, ctx);for (Map.Entry<Field, Object> property: fieldValues.entrySet()) {try {Field key = property.getKey();key.setAccessible(true);Class<?> baseType = key.getType();String value = property.getValue().toString();if (baseType == String.class) {key.set(instance, value);} else if (baseType == Integer.class) {key.set(instance, Integer.valueOf(value));} else {pit.addDefinitionError(new InjectionException("Type " + baseType + " of Field " + key.getName() + " not recognized yet!"));}} catch (Exception e) {pit.addDefinitionError(new InjectionException(e));}}}@Overridepublic void postConstruct(T instance) {it.postConstruct(instance);}@Overridepublic void preDestroy(T instance) {it.dispose(instance);}@Overridepublic void dispose(T instance) {it.dispose(instance);}@Overridepublic Set<InjectionPoint> getInjectionPoints() {return it.getInjectionPoints();}@Overridepublic T produce(CreationalContext<T> ctx) {return it.produce(ctx);}};pit.setInjectionTarget(wrapped);
这就是做魔术的全部。 最后,这里是ProperyLoaderExtension的完整代码。
PropertyLoaderExtension
package com.coderskitchen.propertyloader;import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.InjectionException;
import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.enterprise.inject.spi.InjectionTarget;
import javax.enterprise.inject.spi.ProcessInjectionTarget;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;public class PropertyLoaderExtension implements Extension {final Map<Field, Object> fieldValues = new HashMap<Field, Object>();public <T> void initializePropertyLoading(final @Observes ProcessInjectionTarget<T> pit) {AnnotatedType<T> at = pit.getAnnotatedType();if(!at.isAnnotationPresent(PropertyyFile.class)) {return;}PropertyyFile propertyyFile = at.getAnnotation(PropertyyFile.class);String filename = propertyyFile.value();InputStream propertiesStream = getClass().getResourceAsStream("/" + filename);Properties properties = new Properties();try {properties.load(propertiesStream);assignPropertiesToFields(at.getFields(), properties);} catch (IOException e) {e.printStackTrace();}final InjectionTarget<T> it = pit.getInjectionTarget();InjectionTarget<T> wrapped = new InjectionTarget<T>() {@Overridepublic void inject(T instance, CreationalContext<T> ctx) {it.inject(instance, ctx);for (Map.Entry<Field, Object> property: fieldValues.entrySet()) {try {Field key = property.getKey();key.setAccessible(true);Class<?> baseType = key.getType();String value = property.getValue().toString();if (baseType == String.class) {key.set(instance, value);} else if (baseType == Integer.class) {key.set(instance, Integer.valueOf(value));} else {pit.addDefinitionError(new InjectionException("Type " + baseType + " of Field " + key.getName() + " not recognized yet!"));}} catch (Exception e) {pit.addDefinitionError(new InjectionException(e));}}}@Overridepublic void postConstruct(T instance) {it.postConstruct(instance);}@Overridepublic void preDestroy(T instance) {it.dispose(instance);}@Overridepublic void dispose(T instance) {it.dispose(instance);}@Overridepublic Set<InjectionPoint> getInjectionPoints() {return it.getInjectionPoints();}@Overridepublic T produce(CreationalContext<T> ctx) {return it.produce(ctx);}};pit.setInjectionTarget(wrapped);}private <T> void assignPropertiesToFields(Set<AnnotatedField<? super T>> fields, Properties properties) {for (AnnotatedField<? super T> field : fields) {if(field.isAnnotationPresent(Propertyy.class)) {Propertyy propertyy = field.getAnnotation(Propertyy.class);Object value = properties.get(propertyy.value());Field memberField = field.getJavaMember();fieldValues.put(memberField, value);}}}
}
资料下载
完整的源代码将在git hub上提供,直到星期一晚上。 jar存档可在此处PropertyLoaderExtension中找到 。
最后的笔记
本教程说明了如何简单地向CDI框架添加新功能。 在几行代码中,添加了工作属性加载和注入机制。 在应用程序的生命周期内触发的事件提供了一种松散耦合且功能强大的方法来添加新功能,拦截bean创建或更改行为。
还可以通过引入一组生产者方法来实现属性注入,但是这种方法需要每种字段类型使用一个生产者方法。 使用这种通用方法,您只需要添加新的转换器即可启用其他类型的值的注入。
翻译自: https://www.javacodegeeks.com/2014/02/tutorial-writing-your-own-cdi-extension.html
php cdi