这两天其实一直在为性能问题发愁,忙得连日志都没有时间写,前天晚上想到一个解决性能问题的可能方案,结果半宿都没有睡着,脑袋一直在不由自主的想着代码怎么写,但是这个方案只是提高了一倍的性能,今天把代码覆盖的报告又仔细的看了一遍,发现有个方法竟然被调用了两千三百万次,虽然那个代码就是一个直接的return
一个成员变量的语句,但是拿到那个对象后MockEJB会和Aspect进行匹配,需要使用Apache ORO的正则表达式,这个不慢才怪。MockEJB默认使用AspectSystemImpl,它会遍历所有的Aspect,而我的某个测试用例可能会牵涉到20个以上的Entity Bean,每个Entity Bean有三个Aspect,然后调用EJB的每个方法都会这么遍历并且进行匹配,天啊!
没有办法,自己实现了一个AspectSystem,先把和我的工程无关的调用过滤掉,然后再根据调用的方法解析出对应的Entity Bean,从Map里面查找对应的Aspect(只有三个),这样速度一举提升10倍。刚才那个被调用两千三百万次的方法的调用次数也下降到四十万次左右。
还没有完,还是不满意,原来的那些Pointcut是使用的MockEJB自己带的一个实现类:MethodPatternPointcut,我写的那个表达式还是比较复杂的,想到我已经解析出来对应的Entity Bean了,我只关心方法名或者方法前缀就可以了,就自己实现了Pointcut。
这样修改以后性能又提升一倍。

代码如下:

package com.activereasoning.test.base;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.mockejb.interceptor.Aspect;
import org.mockejb.interceptor.AspectSystem;
import org.mockejb.interceptor.Interceptor;
import org.mockejb.interceptor.Pointcut;

import com.activereasoning.test.util.EJBUtil;
import com.activereasoning.test.util.StringUtil;

/**
 * ARAspectSystem, for performance problem.
 * @author cherami
 *
 */
public class ARAspectSystem implements AspectSystem
{

    private static Pattern pattern = Pattern.compile("com\\.activereasoning(?:\\.(\\w+))+\\.\\w+\\(");

    private Map beanAspectMap = Collections.synchronizedMap(new HashMap());

    private List otherAspectList = Collections.synchronizedList(new LinkedList());

    /**
     *
     * @see org.mockejb.interceptor.AspectSystem#add(org.mockejb.interceptor.Aspect)
     */
    public void add(Aspect aspect)
    {

        removeIfExists(aspect);

        List aspects = getBeanAspect(aspect);
        if (aspects != null)
        {
            aspects.add(aspect);
        }
        else
        {
            otherAspectList.add(aspect);
        }
    }

    /**
     *
     * @see org.mockejb.interceptor.AspectSystem#addFirst(org.mockejb.interceptor.Aspect)
     */
    public void addFirst(Aspect aspect)
    {

        removeIfExists(aspect);

        List aspects = getBeanAspect(aspect);
        if (aspects != null)
        {
            aspects.add(0, aspect);
        }
        else
        {
            otherAspectList.add(0, aspect);
        }
    }

    /**
     * @see org.mockejb.interceptor.AspectSystem#add(org.mockejb.interceptor.Pointcut, org.mockejb.interceptor.Interceptor)
     */
    public void add(Pointcut pointcut, Interceptor interceptor)
    {

        Aspect aspect = new ARInterceptorContainerAspect(pointcut, interceptor);

        removeIfExists(aspect);
        otherAspectList.add(aspect);

    }

    /**
     * @see org.mockejb.interceptor.AspectSystem#addFirst(org.mockejb.interceptor.Pointcut, org.mockejb.interceptor.Interceptor)
     */
    public void addFirst(Pointcut pointcut, Interceptor interceptor)
    {

        Aspect aspect = new ARInterceptorContainerAspect(pointcut, interceptor);

        removeIfExists(aspect);
        otherAspectList.add(0, aspect);

    }

