浅谈-Spring AOP

关于 AOP我一直都是懵懂,面试 也经常被问到。有时候 自己做一两个小项目 也 没想到过用AOP,最近把重构项目基本 搞完之后,又想起AOP。从网上查了很多,讲法大致相同。

AOP即面向切面编程,在面向切面编程的思想里面,把功能分为,核心业务功能和周边功能。 所谓的核心业务功能指的是:登陆,增加删除数据等。周边功能指的是:日志统计,性能测试,事务管理等。

周边功能在Spring的面向切面编程AOP思想里,叫切面。在整个Spring里 核心业务功能 和切面分别独立开发,然后把切面功能和核心业务功能"编织"在一起,这就叫AOP。

举个栗子如果要记录日志,通常是在每个方法 中输出一些日志信息,这就造成高耦合,如果这些功能修改了,输出的日志信息也要修改。


通过动态代理,可以在指定位置执行对应流程。这样就可以将一些横向的功能抽离出来形成一个独立的模块,然后在指定位置插入这些功能。这样的思想,被称为面向切面编程,亦即AOP。


通知类型 标注
@Component 把普通pojo实例化到Spring容器中,相当于<bean id=" " class=" ">
@Aspect 切面
Before(前置通知) 目标方法调用之前执行
After(后置通知) 目标方法调用之后执行
After-returning(返回通知) 目标方法执行成功后执行
After-throwing(异常通知) 目标方法抛出异常后执行
Around(环绕通知) 相当于合并了前置和后置
/**
 * 方法切入点(execution:执行)
 * execution(修饰词   类名.方法名(参数类型))
 * execution(* cn.tedu.note.service.UserService.login(...))
 * Component (把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/> */
@Component
@Aspect
public class PointcutAspect {
    //匹配com.joeqiang.cloudbook.controller包及其子包下的所有类的所有方法

    @After("bean(*Service)")
    public void deBefore(JoinPoint jp) throws Throwable {
        System.out.println("******************执行后置通知******************");
        // 接收到请求,记录请求内容
        Long startTimeMillis = System.currentTimeMillis();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        System.out.println("URL :" + request.getRequestURI());
        System.out.println("HTTP_METHOD :" + request.getMethod());
        // System.out.println("IP:" + request.getRemoteAddr());
        //用的最多 通知的签名
        Signature signature = jp.getSignature();
        System.out.println("CLASS_METHOD :" + jp.getStaticPart());
        System.out.println("ARGS:" + Arrays.toString(jp.getArgs()));
        //代理的是哪一个方法
        System.out.println("代理方法名 :" + signature.getName());
        //AOP代理类的名字
        System.out.println("代理类名字:" + signature.getDeclaringTypeName());
        //AOP代理类的类(class)信息
        System.out.println("AOP代理类信息:" + signature.getDeclaringType());
        //获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //如果要获取Session信息的话,可以这样写:
        HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
        User user = (User) session.getAttribute("user");
        Long endTimeMillis = System.currentTimeMillis();
        //格式化开始时间
        String startTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(startTimeMillis);
        //格式化结束时间
        String endTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(endTimeMillis);
        if (user != null) {
            System.out.println("Session信息 :" + user.getName());

            System.out.println("操作人 : " + user.getName() + "操作方法 : " + signature.getName()
                    + " 开始时间 : " + startTime + " 结束时间 : " + endTime);
        }


    }
@AfterReturning(value = "execution( * com.joeqiang.cloudbook.controller..*.*(..))", returning = "keys", argNames = "keys")

public void doAfterReturningAdvice2(String keys) {
    System.out.println("******************后置返回通知******************");
    System.out.println("后置返回通知的返回值:" + keys);
}

//捕获 Controller 发生的异常信息
@AfterThrowing(value = "bean(*Controller)", throwing = "exception")
public void doAfterThrowingAdvice(JoinPoint joinPoint, Throwable exception) {
    System.out.println("******************异常通知******************");
    // /目标方法名:
    System.out.println("方法名:" + joinPoint.getSignature().getName());

    System.out.println("异常信息:" + exception.getMessage());

}

性能测试:

/*
 *对软件的业务层进行性能测试
 * * 环绕通知方法:
* 1.必须有返回值Object
 * 2.必须有参数ProceedingJoinPoint
 * 3.需要在方法中调用 jp.proceed()
 * 4.必须抛出异常
 * 5.返回业务方法的返回值
 */
@Component
@Aspect
public class TimeAspect {
    @Around("bean(*Service)")
   public Object test(ProceedingJoinPoint jp) throws Throwable {
      System.out.println("****************执行环绕通知******************");
      long t1 = System.currentTimeMillis();
      Object val = jp.proceed();//目标方法
      long t2 = System.currentTimeMillis();
      long t = t2 - t1;
      //JoinPoint 对象可以获取目标业务方法的详细信息:方法签名,调用参数
      Signature m = jp.getSignature();
      //方法名
      String name = m.getName();
      //Sinnature:签名:这里是方法签名
      System.out.println("执行方法 :"+name + "总共用时:" + t+"毫秒");
      return val;
   }
}

输出 信息:


如果你想在登陆方法前,增加点什么逻辑,比如:

@Before("execution(* com.joeqiang.cloudbook.controller.LoginController.login())")
public void after() {


        throw new RuntimeException("不给登陆");


}

输出信息:





猜你喜欢

转载自blog.csdn.net/joe_wang1/article/details/80966184
今日推荐