Spring AOP(aop) 详解

     Spring有两大核心,一个是IOC,一个是AOP。IOC比较好理解,控制反转,也就是将bean的控制权交给Spring来管理,让对象之间解耦。但是对于AOP,只是听老师说功能非常强大,却一直没有领略到其强大之处。却是被通知,切入点,切面,切点表达式这一堆的概念所迷惑。感觉AOP离我们遥遥无期,遥不可及。导致这种情况并不能怪老师,而是想要领略到AOP的强大,是需要大的项目规模来进行支撑的。

     AOP的应用场景有很多,比如记录日志开启事务异常处理等等。使用AOP结合注解功能更是不得了,比如缓存的使用,权限的控制等等。那么下面我们就来了解了解这些内容。

开启AOP

     要使用AOP首先要开启Spring的自动代理配置,配置方法非常简单,只需要在配置类中加上@EnableAspectJAutoProxy注解就可以了。对Spring的Java配置不熟悉的朋友可以参考这篇文章:https://blog.csdn.net/king_kgh/article/details/75223464

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com")
@EnableAspectJAutoProxy // 开启自动代理
public class JavaConfig {

}

案例

我们通过一个例子来演示如何通过AOP来实现对指定方法的动态代理。

1.创建配置类

像上面提到的那样,在核心配置类中,加上@EnableAspectJAutoProxy注解。

package com.hy.spring.test9;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com.hy.spring.test9")
@EnableAspectJAutoProxy
public class Config {

}

2.创建服务接口

这个接口是一个普通的接口,定义了一个hello方法,这个方法是后面使用AOP处理的方法,接收两个参数,并且返回一个Result。

package com.hy.spring.test9;

public interface BusinessService {

     public Result hello (String first, Param param) ;

}

3.创建服务实现类

这个实现类就是普通的实现类,实现了上面接口中的hello方法。

package com.hy.spring.test9;

import org.springframework.stereotype.Service;

@Service
public class BusinessServiceImpl implements BusinessService {

     @Override
     public Result hello(String first, Param param) {

          // 进行一些无厘头的操作
          Result result = new Result();

          if(first == null) {
              result.setCode("001");
              result.setMessage("参数异常!");
              return result;
          }

          first = "处理后的数据:" + first;

          result.setMessage(first);
          result.setObj(param);

          return result;
     }
}

参数封装类

package com.hy.spring.test9;

public class Param {

     private String name;

     private Integer age;

     // 省略 getter and setter and toString ...
}

响应封装类

package com.hy.spring.test9;

public class Result {

     private String code;

     private String message;

     private Object obj;

     // 省略 getter and setter and toString ...
}

4.创建运行的主类

通过创建一个带有main方法的类,来调用上面接口中定义的hello方法

package com.hy.spring.test9;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {

     public static void main(String[] args) {

          // 初始化Spring容器
          AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);

          // 获取服务层对象
          BusinessService service = context.getBean(BusinessService.class);

          // 调用服务方法
          Result result = service.hello("hi", new Param());

          // 输出响应结果
          System.out.println("执行的最终结果为 : " + result);

          context.close();
     }
}

到这里,Spring的一个简单结构就搭建起来了,运行上面的代码,会调用到Spring管理的接口中的方法,这里没有用到任何的AOP,还是使用了IOC。先执行看下效果,然后使用AOP进行动态代理,执行上面代码的结果如下:

八月 30, 2017 4:39:27 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@6e2c634b: startup date [Wed Aug 30 16:39:27 CST 2017]; root of context hierarchy
Result [code=null, message=处理后的数据:hi, obj=com.hy.spring.test9.Param@679b62af]
八月 30, 2017 4:39:28 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@6e2c634b: startup date [Wed Aug 30 16:39:27 CST 2017]; root of context hierarchy

5.创建切面

上面做了很多的准备工作,下面才到了AOP的核心,通过AOP对服务方法进行代理,修改方法的执行参数,修改方法的返回值等等,代码如下

package com.hy.spring.test9;

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.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Service;

@Service
@Aspect  // 声明为一个切面
public class LogAspect {

     /**
      * 在方法执行前执行,并且能够拿到方法所传入的参数
      *
      * @param joinPoint
      */
     @Before(value="execution(* * (..))")
     public void logBefore (JoinPoint joinPoint) {

          System.out.println("方法执行前执行...");

          // 获取所有的参数列表
          Object []args = joinPoint.getArgs();
          printArgs(args);
     }

     /**
      * 在方法执行后执行
      *
      * @param joinPoint
      */
     @After(value = "execution(* * (..))")
     public void logAfter (JoinPoint joinPoint) {
          System.out.println("方法执行后执行...");
     }

     /**
      * 在方法返回值之后执行,并且能够拿到返回值。
      *
      * @param joinPoint
      * @param obj 所拦截的方法的返回值
      */
     @AfterReturning(value = "execution(* * (..))",returning="obj")
     public void logAfterReturning (JoinPoint joinPoint,Object obj) {
          System.out.println("方法执行后执行,并且方法已经返回返回值...");
          System.out.println("方法的返回结果为 : " + obj);
     }

     /**
      * 环绕通知,控制方法执行,可以修改方法的参数,修改方法的返回值,控制方法的执行
      *
      * @param joinPoint
      * @param obj
      */
     @Around(value = "execution(* * (..))")
     public Object logAround (ProceedingJoinPoint joinPoint) {

          // 获取方法的参数
          Object []params = joinPoint.getArgs();
          printArgs(params);

          // 修改方法的参数,将 传入的hi 修改为 hello
          params[0] = "hello";

          // 控制方法执行
          Object obj = null; // 用来接收方法的返回值
          try {
              obj = joinPoint.proceed(params); // 方法执行,获取到方法返回值
          } catch (Throwable e) {
              e.printStackTrace(); // 这里处理异常
          }

          // 修改返回值并返回
          Result result = (Result) obj;
          result.setMessage("代理处理之后的数据:" + result.getMessage());

          return result;
     }

     /**
      * 打印方法的参数
      *
      * @param params
      */
     private void printArgs(Object[] params) {
          for(Object obj : params) {
              System.out.println("param : " + obj);
          }
     }
}

执行后的结果如下

八月 30, 2017 5:06:18 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@6e2c634b: startup date [Wed Aug 30 17:06:18 CST 2017]; root of context hierarchy
param : hi
param : com.hy.spring.test9.Param@16d04d3d
方法执行前执行...
param : hello
param : com.hy.spring.test9.Param@16d04d3d
方法执行后执行...
方法执行后执行,并且方法已经返回返回值...
方法的返回结果为 : Result [code=null, message=代理处理之后的数据:处理后的数据:hello, obj=com.hy.spring.test9.Param@16d04d3d]
执行的最终结果为 : Result [code=null, message=代理处理之后的数据:处理后的数据:hello, obj=com.hy.spring.test9.Param@16d04d3d]
八月 30, 2017 5:06:19 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@6e2c634b: startup date [Wed Aug 30 17:06:18 CST 2017]; root of context hierarchy

 到这里,AOP的核心你就了解了,对于切点表达式一些知识点,可以参考Spring的官方文档,另外通过注解来实现缓存或者记录日志功能,可以参考https://blog.csdn.net/king_kgh/article/details/75270558

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值