使用Spring自定义注解生产Http接口描述信息

最近在做一个手机后台项目,使用的是SpringMVC,开发的接口是HTTP接口。在接口写完后需要在网页中吧接口的名称测试地址等信息添加到网页中,感觉这样很麻烦还容易漏。于是就写了一个自定义注解通过注解的方式将接口的描述信息加入到接口中,通过注解描述接口信息并且生产接口测试地址

先看使用方法及最终效果

	@ResponseBody
	@RequestMapping("/getBusWaiting")
	@AppInterface(value="获取候车信息",group="test",order=1,params={
			@InterfaceParam(name="lineName",desc="线路名称",testValue="B2"),
			@InterfaceParam(name="isUpDown",desc="上下行标识",testValue="1"),
			@InterfaceParam(name="stationNum",desc="站序",testValue="0"),
			@InterfaceParam(name="len",desc="长度",testValue="700")
	})
	public AppResponse getBusWaitingInfo(String lineName,Integer isUpDown,Integer stationNum,Integer len){
		AppResponse result = new AppResponse();
		return result;
	}

生成的效果

下面是具体实现

接口描述类

/**
 * <p>创建人:王成委  </p>
 * <p>创建时间:2014年12月8日 下午5:28:11  </p>
 * <p>类描述: 标记手机App接口,接口描述信息,用于生成接口信息及测试路径 </p>
 * <p>版权说明: © 2014 Tiamaes </p>
 */
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AppInterface {

	/**
	 * <p>方法描述:接口名称</p>
	 * @return String
	 */
	String value();

	/**
	 * <p>方法描述:分组</p>
	 * @return String
	 */
	String group() default "";

	/**
	 * <p>方法描述:排序</p>
	 * @return int
	 */
	int order () default 0;

	/**
	 * <p>方法描述:参数列表</p>
	 * @return InterfaceParam[]
	 */
	InterfaceParam[] params() default {};
}

接口参数类

/**
 * <p>创建人:王成委  </p>
 * <p>创建时间:2014年12月8日 下午5:29:34  </p>
 * <p>类描述: 手机App接口参数说明 </p>
 * <p>版权说明: © 2014 Tiamaes </p>
 */
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InterfaceParam {

	/**
	 * <p>方法描述:参数名称</p>
	 * @return String
	 */
	String name();

	/**
	 * <p>方法描述:接口说明</p>
	 * @return String
	 */
	String desc();

	/**
	 * <p>方法描述:测试参数值</p>
	 * @return String
	 */
	String testValue() default "";
}

testValue支持自定义变量,在主类中有具体实现

package com.tiamaes.gjds.app.aop;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import com.tiamaes.gjds.app.annotation.AppInterface;
import com.tiamaes.gjds.app.annotation.InterfaceParam;
import com.tiamaes.gjds.app.base.RequestMethodMapping;
import com.tiamaes.gjds.app.base.RequestMethodParameter;
import com.tiamaes.gjds.app.base.Variable;
import com.tiamaes.gjds.util.SetUtils;

/**
 * <p>类描述: 生成接口描述信息并放入Application中 </p>
 * <p>创建人:王成委  </p>
 * <p>创建时间:2015年1月19日 下午4:42:24  </p>
 * <p>版权说明: © 2015 Tiamaes </p>
 * @see com.tiamaes.gjds.app.annotation.AppInterface
 */

public class InterfaceAnnotationConfigProcesser implements ApplicationContextAware,InitializingBean{

	private Log logger = LogFactory.getLog(getClass());

	private Map<String,List<RequestMethodMapping>> mappers =
			new HashMap<String,List<RequestMethodMapping>>();

	private WebApplicationContext applicationContext;

	/**
	 * <p>方法描述:加载带有{@link com.tiamaes.gjds.app.annotation.AppInterface}注解的接口</p>
	 * <p>首先需要获取所有接口,然后过滤方法中带有@AppInterface</p>
	 * <p>创建人: 王成委  </p>
	 * <p>创建时间: 2015年1月10日 上午10:50:06 </p>
	 */
	public void loadHandlerMapping(){
		this.logger.info("初始化配置");
		Map<String, HandlerMapping> handlers = BeanFactoryUtils.beansOfTypeIncludingAncestors(
				applicationContext, HandlerMapping.class, true, false);

		for(Entry<String, HandlerMapping> entry : handlers.entrySet()){
			HandlerMapping mapping = entry.getValue();
			if(mapping instanceof RequestMappingHandlerMapping){
				RequestMappingHandlerMapping requestHandler = (RequestMappingHandlerMapping)mapping;
				Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestHandler.getHandlerMethods();
				for(Entry<RequestMappingInfo, HandlerMethod> handlerMethod : handlerMethods.entrySet()){
					AppInterface annotation = handlerMethod.getValue().getMethodAnnotation(AppInterface.class);
					if(annotation== null)continue;
					PatternsRequestCondition patternsCondition = handlerMethod.getKey().getPatternsCondition();
					String requestUrl = SetUtils.first(patternsCondition.getPatterns());
					this.register(requestUrl, annotation,handlerMethod.getValue().getBeanType());
				}
			}
		}
	}

