Spring下基于注解的参数校验

        前段时间,为了校验接口数据方便,实现了一个简易的参数校验功能。通过参数上加注解并且配置spring aop实现的。

大概配置如下:

public Object ArroundParamCheck(ProceedingJoinPoint pj) throws Throwable{
		if(null == pj){
			log.info("传入的对象是空的!!!!!!!!!!");
		}
		log.info("参数校验开始================");
		Object[] obs = pj.getArgs();
		Response_CM resp = paramCheckService.paramCheck(obs);
		log.info("参数校验结束==============" + resp.toString());
		if(!CodeMsgConstant.CODE_000000.equals(resp.getRespCode())){
			MethodSignature methodSg = (MethodSignature) pj.getSignature();
			Class returnType = methodSg.getReturnType();
			return paramCheckService.getCheckResult(returnType, resp);
		}
		Object pjResult = pj.proceed();
		return pjResult;
		
	}
 
 
    	
    	
    	
    	
    		
    		
    		
    	
     

但是引入的系统时候需要显性的设置一个环绕增强,并且要在xml中配置切面切点。


        使用过@Transactional的注解的应该都知道,只需要在方法或者类上标注@Transactional注解,就可以开启spring事务。使用起来比较便捷。为了作者练手,并且使用方便,于是便想参数校验做成类似模式。

1、spring下自定义xml解析的实现

    使用@Transactional需要在spring xml下配置来开启注解事务。同样这种方式的校验也是从自定义xml解析开始。  

    这里的xml解析很简单

  • 编写对应的xsd

Spring下基于注解的参数校验_第1张图片



	
	

    这里的xsd编写也很简单,因为只是一个标记,表示开启自动参数解析,只需要配置一个element,不需要额外的配置。

  • 编写对应的NamespaceHandlerSupport和BeanDefinitionParser。
固定写法。AvdNamespaceHandler继承NamespaceHandlerSupport 重写init方法。AvdAnnotationDrivenBeanDefinitionParse实现BeanDefinitionParse重写parse方法。
public class AvdNamespaceHandler extends NamespaceHandlerSupport{

	@Override
	public void init() {
		registerBeanDefinitionParser("annotation-driven", new AvdAnnotationDrivenBeanDefinitionParser());
	}

}
public class AvdAnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser{

	public static final String AVD_ADVISOR_BEAN_NAME = "com.fosun.component.validate.interceptor.avdAdvisor";
	@Override
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		configureAutoProxyCreator(element,parserContext);
		return null;
	}
	
	private void configureAutoProxyCreator(Element element, ParserContext parserContext){
		/**
		 * 如果必要开启自动创建代理对象
		 * 此方法可以将targetClass委托给spring让spring为符合切点的目标类自动创建代理对象
		 */
		AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
		String avdAdvisorName = AVD_ADVISOR_BEAN_NAME;
		if(!parserContext.getRegistry().containsBeanDefinition(avdAdvisorName)){
			Object eleSource = parserContext.extractSource(element);
			RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationAvdAttributeSource.class);
			sourceDef.setSource(eleSource);
			sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
			
			RootBeanDefinition interceptorDef = new RootBeanDefinition(AutoAvdInterceptor.class);
			interceptorDef.setSource(eleSource);
			interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
			
			
			RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryAutoAvdDefineAdvisor.class);
			advisorDef.setSource(eleSource);
			advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			advisorDef.getPropertyValues().add("avdAttributeSource", new RuntimeBeanReference(sourceName));
			advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
			parserContext.getRegistry().registerBeanDefinition(avdAdvisorName, advisorDef);
			
			CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
			compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
			compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
			compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, avdAdvisorName));
			parserContext.registerComponent(compositeDef);
		}
		
	}

}
  • 配置spring.handlers和spring.schmas
Spring下基于注解的参数校验_第2张图片
两个文件的目录格式如上图所示(严格按照)
spring.handlers
http\://www.component.org/schema/avd = com.fosun.component.validate.config.AvdNamespaceHandler
spring.schemas
http\://www.component.org/schema/avd.xsd = com/fosun/component/validate/config/component-avd-1.0.xsd
  • pom文件配置
        作者使用maven打jar,需要在pom文件下做如下配置,不然打的jar中不包含spring.handlers、spring.schemas这两个文件
	
		
			maven-jar-plugin
								
				target/classes/
				
					false
				
			
		
	
  • spring配置文件中使用
Spring下基于注解的参数校验_第3张图片
如上图在配置文件中增加
xmlns:avd="http://www.component.org/schema/avd"

http://www.component.org/schema/avd http://www.component.org/schema/avd.xsd

2、Spring aop实现

    同样,如同普通的aop实现一样,这种方式也需要配置增强、切点和切面。同时,需要声明一个类似@Transactional的注解,用来标记在方法或者类上来表示该方法或者类开启校验。作者在这里定义了@AutoAvd注解
  • 切点类

继承StaticMethodMatcherPointcut表示这是一个静态切点。这个方法的主要目的就是匹配目标方法或者类是否标记了@AutoAvd注解。

public abstract class AutoAvdDefinePointcut extends StaticMethodMatcherPointcut{

	@Override
	public boolean matches(Method method, Class targetClass) {
		AvdAttributeSource aad = getAvdAttributeSource();
		return (aad == null || aad.getAutoAvdDefine(method, targetClass) != null);
	}
	
	public abstract AvdAttributeSource getAvdAttributeSource();

}
  • 增强类

实现MethodInterceptor接口,标识这是一个环绕增强。

