18 Spring注解驱动AOP开发入门


1 AOP思想及实现原理

1.1 AOP思想

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

1.2 实现原理

2 Spring中AOP的术语

  • Joinpoint(连接点):
    所谓连接点是指那些被拦截到的点。在spring中,指的是方法,因为spring只支持方法类型的连接点。
  • Pointcut(切入点):
    所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
  • Advice(通知/增强):
    所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知
  • Introduction(引介):
    引介是一种特殊的通知在不修改类代码的前提下, 可以在运行期为类动态地添加一些方法或Field。
  • Target(目标对象):
    代理的目标对象。
  • Weaving(织入):
    是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
  • Proxy(代理):
    一个类被AOP织入增强后,就产生一个结果代理类。
  • Aspect(切面):
    是切入点和通知(引介)的结合。

3 AOP联盟(规范 org.aopalliance.aop.Advice)通知类型

AOP联盟为通知定义了几种类型,简单来说我们拦截的方法,我们有时需要再方法执行前做处理,也可能在执行后做一些处理,也可能在方法出现异常的时候做处理。

AOP联盟为通知Advice定义了org.aopalliance.aop.Advice
Spring按照通知Advice在目标类方法的连接点位置,可以分为5类:

  • 前置通知 org.springframework.aop.MethodBeforeAdvice

    • 在目标方法执行前实施增强
  • 后置通知 org.springframework.aop.AfterReturningAdvice

    • 在目标方法执行后实施增强
  • 环绕通知 org.aopalliance.intercept.MethodInterceptor

    • 在目标方法执行前后实施增强
  • 异常抛出通知 org.springframework.aop.ThrowsAdvice

    • 在方法抛出异常后实施增强
  • 引介通知 org.springframework.aop.IntroductionInterceptor

    • 在目标类中添加一些新的方法和属性

3 Spring注解驱动AOP开发入门

需求:实现在执行service方法时输出执行日志。(除了业务层外,表现层和持久层也可以实现)

3.1 java动态代理实现

  1. 日志切面类
package study.wyy.spring.anno.aop.java;

import lombok.extern.slf4j.Slf4j;

/**
 * @author: wyaoyao
 * @date: 2020-12-24 16:01
 * @description:
 */
@Slf4j
public class LogAspect {


    /***
     * 前置通知: 在方法执行之前进行日志打印
     *
     * */
    public void beforeLog() {
        log.info("begin execute method");
    }

    /***
     * 后置通知: 在目标方法执行后实施增强,比如在方法执行结束后,打印日志
     * */
    public void afterReturningLog() {
        log.info("execute success ...");
    }

    /***
     * 异常通知
     *  发生异常的时候执行
     * */
    public void afterThrowingLog() {
        log.error("execute method error");
    }

}

  1. service 方法
package study.wyy.spring.anno.aop.service;

import study.wyy.spring.anno.aop.model.User;

public interface UserService {

    public User save(User user);

    public Boolean update(User user);
}

@Service
@Slf4j
public class UserServiceImpl implements UserService {
    @Override
    public User save(User user) {
        String uuid = UUID.randomUUID().toString();
        user.setId(uuid);
        log.info("模拟保存用户。。。。。");
        return user;
    }

    @Override
    public Boolean update(User user) {
        String id = user.getId();
        if(null == id || id.length() ==0){
            throw new RuntimeException("user.id.not.null");
        }
        log.info("模拟更新用户。。。。。");
        return Boolean.TRUE;
    }
}
  1. 生成代理类(使用jdk的动态代理)
public class MyProxyBeanFactory {