	/**
	 * <p>方法描述:注册方法</p>
	 * <p>创建人: 王成委  </p>
	 * <p>创建时间: 2015年1月10日 上午10:50:06 </p>
	 * @param requestUrl
	 * @param annotation
	 * @param beanType
	 */
	private void register(String requestUrl, AppInterface annotation,
			Class<?> beanType) {
		String group = annotation.group();
		List<RequestMethodMapping> groupMappers = this.mappers.get(group);
		if(groupMappers == null)groupMappers = new ArrayList<RequestMethodMapping>();
		RequestMethodMapping mapper = new RequestMethodMapping();
		mapper.setGroup(group);
		mapper.setController(beanType.getName());
		mapper.setOrder(annotation.order());
		mapper.setName(annotation.value());
		mapper.setUrl(requestUrl);
		mapper.setParams(this.toParameters(annotation.params()));
		groupMappers.add(mapper);
		this.mappers.put(group, groupMappers);
	}

	/**
	 * <p>方法描述:读取参数</p>
	 * <p>创建人: 王成委  </p>
	 * <p>创建时间: 2015年1月10日 上午10:50:06 </p>
	 * @param params
	 * @return
	 */
	private List<RequestMethodParameter> toParameters(InterfaceParam[] params){
		List<RequestMethodParameter> parameters = new ArrayList<RequestMethodParameter>();

		for(InterfaceParam param : params){
			RequestMethodParameter bean = new RequestMethodParameter();
			bean.setName(param.name());
			bean.setDesc(param.desc());
			if(StringUtils.startsWithIgnoreCase(param.testValue(), "#")){
				String var = param.testValue();
				String value = getByVariable(var.substring(var.indexOf("#")+1));
				bean.setTestValue(value);
			}else{
				bean.setTestValue(param.testValue());
			}
			parameters.add(bean);
		}
		return parameters;
	}

	/**
	 * <p>方法描述:获取变量的值</p>
	 * <p>创建人: 王成委  </p>
	 * <p>创建时间: 2015年1月10日 上午10:50:06 </p>
	 * @param var
	 * @return
	 */
	private String getByVariable(String var){
		Variable variable = Variable.valueOf(var);
		return variable.getValue();
	}

	/**
	 * <p>方法描述:对接口方法根据分组排序</p>
	 * <p>创建人: 王成委  </p>
	 * <p>创建时间: 2015年1月20日 下午16:00:06 </p>
	 */
	private void orderMappers(){
		for(Entry<String,List<RequestMethodMapping>> entry : this.mappers.entrySet() ){
			Collections.sort(entry.getValue(), new Comparator<RequestMethodMapping>() {

				@Override
				public int compare(RequestMethodMapping o1, RequestMethodMapping o2) {
					Integer one = o1.getOrder();
					Integer two = o2.getOrder();
					if(one != null && two != null) return one.intValue()-two.intValue();
					return 0;
				}
			});
		}
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.applicationContext = (WebApplicationContext)applicationContext;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		this.loadHandlerMapping();
		this.orderMappers();
		this.applicationContext.getServletContext().setAttribute("api", this.mappers);
	}

	/**
	 * <p>方法描述:刷新接口信息</p>
	 * <p>创建人: 王成委  </p>
	 * <p>创建时间: 2015年1月10日 上午10:50:06 </p>
	 * @throws Exception
	 */
	public void refresh() throws Exception{
		this.mappers = new HashMap<String,List<RequestMethodMapping>>();
		this.afterPropertiesSet();
	}
}

利用C标签生产接口描述信息

<div class="api-layout">
	<table class="api-table">
		<tr class="api-table-header">
       		<td width="300px;">接口地址</td>
       		<td width="200px;">接口名称</td>
       		<td>参数说明</td>
       	</tr>
		<c:forEach items="${api}" var="map">
        	<tr>
        		<td colspan="3" class="api-group">
					<c:out value="${map.key}" />
				</td>
			</tr>
			<c:forEach items="${map.value}" var="list">

	            <tr>
	        		<td width="300px;">
	        			<a href="<%=baseUrl%>${list.url}${list.testUrl}" target="_blank">${list.url}</a>
	        		</td>
	        		<td width="200px;">${list.name}</td>
	        		<td>
					<c:forEach items="${list.params}" var="params">
						${params.name}:${params.desc}<br/>
					</c:forEach>
					</td>
	        	</tr>
	        </c:forEach>
		</c:forEach>
	</table>
