栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

spring(三)aop(五种通知方法结合动态代理案例)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

spring(三)aop(五种通知方法结合动态代理案例)

一.AOP 1.1 AOP概念

理解:项目运行时,在不改变已有代码的情况下,自动向方法中添加新的功能。

1.2 Aop小知识点 1.2.1 设计知识点底层用到了动态代理

(22条消息) 静态代理与动态代理_谁是黄黄的博客-CSDN博客

概念说明
切点要添加代码的地方,称作切点,一般是在某个方法执行前后加入目标代码,这个方法的位置,就是切点
通知(增强)通知就是向切点动态添加的代码,称之为advice
切面切点+通知
连接点切点的定义
1.2.2 怎么让JKD 与CGLIB动态代理相互转化
  • 默认情况下
  • AOP 底层就是动态代理,而动态代理有两种实现方式,在Spring中,如果被代理的对象有接口,则动态代理使用JDK动态代理,如果被代理的对象没有接口,则会自动去使用CGLIB动态代理
  • 手动更换JDK 与CGLIB动态代理模式

第一步:这里我们还是利用

(22条消息) 静态代理与动态代理_谁是黄黄的博客-CSDN博客

这篇博文里面的计算器类并给他导入AOP五种通知的依赖

  
            org.springframework
            spring-context
            5.3.20
        

            org.aspectj
            aspectjweaver
            1.9.9
        
        
            org.aspectj
            aspectjrt
            1.9.9
        

public class CalculatorImpl{

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

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

第二步:然后定义通知/增强:

public class LogAdvice {

    
    public void before(JoinPoint jp) {
        //获取目标方法名称
        String name = jp.getSignature().getName();
        System.out.println(name + "开始执行了...");
    }

    
    public void after(JoinPoint jp) {
        System.out.println(jp.getSignature().getName() + " 方法执行结束了...");
    }

    
    
    
    public void throwing(JoinPoint jp, Exception e) {
        System.out.println(jp.getSignature().getName() + " 方法抛出 " + e.getMessage() + " 异常");
    }
    
    
    
     public void returning(JoinPoint jp, Object result) {
        System.out.println(jp.getSignature().getName() + " 方法返回了 " + result);
    }
    
    
    
    
    public Object around(ProceedingJoinPoint pjp) {
        try {
            //这行代码就类似于 method.invoke 方法
            //当执行这行代码的时候,目标方法才会被真正的执行
            Object proceed = pjp.proceed();
            //注意这个地方有返回值
            return proceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
  }  

第三步配置xml




    
    
    
    
       
        
        
        
            
            
            
            
            
            
        

    

第四步 .加载Spring容器

public class Demo01 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //注意这里返回的对象,是 Spring 容器根据 Calculator 接口自动生成的一个实现类的对象
        //是一个代理的对象
        CalculatorImpl calculator = (CalculatorImpl) ctx.getBean("calculator");
        System.out.println("calculator.getClass() = " + calculator.getClass());
//        calculator.add(3, 4);
        calculator.min(3,4);
    }
}

注意,这个地方虽然我们用 CalculatorImpl 类型接收的 Spring 容器中的 Bean,但实际上返回的对象并不是 CalculatorImpl 本身,而是它的子类的实例,此时使用的动态代理是 CGLIB动态代理

calculator.getClass() = class com.qfedu.demo.p1.service.CalculatorImpl$$EnhancerBySpringCGLIB$$9e71c934

第五步.更改JDK动态代理所需要的两步

步骤一:给 CalculatorImpl 添加一个接口

public class CalculatorImpl implements Calculator {
    @Override
    public int add(int a, int b) {
//        int i = 1 / 0;
        return a + b;
    }

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

    void min(int a, int b);
}

步骤二:加载容器的时候,使用 Calculator 去接收:

public class Demo01 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //注意这里返回的对象,是 Spring 容器根据 Calculator 接口自动生成的一个实现类的对象
        //是一个代理的对象
        Calculator calculator = (Calculator) ctx.getBean("calculator");
        System.out.println("calculator.getClass() = " + calculator.getClass());
//        calculator.add(3, 4);
        calculator.min(3,4);
    }
}

此时,由于被代理的对象有接口,所以就会使用 JDK 动态代理。

打印的日志如下:

calculator.getClass() = class com.sun.proxy.$Proxy5

修改xml配置来实现使用CGLIB动态代理


    
    
    
    
        
        
        
        
        
        
    

1.3 AOP中的五种通知
  • 前置通知:目标方法执行之前执行
  • 后置通知: 目标方法执行之后执行
  • 异常通知: 目标方法抛出异常的时候执行
  • 返回通知 : 当目标方法返回值的时候执行
  • 环绕通知: 以上四个的封装与集成
1.4 案例 1.4.1第一步

导入依赖

  
            org.springframework
            spring-context
            5.3.20
        

            org.aspectj
            aspectjweaver
            1.9.9
        
        
            org.aspectj
            aspectjrt
            1.9.9
        
1.4.2 第二步定义LogAdvice通知类 1.4.2.1 前置通知
    public void  before(JoinPoint jp){
        //获取目标方法名称
        String name = jp.getSignature().getName();
        System.out.println(name + "开始执行了。。。。");
    }
1.4.2.2 后置通知
    
    public  void  after(JoinPoint jp) {
        System.out.println(jp.getSignature().getName() + "方法结束了");
    }
