ContainerImpl

本文深入探讨Java中依赖注入(DI)的实现原理,通过具体代码示例解释如何使用注解进行依赖注入,并介绍DI容器的工作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package com.opensymphony.xwork2.inject;
import com.opensymphony.xwork2.inject.util.ReferenceCache;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.io.Serializable;
class ContainerImpl implements Container {
    final Map<Key<?>, InternalFactory<?>> factories;
    final  Map<Class<?>, Set<String>> factoryNamesByType;
    ContainerImpl(Map<Key<?>, InternalFactory<?>> factories) {
        this.factories = factories;
        Map<Class<?>, Set<String>> map = new HashMap<Class<?>, Set<String>>();
        for (Key<?> key :factories.keySet()) {
            Set<String> names = map.get(key.getType());
            if (names == null) {
                names = new
                        HashSet<String>();
                map.put(key.getType(), names);
            }
            names.add(key.getName());
        }
        for (Entry<Class<?>, Set<String>> entry :
             map.entrySet()) {
            entry.setValue(Collections.unmodifiableSet(entry.getValue()));
        }
        this.factoryNamesByType = Collections.unmodifiableMap(map);
    }
    @SuppressWarnings("unchecked")<T> InternalFactory<? extends T>
            getFactory(Key<T> key) {
        return (InternalFactory<T>)
                factories.get(key);
    }
    /**    * Field and method injectors.    */
    //injector.get(Test.class)就会去执行这些方法   
    //factoryNamesByType和factories是有了的 
    final Map<Class<?>, List<Injector>> injectors =
            new ReferenceCache<Class<?>, List<Injector>>() {
        protected
                List<Injector> create(Class<?> key) {
            List<Injector>
                    injectors = new ArrayList<Injector>();
            addInjectors(key,
                         injectors);
            return injectors;
        }
    };
    /**    * Recursively adds injectors for fields and methods from the
     given class to    * the given list. Injects parent classes before sub
               classes.    *        *
               处理所有的不是static的,并含有@inject注释的属性和方法        *
               并根据属性或者方法分别添加FieldInjector,MethodInjector,第三个参数为
     * 注释中的value,并放在injectors        * */
    void addInjectors(Class clazz, List<Injector> injectors) {
        if (clazz ==
            Object.class) {
            return;
        }
        // Add injectors for superclass first.
        addInjectors(clazz.getSuperclass(), injectors);
        // TODO (crazybob): Filter out overridden members.
        addInjectorsForFields(clazz.getDeclaredFields(), false, injectors);
        addInjectorsForMethods(clazz.getDeclaredMethods(), false, injectors);
    }
    void injectStatics(List<Class<?>> staticInjections) {
        final
                List<Injector> injectors = new ArrayList<Injector>(); /*      *
                  把staticInjections的所有类中含有@inject并且是static的属性和方法      *
        并根据属性或者方法分别添加FieldInjector,MethodInjector,第三个参数为      *
                  注释中的value,并放在injectors      * */for (Class<?> clazz :
               staticInjections) {
           addInjectorsForFields(clazz.getDeclaredFields(), true, injectors);
           addInjectorsForMethods(clazz.getDeclaredMethods(), true, injectors);
       }
       /*      *
                  callInContext方法就是执行ContextualCallable的call方法并把结果值返回
        *      * reference[0] = new InternalContext(this);              return
        callable.call(reference[0]);                      * 在此处的context = new
                  InteralContext(this);      * */ callInContext(new
               ContextualCallable<Void>() {
           public Void call(InternalContext
                            context) {
               for (Injector injector : injectors) { /*
                    *
                              就让FieldInjector和MethodInjector来注入,因为是静态属性和方法,
所以第二
                              的一个参 数            * 为null            * */
                   injector.inject(context, null);
                }
                return null;
           }
       });
    }
    void addInjectorsForMethods(Method[] methods, boolean statics,
                                List<Injector> injectors) {
        addInjectorsForMembers(Arrays.asList(methods), statics, injectors,
                               new InjectorFactory<Method>() {
            public Injector
                    create(ContainerImpl container, Method method, String
                           name) throws MissingDependencyException {
                return new
                        MethodInjector(container, method, name);
            }
        });
    }
    void addInjectorsForFields(Field[] fields, boolean statics,
                               List<Injector> injectors) {
        addInjectorsForMembers(Arrays.asList(fields), statics, injectors,
                               new InjectorFactory<Field>() {
            public Injector
                    create(ContainerImpl container, Field field, String
                           name) throws MissingDependencyException {
                return new
                        FieldInjector(container, field, name);
            }
        });
    }
    <M extends Member & AnnotatedElement> void addInjectorsForMembers(
            List<M> members, boolean statics /*false*/,
            List<Injector> injectors,
            InjectorFactory<M> injectorFactory) {
        for (M member : members) {
            if (isStatic(member) == statics) {
                Inject inject =
                        member.getAnnotation(Inject.class);
                if (inject != null) {
                    try {
                        injectors.add(injectorFactory.create(this, member,
                                inject.value()));
                    } catch (MissingDependencyException e) {
                        if (inject.required()) {
                            throw new
                                    DependencyException(e);
                        }
                    }
                }
            }
        }
    }
    interface InjectorFactory<M extends Member & AnnotatedElement> {
        Injector create(ContainerImpl container, M member, String name) throws
                MissingDependencyException;
    }
    private boolean isStatic(Member member) {
        return
                Modifier.isStatic(member.getModifiers());
    }
    static class FieldInjector implements Injector {
        final Field field;
        final InternalFactory<?> factory;
        final
                ExternalContext<?> externalContext;
        public FieldInjector(ContainerImpl container /*this*/, Field
                             field
                             /*属性名*/, String name /*@inject中的value*/) throws
                MissingDependencyException {
            this.field = field;
            field.setAccessible(true);
            Key<?> key = Key.newInstance(field.getType(), name);
            factory
                    = container.getFactory(key);
            if (factory == null) {
                throw new MissingDependencyException("No mapping
                        found for dependency " + key + "in " + field + ".");       }
                        this.externalContext = ExternalContext.newInstance(
                        field, key,
                        container);
            } 
            /** inject(context,null);     
             
                          在构造函数中初始化了externalContext,是为了factory.create(context)
                          调用中可 以      
                          context.getExternalContext()方法来获得我其中的key值,再得到Key(里面
                          的是Fiel d和注入的值)      
                          这里面用的容器都是一个,这里用到了ContainerImpl传进来的factories
                          这个地方不对啊factory = container.getFactory(key);      
                          而key又一直不一样啊,因为name在变化啊   
                          总之就是对此属性进行了重新赋值      
             */ 
            public void inject(InternalContext context, Object o) {
               ExternalContext<?> previous = context.getExternalContext();
               context.setExternalContext(externalContext);
               try {
                   field.set(o, factory.create(context));
               } catch
                       (IllegalAccessException e) {
                   throw new AssertionError(e);
               } finally {
                   context.setExternalContext(previous);
               }
           }
        }
        /**    * Gets parameter injectors.    *    * @param member to which
         the parameters belong    * @param annotations on the parameters    *
         @param parameterTypes parameter types    * @return injections    */
        <M extends AccessibleObject & Member> ParameterInjector<?>[]
                getParametersInjectors(M member, Annotation[][] annotations,
                                       Class[] parameterTypes,
                                       String defaultName /*
                           是member注入的value       */) throws
                MissingDependencyException {
            List<ParameterInjector<?>> parameterInjectors = new
                    ArrayList<ParameterInjector<?>>(); //得到每个参数的@Inject
            Iterator<Annotation[]> annotationsIterator =
                    Arrays.asList(annotations).iterator();
            for (Class<?>
                 parameterType : parameterTypes) {
                Inject annotation =
                        findInject(annotationsIterator.next());
                String name = annotation
                              == null ? defaultName : annotation.value();
                Key<?> key =
                        Key.newInstance(parameterType, name);
                parameterInjectors.add(createParameterInjector(key, member));
            }
            return toArray(parameterInjectors);
        }
        <T> ParameterInjector<T> createParameterInjector(Key<T> key,
                Member member) throws MissingDependencyException {
            InternalFactory<? extends T> factory = getFactory(key);
            if
                    (factory == null) {
                throw new MissingDependencyException(
                        "No mapping found for dependency " + key + " in " +
                        member + ".");
            }
            ExternalContext<T> externalContext =
                    ExternalContext.newInstance(member, key, this);
            return new
                    ParameterInjector<T>(externalContext, factory);
        }
        @SuppressWarnings("unchecked")private ParameterInjector<?>[]
                toArray(List<ParameterInjector<?>> parameterInjections) {
            return parameterInjections.toArray(new
                                               ParameterInjector[
                                               parameterInjections.size()]);
        }
        /**    * Finds the {@link Inject} annotation in an array of
                   annotations.    */ 
        Inject findInject(Annotation[] annotations) {
            for (Annotation annotation : annotations) {
                if
                        (annotation.annotationType() == Inject.class) {
                    return
                            Inject.class.cast(annotation);
                }
            }
            return null;
        }
        static class MethodInjector implements Injector {
            final Method method;
            final ParameterInjector<?>[]
                    parameterInjectors; /*      *
                        MethodInjector构造函数对函数里面的参数进行注入      * */
           public
                   MethodInjector(ContainerImpl container, Method method,
                                  String name /*
                    是此method前面的值,比如:        @Inject(value="woaiyan") public void
                                test(@Inject(value="wo") String s){        }
                                则name就等于"woaiyan"     */) throws
                   MissingDependencyException {
               this.method = method;
               method.setAccessible(true);
               Class<?> [] parameterTypes = method.getParameterTypes();
               if
                       (parameterTypes.length == 0) { /*         * 如果只有@inject
                    而没有函数参数,当然应该抛出异常         * */throw new
                           DependencyException(method + " has no parameters to
                                               inject.");       }       /*        *
                                               把每个参数的类型class和它前面的 @Inject的
value()
                           组成Key, method, 和th
                           is 组合成ExteralContext * 然后再用factory =
                                   factories.get(Key) 得到InteralFactory,
                                   来初始化ParameterInjector, 逐
                                   个 * 加入数组. * * / parameterInjectors =
                                           container.getParametersInjectors(
                           method,
                           method.getParameterAnnotations(), parameterTypes,
                           name);
                }
               /*      * o = null,这种情况是静态方法      * */
               public void inject(InternalContext context, Object o) {
                   try { /*
                        * getParameters 方法仍然象属性一样factory.create(context);       *
                        */method.invoke(o, getParameters(method, context,
                               parameterInjectors));
                    } catch (Exception e) {
                        throw
                                new RuntimeException(e);
                    }
               }
           } /*  *
                          constructors.get(Test.class)就会去调create方法从而得到  *
            一个new ConstructorInjector(ContainerImpl.this, class);  * */
           Map<Class<?>, ConstructorInjector> constructors = new
                   ReferenceCache<Class<?>, ConstructorInjector>() {
               @SuppressWarnings("unchecked")protected
                       ConstructorInjector<?> create(Class<?> implementation) {
                   return new ConstructorInjector(ContainerImpl.this,
                                                  implementation);
               }
           };
            static class ConstructorInjector<T> {
                final Class<T> implementation;
                final List<Injector> injectors;
                final Constructor<T> constructor;
                final ParameterInjector<?>[]
                        parameterInjectors; /*      *      * */
                ConstructorInjector(ContainerImpl container, Class<T>
                        implementation) {
                    this.implementation = implementation;
                    /*        *        *
                                 去找implementation的构造函数,找到有@Inject的构造函数,但是如
果有两个@
                                 Inject的 构造函数的话则抛出异常        * 否则返回默认的构造
函数
                     */constructor = findConstructorIn(implementation);
                    constructor.setAccessible(true);
                    MissingDependencyException exception = null;
                    Inject inject =
                            null;
                    ParameterInjector<?> [] parameters = null;
                    try { //当没有@Inject注释的构造函数时,inject = null;
                        inject = constructor.getAnnotation(Inject.class);
                        //如果有@Inject的话.和Method差不多         parameters =
                        constructParameterInjector(inject, container,
                                constructor);
                    } catch (MissingDependencyException e) {
                        exception = e;
                    }
                    parameterInjectors = parameters;
                    if (exception != null) {
                        if (inject != null &&
                            inject.required()) {
                            throw new
                                    DependencyException(exception);
                        }
                    }
                    //把属性方法的注入器也加进injectors       injectors =
                    container.injectors.get(implementation);
                }
                ParameterInjector<?>[] constructParameterInjector(Inject
                        inject, ContainerImpl container, Constructor<T>
                        constructor) throws
                        MissingDependencyException {
                    return
                            constructor.getParameterTypes().length == 0 ? null // default
                            constructor. : container.getParametersInjectors(
                                    constructor,
                                    constructor.getParameterAnnotations(),
                                    constructor.getParameterTypes(),
                                    inject.value());
                }
                @SuppressWarnings("unchecked")private Constructor<T>
                        findConstructorIn(Class<T> implementation) {
                    Constructor<T>
                            found = null;
                    Constructor<T> [] declaredConstructors =
                            (Constructor<T>[]) implementation
                            .getDeclaredConstructors();
                    for (Constructor<T> constructor :
                         declaredConstructors) {
                        if
                                (constructor.getAnnotation(Inject.class) != null) {
                            if
                                    (found != null) {
                                throw new DependencyException("More
                                        than one constructor annotated
                                        "               + "with @Inject found
                                        in " + implementation + ".");           }           
found =
                                        constructor;
                            }
                        }
                        if (found != null) {
                            return found;
                        }
                        // If no annotated constructor is found, look for a no-arg
                        constructor // instead.       try {         return
                                implementation.getDeclaredConstructor();
                    } catch
                            (NoSuchMethodException e) {
                        throw new
                                DependencyException(
                                "Could not find a suitable constructor"
                                + " in " + implementation.getName() + ".");
                    }
                }
                /**      * Construct an instance. Returns {@code Object} instead of
                 {@code T}      * because it may return a proxy.      */
                Object
                        construct(InternalContext context,
                                  Class<? super T> expectedType) {
                    /*        *
                     参数context容器是有了,ConstructionContext倒没有实例化,在这里进行实例
                                 化,并返 回        *
                     实际上是执行了constructionContexts.put(this,constructionContext)
                     * */ConstructionContext<T> constructionContext =
                             context.getConstructionContext(this);
                    // We have a circular reference between constructors. Return a
                    proxy. //如果正在建造,就创建一个代理,expectedType是接口
                            if (constructionContext.isConstructing()) { // TODO
                        (crazybob):if we can 'tproxy this object,
                                can we proxy the
                                // other object?         return
                                constructionContext.createProxy(expectedType); {
                        }
                    }
                    // If we're re-entering this factory while injecting fields or
                    methods, // return the same instance. This prevents infinite
                            loops.T t = constructionContext.getCurrentReference();
                    if (t != null) {
                        return t;
                    }
                    try { // First time through...
                        constructionContext.startConstruction();
                        try {
                            Object[] parameters = getParameters(constructor,
                                    context, parameterInjectors);
                            t =
                                    constructor.newInstance(parameters);
                            constructionContext.setProxyDelegates(t);
                        } finally {
                            constructionContext.finishConstruction();
                        }
                        // Store reference. If an injector re-enters this factory,
                        they 'll // get the same reference.
                                constructionContext.setCurrentReference(t);
                        // Inject fields and methods.         for (Injector injector :
                        injectors) {
                            injector.inject(context, t);
                        }
                        return t;
                    } catch (InstantiationException e) {
                        throw new RuntimeException(e);
                    } catch
                            (IllegalAccessException e) {
                        throw new
                                RuntimeException(e);
                    } catch (InvocationTargetException e) {
                        throw new RuntimeException(e);
                    } finally {
                        constructionContext.removeCurrentReference();
                    }
                }
            }
            static class ParameterInjector<T> {
                final ExternalContext<T> externalContext;
                final
                        InternalFactory<? extends T> factory;
                public ParameterInjector(ExternalContext<T> externalContext,
                        InternalFactory<? extends T> factory) {
                    this.externalContext =
                            externalContext;
                    this.factory = factory;
                }
                T
                        inject(Member member, InternalContext context) {
                    ExternalContext<?> previous = context.getExternalContext();
                    context.setExternalContext(externalContext);
                    try {
                        return factory.create(context);
                    } finally {
                        context.setExternalContext(previous);
                    }
                }
            }
            private static Object[] getParameters(Member member,
                                                  InternalContext
                                                  context,
                                                  ParameterInjector[] parameterInjectors) {
                if
                        (parameterInjectors == null) {
                    return null;
                }
                Object[] parameters = new Object[parameterInjectors.length];
                for
                        (int i = 0; i < parameters.length; i++) { /*        *
                     仍然是ExternalContext<?> previous = context.getExternalContext();
                     context.setExternalContext(externalContext);           return
                                 factory.create(context);        * */parameters[
                            i] =
                                    parameterInjectors[i].inject(member,
                            context);
                 }
                 return
                        parameters;
            } //注入Field和Method   void inject(Object o,
            InternalContext context) {
                List<Injector> injectors =
                        this.injectors.get(o.getClass());
                for (Injector injector :
                     injectors) {
                    injector.inject(context, o);
                }
            }
            //注入整个对象并返回   <T> T inject(Class<T> implementation,
            InternalContext context) {
                try {
                    ConstructorInjector<T>
                            constructor = getConstructor(implementation);
                    return
                            implementation.cast(constructor.construct(context,
                            implementation));
                } catch (Exception e) {
                    throw new
                            RuntimeException(e);
                }
            } /*  *
                        实际上是以type,name组成Key来找到InternalFactory  * return
                        getFactory(key).create(context);  * */
           @SuppressWarnings("unchecked")<T> T getInstance(Class<T> type,
                   String name, InternalContext context) {
               ExternalContext<?>
                       previous = context.getExternalContext();
               Key<T> key =
                       Key.newInstance(type, name);
               context.setExternalContext(ExternalContext.newInstance(null, key,
                       this));
               try {
                   InternalFactory o = getFactory(key);
                   if (o != null) {
                       return getFactory(key).create(context);
                   } else {
                       return null;
                   }
               } finally {
                   context.setExternalContext(previous);
               }
           }
            <T> T getInstance(Class<T> type, InternalContext context) {
                return
                        getInstance(type, DEFAULT_NAME, context);
            }
            public void inject(final Object o) {
                callInContext(new
                              ContextualCallable<Void>() {
                    public Void call(InternalContext
                                     context) {
                        inject(o, context);
                        return null;
                    }
                });
            }
            public <T> T inject(final Class<T> implementation) {
                return
                        callInContext(new ContextualCallable<T>() {
                    public T
                            call(InternalContext context) {
                        return inject(implementation,
                                      context);
                    }
                });
            }
            public <T> T getInstance(final Class<T> type, final String name) {
                return callInContext(new ContextualCallable<T>() {
                    public T
                            call(InternalContext context) {
                        return getInstance(type, name,
                                           context);
                    }
                });
            }
            public <T> T getInstance(final Class<T> type) {
                return
                        callInContext(new ContextualCallable<T>() {
                    public T
                            call(InternalContext context) {
                        return getInstance(type,
                                           context);
                    }
                });
            }
            public Set<String>
                    getInstanceNames(final Class<?> type) {
                return
                        factoryNamesByType.get(type);
            } /*  * 相当于localContext.set(new
                      InternalContext[1]);  * */ ThreadLocal<Object[]>
                   localContext =
                           new ThreadLocal<Object[]>() {
               protected InternalContext[]
                       initialValue() {
                   return new InternalContext[1];
               }
           };
            /**    * Looks up thread local context. Creates (and removes) a new
                       context if    * necessary.    *    *
                       这个方法就是调用接口ContextualCallable的call方法并返回对象.    */
            <T> T callInContext(ContextualCallable<T> callable) {
                InternalContext[] reference = (InternalContext[]) localContext.
                                              get();
                if (reference[0] == null) {
                    reference[0] = new
                                   InternalContext(this);
                    try {
                        return
                                callable.call(reference[0]);
                    } finally { // Only remove
                        the context if this call created it.reference[0] = null; {
                        }
                    }
                } else { // Someone else will clean up this context.
                    return callable.call(reference[0]);
                }
            }
            interface ContextualCallable<T> {
                T call(InternalContext context);
            }
            /**    * Gets a constructor function for a given implementation class.
             */ @SuppressWarnings("unchecked")<T> ConstructorInjector<T>
                    getConstructor(Class<T> implementation) {
                return
                        constructors.get(implementation);
            }
            final ThreadLocal<Object> localScopeStrategy = new
                    ThreadLocal<Object>();
            public void setScopeStrategy(Scope.Strategy scopeStrategy) {
                this.localScopeStrategy.set(scopeStrategy);
            }
            public void removeScopeStrategy() {
                this.localScopeStrategy.remove();
            }
            /**    * Injects a field or method in a given object.    */
            interface Injector extends Serializable {
                void
                        inject(InternalContext context, Object o);
            }
            static class MissingDependencyException extends Exception {
                MissingDependencyException(String message) {
                    super(message);
                }
            }
        }
<think>我们正在处理用户的问题:如何测试SAP PO Function Library的方法。根据引用[3],用户提到了使用NWDS(NetWeaver Developer Studio)开发和测试PI Function Library的方法。具体来说,引用[3]中展示了一个示例,其中创建了一个测试类(Test_PI_Utils)来调用PI Function Library中的方法(replaceBlankValue)并验证其功能。 因此,我们可以按照以下步骤来测试SAP PO Function Library的方法: 1. **开发Function Library**:首先,确保已经在NWDS中创建了Function Library项目,并编写了所需的Java方法(例如,在类`PI_Utils`中有一个方法`replaceBlankValue`)。 2. **创建测试类**:在同一个项目中,创建一个测试类(例如`Test_PI_Utils`),其中包含一个`main`方法,用于模拟PI运行时环境,并调用Function Library中的方法。 3. **模拟PI运行时对象**:在测试类中,需要模拟PI映射运行时传递的对象,主要是`Container`和`ResultList`对象。引用[3]中使用了自定义的实现(`ContainerImpl`和`ResultListImpl`)来模拟这些对象。 4. **调用方法并验证结果**:在`main`方法中,准备测试数据,调用Function Library的方法,然后检查输出(例如,打印`ResultList`的内容或检查方法返回的结果)。 5. **运行测试**:在NWDS中,将测试类作为Java应用程序运行(Run as Java Application),观察控制台输出,验证方法行为是否符合预期。 具体代码示例可以参考引用[3]中提供的代码: ```java package sap.myfunctions.co.za.samples; import com.sap.aii.mappingtool.tf7.rt.Container; import com.sap.aii.mappingtool.tf7.rt.ResultList; public class Test_PI_Utils { public static void main(String[] args) { // 模拟ResultList和Container对象 ResultList result = new ResultListImpl(); Container container = new ContainerImpl(); PI_Utils pi_utils = new PI_Utils(); // 准备测试数据 String[] var1 = {"111",ResultList.CC,"222",ResultList.CC,"",ResultList.CC,"333"}; String var2 = "0"; try { // 调用Function Library中的方法 pi_utils.replaceBlankValue(var1, var2, result, container); } catch (Exception e) { System.out.println(e.toString()); } // 输出结果 System.out.println(result.toString()); } } ``` 注意:上述代码中的`ResultListImpl`和`ContainerImpl`是模拟实现,需要根据PI的API自行编写或使用测试工具提供。在PI的映射开发中,通常我们使用NWDS提供的测试环境,但直接运行Java应用程序时,需要自己实现这些接口的模拟类。 另外,如果Function Library中的方法使用了动态配置(如引用[2]中提到的),则需要在测试环境中模拟`DynamicConfiguration`对象,以确保方法能够正确操作。 **相关问题**: 1. 如何在NWDS中创建和配置SAP PO Function Library项目? 2. 如何模拟PI映射运行时中的`Container`和`ResultList`对象? 3. 如何测试涉及动态配置(Dynamic Configuration)的Function Library方法? 4. 在测试Function Library时,如何处理异常情况?</think>### 如何测试 SAP PO Function Library 的方法 测试 SAP PO Function Library 的方法需要以下步骤,主要基于引用[3]的实践方法: #### 1. **创建测试类** 在 NWDS (NetWeaver Developer Studio) 中创建独立的测试类,用于调用 Function Library 的方法: ```java public class Test_PI_Utils { public static void main(String[] args) { // 模拟运行时对象 ResultList result = new ResultListImpl(); Container container = new ContainerImpl(); // 实例化 Function Library PI_Utils pi_utils = new PI_Utils(); // 准备测试数据 String[] input = {"111", ResultList.CC, "222", ResultList.CC, "", ResultList.CC, "333"}; String replacement = "0"; try { // 调用目标方法 pi_utils.replaceBlankValue(input, replacement, result, container); } catch (Exception e) { System.out.println("Error: " + e.toString()); } // 验证结果 System.out.println("Output: " + result.toString()); } } ``` #### 2. **模拟运行时环境** - **`ResultList` 和 `Container`** 需实现模拟类(如 `ResultListImpl`, `ContainerImpl`),提供与 PI 运行时兼容的存根方法。 例如 `ContainerImpl` 需实现 `getTransformationParameters()` 等接口方法。 #### 3. **准备测试数据** - 根据方法逻辑构造输入参数,如: - 空字符串 `""` 测试替换逻辑 - 特殊字符验证异常处理 - 边界值(如超大数组) #### 4. **执行测试** - 在 NWDS 中右键测试类 → **Run As → Java Application** - 检查控制台输出是否符合预期(如引用[3]中将空值替换为 `"0"`) #### 5. **验证动态配置(如适用)** 若方法涉及动态参数(如引用[2]的 HTTP header 配置),需模拟 `DynamicConfiguration`: ```java // 获取动态配置对象 DynamicConfiguration conf = (DynamicConfiguration) container .getTransformationParameters() .get(StreamTransformationConstants.DYNAMIC_CONFIGURATION); // 验证配置项 DynamicConfigurationKey key = DynamicConfigurationKey.create( "http://sap.com/xi/XI/System/REST", "headertoken" ); String tokenValue = conf.get(key); // 检查赋值是否正确 ``` #### 6. **异常测试** 主动传入非法参数触发异常,验证错误处理: ```java try { pi_utils.replaceBlankValue(null, "0", result, container); // 传入null } catch (NullPointerException e) { System.out.println("Caught expected exception: " + e); } ``` --- ### 关键注意事项 1. **依赖项管理** 确保测试类与 Function Library 使用相同的 JAR 依赖(如 `com.sap.aii.mapping.api`)。 2. **与 PI 配置联动** 若方法涉及 ESR 对象(如接口映射),需在 PO 中部署 Function Library 后,通过实际消息流验证端到端功能。 3. **调试技巧** 在 NWDS 中使用断点调试(Debug As),观察方法内部变量变化。 --- ### 相关问题 1. 如何模拟 SAP PO 的 `DynamicConfiguration` 对象进行单元测试? 2. Function Library 中如何处理和记录错误日志到 PI 消息监控? 3. 在非 NWDS 环境(如 Eclipse)中能否测试 SAP PO 的 Function Library? 4. 如何为涉及数据库操作的 Function Library 方法构建测试环境? [^1]: SAP PI/PO 证书配置问题解决方法 [^2]: SAP PO 动态参数传递技术实现 [^3]: NWDS 开发和测试 Function Library 的实践方法
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值