前段时间,为了校验接口数据方便,实现了一个简易的参数校验功能。通过参数上加注解并且配置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;
}
<bean id="paramCheckAop" class="com.huateng.iccs.pre.corp.aop.ParamCheckAop"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.huateng.iccs.pre.corp.service.impl.ScfContractServiceImpl.*(..))"
id="scfContractPc"/>
<aop:pointcut expression="execution(* com.huateng.iccs.pre.corp.service.impl.ScfCustomerServiceImpl.*(..))"
id="scfCustomerPc"/>
<aop:pointcut expression="execution(* com.huateng.iccs.pre.corp.service.impl.ClientInformServiceImpl.*(..))"
id="clientInformPc"/>
<aop:aspect id = "paramCheck" ref ="paramCheckAop">
<aop:around pointcut-ref="scfContractPc" method="ArroundParamCheck"/>
<aop:around pointcut-ref="scfCustomerPc" method="ArroundParamCheck"/>
<aop:around pointcut-ref="clientInformPc" method="ArroundParamCheck"/>
</aop:aspect>
</aop:config>
但是引入的系统时候需要显性的设置一个环绕增强,并且要在xml中配置切面切点。
使用过@Transactional的注解的应该都知道,只需要在方法或者类上标注@Transactional注解,就可以开启spring事务。使用起来比较便捷。为了作者练手,并且使用方便,于是便想参数校验做成类似模式。
1、spring下自定义xml解析的实现
使用@Transactional需要在spring xml下配置<tx:annotation-driven />来开启注解事务。同样这种方式的校验也是从自定义xml解析开始。
这里的xml解析很简单
- 编写对应的xsd
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.component.org/schema/avd"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
targetNamespace="http://www.component.org/schema/avd"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"></xsd:import>
<xsd:element name="annotation-driven"></xsd:element>
</xsd:schema>
这里的xsd编写也很简单,因为只是一个标记,表示开启自动参数解析,只需要配置一个element,不需要额外的配置。
<xsd:element name="annotation-driven"></xsd:element>
- 编写对应的NamespaceHandlerSupport和BeanDefinitionParser。
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
http\://www.component.org/schema/avd = com.fosun.component.validate.config.AvdNamespaceHandler
spring.schemashttp\://www.component.org/schema/avd.xsd = com/fosun/component/validate/config/component-avd-1.0.xsd
- pom文件配置
<build><plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<classesDirectory>target/classes/</classesDirectory>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
</configuration>
</plugin>
</plugins></build>
- spring配置文件中使用
如上图在配置文件中增加
xmlns:avd="http://www.component.org/schema/avd"
http://www.component.org/schema/avd http://www.component.org/schema/avd.xsd
<avd:annotation-driven/>
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);
}
}
关键
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));/**<property name="name" ref=ref/>*/
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);/**<property name="name" value="value"/>*/
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);
}
}