</div>
时间: 2024-10-13 00:53:42

使用Spring自定义注解生产Http接口描述信息的相关文章

深入Spring 自定义注解加载和使用

前言 在工作中经常使用Spring的相关框架,免不了去看一下Spring的实现方法,了解一下Spring内部的处理逻辑.特别是开发Web应用时,我们会频繁的定义@Controller,@Service等JavaBean组件,通过注解,Spring自动扫描加载了这些组件,并提供相关的服务.Spring是如何读取注解信息,并注入到bean容器中的,本文就是通过嵌入Spring的Bean加载,来描述Spring的实现方法.完整的例子都在Github上了. 自定义注解 先看一个最简单的例子,在使用Spr

【转】spring 自定义注解(annotation)与 aop获取注解

首先我们先介绍Java自定义注解. 在开发过程中,我们实现接口的时候,会出现@Override,有时还会提示写@SuppressWarnings.其实这个就是Java特有的特性,注解. 注解就是某种注解类型的一种实例,我们可以把它用在某个类上进行标注.下面这张图解释注解都是什么? 上图可以看出注解大体分为三种:元注解,标记注解,一般注解: 这一块其他的我就不多做介绍,我们这里主要说一下如何定义自己的注解,在这之前我们必须了解标准元注解和相关定义注解的语法.元注解的作用就是负责注解其他注解.Jav

SpringMVC拦截器+Spring自定义注解实现权限验证

设计思路 主要针对需要登录后操作的接口进行校验.接入层在对外暴露接口后,网页.APP.第三方等等途径进行访问接口.用户请求首先会被SpringMVC拦截器拦截到,在拦截器里第一步就是需要校验用户的登录身份(由于是分布式系统这里采用的是userId+accessToken方式来校验),登录校验通过之后再进行用户权限校验,此时会自动拦截@AuthValidate注解的method(核心),如果权限校验失败则抛出权限不足异常,否则校验通过之后再执行具体接口并返回结果. 1.自定义注解 1 packag

Spring自定义注解扫描的实现

目标:实现自定义spring自动扫描注解.主要为后期实现分布式服务框架自动注解提供技术支持 技术分析:通过配置组件扫描标签使spring解析标签. 1. JewelScanBeanDefaultParser.java public class JewelScanBeanDefaultParser implements BeanDefinitionParser { private static final String RESOURCE_PATTERN = "/**/*.class";

spring - 自定义注解

本自定义注解的作用:用于控制类方法的调用,只有拥有某个角色时才能调用. java内置注解 1.@Target 表示该注解用于什么地方,可能的 ElemenetType 参数包括: ElemenetType.CONSTRUCTOR   构造器声明 ElemenetType.FIELD   域声明(包括 enum 实例) ElemenetType.LOCAL_VARIABLE   局部变量声明 ElemenetType.METHOD   方法声明 ElemenetType.PACKAGE   包声明

Spring自定义注解实现Controller获取想要的数据

最近看组内一个哥们写了一个HandlerAdapter,能自动获取Http请求里面的Cookie并组装成一个Model来直接使用.觉得很牛逼.因此自己做了一个,特来分享. 原理: 利用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter. 在DispatcherServlet里面定义了: private List<HandlerAdapter> handlerAdapters; 用于

创建spring自定义注解进行自动装配

1.创建自定义注解 1 import org.springframework.beans.factory.annotation.Qualifier; 2 import java.lang.annotation.ElementType; 3 import java.lang.annotation.Retention; 4 import java.lang.annotation.RetentionPolicy; 5 import java.lang.annotation.Target; 6 7 8

spring --自定义注解(配合@Aspect)

1:首先,声明自定义注解 @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface DtTransactional { /* * Whether need to rollback */ public boolean includeLocalTransaction() default true; public boolean confirmMethodExist() default tru

Spring AOP 自定义注解获取http接口及WebService接口入参和出参

注解方法实现过程中可以采用如下获取方式:-以下为例  HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); 1.定义两个方法注解,分别标记要处理的http接口及Webservice接口: http接口注解 1 2 3 4 5 @Retention(RetentionPolicy.RUNTIME) @Target({ El