    public static UserService createUserService() {
        // 1 目标而对象
        UserService userService = new UserServiceImpl();
        // 2 切面类
        LogAspect logAspect = new LogAspect();
        // 3 创建代理对象
        /*****
         * 参数一:类加载器
         * 参数二:目标对象和代理对象实现的接口的字节码对象的数组,接口的方法会被拦截,这里要拦截的就是UserService中的方法,
         *
         * 参数三:InvocationHandler h 处理器,在此内部实现方法的增强
         */
        Object o = Proxy.newProxyInstance(MyProxyBeanFactory.class.getClassLoader(), new Class[]{UserService.class}, new InvocationHandler() {
            /****
             *
             * @param proxy
             * @param method: 要拦截的方法,要执行增强的方法
             * @param args:拦截方法的参数
             * @return 拦截方法的返回值
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 执行方法之前进行日志打印
                logAspect.beforeLog();
                try {
                    // 执行方法
                    Object invoke = method.invoke(userService, args);
                    // 执行后再打印日志
                    logAspect.afterReturningLog();
                    return invoke;
                } catch (Exception e) {
                    // 发生异常,打印异常日志
                    logAspect.afterThrowingLog();
                    // 把人家的异常接着在跑出去
                    throw e;
                }

            }
        });

        return (UserService) o;
    }
}
  1. 测试
public class ClientTest {
    public static void main(String[] args) {
        UserService userService = MyProxyBeanFactory.createUserService();
        User user = new User();
        user.setMobile("13100001111");
        user.setBirthday(new Date());
        user.setNickname("kobe");
        userService.save(user);
        // 发生异常
        user.setId(null);
        userService.update(user);

    }
}
20:47:45.795 [main] INFO study.wyy.spring.anno.aop.java.LogAspect - begin execute method
20:47:45.810 [main] INFO study.wyy.spring.anno.aop.service.impl.UserServiceImpl - 模拟保存用户。。。。。
20:47:45.811 [main] INFO study.wyy.spring.anno.aop.java.LogAspect - execute success ...
20:47:45.811 [main] INFO study.wyy.spring.anno.aop.java.LogAspect - begin execute method
20:47:45.811 [main] ERROR study.wyy.spring.anno.aop.java.LogAspect - execute method error

3.2 springAOP实现

spring 创建代理对象,从spring容器中手动的获取代理对象.

  1. 定义切面类
package study.wyy.spring.anno.aop.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @author: wyaoyao
 * @date: 2020-12-24 16:01
 * @description:
 */
@Slf4j
@Aspect
@Component
public class LogSpringAspect {


    /***
     * 前置通知: 在方法执行之前进行日志打印
     *
     * */
    @Before("execution(* study.wyy.spring.anno.aop.service.impl.*.*(..))")
    public void beforeLog() {
        log.info("execute method with params is {}");
    }

    /***
     * 后置通知: 在目标方法执行后实施增强,比如在方法执行结束后,打印日志
     * */
    @AfterReturning("execution(* study.wyy.spring.anno.aop.service.impl.*.*(..))")
    public void afterReturningLog() {
        log.info("execute success ...");
    }

    /***
     * 异常通知
     *  发生异常的时候执行
     * */
    @AfterThrowing("execution(* study.wyy.spring.anno.aop.service.impl.*.*(..))")
    public void afterThrowingLog() {
        log.info("execute method error {} with params {}");
    }

}

  1. 配置
package study.wyy.spring.anno.aop.config;

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

/**
 * @author wyaoyao
 * @description
 * @date 2021/2/1 16:25
 */
@Configuration
@ComponentScan("study.wyy.spring.anno.aop")
@EnableAspectJAutoProxy // 开启AspectJ
public class SpringConfiguration {
}

  1. 启动测试
package study.wyy.spring.anno.aop;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import study.wyy.spring.anno.aop.config.SpringConfiguration;
import study.wyy.spring.anno.aop.model.User;
import study.wyy.spring.anno.aop.service.UserService;

/**
 * @author wyaoyao
 * @description
 * @date 2021/2/1 16:27
 */
public class SpringAOPTest {

    public static void main(String[] args) {
        // 1 获取容器
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2.获取bean对象
        UserService userService = ac.getBean(UserService.class);
        // 3.准备数据
        User user = new User();
        user.setId("1");
        user.setUsername("test");
        user.setNickname("泰斯特");
        // 4.执行方法
        userService.save(user);
        // 5测试出现异常
        user.setId(null);
        userService.update(user);

    }
}

20:57:01.457 [main] INFO study.wyy.spring.anno.aop.aop.LogSpringAspect - execute method with params is {}
20:57:01.469 [main] INFO study.wyy.spring.anno.aop.service.impl.UserServiceImpl - 模拟保存用户。。。。。
20:57:01.469 [main] INFO study.wyy.spring.anno.aop.aop.LogSpringAspect - execute success ...
20:57:01.469 [main] INFO study.wyy.spring.anno.aop.aop.LogSpringAspect - execute method with params is {}
20:57:01.470 [main] INFO study.wyy.spring.anno.aop.aop.LogSpringAspect - execute method error {} with params {}
Exception in thread "main" java.lang.RuntimeException: user.id.not.null
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值