code: public Object invoke(MethodInvocation mi) throws Throwable {
  		if (!Modifier.isPublic(mi.getMethod().getModifiers())) {
  			return mi.proceed();
  		}
  
  		Method searchMethod = mi.getMethod();
  		Object[] args = mi.getArguments();
  		String appid = AOPUtils.getFirstArgOfString(args);
  
  		Method superMethod = null;
  		Measured measuredAnno = null;
  
  		try {
  			superMethod = Search.class.getMethod(searchMethod.getName(), searchMethod.getParameterTypes());
  			measuredAnno = superMethod.getAnnotation(Measured.class);
  		} catch (Exception e) {
  			logger.error("Error in search AOP layer!", e);
  		}
  
  		Set<IOListener> ioListeners = Para.getSearchQueryListeners();
  		for (IOListener ioListener : ioListeners) {
  			ioListener.onPreInvoke(superMethod, args);
  			logger.debug("Executed {}.onPreInvoke().", ioListener.getClass().getName());
  		}
  
  		Object result = null;
  		if (measuredAnno != null) {
  			result = invokeTimedSearch(appid, searchMethod, mi);
  		} else {
  			result = mi.proceed();
  		}
  
  		for (IOListener ioListener : ioListeners) {
  			ioListener.onPostInvoke(superMethod, result);
  			logger.debug("Executed {}.onPostInvoke().", ioListener.getClass().getName());
  		}
  
  		return result;
  	}
docs: Executes code when a method is invoked.
  @param mi method invocation
  @return the returned value of the method invoked or something else (decided here)
  @throws Throwable error