为什么每次在SpringMVC服务中调用简单方法都比静态方法慢得多?

时间:2019-05-18 09:00:06

标签: java spring profiling benchmarking static-methods

在SpringMVC服务中进入方法并退出该方法花费了太多毫秒。但是,如果我将方法修改为静态,则只需花费1毫秒的时间。 我们的项目是SpringMVC-Mybatis项目,我发现控制器进入服务方法并从该方法退出大约花费70毫秒。 该方法从具有128个键的静态映射中获取值。

@Controller 
public class OrdersController extends BaseAction
{
    @Autowired(required=false)
    private CompanyService<Company> companyService; 

    public void validateInfo()
    {
            Company company = CompanyService.queryByIdFromCache(account);// cost about 70 milliseconds, but if the method is static it is 1 milliseconds.
    }

}

@Service("companyService")
public class CompanyService<T> extends BaseService<T> 
{

    private static ConcurrentMap<Integer, Company>cache = new ConcurrentHashMap<>(); //map with 128 keys

    public Company queryByIdFromCache(Integer id)
    {
        return cache.get(id);
    }
}

我希望方法在2毫秒内完成。 该服务在单例模式下工作。和companyService是同一实例。 我不想将所有方法都修改为静态方法,因为某些代码必须以非静态方式调用。

1 个答案:

答案 0 :(得分:0)

某个方面可能正在拦截对非静态服务方法的调用。

有几种方法可以分析执行情况并找到服务呼叫的热点。我使用NetBeans的Test Profiler来复制此问题。

首先,我创建了静态(组件)和非静态服务:

@Service
public class DemoService {

    private static final ConcurrentMap<Integer, String> CACHE = new ConcurrentHashMap<>();

    public DemoService() {
        for (int i = 0; i < 128; i++) {
            CACHE.put(i, String.valueOf(i));
        }
    }

    public String queryByIdFromCache(Integer id) {
        return CACHE.getOrDefault(id, "");
    }

}


public class DemoStaticService {

    private static final ConcurrentMap<Integer, String> CACHE = new ConcurrentHashMap<>();


    static {
        for (int i = 0; i < 128; i++) {
            CACHE.put(i, String.valueOf(i));
        }
    }

    public static String queryByIdFromCache(Integer id) {
        return CACHE.getOrDefault(id, "");
    }

}

然后我创建了一个控制器,该控制器具有两个操作,一个调用非静态注入服务,另一个调用使用静态方法的服务:

@RestController
@RequestMapping(path = "/demo")
public class DemoController {

    @Autowired
    private DemoService demoService;

    @GetMapping(path = "/demo")
    public String callService(@RequestParam Integer id) {
        return demoService.queryByIdFromCache(id);
    }

    @GetMapping(path = "/static-demo")
    public String callStaticService(@RequestParam Integer id) {
        return DemoStaticService.queryByIdFromCache(id);
    }

}

之后,我编写了两个单元测试来帮助我分析服务方法:

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoControllerTest {

    @Autowired
    private DemoController demoController;

    @Before
    public void before() {
        demoController.callService(1);
        demoController.callStaticService(1);
    }

    @Test
    public void testCallService() {
        for (int id = 0; id < 128; id++) {
            demoController.callService(id);
        }
    }

    @Test
    public void testCallStaticService() {
        for (int id = 0; id < 128; id++) {
            demoController.callStaticService(id);
        }
    }

}

打开测试文件后,我选择了Profile -> Profile Test File菜单项:

Profile Test File

然后从▶ Profile下拉菜单中,选择了Methods选项:

Profile Dropdown

最后,我单击了▶ Profile按钮以配置测试。我得到了这个结果,表明对注入服务的调用仅比对静态方法的调用贵50%:

Profile 1

但是如果第二种方法被某个方面(例如@Transactional)拦截了怎么办?

为了对此进行测试,我更新了DemoService并使它的方法具有事务性

@Service
public class DemoService {

    private static final ConcurrentMap<Integer, String> CACHE = new ConcurrentHashMap<>();

    public DemoService() {
        for (int i = 0; i < 128; i++) {
            CACHE.put(i, String.valueOf(i));
        }
    }

    @Transactional
    public String queryByIdFromCache(Integer id) {
        return CACHE.getOrDefault(id, "");
    }

}

重新运行测试后,这次我得到了此分析结果:

Profile 2

可以看出,事务方面使DemoService.queryByIdFromCache的调用速度降低了14.5(10.2 / 0.708)倍。

要找到导致服务方法变慢的根本原因,建议您设置类似的测试并使用NetBeans Profiler(或类似工具)对其进行分析。