Spring AOP切面基础 实现请求参数的验证

1.应用的场景

  1. 对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用情况
  2. 监控部分重要函数,若抛出指定的异常,需要以短信或邮件方式通知相关人员
  3. 监控部分重要函数的执行时间,更灵活植入和取消
  4. 接口报文的参数验证
基本的Demo代码
切面类
package com.wei.controller.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;

/**
 * 切面
 * 
 */
@Component
public class AspectShow {

	public void doAfter(JoinPoint jp) {
		System.out.println("======>>>>>>Ending method:<<<<<<<====== "
				+ jp.getTarget().getClass().getName() + "."
				+ jp.getSignature().getName());
	}

	public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
		long time = System.currentTimeMillis();
		Object retVal = pjp.proceed();
		time = System.currentTimeMillis() - time;
		System.out.println("--------->>process time: " + time + " ms");
		return retVal;
	}

	public void doBefore(JoinPoint jp) {
		System.out.println("======>>>>>>Begin method:<<<<<<<====== "
				+ jp.getTarget().getClass().getName() + "."
				+ jp.getSignature().getName());
	}

	public void doThrowing(JoinPoint jp, Throwable ex) {
		System.out.println("======>>>>>>method Throwable<<<<<<<======" + jp.getTarget().getClass().getName()
				+ "." + jp.getSignature().getName() + " throw exception");
		System.out.println("======>>>>>>"+ex.getMessage()+"<<<<<<<======");
	}

}
package com.wei.controller.aop;  
/** 
 * AopServiceShow 
 */  
public interface AopServiceShow {  
      
    public void dance(String _msg);  
  
    public void singe();  
} 
 
package com.wei.controller.aop;  

import org.springframework.stereotype.Component;

/** 
 *AopServiceShowImpl
 */
@Component
public class AopServiceShowImpl implements AopServiceShow {  
  
    public void singe() {  
        System.out.println("======>>>>>>AopServiceShowImpl.singe()");
    }  
  
    public void dance(String _msg) {  
        System.out.println("======>>>>>>AopServiceShowImpl.dance()-->"+_msg);
    }  
}  
 

异常service
package com.wei.controller.aop;  

import org.springframework.stereotype.Service;
  
/** 
 *   BServiceImpl 
 */  
@Service
public class AopServiceImpl {  
  
    public void run(String _msg, int _type) {
    	System.out.println("======>>>>>>AopServiceImpl.run()");
        System.out.println("======>>>>>>AopServiceImpl.run()-->(msg:"+_msg+" type:"+_type+")");  
        if(_type == 1)  
            throw new IllegalArgumentException("======>>>>>>测试异常");  
    }  
  
    public void walk() {  
        System.out.println("======>>>>>>AopServiceImpl.walk()");
    }  
  
} 
junit测试类
package com.wei.dao;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.wei.controller.aop.AopServiceImpl;
import com.wei.controller.aop.AopServiceShow;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring-context.xml")
public class AopServiceTestCase {
	private Logger logger=LoggerFactory.getLogger(this.getClass());
	@Autowired
	private AopServiceShow aopServiceShowImpl;
	@Autowired
	private AopServiceImpl aopServiceImpl;

	/**
	 * 测试正常调用
	 */
	@Test
	public void testCall() {
		System.out.println("------->start-------");
		aopServiceShowImpl.dance("JUnit Dance");
		aopServiceShowImpl.singe();
		aopServiceImpl.walk();
		aopServiceImpl.run("JUnit Run", 0);
		System.out.println("------->end-------");
	}

	/**
	 * 测试After-Throwing
	 */
	@Test
	public void testThrow() {
		System.out.println("------->start-------");
		try {
			aopServiceImpl.run("JUnit Run", 10001);
		} catch (IllegalArgumentException e) {

		}
		System.out.println("------->end-------");
	}


}
AOP配置
      <aop:config proxy-target-class="true"> 
	<aop:aspect  ref="aspectShow"> 
            <aop:pointcut id="aopService" expression="execution(* com.wei.controller..*(..))"/>  
            <aop:before pointcut-ref="aopService" method="doBefore"/>  
            <aop:after pointcut-ref="aopService" method="doAfter"/>  
            <aop:around pointcut-ref="aopService" method="doAround"/>  
            <aop:after-throwing pointcut-ref="aopService" method="doThrowing" throwing="ex"/>  
       </aop:aspect>  
      </aop:config>




2.报文验证

2.1声明一个注解,在需要验证呢过报文的地方加上注解

package com.wei.service.validate;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
 
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessRequired {
     
}


在实现的方法上加注解,例如:
	@Override
	@AccessRequired
	public CommonResonse<User>  select(UserVo userVo) {}


切面bean如下实现MethodInterceptor接口,大家可以看下这个接口的实现其实就是通知

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;

import com.pingan.toa.asset.common.enums.MessageEnums;
import com.pingan.toa.asset.common.enums.ResponseTypeEnums;
import com.pingan.toa.asset.model.response.CommonResponse;

@Component
public class ValidateParamMethedIntercepter implements MethodInterceptor {

	private static final Validator validator;

	static {
		ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
		validator = vf.getValidator();
	}

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {

		Object[] args = invocation.getArguments();
		if (args != null && args.length > 0) {
			List<string> errors = violation(args[0]);
			if (errors.size() > 0) {
				CommonResponse<object> commonResonse = new CommonResponse</object><object>();
				commonResonse.setErrorMsg(StringUtils.join(errors, ", "));
				commonResonse.setErrorCode(MessageEnums.ERROR00.getCode());
				commonResonse.setStatus(ResponseTypeEnums.FAILED);
				return commonResonse;
			}
		}
		return invocation.proceed();
	}

	public static List<string> violation(Object object) {
		List<string> msgs = new ArrayList<string>();
		Set<ConstraintViolation<object>> set = validator.validate(object);
		if (set != null && !set.isEmpty()) {
			for (ConstraintViolation</object><object> cvo : set) {
				msgs.add(cvo.getMessage());
			}
		}
		return msgs;
	}
}


2.2 这个类里面又返回的Object,就是返回前端的对象,代码里是我自己用的大家可以换掉

表达式加上注解,只匹配加上注解的,可以看出其实这个注解只是一个标识的作用

            <aop:pointcut id="aopService"  
                expression="execution(@com.wei.service.validate.AccessRequired* com.wei.controller..*(..))" />  


2.3这个时候我们就用切面实现了对符合JSR303校验规范的统一验证处理,上面的切面类只校验方法的第一个参数,具体JSR303校验实现大家可以看hibernate的一个实现去。

 有一个疑问就是这个切面类到底是个前置通知还是环绕通知,是和实现的接口有关吗?个人觉得还是用<aop:before>这种写法比较直观。

你可能感兴趣的:(java,spring,AOP)