Spring Aop表达式以及EL表达式处理

   在Spring AOP 中,通常需要借助AspectJ 的切点表达式语言来定义切点。重要的是Spring 中仅支持AspectJ切点指示器的一个子集。

Spring 支持的AspectJ的切点指示器
AspectJ 指示器 描述
args() 限制连接点匹配参数为执行类型的执行方法
@args() 限制连接点匹配参数由执行注解标注的执行方法
execution() 匹配连接点的执行方法
this() 限制连接点匹配AOP代理的Bean引用类型为指定类型的Bean
target() 限制连接点匹配目标对象为指定类型的类
@target() 限制连接点匹配目标对象被指定的注解标注的类
within() 限制连接点匹配匹配指定的类型
@within() 限制连接点匹配指定注解标注的类型
@annotation 限制匹配带有指定注解的连接点

 

Spring AOP 中常用的是:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
          throws-pattern?)

  

 匹配所有

       execution("* *.*(..)")

匹配所有以set开头的方法

        execution("* *.set*(..))

匹配指定包下所有的方法

        execution("* com.david.biz.service.impl.*(..))

匹配指定包以及其子包下的所有方法

        execution("* com.david..*(..)")

匹配指定包以及其子包下 参数类型为String 的方法

        execution("* com.david..*(java.lang.String))

  

 

@Service("bookService")
public class BookServiceImpl implements BookService {

    private static final Logger logger      = LogManager.getLogger(BookServiceImpl.class);
    public static final String  ADD_BOOK    = "insert into t_book(id,name) values(1,'duck-j2ee')";

    public static final String  DELETE_BOOK = "delete from  t_book where id=1";

    private JdbcTemplate        jdbcTemplate;
    @Autowired
    private BookDao             bookDao;

    public void addBook() throws Exception {
        Book book = new Book();
        book.setName("ibatis");
        book.setPrice(11);
        bookDao.insert(book);
        throw new UnRollbackException("受检查异常,不会回滚");
    }

    public void deleteBook(int id) {
        try {
            bookDao.deleteById(id);
        } catch (SQLException e) {
            logger.error("", e);
        }
    }

    @LoggingRequired
    public void addNewBook(String name, int price) {
        try {
            Book book = new Book();
            book.setName(name);
            book.setPrice(price);
            bookDao.insert(book);
            List<Book> lists = bookDao.selectAll();
            System.out.println(lists);
        } catch (SQLException e) {
            logger.error("", e);
        }
    }

    public void addUserBook() {
        jdbcTemplate.execute("insert into t_book(id,name) values(3,'UserBook')");
    }

    /**
     * Setter method for property <tt>jdbcTemplate</tt>.
     *
     * @param jdbcTemplate value to be assigned to property jdbcTemplate
     */
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    /**
     * @see com.david.biz.service.BookService#queryAll()
     */
    public List<Book> queryAll() {
        try {
            return bookDao.selectAll();
        } catch (SQLException e) {
            logger.error("", e);
        }
        return null;
    }

}

  

 

 

/**
 * execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
          throws-pattern?)
 *arg()  限制连接点匹配参数为指定类型的执行方法
 *@args() 限制连接点匹配参数由执行注解标注的执行
 *execution() 用于匹配连接点的执行方法
 *this() 限制连接点匹配AOP代理的Bean引用为执行类型的类
 *target() 限制连接点匹配目标对象为指定类型的类
 *@target() 限制连接点匹配特定的执行对象,这些对象应具备指定的注解类型
 *@annotation()限制匹配带有指定注解的连接点
 *
 *
 *
 * @author zhangwei_david
 * @version $Id: LogAspect.java, v 0.1 2014年11月29日 下午1:10:13 zhangwei_david Exp $
 */
@Component
@Aspect
public class LogAspect {
    private static final Logger logger = LogManager.getLogger(LogAspect.class);

    /**
     * 匹配参数是任何类型,任何数量 且在com,david.biz包或子包下的方法
     */
    @Pointcut("args(..)&&within(com.david.biz..*)")
    public void arg() {

    }

    @Pointcut("@args(com.david.aop.LoggingRequired)")
    public void annotationArgs() {

    }

    @Pointcut("@annotation(com.david.aop.LoggingRequired)")
    public void logRequiredPointcut() {

    }

    @Pointcut("args(java.lang.String,*)")
    public void argsWithString() {

    }

    @Pointcut("target(com.david.biz.service.impl.BookServiceImpl)")
    public void targetPointcut() {

    }

    @Pointcut("@target(org.springframework.stereotype.Service)")
    public void targetAnnotation() {

    }

    //    @Around("execution(* org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(..))")
    //    public Object aa(ProceedingJoinPoint pjp) throws Throwable {
    //        try {
    //            Object retVal = pjp.proceed();
    //            System.out.println(retVal);
    //            return retVal;
    //        } catch (Exception e) {
    //            System.out.println("异常");
    //            return null;
    //        }
    //    }
    @Before(value = "logRequiredPointcut()")
    public void before(JoinPoint joinPoint) {
        LogUtils.info(logger,
            " 连接点表达式@annotation(com.david.aop.LoggingRequired) - method={0} has been visited",
            joinPoint.getSignature().getName());
    }

    @Before(value = "arg()")
    public void beforeArg(JoinPoint joinPoint) {
        LogUtils.info(logger,
            "连接点表达式:args(..)&&within(com.david.biz..*)  method ={0}, args ={1},target={2}",
            joinPoint.getSignature().getName(), ToStringBuilder.reflectionToString(
                joinPoint.getArgs(), ToStringStyle.SHORT_PREFIX_STYLE), joinPoint.getTarget()
                .getClass().getName());
    }

    @Before(value = "argsWithString()")
    public void beforeArgWithString(JoinPoint joinPoint) {
        LogUtils.info(logger, "连接点表达式:args(java.lang.String,*)  method={0} ,args ={1},target={2}",
            joinPoint.getSignature().getName(), ToStringBuilder.reflectionToString(
                joinPoint.getArgs(), ToStringStyle.SHORT_PREFIX_STYLE), joinPoint.getTarget()
                .getClass().getName());
    }

    @Before(value = "annotationArgs()")
    public void beforeAnnotationArgs(JoinPoint joinPoint) {
        LogUtils
            .info(
                logger,
                "连接点表达式:@args(com.david.annotation.validate.Length,*)  method={0} ,args ={1},target={2}",
                joinPoint.getSignature().getName(), ToStringBuilder.reflectionToString(
                    joinPoint.getArgs(), ToStringStyle.SHORT_PREFIX_STYLE), joinPoint.getTarget()
                    .getClass().getName());
    }

    @Before(value = "targetPointcut()")
    public void beforeTarget(JoinPoint joinPoint) {
        LogUtils
        .info(
            logger,
            "连接点表达式:target(com.david.biz.service.impl.BookServiceImpl)  method={0} ,args ={1},target={2}",
            joinPoint.getSignature().getName(), ToStringBuilder.reflectionToString(
                joinPoint.getArgs(), ToStringStyle.SHORT_PREFIX_STYLE), joinPoint.getTarget()
                .getClass().getName());
    }

    @Before(value = " targetAnnotation()")
    public void beforeTargetAnnotation(JoinPoint joinPoint) {
        LogUtils
            .info(
                logger,
                "连接点表达式:@target(org.springframework.stereotype.Service)  method={0} ,args ={1},target={2}",
                joinPoint.getSignature().getName(), ToStringBuilder.reflectionToString(
                    joinPoint.getArgs(), ToStringStyle.SHORT_PREFIX_STYLE), joinPoint.getTarget()
                    .getClass().getName());
    }
}

  

 

2014-12-01 11:14:39  [ main:1577 ] - [ INFO ]   连接点表达式@annotation(com.david.aop.LoggingRequired) - method=addNewBook has been visited
2014-12-01 11:14:39  [ main:1587 ] - [ INFO ]  连接点表达式:args(..)&&within(com.david.biz..*)  method =addNewBook, args =Object[][{Junit  Test,1000}],target=com.david.biz.service.impl.BookServiceImpl
2014-12-01 11:14:39  [ main:1588 ] - [ INFO ]  连接点表达式:args(java.lang.String,*)  method=addNewBook ,args =Object[][{Junit  Test,1000}],target=com.david.biz.service.impl.BookServiceImpl
2014-12-01 11:14:39  [ main:1588 ] - [ INFO ]  连接点表达式:target(com.david.biz.service.impl.BookServiceImpl)  method=addNewBook ,args =Object[][{Junit  Test,1000}],target=com.david.biz.service.impl.BookServiceImpl
2014-12-01 11:14:39  [ main:1589 ] - [ INFO ]  连接点表达式:@target(org.springframework.stereotype.Service)  method=addNewBook ,args =Object[][{Junit  Test,1000}],target=com.david.biz.service.impl.BookServiceImpl
2014-12-01 11:14:39  [ main:1589 ] - [ INFO ]  连接点表达式:args(..)&&within(com.david.biz..*)  method =insert, args =Object[][{Book[id=0,name=Junit  Test,price=1000]}],target=com.david.biz.dao.impl.BookDaoImpl
2014-12-01 11:14:39  [ main:1590 ] - [ INFO ]  连接点表达式:@args(com.david.annotation.validate.Length,*)  method=insert ,args =Object[][{Book[id=0,name=Junit  Test,price=1000]}],target=com.david.biz.dao.impl.BookDaoImpl
2014-12-01 11:14:39  [ main:1591 ] - [ INFO ]  连接点表达式:args(java.lang.String,*)  method=insert ,args =Object[][{demo.insert,Book[id=0,name=Junit  Test,price=1000]}],target=com.ibatis.sqlmap.engine.impl.SqlMapClientImpl
 

  

  在AOP中如果还需要获取方法的参数值应该怎么处理呢?

    /**
     * 获取被拦截方法对象
     * <p/>
     * MethodSignature.getMethod() 获取的是顶层接口或者父类的方法对象
     * 而缓存的注解在实现类的方法上
     * 所以应该使用反射获取当前对象的方法对象
     */
    public Method getMethod(ProceedingJoinPoint pjp) {
        //获取参数的类型
        Class[] argTypes = ((MethodSignature) pjp.getSignature()).getMethod().getParameterTypes();

        Method method = null;
        try {
            method = pjp.getTarget().getClass().getMethod(pjp.getSignature().getName(), argTypes);
        } catch (NoSuchMethodException | SecurityException e) {
            logger.error("操作日志记录异常 :", e);
        }
        return method;

    }

  

 

private PromotionOpLog initPromotionOpLog(OpLogAnnotation opLogAnnotation, Method method, Object[] args) {
        PromotionOpLog opLog = new PromotionOpLog();

        opLog.setOpModule(opLogAnnotation.opModule());
        opLog.setOpAction(opLogAnnotation.opAction());

        try {
            //获取被拦截方法参数名列表(使用Spring支持类库)
            LocalVariableTableParameterNameDiscoverer u =
                    new LocalVariableTableParameterNameDiscoverer();
            String[] paraNameArr = u.getParameterNames(method);

            //SPEL上下文
            StandardEvaluationContext context = new StandardEvaluationContext();            //把方法参数放入SPEL上下文中
            for (int i = 0; i < paraNameArr.length; i++) {
                context.setVariable(paraNameArr[i], args[i]);
            }

            //使用SPEL进行key的解析
            if (StringUtils.isNotBlank(opLogAnnotation.opPkId())) {
                opLog.setOpPkId(parser.parseExpression(opLogAnnotation.opPkId()).getValue(context, String.class));
            }

            if (StringUtils.isNotBlank(opLogAnnotation.creator())) {
                opLog.setCreator(parser.parseExpression(opLogAnnotation.creator()).getValue(context, String.class));
            }

            Map<String, Object> opInfo = Maps.newHashMap();
            StringBuffer path = new StringBuffer();
            path.append(method.getDeclaringClass().getName()).append(".").append(method.getName());
            opInfo.put("path", path);
            for (int i = 0; i < paraNameArr.length; i++) {
                if (i < args.length) {
                    if (args[i] instanceof Serializable) {
                        opInfo.put(paraNameArr[i], args[i]);
                    } else {
                        String parameterValue = null == args[i] ? "" : args[i].toString();
                        opInfo.put(paraNameArr[i], parameterValue);
                    }
                } else {
                    opInfo.put(paraNameArr[i], "");
                }
            }
            opLog.setOpInfo(FastJsonUtil.toJSONString(opInfo));
            opLog.setCreateTime(new Timestamp(new Date().getTime()) );
        } catch (Exception e) {
            logger.info("操作日志记录-解析参数异常", e);
        }
        return opLog;
    }

  使用示例:

@OpLogAnnotation(opPkId = "#schemeVO.schemeId", opModule = OpLogConstant.OP_MODULE_ACTIVITY, opAction =
    OpLogConstant.OP_ACTION_UPDATE, creator = "#schemeVO.creator")
    public BaseResult<Long> saveAndPublishAndPauseScheme(ActivitySchemeVO schemeVO) 

  这样就可以通过AOP方式记录日志了

 

posted @ 2014-12-01 11:43  开心朵朵  阅读(1179)  评论(0编辑  收藏  举报