CDI实例的等效弹簧

时间:2017-02-02 05:52:57

标签: spring cdi

我是Spring新手,我需要将CDI类转换为Spring。

我在CDI中有以下代码

@Inject
private Instance<HealthCheck> healthChecks;

然后我遍历healthChecks。

我发现了一个类似的问题What is the Spring equivalent for CDI's Instance, or Guices Provider,建议使用

@Inject
Provider<MyObject> myObjectInstance;
//...
MyObject myObjectInstance.get();

但是,由于提供程序不实现iterable,我无法迭代。

有人可以帮助我如何将CDI代码块转换为弹簧。

1 个答案:

答案 0 :(得分:2)

在META-INF中创建一个新文件spring.factories,内容如下:

org.springframework.context.ApplicationContextInitializer=package_name.CustomApplicationContextInitializer

或者您可以在junit测试中使用它,如:

@SpringApplicationConfiguration(initializers = CustomApplicationContextInitializer.class)

现在你可以使用:

@Autowired
private Instance<HealthCheck> healthChecks;

<强> CustomApplicationContextInitializer.class

public class CustomApplicationContextInitializer implements
        ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {

        // Configure custom CustomAutowireCandidateResolver to handle CDI
        // Instance<T> dependency requests
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
        beanFactory.setAutowireCandidateResolver(new CustomAutowireCandidateResolver());
    }
}

<强> CustomAutowireCandidateResolver.class

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.annotation.Priority;
import javax.enterprise.inject.Instance;
import javax.enterprise.util.TypeLiteral;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.core.annotation.Order;
import org.springframework.util.ClassUtils;

public class CustomAutowireCandidateResolver extends ContextAnnotationAutowireCandidateResolver {

    static final boolean IS_CDI_INSTANCE_CLASS_PRESENT = ClassUtils.isPresent("javax.enterprise.inject.Instance", null);

    @Override
    public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {

        if (IS_CDI_INSTANCE_CLASS_PRESENT && Instance.class.equals(descriptor.getDependencyType())) {

            // TODO refactor getLazyResolutionProxyIfNecessary to allow to
            // customize lazy dependency resolution for Instance<T>
            return getInstanceAdapterFor(descriptor);
        }

        return super.getLazyResolutionProxyIfNecessary(descriptor, beanName);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private Object getInstanceAdapterFor(DependencyDescriptor descriptor) {

        ListableBeanFactory listableBeanFactory = (ListableBeanFactory) getBeanFactory();
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) listableBeanFactory;

        // Instance<TargetType>
        Class targetType = descriptor.getResolvableType().getGeneric(0).getRawClass();
        Map<String, Object> beansOfType = listableBeanFactory.getBeansOfType(targetType);

        List<Bean> beansInstances = beansOfType.entrySet().stream() //
                .map(e -> new Bean(e.getValue(), registry.getBeanDefinition(e.getKey()).isPrimary()))//
                .collect(Collectors.toList());

        Annotation[] qualifiers = retainQualifierAnnotations(descriptor.getAnnotations());

        Beans beans = new Beans(targetType, beansInstances);
        return qualifiers.length == 0 ? beans : beans.select(qualifiers);
    }

    private Annotation[] retainQualifierAnnotations(Annotation[] annotations) {
        return Arrays.stream(annotations) //
                .filter(a -> a.annotationType().isAnnotationPresent(Qualifier.class)) //
                .toArray(Annotation[]::new);
    }

    static class Beans<T> implements Instance<T> {

        private final List<Bean> beans;
        private final Class<?> type;

        public Beans(Class<?> type, List<Bean> beans) {
            this.type = type;
            this.beans = beans;
        }

        protected List<Bean> getBeans() {
            return beans;
        }

        @Override
        public T get() {
            return (T) findDefaultInstance();
        }

        protected Object findDefaultInstance() {

            List<Bean> beans = getBeans();

            if (beans.size() == 1) {
                return beans.get(0).getInstance();
            }

            Object highestPrioBean = returnPrimaryOrHighestPriorityBean(beans);

            if (highestPrioBean != null) {
                return highestPrioBean;
            }

            // TODO figure out a sane default to use here - maybe throw an
            // exception?
            return beans.get(0).getInstance();
        }

        private Object returnPrimaryOrHighestPriorityBean(List<Bean> beans) {

            long highestPriority = Integer.MIN_VALUE;
            Object highestPrioBean = null;

            for (Bean bean : beans) {

                if (bean.isPrimary()) {
                    return bean.getInstance();
                }

                // TODO figure out to retrieve order from BeanDefinition /
                // BeanDeclaration

                Object instance = bean.getInstance();
                Order order = instance.getClass().getAnnotation(Order.class);
                if (order != null) {
                    if (order.value() > highestPriority) {
                        highestPriority = order.value();
                        highestPrioBean = instance;
                    }
                }

                Priority priority = instance.getClass().getAnnotation(Priority.class);
                if (priority != null) {
                    if (priority.value() > highestPriority) {
                        highestPriority = priority.value();
                        highestPrioBean = instance;
                    }
                }
            }

            return highestPrioBean;
        }

        @Override
        @SuppressWarnings("unchecked")
        public Instance<T> select(Annotation... qualifiers) {
            return select((Class<T>) type, qualifiers);
        }

        @Override
        public <U extends T> Instance<U> select(Class<U> subtype, Annotation... qualifiers) {
            return new Beans<U>(subtype, filterBeans(subtype, qualifiers));
        }

        protected List<Bean> filterBeans(Class<?> subtype, Annotation... qualifiers) {

            List<Annotation> requiredQualifiers = Arrays.asList(qualifiers);

            return getBeans().stream() //
                    .filter(bean -> subtype.isInstance(bean.getInstance())) //
                    .filter(bean -> bean.getAnnotations().containsAll(requiredQualifiers)) //
                    .collect(Collectors.toList());
        }

        @Override
        public <U extends T> Instance<U> select(TypeLiteral<U> subtype, Annotation... qualifiers) {

            // TODO implement (Class<U> subtype, Annotation... qualifiers) via
            // select(TypeLiteral<U> subtype, Annotation... qualifiers)
            return select(subtype.getRawType(), qualifiers);
        }

        @Override
        public Iterator<T> iterator() {
            return getBeans().stream().map(bean -> (T) bean.getInstance()).iterator();
        }

        @Override
        public boolean isUnsatisfied() {
            return getBeans().isEmpty();
        }

        @Override
        public boolean isAmbiguous() {
            return getBeans().size() > 1;
        }

        @Override
        public void destroy(Object bean) {

            if (bean instanceof DisposableBean) {
                try {
                    ((DisposableBean) bean).destroy();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    static class Bean {

        private final boolean primary;
        private final Object instance;
        private final List<Annotation> annotations;

        public Bean(Object instance, boolean primary) {
            this.primary = primary;
            this.instance = instance;
            this.annotations = Arrays.asList(instance.getClass().getAnnotations());
        }

        public Object getInstance() {
            return instance;
        }

        public boolean isPrimary() {
            return primary;
        }

        public List<Annotation> getAnnotations() {
            return annotations;
        }
    }
}

这里是完整的源链接:https://github.com/thomasdarimont/spring-boot-cdi-instance-example