1.4.2.3 异常通知
 public  void  throwing(JoinPoint jp,Exception e){
        System.out.println(jp.getSignature().getName() + " 方法抛出 " + e.getMessage() + " 异常");
    }

底层原理:

  • 当目标方法抛出异常的时候,这个方法就会执行。

  • 注意异常的参数,只有目标方法抛出的异常,是这个异常参数或者它的子类的时候,才会进入到这个方法中

  • 这个异常通知的原理相当于,目标方法用try-catch裹起来

  • 示例

    try{
    目标方法
    method.invoke(xxxx)
    }catch(NullPointerException e){

}

 public  void  throwing(JoinPoint jp,Exception e){
        System.out.println(jp.getSignature().getName() + " 方法抛出 " + e.getMessage() + " 异常");
    }
  • 小问题:小问题:大家知道最大的异常是什么吗

是Throwable 异常

Throwable = Exception(最大异常)+ error(错误)

  • 那我们在异常通知中的参数异常,能能填写Throwable 吗

可以,但是没必要,error错误是不是意料之外的东西,所以我们没必要去把他包裹进异常里面

1.4.2.4 返回通知
  • 返回通知,目标方法返回值和这里参数类型匹配的时候,这个方法会被触发
  • 注意,返回值为 void,对应的类型为 Void,而 Void 是 Object 的子类,所以,这里如果用 Object 去接收返回类型,那么返回值为 void 的方法也会进入到返回通知中
 public  void  returning(JoinPoint jp , Object result){
        System.out.println(jp.getSignature().getName() + " 方法返回了 " + result);
    }
1.4.2.5 环绕通知
 public Object around(ProceedingJoinPoint pjp) {
        try {
            //这行代码就类似于 method.invoke 方法
            //当执行这行代码的时候,目标方法才会被真正的执行
            Object proceed = pjp.proceed();
            //注意这个地方有返回值
            return proceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

1

1.4.3 第三步 定义一个加法动态代理

底层:动态代理

(22条消息) 静态代理与动态代理_谁是黄黄的博客-CSDN博客

1.4.4 第四步 配置且点,切面,且注入对象到spring容器



        
        

    
        
        
        
        
            
            
            

            
            
            
                
    

笔记:

  • 定义切点中:如何精确定义一个包?
  • 首先通过包下类的路径
  • 其次你的同一个类可能有方法重载,方法的参数要传递进去
  • 然后还可能一样,方法的返回类型也要定义
  • 1.id 表示切点的名称
  • ​ expression:表示切点的定义,第一个 * 表示方法返回值任意(这个位置也可以给定一个具体的返回类型)
  • ​ 第二个 * 表示 service 下的所有类
  • ​ 第三个 * 表示 任意方法
  • ​ … 表示参数任意(参数可有可无,如果有,参数类型也是任意的)
二.java代码配置AOP(一般业务层) 2.1 定义切面

2.2 前置通知
  
    @Before("execution(* com.huang.demo.*.*(..))")
    public void  before(JoinPoint jp){
        //获取目标方法名称
        String name = jp.getSignature().getName();
        System.out.println(name + "开始执行了。。。。");
    }
2.3 后置通知
 
    @After("execution(* com.huang.demo.*.*(..))")
    public  void  after(JoinPoint jp) {
        System.out.println(jp.getSignature().getName() + "方法结束了");
    }


2.4 异常通知
 @AfterThrowing
    public  void  throwing(JoinPoint jp,Exception e){
        System.out.println(jp.getSignature().getName() + " 方法抛出 " + e.getMessage() + " 异常");
    }

2.5 返回通知
   @AfterReturning()
    public  void  returning(JoinPoint jp , Object result){
        System.out.println(jp.getSignature().getName() + " 方法返回了 " + result);
    }
2.6 环绕通知
 @Around()
    public Object around(ProceedingJoinPoint pjp) {
        try {
            //这行代码就类似于 method.invoke 方法
            //当执行这行代码的时候,目标方法才会被真正的执行
            Object proceed = pjp.proceed();
            //注意这个地方有返回值
            return proceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
2.7 问题:每次都这么写是否麻烦,我们怎么办 2.7.1 第一步: 统一定义切点

 
2.7.2 第二步: 直接在注解中引用统一定义好的切点方法 

package com.huang.demo.p4.service;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

@Aspect
public class LogAspect {
    @Pointcut("execution(* com.huang.demo.*.*(..))")
    public  void  pc(){

    }


   
    @Before("pc()")
    public void  before(JoinPoint jp){
        //获取目标方法名称
        String name = jp.getSignature().getName();
        System.out.println(name + "开始执行了。。。。");
    }


    @After("pc()")
    public  void  after(JoinPoint jp) {
        System.out.println(jp.getSignature().getName() + "方法结束了");
    }


    @AfterThrowing(value = "pc()", throwing = "e")
    public  void  throwing(JoinPoint jp,Exception e){
        System.out.println(jp.getSignature().getName() + " 方法抛出 " + e.getMessage() + " 异常");
    }



    
    @AfterReturning(value = "pc()",returning = "result")
    public  void  returning(JoinPoint jp , Object result){
        System.out.println(jp.getSignature().getName() + " 方法返回了 " + result);
    }




    @Around("pc()")
    public Object around(ProceedingJoinPoint pjp) {
        try {
           
            Object proceed = pjp.proceed();
            return proceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/998702.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号