AOP - 2 实例(SpringBoot 注解方式)

  1、创建Spring Boot项目

  创建一个Spring Boot 项目,然后pom中引入web 模块与AOP相关依赖。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.0.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>

其中: 
aspectjweaver是与aspectj相关的包,用来支持切面编程的; 
aspectjweaver是aspectj的织入包;

 

2、实现一个web请求,数据通过接口获取(使用POJO类传参与返回值)

@RestController
@RequestMapping("/aop")
public class AopController {

    @Autowired
    private AopService aopService;

    @GetMapping(value = "getResult")
    public ResultVO sayHello(ParamVO paramVO) {
        ParamDTO paramDTO = new ParamDTO();
        BeanUtils.copyProperties(paramVO, paramDTO);
        ResultDTO resultDTO = aopService.getResult(paramDTO);
        ResultVO resultVO = new ResultVO();
        BeanUtils.copyProperties(resultDTO, resultVO);
        return resultVO;
    }
}

  

列出一个POJO类,其他类似。返回给前端的统一使用VO,业务逻辑层之间的传递使用DTO,映射数据库的使用Domain:

public class ParamVO {

    private String name;

    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "ParamVO{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

 3、web请求对应的接口

import cn.latiny.modules.aopone.model.ParamDTO;
import cn.latiny.modules.aopone.model.ResultDTO;

public interface AopService {

    ResultDTO getResult(ParamDTO param);
}
@Service
public class AopServiceImpl implements AopService {

    @Override
    public ResultDTO getResult(ParamDTO param) {
        ResultDTO result = new ResultDTO();
        result.setId(1001);
        result.setMessage(param.toString());
        result.setData(Arrays.asList("Latiny", "30", "20000"));
        return result;
    }
}

  4、定义一个切面类,实现对Service方法进行切面

把一个类变成切面类,需要两步: 
① 在类上使用 @Component 注解 把切面类加入到IOC容器中 
② 在类上使用 @Aspect 注解 使之成为切面类

/**
 * @description AopService切面类
 */
@Aspect
@Component
public class AopServiceAspect {

    private final Logger logger = LoggerFactory.getLogger(AopService.class);
}

  (1) 定义一个切入点

  这里我们对AopService的getResult() 方法进行切面编程。

    /**
     * 定义一个切入点
     */
    @Pointcut("execution(* cn.latiny.modules.aopone.service.AopService.getResult(..))")
    public void pointCut() {

    }

  

  (2) 定义一个前置通知

    /**
     * 前置通知
     * @param joinPoint
     * @param name
     */
    @Before("pointCut() && args(name)")
    public void before(JoinPoint joinPoint, String name) {
        Object[] parameters = joinPoint.getArgs();
        String methodName = joinPoint.getSignature().getName();
        System.out.println(methodName + "方法 Before通知,方法的参数: " + name);
    }

我们通过注解@Before结合切入点完成对getResult() 方法的切面,在before() 方法里,我们可以在getResult() 执行前做任何想做的事。在这个方法里,我们通过JoinPoint可以得到对应切入点的目标对象的所有信息,类名,方法名,方法参数名以及传递的值。

  (3) 其他通知的定义

    /**
     * 后置通知,异常时不执行
     * @param joinPoint
     * @param name
     */
    @AfterReturning(value = "pointCut() && args(name)", returning = "result")
    public void afterReturning(JoinPoint joinPoint, String result, String name) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println(methodName + "方法 AfterReturning通知,方法的参数: " + name + ", 方法返回值:" + result);
    }

    /**
     * 后置通知,不管对应的连接点方法是否正常执行,都会执行此通知
     * @param joinPoint
     * @param name
     */
    @After(value = "pointCut() && args(name)")
    public void after(JoinPoint joinPoint, String name) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println(methodName + "方法 After通知,方法的参数: " + name);
    }

    /**
     * 环绕通知,在不修改原来方法的前提下,可以在方法执行前修改方法的入参,也可以在方法执行之后修改方法的返回值。
     * 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。
     * 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
     * @param joinPoint
     * @return
     */
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        System.out.println(joinPoint.getSignature().getName() + "方法 Around通知开始");
        processInputArg(joinPoint.getArgs());
        try {
            Object obj = joinPoint.proceed();
            processOutputObj(obj);
            if(obj instanceof ResultDTO) {
                ResultDTO resultDTO = (ResultDTO) obj;
                System.out.println("修改后的返回值:" + resultDTO.toString());
            }
            System.out.println(joinPoint.getSignature().getName() + "方法 Around通知结束");
            return obj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println(joinPoint.getSignature().getName() + "方法 Around通知结束");
        return null;

    }

    /**
     * 处理输入参数
     */
    private void processInputArg(Object[] args) {
        for(Object arg: args) {
            System.out.println("参数原来的值为:" + arg.toString());
            if (arg instanceof ParamDTO) {
                ParamDTO param = (ParamDTO)arg;
                param.setAge(18);
                param.setName("Nowesiki");
            }
            System.out.println("参数修改的值为:" + arg.toString());
        }
    }

    /**
     * 返回值处理
     * @param obj
     */
    private void processOutputObj(Object obj) {
        if(obj instanceof ResultDTO) {
            ResultDTO result = (ResultDTO) obj;
            result.setId(1002);
            result.setMessage(result.getMessage());
            result.setData(Arrays.asList("Nowesike", "40", "1000000000"));
        }
    }

 

  5、测试

  这里测试一下环绕通知,其他的通知暂时注释掉,启动项目之后,直接在浏览器访问AopController接口即可。

  这里前端传的参数是,name = 马云,age = 44

  

  最后的结果是:

getResult方法 Around通知开始
参数原来的值为:ParamVO{name=马云, age=44}
参数修改的值为:ParamVO{name=Nowesiki, age=18}
修改后的返回值:ResultDTO{id=1002, message='ParamVO{name=Nowesiki, age=18}', data=[Nowesike, 40, 1000000000]}
getResult方法 Around通知结束

  环绕通知里不仅可以获取到目标对象的所有信息,还能改变目标对象的入参与返回值,功能非常强大。

  

猜你喜欢

转载自www.cnblogs.com/Latiny/p/10703520.html
今日推荐