2021-12-28 Spring day4(动态代理,Aop五种通知)

2021-12-28 Spring day4(动态代理,Aop五种通知),第1张

2021-12-28 Spring day4(动态代理,Aop五种通知

文章目录
  • 一、Aop
  • 二、动态代理举例
  • 三、Aop五种通知
    • 1. 添加依赖3个
    • 2. 定义切点
    • 3. 新建切面
    • 4. 推荐使用@Pointcut注解定义切点

一、Aop

Aspect Oriented Programming ,面向切面编程,这是对面向对象思想的一种补充。

面向切面编程,就是在程序运行时,不改变程序源码的情况下,动态的增强方法的功能,常见的使用场景非常多:

  1. 日志
  2. 事务 --公共代码抽取出来
  3. 数据库 *** 作

以上 *** 作有很多模板化的代码

AOP常见概念
切点:要添加代码的地方,称作切点
通知(增强):通知就是向切点动态添加的代码
切面:切点+通知
连接点:切点的定义

AOP实现
Aop实际上是基于Java的动态代理来实现的
Java中的动态代理有两种实现方式:cglib,jdk

二、动态代理举例
  1. 定义一个计算器接口
package org.kk.aop;


public interface MyCalculator {
    int add(int a,int b);

}

  1. 实现接口
    MyCalculatorImpl是接口的实现类
package org.kk.aop;


public class MyCalculatorImpl implements MyCalculator{


    @Override
    public int add(int a, int b) {
        return a+b;
    }
}

  1. 实现代理类 利用Proxy类
    上面的实体经过代理类的加工,返回了一个新实体(添加了方法)
package org.kk.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class CalculatorProxy {
    public static Object getInstance(MyCalculatorImpl myCalculator)
    {
        //第一个参数:classLoader,第二个参数实现的接口,第三个参数为new的InvocationHandler
        return Proxy.newProxyInstance(CalculatorProxy.class.getClassLoader(), myCalculator.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("方法执行前");
                Object invoke = method.invoke(myCalculator, args);
                System.out.println("方法执行后");
                return invoke;
                //也可以return 99,那么调用add后的结果也就是99了
            }
        });

    }
}

  1. 调用,获得一个代理类加工后的对象
public class Main {
    @Test
    public void test1()
    {
        MyCalculatorImpl myCalculator=new MyCalculatorImpl();
        MyCalculator calculator = ((MyCalculator) CalculatorProxy.getInstance(myCalculator));
        int add=calculator.add(3,4);
        System.out.println("add="+add);
    }

}

调用结果:

方法执行前
方法执行后
add=7
  1. 总结
    Aop的原理与上面的动态代理一样,都是不需要改变原来的代码(Impl),添加新的方法(通过代理类)
三、Aop五种通知

Aop的通知类型有五种:

在以上代码中,在method.invoke之前执行——前置通知,在method.invoke之后执行——后置通知,异常通知,返回通知,环绕通知

1. 添加依赖3个
    

        
            org.springframework
            spring-context
            5.2.19.RELEASE
        
        
            org.aspectj
            aspectjweaver
            1.9.7
        
        
            org.aspectj
            aspectjrt
            1.9.7
        


    
2. 定义切点

两种方式:使用注解(不推荐)、使用规则

使用注解

  1. 自定义Action注解
package org.kk.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Action {
}

  1. Impl中对方法添加自定义注解
package org.kk.aop;

import org.springframework.stereotype.Component;



@Component
public class MyCalculatorImpl implements MyCalculator{
    @Override
    @Action  //对需要增强的方法添加这个自定义注解 侵入式--要改源码
    public int add(int a, int b) {
        return a+b;
    }

    @Override
    public void min(int a, int b) {
        System.out.println(a+"-"+b+"="+(a-b));
    }

    //然后想要给这两个方法增加一个日志 在不改变源码的前提下
}

  1. 新建类,用于定义通知
3. 新建切面

@Component
@Aspect

其中的方法用于通知:前置通知 @Before("@annotation(org.javaboy.app.Action)" ) 注解的包路径

package org.kk.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;


@Component
@Aspect //表示这是一个切面 五种通知
public class LogAspect {
    
