Spring第三谈(aop的实现,切入点表达式,aop的作用,浅谈aop底层)

在这里插入图片描述

本人是一名物联网工程专业大二的学生,是互联网浪潮中一朵小小的浪花,写博客即是为了记录自己的学习历程,又希望能够帮助到很多和自己一样处于起步阶段的萌新。临渊羡鱼,不如退而结网。与其羡慕别人有顶尖的技术,还不如自己从慢慢摸索学习。终有一天,你也能称为别人“羡慕”的人
博客主页:https://blog.csdn.net/qq_44895397

aop是一个规范,是动态化的一个规范,一个标准

AOP:

        面向切面编程,基于动态代理的,可以使用jdk,cglib两种代理方式
        AOP就是动态代理的规范化,把动态代理的实现步骤,方法都定义好了,让开发人员用一种统一的方式,
        使用动态代理。

怎么理解面向切面编程:

1)需要在分析项目功能时找出切面
2)合理的安排切面的执行时间
3)合理的安排切面执行的位置

术语:

 1)Aspect:切面,表示增强功能,完成某一个个的功能,非业务功能
        常见的切面功能有日志、事务、统计信息、参数检查、权限验证
 2)JoinPoint:连接点,连接业务方法和切面的位置,就某类中的业务方法
 3)Pointcut:切入点,指多个连接点方法的集合,多个方法
 4)目标对象:哪个类要增加功能,这个类就是目标对象
 5)Advice:通知,通知表示切面功能执行的时间

切面的三个关键要素:

1)切面的功能代码,切面干什么的
2)切面的执行位置,使用Pointcut表示切面的执行位置
3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后 

aop的技术实现框架:

    1、spring:spring在内部实现了aop规范能够做aop的工作
    spring主要在事务处理时使用aop
    但是spring的aop比较笨重

    2、aspectJ:一个开源的专门做aop的框架,spring框架集成了aspectJ框架,通过spring就能够使用aspectj的功能
   	实现方式有两种:
        1、使用xml的配置文件:配置全局事务
        2、使用注解,项目中使用aop一般都使用注解,aspectj有5个注解

在这里插入图片描述

切入点表达式:

在这里插入图片描述
execution表达式中明显就是方法的签名黑色字体部分可省略,各部分用空格分开,可以用到一下符号:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实现步骤:

1、增加maven依赖

     1)spring依赖
     2)aspectj的依赖
     3)单元测试依赖
<!--添加spring依赖-->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>5.2.5.RELEASE</version>
</dependency>
<!--aspectj依赖-->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aspects</artifactId>
	<version>5.2.5.RELEASE</version>
</dependency>

2、创建,目标类:接口和他的实现类

    给类中的方法增加功能

3、创建切面类:普通的类

    1)在类的上面加入@Aspectj
	2)在类中定义方法,方法就是切面要执行的功能代码,
         在方法上添加aspectj中的通知注解(@Before)
    	 需要指定切入点表达式,execution()

4、创建配置文件:声明对象,把对象交给容器统一管理,

声明对象你可以使用注解或者xml文件配置文件

  1. 声明目标对象
  2. 声明切面类对象
  3. 声明aspectj框架中的自动代理生成器标签
    自动代理生成器,用来完成代理对象的自动创建功能的

5、创建测试类,从spring容器中获取目标对象(实际是代理对象)

通过代理方法,实现aop的功能增强

代码:

xml文件:

<!--创建实现类对象-->
<bean id="someServiceImpl" class="com.aspectj.domain.SomeServiceImpl"/>

<!--创建切面类的对象-->
<bean id="utilsTools" class="com.aspectj.domain.UtilsTools"/>

<!--
        声明自动代理生成器,使用aspectj框架内部的功能,创建目标对象的代理对象
        创建代理对象是在内存中实现的,修改目标对象的内存中的结构,创建为代理对象
        所以目标对象就是被修改后的代理对象
        -->
<aop:aspectj-autoproxy />

切面类:

/**
 * 切面类
 * @Aspect :是aspectj框架中的注解
 *  作用:表示当前的类是切面类
 *  切面类:只用来给业务方法增加功能的类,在这个类中有切面的功能代码
 *  位置:类定义的上面
 */
@Aspect
public class UtilsTools {

    /**
     * 定义方法,是实现切面功能的
     * 方法定义的要求:
     *      1、公共方法:public
     *      2、方法没有返回值
     *      3、方法名称自定义
     *      4、方法可以有参数,也可以没有参数
     *          如果有参数,参数不是自定义的,有几个参数类型可以使用
     */
    /**
     * @Before  前置通知注解
     *      属性value,是切入点表达式,表示切面的功能执行的位置
     *      位置:方法的上面
     *    public void doSome(String name,Integer age)
     */
    @Before(value = "execution(public void com.aspectj.domain.SomeServiceImpl.doSome(String,Integer))")
    public void dolog() {
        //就是切面类要执行的功能代码
        System.out.println("切面类" + new Date());
    }
}

JoinPoint:

指定通知方法中的参数
业务方法,要加入切面功能的业务方法,
**作用是:**可以在通知方法中获取方法执行时的信息,例如方法名,方法参数
如果你的方法中需要用到方法的信息,就加入JoinPoint
这个JoinPoint参数的值是由框架赋予的,必须是第一个位置的参数
使用方法:

