cdi name 日志_教程:编写自己的CDI扩展

cdi name 日志

cdi name 日志

今天,我将向您展示如何编写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>() {
      @Override
      public 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));
          }
        }
      }
 
      @Override
      public void postConstruct(T instance) {
        it.postConstruct(instance);
      }
 
      @Override
      public void preDestroy(T instance) {
        it.dispose(instance);
      }
 
      @Override
      public void dispose(T instance) {
        it.dispose(instance);
      }
 
      @Override
      public Set<InjectionPoint> getInjectionPoints() {
        return it.getInjectionPoints();
      }
 
      @Override
      public 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>() {
      @Override
      public 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));
          }
        }
      }
 
      @Override
      public void postConstruct(T instance) {
        it.postConstruct(instance);
      }
 
      @Override
      public void preDestroy(T instance) {
        it.dispose(instance);
      }
 
      @Override
      public void dispose(T instance) {
        it.dispose(instance);
      }
 
      @Override
      public Set<InjectionPoint> getInjectionPoints() {
        return it.getInjectionPoints();
      }
 
      @Override
      public 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创建或更改行为。

还可以通过引入一组生产者方法来实现属性注入,但是这种方法需要每种字段类型使用一个生产者方法。 使用这种通用方法,您只需要添加新的转换器即可启用其他类型的值的注入。

参考:教程:Coders Kitchen博客上,由我们的JCG合作伙伴Peter Daum编写您自己的CDI扩展

翻译自: https://www.javacodegeeks.com/2014/02/tutorial-writing-your-own-cdi-extension.html

cdi name 日志

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值