Spring 注解方式实现AOP

什么是AOP

AOP(Aspect Orient Programming),也就是面向方面编程,作为面向对象编程的一种补充,专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在 Java EE 应用中,常常通过 AOP 来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等。AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表;而动态代理则以 Spring AOP 为代表。


Spring AOP与 AspectJ的异同

Spring AOP 同样需要对目标类进行增强,也就是生成新的 AOP 代理类;与 AspectJ 不同的是,Spring AOP 无需使用任何特殊命令对 Java 源代码进行编译,它采用运行时动态地、在内存中临时生成“代理类”的方式来生成 AOP 代理。
Spring 允许使用 AspectJ Annotation 用于定义方面(Aspect)、切入点(Pointcut)和增强处理(Advice),Spring 框架则可识别并根据这些 Annotation 来生成 AOP 代理。Spring 只是使用了和 AspectJ 5 一样的注解,但并没有使用 AspectJ 的编译器或者织入器(Weaver),底层依然使用的是 Spring AOP,依然是在运行时动态生成 AOP 代理,并不依赖于 AspectJ 的编译器或者织入器。
简单地说,Spring 依然采用运行时生成动态代理的方式来增强目标对象,所以它不需要增加额外的编译,也不需要 AspectJ 的织入器支持;而 AspectJ 在采用编译时增强,所以 AspectJ 需要使用自己的编译器来编译 Java 文件,还需要织入器。


写本教程的目的

对于初次了解spring aop的小白来说,spring AOP 基于注解方式的实现,在网上搜索了很久都没有发现合适的一个讲解教程,并且AOP不是很常用,为了以后找笔记方便,并且避免初始配置时犯了许多错,因此才有了本教程。


面向切面的编程步骤
  • 定义切面 : 也就是声明注解@Aspect
  • 定义切点 : 也就是声明注解@Pointcut
  • 定义增强方法: 也就是声明注解@Before,@After,@Around,@AfterReturning,@AfterThrowing 的增强处理方法代码

demo 测试需求

在jsp界面登录的时候,实现在登录之前,之后,出现异常的时候做一些处理,假设说我们现在的登录逻辑已经很复杂了,不允许在登录界面继续添加复杂的处理逻辑,这个时候使用AOP就能达到这样的效果。


创建一个java web 工程命名为 SpringAop-test 整个工程的目录如下

Spring 注解方式实现AOP_第1张图片
工程目录
Spring 注解方式实现AOP_第2张图片
第三方库目录

web.xml 文件配置如下:

    

    
    
        springmvc
        
                org.springframework.web.servlet.DispatcherServlet
        
        
          contextConfigLocation
          /WEB-INF/xml/ibatis-config.xml
        
        1
    
    
        springmvc
        /
    
  
  
    
        CharacterEncodingFilter
        org.springframework.web.filter.CharacterEncodingFilter
        
            encoding
            utf-8
        
    
    
        CharacterEncodingFilter
        /*
    
    



ibatis-config.xml 配置如下:


    
    
    
    
            


切面类代码如下:
package com.spring.aspect;

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class UserAspect {
    @Pointcut("execution(* com.spring.service.I_UserService.checkUser(..))")
    public void loginAspect(){}
    
    @Before("loginAspect()")
    public void beforeLogin(JoinPoint joinPoint){
//      1 第一步执行
        System.out.println("beforeLogin action---");
    }
    
    @After("loginAspect()")
    public void afterLogin(JoinPoint joinPoint){
//      4 第四步执行
        System.out.println("afterLogin action---");
    }
    
    @Around("loginAspect()")
    public Object aroundLogin(ProceedingJoinPoint joinPoint)
            throws java.lang.Throwable {
//      2 第二步执行
        System.out.println("执行登录方法之前,模拟开始事务 ..."); 
        Object rvt = joinPoint.proceed(); 
//      5 第五步执行
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("参数:"+method.getTypeParameters()+";切入方法:"+method.getName()+";返回值:"+method.getReturnType());
        System.out.println("执行目标方法之后,模拟结束事务 ..."); 
        return rvt; 
    }
    
     @AfterReturning(returning="rvt", pointcut="loginAspect()")
     public void afterReturning(Object rvt){ 
//      3  第三步执行
         System.out.println("获取登陆结果返回值 :" + rvt); 
         System.out.println("添加登录成功日志功能 ..."); 
     } 
     
//   如果执行切点方法被抛出异常,则1,2,4步执行后执行@AfterThrowing增强处理。 @AfterReturning将不再执行
     @AfterThrowing(pointcut="loginAspect()",throwing="ex")
     public void afterThrowingSayHello(Exception ex){
         System.out.println("After Throwing : "+ex.getMessage());
     }
        
}


控制器类代码如下:
package com.spring.controller;

import javax.annotation.Resource;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.spring.service.I_UserService;

@Controller
@RequestMapping("/user")
public class UserController {

    @Resource 
    I_UserService userService;
    
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    public String loginAction(String userName,String userPsw) {
        boolean checkOK = userService.checkUser(userName,userPsw);
        System.out.println("________________"+checkOK + "___________");
        return "/index.jsp";
    }
}


接口类代码如下:
package com.spring.service;

public interface I_UserService {

    boolean checkUser(String userName, String userPsw);
    
}


实现类代码如下:
package com.spring.service;

import org.springframework.stereotype.Service;

@Service("userService")
public class UserService implements I_UserService{

    @Override
    public boolean checkUser(String userName, String userPsw) {
        if (userName != null && userName.length() > 0 
                && userPsw != null && userPsw.length() > 0) {
            return true;
        }else {
            throw new IllegalArgumentException("账号密码不能有一个为空");  
        }
    }
    
}

index.jsp界面比较简单就不在贴代码了。


运行结果如下
Spring 注解方式实现AOP_第3张图片
正常情况

Spring 注解方式实现AOP_第4张图片
异常捕获输出

注意事项:
  • UseAspect.java 一定要是用注解@Component 否则spring将不会自动加载此类,直接将导致注入失败,增强代码一直不会执行;因为作为切面类需要Spring管理起来,所以在初始化时就需要将这个类初 始化加入Spring的管理;
  • ibatis-config.xml 需要加入 支持aspectj aop注解。

相关报错
严重: Allocate exception for servlet springmvc java.lang.IllegalArgumentException: Pointcut is not well-formed: expecting ')' at character position 11 exection(* com.spring.service.I_UserService.checkUser(..))

Spring 注解方式实现AOP_第5张图片
Paste_Image.png


最后发现原因是execution 单词拼写错误,浪费了我好长时间才发现,细心是多么重要。

demo 下载 望多多star


相关引用

Spring AOP 实现原理与 CGLIB 应用
Spring实现AOP的4种方式

你可能感兴趣的:(Spring 注解方式实现AOP)