Spring 通過註解方式實現AOP切面

        Spring 切面編程的目的是實現代碼的業務邏輯的解耦。切面編程用於諸如日誌記錄,事務處理,等非業務性的邏輯操作。目前Spring的Aop只能應用於方法層級上,無法在類、成員字段等層級上操作。以下是Srping的Aop編程分爲註解方式和xml配置方式。以下過程詳細說明了通過註解方式實現AOP編程的過程。

第一步:自定義註解

  1. /**
  2. * 定義自定義管理員註解
  3. *
  4. */
  5. public @interface RoleAdmin {
  6. String values() default "";
  7. String descript() defalut "自定義管理員註解";
  8. }

  • 第二步:編寫AOP切面類

  1. /*聲明爲組件,讓spring 自動管理*/
  2. @Component
  3. /*聲明該類爲切面bean*/
  4. @Aspect
  5. public class RequestInterceptor {
  6. private final Logger logger = LoggerFactory.getLogger(RequestInterceptor.class);
  7. /* 攔截管理員訪問的請求,(定義切點,該方法無方法體,主要爲方便同類中其他方法使用此處配置的切入點)*/  
  8. @Pointcut("@annotation(com.frame.annotation.RoleAdmin)")
  9. public void adminRequired() {}
  10. /*定義前置advice,同時接受JoinPoint切入點對象,可以沒有該參數
  11. 在環繞通知裏面proceedingJoinPoint參數是必須的,其他情況JoinPoint 並不是必須的
  12. */
  13. @Before("adminRequired()")
  14. public void adminCommon(JoinPoint point) {       
  15. HttpServletRequest request = getRequest();
  16.         LoginUser user = (LoginUser) request.getSession().getAttribute(Constants.SESSION_USER_KEY);
  17.         if ( user == null ) {
  18.             setForward(request);
  19.             throw new NoAdminException();
  20.             //throw new NotLoginException();
  21.         }
  22.         if  ( user.getMemberType() != Constants.MEMBER_TYPE_ADMIN ) {
  23.             throw new NoAdminException();
  24.             //throw new NoAuthException();
  25.         }
  26.         String mac = (String)request.getSession().getAttribute(AdminUtil.ADMIN_MAC_ATTR_NAME);
  27.         if(UtilString.isEmpty(mac)){
  28.             throw new NoAdminException();
  29.         }
  30. }
  31. private void setForward(HttpServletRequest request) {
  32. if ( "get".equalsIgnoreCase(request.getMethod()) ) {
  33. String fw = request.getRequestURI().substring(request.getContextPath().length()+1);
  34. String qs = request.getQueryString();
  35. if ( ! UtilString.isEmpty(qs) ) {
  36. fw += "?" + qs;
  37. }
  38. logger.debug("After login will forward to : {}", fw);
  39. request.getSession().setAttribute(Constants.URL_FORWARD_KEY, fw);
  40. }
  41. }
  42. /** * 在切面中獲取http請求 * @return */
  43. private HttpServletRequest getRequest() {
  44. return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
  45. }
  46. }

  • 第三步:在application.xml中配置aop代理

               需要在application.xml中添加AOP代理。AOP代理分爲JDK代理和cglib代理

               如果配置proxy-target-class="true",則表示是cglib代理。如下:

  1. <!-- 激活自動代理功能 -->
  2. <aop:aspectj-autoproxy proxy-target-class="true"/>

               如果採用JDK代理,則配置如下:

       <aop:aspectj-autoproxy/>

  •  第四步: 定義切面的引入點

  1. /*在需要切面的方法上定義JoinCut點*/
  2. @RoleAdmin(descript="在這裏定義")
  3. @ResponseBody
  4. @RequestMapping(value="admin/activity/updateComment")
  5. public boolean updateCommentIsDelete(HttpServletRequest request,CommentPlan commentPlan){
  6. return false;
  7. }

        切入點匹配多個連接點的兩種方式

  • 通過@annotation精確切入到某個自定義標籤中

       @Pointcut("@annotation(com.frame.annotation.RoleAdmin)")

  • 通過exrcution模糊匹配

       @Pointcut("execution(* cn.ysh.studio.spring.aop.service..*(..))")  

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

  • modifiers-pattern:方法的操作權限
  • ret-type-pattern:返回值
  • declaring-type-pattern:方法所在的包
  • name-pattern:方法名
  • parm-pattern:參數名
  • throws-pattern:異常

         其中,除ret-type-pattern和name-pattern之外,其他都是可選的。上例中,execution(* com.spring.service.*.*(..))表示com.spring.service包下,返回值爲任意類型;方法名任意;參數不作限制的所有方法。

         AOP獲取註解和Target對象

  1. /*定義前置advice*/
  2. @Before("execution(* cn.ysh.studio.spring.aop.service..*(..))&&@annotation(roleAdmin)")
  3. public void adminCommon(JoinPoint point, RoleAdmin roleAdmin) {
  4. /*輸出註解描述內容*/
  5. System.out.println(roleAdmin.descript());
  6. /*利用反射顯示目標對象的相關信息*/
  7. String targetName = joinPoint.getTarget().getClass().getName();   
  8.       String methodName = joinPoint.getSignature().getName();    
  9.       Object[] arguments = joinPoint.getArgs();   
  10.       Class targetClass = Class.forName(targetName);    
  11.       Method[] methods = targetClass.getMethods();   
  12.       String description = "";
  13.       for(Method method : methods) {   
  14.       if(method.getName().equals(methodName)) {    
  15.       Class[] clazzs = method.getParameterTypes();    
  16.         if (clazzs.length == arguments.length) {   
  17.         description = method.getAnnotation(SystemControllerLog.class).description();    
  18.           break;    
  19.         }   
  20.       } 
  21. }
  22.  


         在Spring中,任何通知(Advice)方法都可以將第一個參數定義爲 org.aspectj.lang.JoinPoint類型用以接受當前連接點對象。JoinPoint接口提供了一系列有用的方法, 比如 getArgs() (返回方法參數)、getThis() (返回代理對象)、getTarget() (返回目標)、getSignature() (返回正在被通知的方法相關信息)和 toString() (打印出正在被通知的方法的有用信息)。

        1、如果要設置多個切點,則使用||進行拼接

    @Pointcut("execution(* aop.annotation.*.*(..))|| execution(*com.action.admin.*.*update*(..))")

        2、環繞通知的方法中一定要有ProceedingJoinPoint 參數,與Filter中的doFilter方法類似)
  1. @Around("execution(* aop.annotation.*.*(..))")
  2. public Object doAround(ProceedingJoinPoint pjp) throws{
  3. }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章