/**
 * JoinPoint :切入点
 *  可以获取方法执行的信息
 */
public void dolog(JoinPoint jp){
        Object[] args = jp.getArgs();
        for(Object arg:args){
        System.out.println("参数"+arg);
        }
        System.out.println("切面"+new Date());
        }

后置通知:@AfterReturning

	属性:value:切入点表达式
            return:自定义的变量,表示目标方法的返回值的
            自定义的变量名必须和通知方法的形参名一样
            位置:在方法定义的上面
            特点:
                1、在目标方法之后执行的
                2、能够获取到方法的返回值,可以根据这个返回值的不同做不同的处理
                3、可以修改这个返回值

具体使用:

@AfterReturning(value = "execution(* *..SomeServiceImpl.*(..))",
        returning = "o")
public void doStudent(JoinPoint jp,Student o){
        System.out.println("事务提交");
        o.setName("多级包");
        o.setAge(12);
}

在这里插入图片描述

环绕通知:@Around

定义格式

1、public
2、必须有一个返回值
3、方法名称自定义自定义
4、方法有参数,固定的参数,ProceedingJoinPoint

属性:value,切入点表达式
位置:在方法的定义上

 特点:
        1、他是功能最强的通知
        2、在目标前后都能增强功能
        3、控制目标方法是是否被调用执行
        4、修改原来的目标方法的执行结果,影响最后的调用结果

环绕通知:

等同于jdk动态代理的,InvocationHandler接口
参数:ProceedingJoinPoint pjp 等同于Method
作用:执行目标方法的 pjp.doSome();
返回值:就是目标方法的执行结果,可以被修改
经常做的是事务
/**
 * 环绕通知:功能最强大,能在业务方法前后增强功能
 * value:切片表达式
 * ProceedingJoinPoint:参数用来执行方法
 */
@Around(value = "execution(* *..SomeServiceImpl.*(..))")
public Object MyAspectj(ProceedingJoinPoint pjp) throws Throwable {
    //控制方法是否执行
    Object[] args = pjp.getArgs();
    String name = "";
    if (args != null && args.length > 1) {
        name = (String) args[0];
    }
    Object proceed = null;
    System.out.println("方法后执行");
    //目标方法的调用
    if ("yky".equals(name)) {
        proceed = pjp.proceed();//相当于,method.invoke(),可以在这个方法前后增加功能
    }
    System.out.println("方法前执行");
    //修改方法的返回值
    if(proceed != null){
        proceed = "hello world";
    }
    return proceed;
}

异常通知:@AfterThrowing

	1、public
	2、没有返回值
    3、方法名称自定义
    4、方法可以有参数Exception,如果还有是JoinPoint

属性:

    value:切入点表达式
    throwing:表示目标方法抛出的异常对象,自定义变量
    变量名必须和方法的参数名一样

特点:

  1. 在目标方法抛出异常时执行
  2. 可以做异常的监控程序,监控方法是不是有异常,如果有异常,可以发邮件,短信进行通知

最终通知:@After

	1、public
	2、没有返回值
    3、方法名称自定义
    4、没有参数,,如果有是JoinPoint

特点:总是会执行,在目标方法后执行的 做资源清除的

@Pointcut:

定义和管理切入点,如果项目中有多个切入点是重复的,可以复用的,可以使用这个注解

  • 属性value:切入点表达式

  • 位置:自定义的方法上面

  • 特点:

     当使用@pointcut定义在一个方法的上面,此时方法的名称就是,切入点表达式的别名
     其他通知中value属性可以使用这个方法名,代替切入点表达式了
    

代码:

@Before(value = "mycut()")
public void before(){
        System.out.println("前置通知");
        }

@After(value = "mycut()")
public void finalDemo(){
        System.out.println("最终通知");
        }

/**
 * @Pointcut: 用来管理切入点
 */
@Pointcut(value = "execution(* *..SomeServiceImpl.*(..))\")")
public void mycut(){}

jdk动态代理实现:

1、创建目标类,SomeServiceImpl目标类,给他的doSome,doOther增加输出时间,事务
 2、创建InvocationHandler接口的实现类,在这个实现类给目标方法增加功能
 3、使用jdk中类Proxy,创建代理对象。实现创建对象的能力
public static void main(String[] args) {
        //创建目标对象
        SomeService someService = new SomeServiceImpl();
        //创建
        MyInvocationHandler handler = new MyInvocationHandler(someService);

创建代理

        SomeService poxy = (SomeService) Proxy.newProxyInstance(someService.getClass().getClassLoader(),
        someService.getClass().getInterfaces(),
        handler);
        poxy.dosome();
        }
public class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //通过代理对象执行方法时调用执行invoke()
        Object res = null;
        utilsTools.dolog();
        //执行目标类的方法,通过Method类实现
        res = method.invoke(target, args);//实现类的方法
        utilsTools.doTrans();
        //执行结果
        return res;
    }
}

本站所有文章均为原创,欢迎转载,请注明文章出处:爱敲代码的小游子

猜你喜欢

转载自blog.csdn.net/qq_44895397/article/details/106653884