spring aop 实践

之前用的ssm框架,大部分只是利用spring的IOC特性,很明显这能够为我们开发人员在对象的创建上面节省大部分时间。当相似得到业务越来越多,很多代码也是越来越重复,轮子是越来越重复,比如验证用户登录这一块的代码。由于我们这边Java是给前端app调用的,在正式请求业务接口的之前,会校验该用户是否登陆过。所以在每个 controller里面最开始都是校验前端传递的登录码是否有效。前几日得闲,想到spring有aop的特性,简单的理解就是拦截器,在请求正式的业务数据之前,我们可以通过这个特性来验证客户端的是否已经登录(像以后如果比较复杂可以验证用户的的组织架构权限,错误日志搜集等等)。业务场景就是这些,下面就是具体实现。

我使用的是自定义注解来做的。首先定义一个切面注解,然后对这个注解添加声明解释,使用的环绕通知,切面方法有具体的校验方法。最后根据自己的实际需要,在对应的方法上面加上这个切面注解。

1.spring.xml、spring-mvc.xml文件中添加aop支持

 

<!--启用AOP-->
<aop:aspectj-autoproxy />

 

2.声明切面注解

package cn.com.org.common.aspectAnnotation;

import java.lang.annotation.*;

/**
 * Created by kevin stark on 2017/7/12.
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AppTokenAspect {
    String methodName() default "";
}

 

3.对切面注解要做的操作的声明

package cn.com.org.control.aspectDefine;

import cn.com.org.common.constant.AllConstant;
import cn.com.org.service.user.service.UserBaseService;
import net.sf.json.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by kevin stark on 2017/7/12.
 * 定义具体的切面类
 */
@Aspect
@Component
public class APPTokenAspectDefine {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private UserBaseService userBaseService;

    // 定义一个 Pointcut, 使用 切点表达式函数 来描述对哪些 Join point 使用 advise.
    @Pointcut("@annotation(cn.com.org.common.aspectAnnotation.AppTokenAspect)")
    public void pointCut() {}

    /**
     * 环绕通知调用切入方法。
     * 在正式调用业务方法前,校验参数(token是否过期)
     * @param pjp
     * @return
     */
    @Around("pointCut()")
    public Object checkAppToken(ProceedingJoinPoint pjp){
        log.info("");
        Object object = null;
        Object[] objs = pjp.getArgs();
        //AopUtils
        try {
            if (null != objs && objs.length > 0) {
                for (int i = 0; i < objs.length; i++) {
                    Object obj = objs[i];
                    // 接口的请求参数都是json类型,循环出来的值判断这个是不是json类型
                    if (obj instanceof JSONObject) {
                        JSONObject json = (JSONObject) objs[i];
                        //判断json结构中是否包含token字段
                        if (json.containsKey("token")) {
                            String token = json.getString("token");
                            Map<String, Object> map = new HashMap<>();
                            Boolean isSuccess = false;
                            Integer responseCode = -1;
                            String responseMsg = "";
                            HashMap<String, String> tokenResult = userBaseService.checkToken(token.trim());
                            if (!"success".equals(tokenResult.get("code"))) {
                                isSuccess = Boolean.FALSE;
                                responseCode = AllConstant.TOKEN_INVALID_CODE;
                                responseMsg = AllConstant.TOKEN_INVALID_MSG;
                                map.put("isSuccess", isSuccess);
                                map.put("responseCode", responseCode);
                                map.put("responseMsg", responseMsg);
                                return map;
                            } else {
                                // 可以对传参进行改造,比如校验通过后,就可以换到这个登录码对应的用户ID
                                json.put("empCode", tokenResult.get("userCode"));
                                objs[i] = json;
                            }

                        }
                    }
                }
            }

            //如果校验通过程序往下执行
            object =  pjp.proceed();
        } catch (Exception e) {
            e.printStackTrace();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

     // 因为我们拦截的方法都是有返回值的,智力要注意null的情况不然会报错,所以这里要做一点判空的处理
if (null == object) { object = new Object(); } return object; } }

4.spring.xml中声明这个bean

<bean id="appTokenAspectDefine" class="cn.com.bluemoon.control.aspectDefine.APPTokenAspectDefine"></bean> 

 

具体应用

/**
     * .获取目录页ICON的角标数
     * @param request
     * @param jsonObject
     * @return
     */
    // 添加切面主键,当有请求访问这个方法的时候,会调用的切面里面的校验登录码的方法
    @AppTokenAspect
    @ResponseBody
    @RequestMapping(value="/getModel")
    public Map<String, Object> getModel(HttpServletRequest request, @RequestBody JSONObject jsonObject) {
}  

所以按照此方法在实际业务方法中,就可以节省更多的代码了。以后的日志手机或者说用户行为买点也可以按照此方法来改造了。

看来我们使用的spring还只是仅仅在增删改查的层面上了,应当多去研究他的特性结合自身的业务需求来改造整个项目。任重而道远啊。欢迎共同交流

  

  

posted @ 2017-07-21 11:01  Ronaldo7  阅读(354)  评论(0)    收藏  举报