public class AutoAvdInterceptor implements MethodInterceptor{
	
	private final Logger log = LoggerFactory.getLogger(getClass());
	
	@Autowired
	ParamCheckService paramCheckService;

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		// TODO Auto-generated method stub
//		Class targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
		Object[] obs = invocation.getArguments();
		log.info("参数校验开始================");
		Response_CM resp = paramCheckService.paramCheck(obs);
		log.info("参数校验结束==============" + resp.toString());
		if(!CodeMsgConstant.CODE_000000.equals(resp.getRespCode())){
			Method method = invocation.getMethod();
			Class returnType = method.getReturnType();
			return paramCheckService.getCheckResult(returnType, resp);
		}
		return invocation.proceed();
	}

}
  • advisor

继承AbstractBeanFactoryPointcutAdvisor重写getPointcut()方法。

BeanFactoryAutoAvdDefineAdvisor中的AvdAttributeSource用来解析@AutoAvd注解的内容,

public class BeanFactoryAutoAvdDefineAdvisor extends AbstractBeanFactoryPointcutAdvisor{
	
	private AvdAttributeSource avdAttributeSource;
	
	private AutoAvdDefinePointcut aadPointcut = new AutoAvdDefinePointcut() {
		@Override
		public AvdAttributeSource getAvdAttributeSource() {
			return avdAttributeSource;
		}
	};

	public void setAvdAttributeSource(AvdAttributeSource avdAttributeSource) {
		this.avdAttributeSource = avdAttributeSource;
	}

	@Override
	public Pointcut getPointcut() {
		return this.aadPointcut;
	}

}

AnnotationAvdAttributeSource实现AvdAttributeSource来实现由@AutoAvd表示的内容。 

回顾切点的matches()方法,利用了AvdAttributeSource的getAutoAvdDefine()方法来具体判读是否匹配切点。

public class AnnotationAvdAttributeSource implements AvdAttributeSource{
	private AutoAvdAnnotationParser annotationParser;
	
	public AnnotationAvdAttributeSource(){
		this.annotationParser = new SpringAutoAvdAnnotationParser();
	}

	@Override
	public AutoAvdDefine getAutoAvdDefine(Method method, Class targetClass) {
		/**
		 * 先这么写
		 */
		return computerAutoAvdDefine(method,targetClass);
	}
	
	private AutoAvdDefine computerAutoAvdDefine(Method method, Class targetClass){
		/**
		 * 不支持非public方法
		 */
		if ( !Modifier.isPublic(method.getModifiers())) {
			return null;
		}

		Class userClass = ClassUtils.getUserClass(targetClass);
		Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
		specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

		AutoAvdDefine aad = findAutoAvdAnnotationDefine(specificMethod);
		if (aad != null) {
			return aad;
		}

		aad = findAutoAvdAnnotationDefine(specificMethod.getDeclaringClass());
		if (aad != null) {
			return aad;
		}

		if (specificMethod != method) {
			aad = findAutoAvdAnnotationDefine(method);
			if (aad != null) {
				return aad;
			}
			return findAutoAvdAnnotationDefine(method.getDeclaringClass());
		}
		return null;
	}
	
	private AutoAvdDefine findAutoAvdAnnotationDefine(Method method){
		return determineAutoAvdDefine(method);
	}
	private AutoAvdDefine findAutoAvdAnnotationDefine(Class targetClass){
		return determineAutoAvdDefine(targetClass);
	}
	private AutoAvdDefine determineAutoAvdDefine(AnnotatedElement ae){
		return this.annotationParser.parseAutoAvdAnnotation(ae);
	}
}

关键

        在第二步中,我们只是定义了切点,环绕增强,切面等。还未介绍如何将这些交给spring托管。并且为符合切点的对象生成代理对象。
private void configureAutoProxyCreator(Element element, ParserContext parserContext){
		/**
		 * 如果必要开启自动创建代理对象
		 * 此方法可以将targetClass委托给spring让spring为符合切点的目标类自动创建代理对象
		 */
		AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
		String avdAdvisorName = AVD_ADVISOR_BEAN_NAME;
		if(!parserContext.getRegistry().containsBeanDefinition(avdAdvisorName)){
			Object eleSource = parserContext.extractSource(element);
			RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationAvdAttributeSource.class);
			sourceDef.setSource(eleSource);
			sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
			
			RootBeanDefinition interceptorDef = new RootBeanDefinition(AutoAvdInterceptor.class);
			interceptorDef.setSource(eleSource);
			interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
			
			
			RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryAutoAvdDefineAdvisor.class);
			advisorDef.setSource(eleSource);
			advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			advisorDef.getPropertyValues().add("avdAttributeSource", new RuntimeBeanReference(sourceName));/***/
			advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);/***/
			parserContext.getRegistry().registerBeanDefinition(avdAdvisorName, advisorDef);
			
			CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
			compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
			compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
			compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, avdAdvisorName));
			parserContext.registerComponent(compositeDef);
		}
		
	}


        回顾AvdAnnotationDrivenBeanDefinitionParser的configureAutoProxyCreator()方法。在这里将自动校验的切点增强等逻辑交给spring托管并生成代理对象。
        方法开始调用AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);。熟悉spring aop的都清楚,在使用spring aop时,一般都需要在xml中配置;。spring这对这个进行解析的时候最终就会调用AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);方法。目的是为符合切点的容器中的bean自动创建代理对象。接下来就是将BeanFactoryAutoAvdDefineAdvisor等组件注册到工厂中。







你可能感兴趣的:(java基础学习,开发实战,aop,参数校验,spring)