精华内容
下载资源
问答
  • 接口作为参数 //通过接口实现多态 class Program { static void Main(string[] args) { //属于不同的类,需要通过接口实现相同的方法。 Iregister re1 = new House (); Iregiste

    接口作为参数

        //通过接口来实现多态
        class Program
        {
            static void Main(string[] args)
            {
                //属于不同的类,需要通过接口来实现相同的方法。   
                Iregister re1 = new House ();
                Iregister re2 = new Car();
                HaveARegister(re1);
                HaveARegister(re2);
    
                Console.ReadKey();
            }
            static void HaveARegister(Iregister re)//接口作为参数
            {
                re.Register();
            }
        }
        interface Iregister
        {
            void Register();
        }
        public class House:Iregister
        {
            public void Register()
            {
                Console.WriteLine("House have a register");
            }
        }
        public class Car:Iregister
        {
            public void Register()
            {
                Console.WriteLine("The car need a register");
            }
        }


    展开全文
  • 背景:在java后端项目开发过程中,其实有很大一部分项目,后端都没有做接口参数校验,或者做了参数校验,但是实现方式都是简单的使用if判断;主要原因在于:API的参数校验目前缺少比较流行的方案、现有方案不适用...

    背景:在java后端项目开发过程中,其实有很多大一部分项目,后端都没有做接口参数校验,或者做了参数校验,但是实现方式都是简单的使用if判断;主要原因在于:API的参数校验目前缺少比较流行的方案、现有方案不适用与自己的项目、项目部署在内网要求不高、或者干脆参数校验这种事情丢给前端来做

    方案一:hibernate-validator

    hibernate-validator是Hibernate项目中的一个数据校验框架,是Bean Validation 的参考实现,hibernate-validator除了提供了JSR 303规范中所有内置constraint 的实现,还有一些附加的constraint 。使用hibernate-validator能够将数据校验从业务代码中脱离出来,增加代码可读性,同时也让数据校验变得更加方便、简单;
    详情可以参考:https://blog.csdn.net/weixin_37162010/article/details/80252154

    方案二:AOP实现方法级的参数校验

    hibernate-validator是在实体类上添加注解;但对于不同的方法,所应用的校验规则也是不一样的,这样子可能就会需要创建多个实体类或者组,甚至于一些接口根本就没实体类参数;所以实际应用过程中还是有一定的困难;
    基于此,我花费了一点业余时间,借鉴了hibernate-validator的思路,实现了一套基于 注解 + AOP 的方式实现接口参数校验框架
    在方法体上使用@CheckParam或者@CheckParams注解标注需要校验的参数;举个栗子:

    @RestController
    @RequestMapping("/paramCheck")
    public class ParamCheckController {
    
    	@CheckParam(value = Check.NotEmpty, argName = "userName", msg = "你大爷的,这个是必填参数!")
    	@PostMapping("/singleCheckNotEmpty")
    	public Object singleCheckNotNull(String userName) {
    		return 1;
    	}
    
    	@CheckParam(value = Check.DateTime, argName = "dateTime")
    	@PostMapping("/singleCheckDateTime")
    	public Object singleCheckDateTime(String dateTime) {
    		return 1;
    	}
    	
    	@CheckParams({
    		@CheckParam(value = Check.NotNull, argName = "userId", msg = "你大爷的,这个是必填参数!"),
    		@CheckParam(value = Check.NotEmpty, argName = "userName"),
    		@CheckParam(value = Check.NotEmpty, argName = "dept.deptName"),
    		@CheckParam(value = Check.Past, argName = "dept.createTime"),
    		@CheckParam(value = Check.lt, argName = "dept.employees", express = "2") // 对集合的size判断
    	})
    	@PostMapping("/entityMultiCheck")
    	public Object entityMultiCheck(Integer userId, String userName, @RequestBody DeptEntity dept) {
    		return 1;
    	}
    }
    

    具体方案实现

    版本仅供参考 jdk1.8 maven3.5.0 spring boot 2.0.2.RELEASE
    在方法体上使用@CheckParam或者@CheckParams注解标注需要校验的参数;
    然后使用AOP切入,获取配置的参数校验规则,同时获取对应的入参值,
    调用CheckUtil工具类,进行判断
    在这里插入图片描述

    • Check参数校验枚举类;目前实现了下列校验方法,后续会继续补充
    public enum Check {
    
    	Null("参数必须为 null", CheckUtil::isNull),
    
    	NotNull("参数必须不为 null", CheckUtil::isNotNull),
    
    	Empty("参数的必须为空", CheckUtil::isEmpty),
    	
    	NotEmpty("参数必须非空", CheckUtil::isNotEmpty),
    	
    	True("参数必须为 true", CheckUtil::isTrue),
    
    	False("参数必须为 false", CheckUtil::isFalse),
    
    	Date("参数必须是一个日期 yyyy-MM-dd", CheckUtil::isDate),
    
    	DateTime("参数必须是一个日期时间  yyyy-MM-dd HH:mm:ss", CheckUtil::isDateTime),
    
    	Past("参数必须是一个过去的日期 ", CheckUtil::isPast),
    
    	Future("参数必须是一个将来的日期 ", CheckUtil::isFuture),
    	
    	Today("参数必须今天的日期 ", CheckUtil::isToday),
    	
    	Enum("参数必须在枚举中 ", CheckUtil::inEnum),
    	
    	Email("参数必须是Email地址", CheckUtil::isEmail),
    
    	Range("参数必须在合适的范围内", CheckUtil::inRange),
    
    	NotIn("参数必须不在指定的范围内 ", CheckUtil::outRange),
    	
    	Length("参数长度必须在指定范围内", CheckUtil::inLength),
    	
    	gt("参数必须大于指定值", CheckUtil::isGreaterThan),
    	
    	lt("参数必须小于指定值", CheckUtil::isLessThan),
    	
    	ge("参数必须大于等于指定值", CheckUtil::isGreaterThanEqual),
    	
    	le("参数必须小于等于指定值", CheckUtil::isLessThanEqual),
    	
    	ne("参数必须不等于指定值", CheckUtil::isNotEqual),
    	
    	Equal("参数必须不等于指定值", CheckUtil::isEqual),
    	
    	Pattern("参数必须符合指定的正则表达式", CheckUtil::isPattern)	;
    
    	public String msg;
    	public BiFunction<Object, String, Boolean> fun;
    
    	Check(String msg, BiFunction<Object, String, Boolean> fun) {
    		this.msg = msg;
    		this.fun = fun;
    	}
    }
    
    • CheckParam注解类
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CheckParam {
    
    	// 字段校验规则,格式:字段名+校验规则+冒号+错误信息,例如:id<10:ID必须少于10
    	Check value() default Check.NotNull;
    
    	// 多个值逗号隔开
    	String express() default "";
    	
    	// 参数名称用.表示层级,最多支持2级如: entity.userName
    	String argName();
    
    	// 自定义错误提示信息
    	String msg() default "";
    }
    
    • CheckParams注解类
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CheckParams {
        // 多个CheckParam,由上往下判断
    	CheckParam[] value();
    }
    
    • AOP切面,篇幅有限有删减,详情请下载源码查看
    @Aspect
    @Component
    public class CheckParamAop {
    
    	@Pointcut("@annotation(com.yuxue.validator.annotation.CheckParam)")
    	public void checkParam() {
    	}
    	
    	@Pointcut("@annotation(com.yuxue.validator.annotation.CheckParams)")
    	public void checkParams() {
    	}
    
    	@Around("checkParam()") // 这里要换成自定义注解的路径
    	public Object check1(ProceedingJoinPoint point) throws Throwable {
    		Object obj;
    		// 参数校验
    		String msg = doCheck(point, false);
    		if (null != msg) {
    			throw new IllegalArgumentException(msg);
    		}
    		// 通过校验,继续执行原有方法
    		obj = point.proceed();
    		return obj;
    	}
    	
    	@Around("checkParams()") // 这里要换成自定义注解的路径
    	public Object check2(ProceedingJoinPoint point) throws Throwable {
    		Object obj;
    		// 参数校验
    		String msg = doCheck(point, true);
    		if (null != msg) {
    			throw new IllegalArgumentException(msg);
    		}
    		// 通过校验,继续执行原有方法
    		obj = point.proceed();
    		return obj;
    	}
    
    	/**
    	 * 参数校验
    	 * @param point 切点
    	 * @param multi 多参数校验
    	 * @return 错误信息
    	 */
    	private String doCheck(JoinPoint point, boolean multi) {
    		Method method = this.getMethod(point);
    		String[] paramName = this.getParamName(point);
    		Object[] arguments = point.getArgs();	// 获取接口传递的所有参数
    		
    		Boolean isValid = true;
    		String msg = null;
    		if(multi) {	// 多个参数校验
    			CheckParams annotation = method.getAnnotation(CheckParams.class);	// AOP监听带注解的方法,所以不用判断注解是否为空
    			CheckParam[] annos = annotation.value();
    			for (CheckParam anno : annos) {
    				String argName = anno.argName();
    				Object value = this.getParamValue(arguments, paramName, argName);	//参数值
    		        isValid = anno.value().fun.apply(value, anno.express());	// 执行判断 // 调用枚举类的 CheckUtil类方法
    		        if(!isValid) {	// 只要有一个参数判断不通过,立即返回
    		        	msg = anno.msg();
    			        if(null == msg || "".equals(msg)) {
    			        	msg = argName + ": " + anno.value().msg + " " + anno.express();
    			        }
    		        	break;
    		        }
    			}
    		} else {	// 单个参数校验
    			CheckParam anno = method.getAnnotation(CheckParam.class);		// AOP监听带注解的方法,所以不用判断注解是否为空
    			
    			String argName = anno.argName();
    			Object value = this.getParamValue(arguments, paramName, argName);	//参数值
    	        isValid = anno.value().fun.apply(value, anno.express());	// 执行判断 // 调用枚举类的 CheckUtil类方法
    	        msg = anno.msg();
    	        if(null == msg || "".equals(msg)) {
    	        	msg = argName + ": " + anno.value().msg + " " + anno.express();
    	        }
    		}
            if(isValid) {
            	// log.info("参数校验通过");
            	return null;
            } else {
            	// log.error("参数校验不通过");
            	return msg ;
            }
    	}
    
    	/**
    	 * 根据参数名称,获取参数值
    	 */
    	private Object getParamValue(Object[] arguments, String[] paramName, String argName) {
    		Object value = null;
    		String name = argName;
    		if(argName.contains(".")) {
    			name = argName.split("\\.")[0];
    		}
    		int index = 0;
    		for (String string : paramName) {
    			if(string.equals(name)) {
    				value = arguments[index];	//基本类型取值	// 不做空判断,如果注解配置的参数名称不存在,则取值为null
    				break;
    			}
    			index++;
    		}
    		if(argName.contains(".")) {	//从对象中取值
    			argName = argName.split("\\.")[1];
    			JSONObject jo = (JSONObject) JSONObject.toJSON(value);
    			// 从实体对象中取值
    			value = jo.get(argName);
    		}
    		return value;
    	}
    }
    
    • CheckUtil参数校验工具类,篇幅有限有删减,详情请下载源码查看
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.Arrays;
    import java.util.Collection;
    import java.util.Date;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    import org.springframework.util.CollectionUtils;
    
    public class CheckUtil {
    
        /**
         * 判断value == null
         * @param value       字段值
         * @param express 这里不需要,只是为了参数统一
         * @return true or false
         */
        public static Boolean isNull(Object value, String express) {
            if (null != value) {
                return Boolean.FALSE;
            }
            return Boolean.TRUE;
        }
    
        /**
         * 判断value != null
         * @param value       字段值
         * @param express 这里不需要,只是为了参数统一
         * @return true or false
         */
        public static Boolean isNotNull(Object value, String express) {
            if (null == value) {
                return Boolean.FALSE;
            }
            return Boolean.TRUE;
        }
    
        /**
         * 判断value !=null && length、size > 0
         * 支持字符串判断
         * 支持集合判断
         */
        public static Boolean isNotEmpty(Object value, String express) {
            if(isNull(value, express)) {
                return Boolean.FALSE;
            }
            if(value instanceof String && "".equals(((String) value).trim())) {
                return Boolean.FALSE;
            }
            if(value instanceof Collection && CollectionUtils.isEmpty((Collection) value)) {
                return Boolean.FALSE;
            }
            return Boolean.TRUE;
        }
    
    
        /**
         * 判断参数是否是 true
         * 支持Boolean类型
         * 支持String类型
         */
        public static Boolean isTrue(Object value, String express) {
            if(isNull(value, express)) {
                return Boolean.FALSE;
            }
            if(value instanceof Boolean) {
                return (Boolean) value;
            }
            if(value instanceof String) {
                try {
                    return Boolean.parseBoolean((String) value);
                } catch (Exception e) {
                    return Boolean.FALSE;
                }
            }
            return Boolean.FALSE;
        }
    
        /**
         * 判断参数是否是一个日期
         * 支持Date类型
         * 支持LocalDate类型
         * 支持String类型,yyyy-MM-dd、yyyyMMdd、yyyy/MM/dd格式; 默认仅支持yyyy-MM-dd
         */
        public static Boolean isDate(Object value, String express) {
            if(isNull(value, express)) {
                return Boolean.FALSE;
            }
            if(value instanceof String) {      // 通常json格式参数,都是以字符串类型传递,优先判断
                // 验证参数,不能处理掉所有异常的符号
                // String v = ((String) value).trim().replaceAll("[-/\\s]", ""); 
                String v = ((String) value); //.replaceAll("[-/]", "");
                try {
                    LocalDate.parse(v, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                    return Boolean.TRUE;
                } catch (Exception e) {
                    return Boolean.FALSE;
                }
            }
            if(value instanceof Date) {
                return Boolean.TRUE;
            }
            if(value instanceof LocalDate) {
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }
    
    
        /**
         * 判断参数是否是一个日期
         * 支持Date类型
         * 支持LocalDateTime类型
         * 支持String类型,yyyy-MM-dd HH:mm:ss、yyyyMMddHHmmss、yyyy/MM/dd HH:mm:ss格式; 默认仅支持yyyy-MM-dd HH:mm:ss
         */
        public static Boolean isDateTime(Object value, String express) {
            if(isNull(value, express)) {
                return Boolean.FALSE;
            }
            if(value instanceof String) {   // 通常json格式参数,都是以字符串类型传递,优先判断
                String v = ((String) value); //.replaceAll("[-/]", "");  // 验证参数,不能处理掉所有异常的符号
                try {
                    LocalDateTime.parse(v, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                    return Boolean.TRUE;
                } catch (Exception e) {
                    /*try {
                        LocalDateTime.parse(v, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
                        return Boolean.TRUE;
                    } catch (Exception e1) {
                        return Boolean.FALSE;
                    }*/
                	return Boolean.FALSE;
                }
            }
            if(value instanceof Date) {
                return Boolean.TRUE;
            }
            if(value instanceof LocalDateTime) {
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }
    
        /**
         * 判断是否是邮箱
         * 使用正则表达式判断
         * @param value
         * @param express
         * @return
         */
        public static Boolean isEmail(Object value, String express) {
            if(isNull(value, express)) {
                return Boolean.FALSE;
            }
            if(value instanceof String) {  
                String regEx = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
                Pattern p = Pattern.compile(regEx);
                Matcher m = p.matcher((String) value);
                if (m.matches()) {
                    return Boolean.TRUE;
                } 
            }
            return Boolean.FALSE;
        }
        
        /**
         * 判断参数的取值范围,逗号隔开,无空格;闭区间
         * 支持Integer
         * 支持Long
         * 支持Short
         * 支持Float
         * 支持Double
         * @param value
         * @param rangeStr
         * @return
         */
        public static Boolean inRange(Object value, String rangeStr) {
            if(isNull(value, rangeStr)) {
                return Boolean.FALSE;
            }
            if(null == rangeStr || "".equals(rangeStr)) {
                return Boolean.FALSE;
            }
            if(value instanceof Integer) {
            	Integer begin = Integer.valueOf(rangeStr.split(",")[0]);
            	Integer end = Integer.valueOf(rangeStr.split(",")[1]);
            	Integer v = ((Integer) value);
                return  begin <= v && v <= end;
            }
            if(value instanceof Long) {
            	Long begin = Long.valueOf(rangeStr.split(",")[0]);
            	Long end = Long.valueOf(rangeStr.split(",")[1]);
            	Long v = ((Long) value);
                return  begin <= v && v <= end;
            }
            if(value instanceof Short) {
            	Short begin = Short.valueOf(rangeStr.split(",")[0]);
            	Short end = Short.valueOf(rangeStr.split(",")[1]);
            	Short v = ((Short) value);
                return  begin <= v && v <= end;
            }
            if(value instanceof Float) {
            	Float begin = Float.valueOf(rangeStr.split(",")[0]);
            	Float end = Float.valueOf(rangeStr.split(",")[1]);
            	Float v = ((Float) value);
                return  begin <= v && v <= end;
            }
            if(value instanceof Double) {
            	Double begin = Double.valueOf(rangeStr.split(",")[0]);
            	Double end = Double.valueOf(rangeStr.split(",")[1]);
            	Double v = ((Double) value);
                return  begin <= v && v <= end;
            }
            return Boolean.FALSE;
        }
        
        /**
         * 判断参数的取值范围,逗号隔开,无空格;闭区间
         * 判断String的length范围, rangeStr取值举例:"6,18"
         * @param value
         * @param rangeStr
         * @return
         */
        public static Boolean inLength(Object value, String rangeStr) {
        	if(isNull(value, rangeStr)) {
                return Boolean.FALSE;
            }
            if(null == rangeStr || "".equals(rangeStr)) {
                return Boolean.FALSE;
            }
            if(value instanceof String) {
            	Integer begin = Integer.valueOf(rangeStr.split(",")[0]);
            	Integer end = Integer.valueOf(rangeStr.split(",")[1]);
            	Integer v = ((String) value).length();
                return  begin <= v && v <= end;
            }
            return Boolean.FALSE;
        }
        
        
        /**
         * 判断参数是否在枚举的数据中, 枚举的表达式用 英文逗号隔开,无空格,如: "男,女,太监"
         * 校验过程,不在对表达式进行校验,所以请确保表达式的格式正确
         * 支持String
         * 支持Integer Short Long
         * @param value
         * @param enumStr
         * @return
         */
        public static Boolean inEnum(Object value, String enumStr) {
            if(isNull(value, null)) {
                return Boolean.FALSE;
            }
            if(null == enumStr || "".equals(enumStr)) {
                return Boolean.FALSE;
            }
        	String[] array = enumStr.split(",");
        	Set<String> set = new HashSet<>(Arrays.asList(array));
            return set.contains(value.toString());
        }
    
        /**
         * 是否大于等于
         * 支持String,判断length值
         * 支持Integer
         * 支持Long
         * 支持Short
         * 支持Float
         * 支持Double
         * 支持Collection,判断size的值
         * @param value
         * @param express
         * @return
         */
        public static Boolean isGreaterThanEqual(Object value, String express) {
            if (value == null) {
                return Boolean.FALSE;
            }
            if(value instanceof Integer) {
                return ((Integer) value) >= Integer.valueOf(express);
            }
            if(value instanceof Long) {
                return ((Long) value) >= Long.valueOf(express);
            }
            if(value instanceof Short) {
                return ((Short) value) >= Short.valueOf(express);
            }
            if(value instanceof Float) {
                return ((Float) value) >= Float.valueOf(express);
            }
            if(value instanceof Double) {
                return ((Double) value) >= Double.valueOf(express);
            }
            if(value instanceof String) {
                return ((String) value).length() >= Integer.valueOf(express);
            }
            if(value instanceof Collection) {
                return  ((Collection) value).size() >= Integer.valueOf(express);
            }
            return Boolean.FALSE;
        }
        
        /**
         * 判断是否Equal指定的值
         * 支持String
         * 支持Integer
         * 支持Long
         * 支持Short
         * 支持Float
         * 支持Double
         * 支持Collection,判断size的值
         * @param value
         * @param express
         * @return
         */
        public static Boolean isEqual(Object value, String express) {
            if (value == null) {
                return Boolean.FALSE;
            }
            if(value instanceof String) {
                return ((String) value).equals(express);
            }
            if(value instanceof Integer) {
                return ((Integer) value).equals(Integer.valueOf(express));
            }
            if(value instanceof Long) {
                return ((Long) value).equals(Long.valueOf(express));
            }
            if(value instanceof Short) {
                return ((Short) value).equals(Short.valueOf(express));
            }
            if(value instanceof Float) {
                return ((Float) value).equals(Float.valueOf(express));
            }
            if(value instanceof Double) {
                return ((Double) value).equals(Double.valueOf(express));
            }
            if(value instanceof Collection) {
                return  ((Collection) value).size() == Integer.valueOf(express);
            }
            return Boolean.FALSE;
        }
        
        /**
         * 判断String是否满足正则表达式
         * @param value
         * @param regEx 正则表达式
         * @return
         */
        public static Boolean isPattern(Object value, String regEx) {
            if(isNull(value, null)) {
                return Boolean.FALSE;
            }
            if(value instanceof String) {  
                Pattern p = Pattern.compile(regEx);
                Matcher m = p.matcher((String) value);
                if (m.matches()) {
                    return Boolean.TRUE;
                } 
            }
            return Boolean.FALSE;
        }
    }
    

    应用文档

    在这里插入图片描述
    修改Application,添加 @SpringBootApplication(scanBasePackages = {"com.yuxue"})

    maven项目添加pom依赖,或者添加jar依赖包即可

    <dependency>
    	<groupId>com.yuxue</groupId>
    	<artifactId>yx-validator</artifactId>
    	<version>1.0.0</version>
    </dependency>
    

    接口参数校验使用demo

    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.yuxue.entity.DeptEntity;
    import com.yuxue.entity.EmployeeEntity;
    import com.yuxue.validator.annotation.CheckParam;
    import com.yuxue.validator.annotation.CheckParams;
    import com.yuxue.validator.enumtype.Check;
    
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiImplicitParam;
    import io.swagger.annotations.ApiImplicitParams;
    
    
    @Api(tags = {"check"}, description = "参数校验")
    @RestController
    @RequestMapping("/paramCheck")
    public class ParamCheckController {
    
    	@ApiImplicitParam(name = "userId", value = "", dataType = "Integer", paramType="query")
    	@CheckParam(value = Check.NotNull, argName = "userId")
    	@PostMapping("/singleCheckNotNull")
    	public Object singleCheckNotNull(Integer userId) {
    		System.err.println(userId);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "userName", value = "", dataType = "String", paramType="query")
    	@CheckParam(value = Check.NotEmpty, argName = "userName", msg = "你大爷的,这个是必填参数!")
    	@PostMapping("/singleCheckNotEmpty")
    	public Object singleCheckNotNull(String userName) {
    		System.err.println(userName);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "bl", value = "", dataType = "Boolean", paramType="query")
    	@CheckParam(value = Check.True, argName = "bl")
    	@PostMapping("/singleCheckTrue")
    	public Object singleCheckTrue(Boolean bl) {
    		System.err.println(bl);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "date", value = "", dataType = "String", paramType="query")
    	@CheckParam(value = Check.Date, argName = "date")
    	@PostMapping("/singleCheckDate")
    	public Object singleCheckDate(String date) {
    		System.err.println(date);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "dateTime", value = "", dataType = "String", paramType="query")
    	@CheckParam(value = Check.DateTime, argName = "dateTime")
    	@PostMapping("/singleCheckDateTime")
    	public Object singleCheckDateTime(String dateTime) {
    		System.err.println(dateTime);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "date", value = "", dataType = "String", paramType="query")
    	@CheckParam(value = Check.Past, argName = "date")
    	@PostMapping("/singleCheckPast")
    	public Object singleCheckPast(String date) {
    		System.err.println(date);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "dateTime", value = "", dataType = "String", paramType="query")
    	@CheckParam(value = Check.Future, argName = "dateTime", msg = "参数必须是一个将来的日期或者时间,并且满足 yyyy-MM-dd HH:mm:ss格式")
    	@PostMapping("/singleCheckFuture")
    	public Object singleCheckFuture(String dateTime) {
    		System.err.println(dateTime);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "date", value = "", dataType = "String", paramType="query")
    	@CheckParam(value = Check.Today, argName = "date")
    	@PostMapping("/singleCheckToday")
    	public Object singleCheckToday(String date) {
    		System.err.println(date);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "gender", value = "", dataType = "String", paramType="query")
    	@CheckParam(value = Check.Enum, argName = "gender", express="男,女,太监")
    	@PostMapping("/singleCheckStringEnum")
    	public Object singleCheckStringEnum(String gender) {
    		System.err.println(gender);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "gender", value = "", dataType = "Integer", paramType="query")
    	@CheckParam(value = Check.Enum, argName = "gender", express="0,1")
    	@PostMapping("/singleCheckIntegerEnum")
    	public Object singleCheckIntegerEnum(Integer gender) {
    		System.err.println(gender);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "password", value = "", dataType = "String", paramType="query")
    	@CheckParam(value = Check.Length, argName = "password", express="6,18", msg="密码length必须在6-18位之间!")
    	@PostMapping("/singleCheckStringLength")
    	public Object singleCheckStringLength(String password) {
    		System.err.println(password);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "password", value = "", dataType = "String", paramType="query")
    	@CheckParams({
    		@CheckParam(value = Check.ge, argName = "password", express = "6"),
    		@CheckParam(value = Check.le, argName = "password", express = "18")
    	})
    	@PostMapping("/singleCheckStringLength1")
    	public Object singleCheckStringLength1(String password) {
    		System.err.println(password);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "age", value = "", dataType = "Integer", paramType="query")
    	@CheckParam(value = Check.Range, argName = "age", express="18,50")
    	@PostMapping("/singleCheckIntegerRange")
    	public Object singleCheckIntegerRange(Integer age) {
    		System.err.println(age);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "age", value = "", dataType = "Integer", paramType="query")
    	@CheckParams({
    		@CheckParam(value = Check.ge, argName = "age", express="18"),
    		@CheckParam(value = Check.le, argName = "age", express="50")
    	})
    	@PostMapping("/singleCheckIntegerRange1")
    	public Object singleCheckIntegerRange1(Integer age) {
    		System.err.println(age);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "age", value = "", dataType = "Integer", paramType="query")
    	@CheckParam(value = Check.NotIn, argName = "age", express="18,50")
    	@PostMapping("/singleCheckIntegerNotIn")
    	public Object singleCheckIntegerNotIn(Integer age) {
    		System.err.println(age);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "age", value = "", dataType = "Integer", paramType="query")
    	@CheckParams({
    		@CheckParam(value = Check.lt, argName = "age", express="18"),
    		@CheckParam(value = Check.gt, argName = "age", express="50")
    	})
    	@PostMapping("/singleCheckIntegerNotIn1")
    	public Object singleCheckIntegerNotIn1(Integer age) {
    		System.err.println(age);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "email", value = "", dataType = "String", paramType="query")
    	@CheckParam(value = Check.Email, argName = "email", msg="你大爷的,输入个邮箱啊!")
    	@PostMapping("/singleCheckEmail")
    	public Object singleCheckEmail(String email) {
    		System.err.println(email);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "age", value = "", dataType = "Integer", paramType="query")
    	@CheckParam(value = Check.ge, argName = "age", express="18", msg = "必须大于等于18岁") // gt、lt、le、ne、Equal不再举例; 具体看注释
    	@PostMapping("/singleCheckIntegerGe")
    	public Object singleCheckIntegerGe(Integer age) {
    		System.err.println(age);
    		return 1;
    	}
    	
    	@ApiImplicitParam(name = "pattern", value = "", dataType = "String", paramType="query")
    	@CheckParam(value = Check.Pattern, argName = "pattern", express="^[\u0021-\u007E]{4,16}$")
    	@PostMapping("/singleCheckPattern")
    	public Object singleCheckPattern(String pattern) {
    		System.err.println(pattern);
    		return 1;
    	}
    	
    	@ApiImplicitParams({
    		@ApiImplicitParam(name = "userId", dataType = "Integer", paramType="query"),
    		@ApiImplicitParam(name = "userName", dataType = "String", paramType="query")
    	})
    	@CheckParams({
    		@CheckParam(value = Check.NotNull, argName = "userId", msg = "你大爷的,这个是必填参数!"),
    		@CheckParam(value = Check.NotNull, argName = "userName")
    	})
    	@PostMapping("/multiCheckNotNull")
    	public Object multiCheckNotNull(Integer userId, String userName) {
    		System.err.println(userId);
    		System.err.println(userName);
    		return 1;
    	}
    	
    	@ApiImplicitParams({
    		@ApiImplicitParam(name = "userId", dataType = "Integer", paramType="query"),
    		@ApiImplicitParam(name = "userName", dataType = "String", paramType="query"),
    		@ApiImplicitParam(name = "employee", dataType = "entity", paramType="body")
    	})
    	@CheckParams({
    		@CheckParam(value = Check.NotNull, argName = "userId", msg = "你大爷的,这个是必填参数!"),
    		@CheckParam(value = Check.NotEmpty, argName = "userName"),
    		@CheckParam(value = Check.NotEmpty, argName = "employee.name")
    	})
    	@PostMapping("/entityCheckNotNull")
    	public Object entityCheckNotNull(Integer userId, String userName, @RequestBody EmployeeEntity employee) {
    		System.err.println(userId);
    		System.err.println(userName);
    		System.err.println(employee.getName());
    		return 1;
    	}
    	
    	@ApiImplicitParams({
    		@ApiImplicitParam(name = "userId", dataType = "Integer", paramType="query"),
    		@ApiImplicitParam(name = "userName", dataType = "String", paramType="query"),
    		@ApiImplicitParam(name = "dept", dataType = "entity", paramType="body")
    	})
    	@CheckParams({
    		@CheckParam(value = Check.NotNull, argName = "userId", msg = "你大爷的,这个是必填参数!"),
    		@CheckParam(value = Check.NotEmpty, argName = "userName"),
    		@CheckParam(value = Check.NotEmpty, argName = "dept.deptName"),
    		@CheckParam(value = Check.Past, argName = "dept.createTime"),
    		@CheckParam(value = Check.lt, argName = "dept.employees", express = "2") // 对集合的size判断
    	})
    	@PostMapping("/entityMultiCheck")
    	public Object entityMultiCheck(Integer userId, String userName, @RequestBody DeptEntity dept) {
    		System.err.println(userId);
    		System.err.println(userName);
    		System.err.println(dept.getDeptName());
    		return 1;
    	}
    }
    

    源码下载地址

    码云git:https://gitee.com/admin_yu/yx-validator
    CSDN下载频道:https://download.csdn.net/download/weixin_42686388/12109312

    可能遇到的问题

    • 切面不生效
      修改Application,添加 @SpringBootApplication(scanBasePackages = {"com.yuxue"}) 扫描到注解即可
    • 参数校验不通过抛出异常
      本框架校验参数,发现不通过,会抛出IllegalArgumentException异常,并支持配置自定义的msg,如果未配置则显示枚举中设置的默认msg,具体的异常处理,需要自己处理
    • 参数校验方法有bug
      可以自行修改CheckUtil工具类里面对应的方法即可
      还可以可以通过yuxue1687@qq.com联系我,将问题及日志整理出来发给我,我会定期处理。也可以通过码云git提交修改后的代码请求。

    参考文档

    https://blog.csdn.net/weixin_37162010/article/details/80252154
    https://www.jb51.net/article/139874.htm
    http://hibernate.org/validator/releases/

    展开全文
  • spring实现动态加载javabean方法,一个接口多实现,根据参数不同加载不同的实现类。

    场景:先讲一下我的应用场景,避免大家看了半天跟自己的场景不同白白的浪费时间;

    我使用动态加载bean是由于程序中有个接口需要根据不同的书商定制实现不同的导入图书功能,但接口定义都是一个,每个书商都去实现该接口,接口内部实现不同,再根据登录的书商不同去加载不同的方法。下面开始调用步骤:

    以下只是我想到的办法,如有什么更好的方法可以私聊我,欢迎大家出谋献策,谢谢。

    一、编写动态获取bean的工具类

    package util;

    import javax.servlet.http.HttpServletRequest;

    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.web.context.support.WebApplicationContextUtils;

    import service.system.ImportExcelService;

    /**
     * 动态加载bean util
     * 该类中主要作用:为书商批量导入图书和采集单所编写的不同的接口实现,根据书商的不同动态的加载不同的接口实现bean
     */
    public class GetDynamicServiceUtil {

    /**

    * @param request
    * @param companyId:需要动态导入bean的companyId,根据conpanyId加载不同的方法
    * @return
    *
    */
    public static ImportExcelService getDynamicService(HttpServletRequest request,String companyId){
    //serviceName 格式service.Impl.system.ImportExcelServiceImpl_3

    //下面的PropertiesConfigManager.getInstance方法为获取.properties配置文件中的名字为serviceName的信息,“notFoun //d”为当找不到名字为serviceName的信息的默认值,可以在我的博客中找到读取.properties文件的方法

    String serviceName = PropertiesConfigManager.getInstance(companyId+".properties").get("serviceName","notFound");
    if("notFound".equals(serviceName)){
    return null;
    }

    ApplicationContext ac = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());  
    ConfigurableApplicationContext context = (ConfigurableApplicationContext) ac;   
    //Bean的实例工厂  
    DefaultListableBeanFactory dbf = (DefaultListableBeanFactory) context.getBeanFactory();  
    //Bean构建  要创建的Bean的Class对象  
    BeanDefinitionBuilder dataSourceBuider = BeanDefinitionBuilder. genericBeanDefinition(serviceName);  
    //拼接bean名字
    String beanName = "importExcelService_"+companyId;
    //将实例注册spring容器中    
    dbf.registerBeanDefinition(beanName, dataSourceBuider.getBeanDefinition());  

    //获取bean
    ImportExcelService serviceImpl = (ImportExcelService)dbf.getBean(beanName); 
        
       return serviceImpl;     
    }
    }


    二、测试

    创建测试类,仅仅是做个栗子,实际上需要在controller层进行调用getDynamicService方法。

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations={"classpath*:spring-applicationContext.xml"})
    @TransactionConfiguration(transactionManager="transactionManager",defaultRollback=true)
    @Transactional
    public class TestOther {

    @Test

    public void testImport(){

    //ImportExcelService 为接口,通过getDynamicService方法获取到的是该接口对用的实现类

    //根据companyId动态的获取该书商所对应的解析excel表格的方法
    ImportExcelService excelService = GetDynamicServiceUtil.getDynamicService(request,companyId);
    List<Book> list = excelService.importBook(
    “此处为要导入的excel表格文件”);
    //下面的importBook方法根据书商不同去调用不同的方法实现
      // List<Book> list=ExcelParse.importBook(file, request);//这里是解析excel文件
       
    boolean result = bookManagerService.saveBookList(list,session.getAttribute("qrtraceName").toString(),Integer.parseInt(companyId),fileInfoTemp);
        

    }

    }

    注:ImportExcelService 该接口的具体实现就不需要在此列出了。


    展开全文
  • 一、背景 ...根据现有逻辑,前台请求http接口的Content-Type有两种,application/json和application/x-www-form-urlencoded。现要求两种请求方式都能够进行参数绑定。想到通过自定义一个HandlerM...

    一、背景
    SpringBoot版本2.1.1-RELEASE。在工作中遇到了这样一个特殊的需求:需要接收前台传入的参数,接收参数并封装对象之后进行后续的处理。根据现有逻辑,前台请求http接口的Content-Type有两种,application/json和application/x-www-form-urlencoded。现要求两种请求方式都能够进行参数绑定。想到通过自定义一个HandlerMethodArgumentResolver来实现。

    二、参数绑定的原理

    测试代码1:

    @RestController
    @RequestMapping("/test")
    @Slf4j
    public class TestWebController {
     
        @RequestMapping("/form")
        public Person testFormData(Person person, HttpServletRequest request) {
            String contentType = request.getHeader("content-type");
            log.info("Content-Type:" + contentType);
            log.info("入参值:{}", JSON.toJSONString(person));
            return person;
        }
    }
    请求参数:

    curl --request POST \
      --url http://localhost:8080/test/form \
      --header 'Content-Type: application/x-www-form-urlencoded' \
      --data 'name=test&age=19'
    控制台输出结果:

    TestWebController     : Content-Type:application/x-www-form-urlencoded
    TestWebController     : 入参值:{"age":19,"name":"test"}
    可以看出表单提交的参数根据字段名被自动绑定到了Person这个对象。

    通过查看源代码可以发现,我们的http请求进入DispatcherServlet的doDispatch方法,通过方法

    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    获取了当前请求的RequestMappingHandlerAdapter对象ha,随后,执行了方法

    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    在该方法中,执行了AbstractHandlerMethodAdapter抽象类的默认方法handle,默认方法又调用了ha的handleInternal方法。随后通过方法形参传入的HandlerMethod对象(HandlerMethod对象其实就是我们Controller里自己写的testFormData的Method对象),获取可执行的方法InvocableHandlerMethod。随后执行了可执行方法对象的getMethodArgumentValues方法。

    MethodParameter[] parameters = getMethodParameters();
    在方法中,获取了当前方法的所有的形参。然后循环遍历这些形参,通过HandlerMethodArgumentResolver接口的一个实现类来处理这个形参。这里我们发现,当前的resolvers是一个组合对象。这个组合对象也实现了这个接口,并且这个对象有一个私有的成员变量:一个接口的实现类的集合。在处理参数的时候,遍历当前resolver的集合,通过接口方法supportsParameter来对当前形参的MethodParameter对象进行校验。当返回了true的时候,证明当前的resolver支持当前的形参,选取当前的resolver对当前的形参进行处理。在第一次匹配到相应的resolver之后,还会进行一个内存级别的缓存。后续对同样类型的形参进行resolver选择的时候,就不再对集合进行遍历选择。

        /**
         * Find a registered {@link HandlerMethodArgumentResolver} that supports
         * the given method parameter.
         */
        @Nullable
        private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
            HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
            if (result == null) {
                for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
                    if (methodArgumentResolver.supportsParameter(parameter)) {
                        result = methodArgumentResolver;
                        this.argumentResolverCache.put(parameter, result);
                        break;
                    }
                }
            }
            return result;
        }
    选择到相应的resolver之后,通过方法传入的request对象,执行resolver的resolveArgument方法,封装形参的值。

    通过观察组合对象,发现有26个内置的对象,分别负责不同场景下的形参的处理。这里也解释了为什么在controller的形参位置会自动注入HttpServletRequest、HttpServletResponse等对象。

    通过观察执行过程,发现当Content-Type为application/x-www-form-urlencoded时,处理形参的resolver是ServletModelAttributeMethodProcessor。

    测试代码2:

    @RequestMapping("/entity")
        public Person testFromEntity(@RequestBody Person person, HttpServletRequest request) {
            String contentType = request.getHeader("content-type");
            log.info("Content-Type:" + contentType);
            log.info("入参值:{}", JSON.toJSONString(person));
            return person;
        }
    请求参数:

    curl --request GET \
      --url http://localhost:8080/test/entity \
      --header 'Content-Type: application/json' \
    可以发现,当形参被@RequestBody注解标注时,如果没有传入请求体,则会报错。通过上面同样的步骤,不难发现,当形参被标注@RequestBody注解的时候,SpringBoot选用的resolver为RequestResponseBodyMethodProcessor。

    当通过请求体传入合适的json时:

    curl --request GET \
      --url http://localhost:8080/test/entity \
      --header 'Content-Type: application/json' \
      --data '{\n    "name": "json",\n    "age": 20\n}'
     可以观察到

    TestWebController     : Content-Type:application/json
    TestWebController     : 入参值:{"age":20,"name":"json"}
    控制台输出了成功绑定的参数。 

    并且,通过观察argumentResolvers集合,发现RequestResponseBodyMethodProcessor的顺序要比ServletModelAttributeMethodProcessor高很多,ServletModelAttributeMethodProcessor是最后一个resolver。

    所以被标注@RequestBody注解的形参不会有机会通过ServletModelAttributeMethodProcessor去实现数据绑定,即使在url后通过地址拼接参数传递对方式请求服务器。在传入空或无法解析的json时,会直接响应400的错误。

    三、自定义PostEntityHandlerMethodArgumentResolver

    通过以上测试不难发现,处理形参参数绑定的resolver都是HandlerMethodArgumentResolver接口的实现类。于是我想到,通过自定义一个这样的实现类,来对我们需要处理的形参进行参数绑定处理。

    新建自定义的resolver并实现接口后,发现需要实现其中的两个方法:supportsParameter和resolveArgument。

    supportsParameter方法为resolver组合对象在通过形参选择resolver的时候进行判断的方法。如果该方法返回了true,代表此解析器可以处理这个类型的形参。否则就返回false,循环继续进行下一轮的选择。所以,我们需要对我们自定义的形参进行标记,以便在这里可以成功的捕捉到。

    我的做法是自定义一个空的接口

    public interface PostEntity {
    }
    让我们的实体类实现这个接口,但是什么都不需要做。 在supportsParameter方法中判断传入的类型是不是PostEntity的实现类。如果是实现类,就返回true,否则返回false,不影响其他类型的形参的值的注入。

    关于resolveArgument方法,我们只需要根据Content-Type不同来直接调用上文提到的两个resolver即可,不需要自己去实现这个逻辑。同时也可以保证参数处理全局的一致性。由于判断依赖Content-Type的值,所以要求调用方必须传入Content-Type。

    @Slf4j
    @AllArgsConstructor
    public class PostEntityHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
     
        private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor;
     
        private ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor;
     
        private static final String APPLICATION_JSON = "application/json";
     
        @Override
        public boolean supportsParameter(MethodParameter parameter) {
            Class<?> parameterType = parameter.getParameterType();
            String parameterName = parameter.getParameterName();
            if (PostEntity.class.isAssignableFrom(parameterType)) {
                log.info("name:{},type:{}", parameterName, parameterType.getName());
                log.info("matched");
                return true;
            }
            return false;
        }
     
        @Override
        public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
            HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
            assert request != null;
            String contentType = request.getContentType();
            log.debug("Content-Type:{}", contentType);
            if (APPLICATION_JSON.equalsIgnoreCase(contentType)) {
                return requestResponseBodyMethodProcessor.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
            } else {
                return servletModelAttributeMethodProcessor.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
            }
        }
    }
    四、注册自定义HandlerMethodArgumentResolver

    构造好了之后就需要把我们自定义的resolver添加到resolver的组合对象中。所有的预加载resolvers在启动过程中被设置到RequestMappingHandlerAdapter对象中。

    定义一个配置类实现WebMvcConfigurer接口,在成员变量位置注入RequestMappingHandlerAdapter对象。确保注入成功之后,定义一个@PostConstruct的init方法,首先通过getArgumentResolvers方法获取所有的resolvers,随后遍历这个集合,获取我们需要的两个resolvers。拿到所需参数之后构造我们自定义的PostEntityHandlerMethodArgumentResolver。

    通过查看获取resolver集合的方法源代码可以发现:

    return Collections.unmodifiableList(this.argumentResolvers);
    这个方法返回的集合是一个不可变的集合,没有办法为其添加新的元素。所以,我们需要构造一个新的集合,大小为原有集合大小+1,并且把我们自定义的resolver添加到集合的第一位,再通过ha对象重新设置回去。这样就完成了我们自定义resolver的注册。

    @Configuration
    @Slf4j
    public class WebMvcConfiguration implements WebMvcConfigurer {
     
        @Autowired
        private RequestMappingHandlerAdapter ha;
     
        private ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor = null;
        private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = null;
     
        @PostConstruct
        private void init() {
            List<HandlerMethodArgumentResolver> argumentResolvers = ha.getArgumentResolvers();
            for (HandlerMethodArgumentResolver argumentResolver : argumentResolvers) {
                if (argumentResolver instanceof ServletModelAttributeMethodProcessor) {
                    servletModelAttributeMethodProcessor = (ServletModelAttributeMethodProcessor) argumentResolver;
                } else if (argumentResolver instanceof RequestResponseBodyMethodProcessor) {
                    requestResponseBodyMethodProcessor = (RequestResponseBodyMethodProcessor) argumentResolver;
                }
                if (servletModelAttributeMethodProcessor != null && requestResponseBodyMethodProcessor != null) {
                    break;
                }
            }
            PostEntityHandlerMethodArgumentResolver postEntityHandlerMethodArgumentResolver = new PostEntityHandlerMethodArgumentResolver(requestResponseBodyMethodProcessor, servletModelAttributeMethodProcessor);
            List<HandlerMethodArgumentResolver> newList = new ArrayList<>(argumentResolvers.size() + 1);
            newList.add(postEntityHandlerMethodArgumentResolver);
            newList.addAll(argumentResolvers);
            ha.setArgumentResolvers(newList);
        }
     
    }
     

    五、结论

    通过测试可以发现,两种请求方式都已经实现了同一个方法形参的参数绑定。虽然此功能也无需这么复杂的实现方式也可以做到,但是通过对这个问题的研究,阅读了一些spring的源码,更清楚的知道了参数绑定数据的流转过程。

    如果需要绑定的形参是外部依赖的vo,无法实现自定义的接口,还可以实现一个自定义的注解,在自定义的resolver中也是可以捕捉到的,并进行自定义的处理。

    还有一个可能有用的场景,就是通过此方式,也可以自定义一种Content-Type,来实现一些不知道为什么你要这么做的需求~~

    Demo的代码:https://github.com/daegis/multi-content-type-demo
    --------------------- 
    作者:AEGISA 
    来源:CSDN 
    原文:https://blog.csdn.net/daegis/article/details/86478129 
    版权声明:本文为博主原创文章,转载请附上博文链接!

    展开全文
  • 通过Callable接口实现可回调参数线程 // ① 实现Callable接口(拥有泛型,call方法的结果决定泛型类型) // ② 实现Call方法(有throws,有返回值)public class AsyncReadFile implements Callable<String> { @...
  • 二、建立接口类:用于声明父类的方法(注意,接口可不用注入,调用看实现类) 三、建立实现多实现类:用于实现具体方法(注意:实现类需要注入容器) 四、建立操作类型工厂 五、使用方法示例:...
  • 近期,在使用Jmeter工具进行接口测试过程中,遇到过类似添加信息的接口参数。每次需要传入不同的参数值,如果使用手动每次去修改,是一件很麻烦而繁琐的事。于是可以想到Jmeter中的time函数,对需要输入的接口参数,...
  • 详述Java实现Callable接口实现多线程的方式

    万次阅读 多人点赞 2018-07-30 14:59:19
    在这里把最近学习的实现Callable接口实现多线程的方式做一个总结: 首先我们都知道,在Java中最常见的是继承Thread类和实现Runnable接口实现多线程( 这里我推荐采用实现接口的方式来实现多线程,原因有两点: ...
  • 由于业务需要在接口方法中使用了泛型,实现类的相同方法传入的实参类型不同,想用反射查看具体的实参类型,进而转换为实参的类型。 但使用中遇到了一个问题,接口定义的方法只有一个“T”参数,反射method....
  • 前言:在向上抽取功能时可能会有需要获取到实现接口的实际泛型参数类型这样的需求,分享一下自己实现的方法。 一、Java 代码 直接上代码,代码上有注释,对API做了相应的解释。 public BasicAction(){ try { ...
  • C#实现多接口

    千次阅读 2007-08-06 23:56:00
    一个类可以从接口中派生出来,当一个类从定义了相同方法的两个或接口中派生时,你有两种选择:第一种是将两个接口方法引导到同一种方法实现中,第二种是提供不同的方法实现(利用显示实现)interface ...
  • 那么实现这种场景的工具有很,这里介绍charles来截包篡改接口数据实现。 1、在电脑先安装charles抓包工具,配置监听端口,在移动端设置代理,安装ssl证书,便于抓取https请求的接口; 2、先请求一次接口或者先...
  • Restful 接口传递参数

    万次阅读 2017-06-12 13:12:59
    首先补充一下什么是 Restful ,这里简单说一下,如果一个架构符合REST... (3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。 具体介绍参考: RESTful API 设计指南 RES...
  • 多参数传递 书上把多参数传递放到了select元素那一块讲,不过这部分实际上增删查改都一样用。 新增一个用来测试的表: 使用Map传递
  • Java接口作为参数传递

    千次阅读 2019-04-08 11:36:07
    记录两种方式,原文链接:Java接口作为参数传递【用处很】http://www.jufanshare.com/content/70.html 原文中解释比较详细,此处只有实现代码 方式一: package com.jufanshare; /** * @功能: * @开发者:...
  • JAVA中implements实现多接口

    万次阅读 2011-03-01 10:47:00
    JAVA中implements实现多接口 这里有一个游戏,人猿泰山。 主角是一个单独的类,这里我们主要用怪物说明接口的用法: 怪物有很多种, 按地域分:有的在天上飞,有的在地上跑,有的在水里游 按攻击方式分:...
  • 接口实现

    千次阅读 2016-08-27 20:30:03
    接口的定义和类的定义很相似,可以分为接口声明和接口体积,和类不同的是,...2、如果一个类声明实现一个接口,但是没有重写接口中的从所有方法,那么这个类必须是抽象类,也就是说抽象类既可以重写接口中的方法,也
  • 现在我想在教师类class teacher里实现这个方法,得出家访周期,这个时候方法体可能需要X和Y两个参数,然而我又想在教学计划类class plan中实现这个方法,得出考试周期,这个时候又的用到另外两个参数,可能数据类型...
  • 文章目录通过AOP切入注解的方式对接口参数进行非空判断1、创建注解2、创建切入点3、应用注解4、注解响应 通过AOP切入注解的方式对接口参数进行非空判断 1、创建注解 package ...
  • 线程实现方式---实现Runnable接口

    千次阅读 2016-05-05 11:00:06
    线程实现方式---实现Runnable接口 线程实现方式---实现Runnable接口 一个类如果需要具备线程的能力,也可以通过实现java.lang.Runnable接口进行实现。按照Java语言的语法,一个类可以实现任意接口,...
  • 一、首先来看一个例子 package net.println.kotlin.chapter4 /** * @author:wangdong * @description:类实现接口的冲突问题 */ interface B{ fun x(): Int = 1 .../**一个类实现了两个接口,两个接口中...
  • 接口中定义的都是抽象方法,没有实现,所以当有接口被继承后,必须实现接口类中的所有构造方法,这个过程就是重写接口方法,之后可以重新填写方法内容,但是方法的名,参数类型和个数、参数返回值类型不能改变。
  • JAVA 实现Runnable接口

    千次阅读 2017-12-11 09:10:45
    如有侵权,请作者联系我删除博客,谢谢 //仅作为学习笔记 ...创建线程的第二种方式:实现Runnable接口 步骤: 1,定义类实现Runnable接口 2,覆盖Runnable接口中的run方法 将线程要运
  • SpringBoot中Rest风格接口传递参数

    万次阅读 2018-03-26 15:02:01
    这次在项目开发中要求写Rest风格接口,在调试时发现有个按条件分页查询的接口涉及到多参数传递。因为参数,不想使用GET请求避免参数太过繁琐,经过试验,发现可以使用POST请求在后台通过JavaBean来接受所有参数...
  • 其中重要的类有BeanFactory,BeanFactory是IoC容器的核心接口,其职责包括:实例化、定位、配置应用程序中的 对象及建立这些对象间的依赖关系。 ApplicationContext作为BeanFactory的子类,在Bea...
  • java找到指定接口实现

    千次阅读 2020-02-10 13:30:20
    一、背景 想在自己开发的项目上加一个算法工具类用来...因此开始在学习如何实现,在学习过程中发现需要写一个方法用来找到指定接口实现类,因此开启了这趟学习之旅。 二、寻求答案的路途 刚开始看到根据指定接...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,363,556
精华内容 545,422
关键字:

多接口参数怎么实现