    @Before("@annotation(Action)")//有Action注解的方法 执行之前会有前置通知
    public void before(JoinPoint joinPoint)
    {
        String name=joinPoint.getSignature().getName();
        System.out.println(name+"方法开始执行了");

    }

}

配置类添加注解,需要开启自动代理

package org.kk.aop;

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


@Configuration
@ComponentScan
//还要开启自动代理
@EnableAspectJAutoProxy
public class JavaConfig {

}

测试

    @Test
    public void test2()
    {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
        MyCalculator bean = context.getBean(MyCalculator.class);
        int res = bean.add(3, 4);
        System.out.println(res);

    }

结果

add方法开始执行了
7

四种通知方法

package org.kk.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;


@Component
@Aspect //表示这是一个切面 五种通知
public class LogAspect {
    
    @Before("@annotation(Action)")//有Action注解的方法 执行之前会有前置通知
    public void before(JoinPoint joinPoint)
    {
        String name=joinPoint.getSignature().getName();
        System.out.println(name+"方法开始执行了");

    }

    //后置通知
    @After("@annotation(Action)")
    public void after(JoinPoint joinPoint)
    {
        String name=joinPoint.getSignature().getName();
        System.out.println(name+"方法执行结束了");
    }

    //返回通知
    
    @AfterReturning(value = "@annotation(Action))",returning = "r")
    public void returning(JoinPoint joinPoint,Integer r)//类型不匹配方法返回类型,是不会返回结果的;可以用Object
    {
        String name=joinPoint.getSignature().getName();
        System.out.println(name+"返回通知:"+r);//r是方法执行的结果
    }

    
    @AfterThrowing(value = "@annotation(Action)",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e)
    {
        String name=joinPoint.getSignature().getName();
        System.out.println(name+"方法异常通知:"+e.getMessage());

    }
    
    @Around("@annotation(Action)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //这个有点类似于method.invoke方法,我们可以在这个方法前后分别添加日志,相当于前置和后置通知
        Object proceed=pjp.proceed(new Object[]{5,5});
        
        return proceed;

    }

}

    //优化1 通过方法来定义切点  下面的注解都改 @Before("pointcut()")
    @Pointcut("@annotation(Action)")
    public void pointcut()
    {

    }

非侵入式定义切点:

4. 推荐使用@Pointcut注解定义切点
    //非侵入式定义切点
    @Pointcut("execution(* org.kk.aop.service.*.*(..))") //任意类 任意方法 参数类型、个数任意
    public void pointcut()
    {

    }

package org.kk.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;


@Component
@Aspect //表示这是一个切面 五种通知
public class LogAspect {
//    //优化1 通过方法来定义切点  下面的注解都改 @Before("pointcut()")
//    @Pointcut("@annotation(Action)")
//    public void pointcut()
//    {
//
//    }
    //非侵入式定义切点
    @Pointcut("execution(* org.kk.aop.service.*.*(..))") //任意类 任意方法 参数类型、个数任意
    public void pointcut()
    {

    }




    
    @Before("pointcut()")//有Action注解的方法 执行之前会有前置通知
    public void before(JoinPoint joinPoint)
    {
        String name=joinPoint.getSignature().getName();
        System.out.println(name+"方法开始执行了");

    }

    //后置通知
    @After("pointcut()")
    public void after(JoinPoint joinPoint)
    {
        String name=joinPoint.getSignature().getName();
        System.out.println(name+"方法执行结束了");
    }

    //返回通知
    
    @AfterReturning(value = "pointcut())",returning = "r")
    public void returning(JoinPoint joinPoint,Integer r)//类型不匹配方法返回类型,是不会返回结果的;可以用Object
    {
        String name=joinPoint.getSignature().getName();
        System.out.println(name+"返回通知:"+r);//r是方法执行的结果
    }

    
    @AfterThrowing(value = "pointcut()",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e)
    {
        String name=joinPoint.getSignature().getName();
        System.out.println(name+"方法异常通知:"+e.getMessage());

    }
    
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //这个有点类似于method.invoke方法,我们可以在这个方法前后分别添加日志,相当于前置和后置通知
        Object proceed=pjp.proceed(new Object[]{5,5});  //return了10

        return proceed;

    }







}


xml配置Aop




    
    
    
        
        
            
            
        
    

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5685221.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存