    protected void removeIfExists(Aspect aspect)
    {

        otherAspectList.remove(aspect);
        List aspects = getBeanAspect(aspect);
        if (aspects != null)
        {
            aspects.remove(aspect);
        }
    }

    private List getBeanAspect(Aspect aspect)
    {
        List aspects = null;
        if (aspect instanceof BaseAspect)
        {
            BaseAspect baseAspect = (BaseAspect) aspect;
            aspects = (List) beanAspectMap.get(baseAspect.getBeanInfo().getShortName());
            if (aspects == null)
            {
                aspects = new ArrayList();
                beanAspectMap.put(baseAspect.getBeanInfo().getShortName(), aspects);
            }
        }
        return aspects;
    }

    /**
     * @see org.mockejb.interceptor.AspectSystem#getAspectList()
     */
    public List getAspectList()
    {

        return otherAspectList;
    }

    /**
     * Clears the list of aspects for this AspectSystem
     *
     */
    public void clear()
    {

        otherAspectList.clear();
        beanAspectMap.clear();

    }

    /**
     * This method is performance key point of this framework.
     * @see org.mockejb.interceptor.AspectSystem#findInterceptors(java.lang.reflect.Method, java.lang.reflect.Method)
     */
    public List findInterceptors(Method proxyMethod, Method targetMethod)
    {
        List resultList = new ArrayList();
        find(proxyMethod, targetMethod, otherAspectList, resultList);
        if (isARMethod(proxyMethod, targetMethod))
        {
            String beanName = getBeanName(proxyMethod, targetMethod);
            if (beanName != null)
            {
                List aspects = (List) beanAspectMap.get(beanName);
                if (aspects != null)
                {
                    find(proxyMethod, targetMethod, aspects, resultList);
                }
            }
        }
        return resultList;
    }

    private void find(Method proxyMethod, Method targetMethod, List aspects, List result)
    {
        for (Iterator i = aspects.iterator(); i.hasNext();)
        {
            Aspect aspect = (Aspect) i.next();

            if (proxyMethod != null && aspect.getPointcut().matchesJointpoint(proxyMethod) || targetMethod != null
                    && aspect.getPointcut().matchesJointpoint(targetMethod))
            {

                result.add(aspect);
            }
        }
    }

    private boolean isARMethod(Method proxyMethod, Method targetMethod)
    {
        if (proxyMethod != null && proxyMethod.toString().indexOf(Constant.AR_PACKAGE) >= 0)
        {
            return true;
        }
        if (targetMethod != null && targetMethod.toString().indexOf(Constant.AR_PACKAGE) >= 0)
        {
            return true;
        }
        return false;
    }

    private String getBeanName(Method proxyMethod, Method targetMethod)
    {
        if (proxyMethod == null)
        {
            return null;
        }
        String method = proxyMethod.toString();
        String name = StringUtil.getFirstMatchGroup(pattern, method);
        BeanInfo beanInfo = null;
        if (name != null)
        {
            beanInfo = EJBUtil.getBeanInfoByName(StringUtil.getBeanName(name));
            if (beanInfo != null)
            {
                return beanInfo.getShortName();
            }
        }
        return null;

    }
}

package com.activereasoning.test.base;

import java.lang.reflect.Method;
import java.util.regex.Pattern;

import org.mockejb.interceptor.Pointcut;

public class MethodNamePatternPointcut implements Pointcut
{
    Pattern pattern;

    public MethodNamePatternPointcut(String pattern)
    {
        this.pattern = Pattern.compile(pattern);
    }

    public boolean matchesJointpoint(Method method)
    {
        return pattern.matcher(method.getName()).find();
    }
}

package com.activereasoning.test.base;

import java.lang.reflect.Method;

import org.mockejb.interceptor.Pointcut;

public class MethodNamePointcut implements Pointcut
{
    String name;

    public MethodNamePointcut(String name)
    {
        this.name = name;
    }

    public boolean matchesJointpoint(Method method)
    {
        return name.equals(method.getName());
    }
}

(Visited 50 times, 1 visits today)