好文:https://blog.csdn.net/javazejian/article/details/56267036

通过一个实例来理解

1.  需求:实现算术计算器,可以加减乘除,同时记录日志

     

2. 实现方式:

  ① 高度耦合(直接pass)

  ② 自己实现动态代理

  ③ 利用Spring AOP框架

二. 自己实现动态代理

1. 定义接口及实现类:

  -- 接口:ArithmeticCalculator  

public interface ArithmeticCalculator {

    int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j); }

  -- 接口的实现类

public class ArithmeticCalculatorImpl implements ArithmeticCalculator{

    public int add(int i, int j) {
int result = i + j;
System.out.println("[add] " + i + " + " + j + " = " + result);
return result;
} public int sub(int i, int j) {
int result = i - j;
System.out.println("[sub] " + i + " - " + j + " = " + result);
return result;
} public int mul(int i, int j) {
int result = i * j;
System.out.println("[mul] " + i + " * " + j + " = " + result);
return result;
} public int div(int i, int j) {
int result = i / j;
System.out.println("[div] " + i + " / " + j + " = " + result);
return result;
}
}

  -- 返回动态代理类

  关键代码已经标红,利用JDK的Proxy类,加入参数,返回代理类

  try-catch-finally分别对应四种通知

public class ArithmeticCalculatorLoggingProxy {

    private ArithmeticCalculator target;

    public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
this.target = target;
} public ArithmeticCalculator getLoggingProxy() {
ArithmeticCalculator proxy = null; //代理对象由哪一个类加载器加载
ClassLoader loader = target.getClass().getClassLoader(); //代理对象的类型
Class[] interfaces = new Class[] {ArithmeticCalculator.class}; //调用代理对象的目标方法,并执行的代理方法
InvocationHandler h = new InvocationHandler() { //proxy: 一般不用proxy中的方法,容易死循环
//method: 目标类中的方法
//args: 目标类方法的参数
public Object invoke(Object proxy, Method method, Object[] args) {
Object result = null;
try {
System.out.println("这是前置通知...");
result = method.invoke(target, args);
System.out.println("这是返回通知,方法正常执行时执行...");
} catch(Exception e) {
System.out.println("这是异常通知,方法异常时执行...");
e.printStackTrace();
} finally {
System.out.println("这是后置通知,不论是否异常,都会执行");
}
return result;
} }; proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h); return proxy;
}
}

  

  -- 调用

public class App
{
public static void main( String[] args )
{
ArithmeticCalculator target = new ArithmeticCalculatorImpl();
ArithmeticCalculator proxy = new ArithmeticCalculatorLoggingProxy(target).getLoggingProxy();
proxy.add(1, 3);
System.out.println();
proxy.div(4, 2);
}
}

  -- 结果(后置通知的执行顺序好像和spring aop不太一样)

    

三  通过Spring AOP + AspectJ注解方式

  -- 配置文件(利用context和aop命名空间)

    <!-- 配置bean自动扫描 -->
<context:component-scan base-package="com.atguigu.spring_1.aop"></context:component-scan> <!-- 配置aspectj起作用 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

  

  -- 接口,实现类同前,需要注意,实现类要加到spring容器中

  

  

  -- 日志切面类

   需要注意,用@Component 加入到Spring IOC容器中, 用 @Aspect 让AspectJ自动扫描

@Component
@Aspect
public class LogginAspect { /**
* 定义一个方法,用于声明切入点表达式,一般的,方法中不需要其他代码
*/
@Pointcut("execution(* com.atguigu.spring_1.aop.ArithmeticCalculator.*(..))")
public void declareJointPointExpression() {}; /**
* 前置通知
*/
@Before("declareJointPointExpression()")
public void beforeMethod(JoinPoint joinPoint) {
// JoinPoint:链接点可以访问到方法的具体信息
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("前置通知: method " + methodName + " begin with arguments:" + args +"");
} /**
* 后置通知: 不论是否有异常,都会如期执行
* 但是无法访问到方法的返回值
*/
@After("declareJointPointExpression()")
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("后置通知: method " + methodName + " end");
} /**
* 返回通知:只有正常执行时,才可以执行
* 能够访问到方法的返回值
*/
@AfterReturning(value="declareJointPointExpression()",
returning="result")
public void afterReturningMethod(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("返回通知: method " + methodName + " end with result: " + result +"");
} /**
* 异常通知:抛出异常时执行
*/
@AfterThrowing(value="declareJointPointExpression()",
throwing="ex")
public void afterThrowingMethod(JoinPoint joinPoint, Exception ex) {
String methodName = joinPoint.getSignature().getName();
System.out.println("异常通知: method " + methodName + " throw an exception " + ex +"");
}
}

  -- 调用

public class App
{
public static void main( String[] args )
{
ApplicationContext ctx = new ClassPathXmlApplicationContext("aop.xml");
ArithmeticCalculator target = ctx.getBean(ArithmeticCalculator.class);
target.add(1, 3);
System.out.println();
target.div(4, 2);
}
}

  -- 结果

    

四)四种通知的执行顺序

  没有异常:前置通知->目标方法->后置通知->返回通知

  有异常: 前置通知->目标方法->后置通知->异常通知

五)后置通知和返回通知的区别

  -- 后置通知(@After)不能访问到目标方法的结果,而返回通知(@AfterReturning)则可以

六)切面等基本概念的理解

  

  

Spring听课笔记(tg)AOP的更多相关文章

  1. Spring学习笔记之aop动态代理(3)

    Spring学习笔记之aop动态代理(3) 1.0 静态代理模式的缺点: 1.在该系统中有多少的dao就的写多少的proxy,麻烦 2.如果目标接口有方法的改动,则proxy也需要改动. Person ...

  2. Spring学习笔记4——AOP

    AOP 即 Aspect Oriented Program 面向切面编程 首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能. 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务 ...

  3. Spring听课笔记(tg)

    0. 地址:https://www.bilibili.com/video/av21335209 1.综述,Spring主要的复习要点集中在以下几点 -- Spring的整体结构,Maven依赖(环境搭 ...

  4. Spring听课笔记(tg)2

    配置Bean -- 配置形式:基于XML 文件的方式, 基于注解的方式 -- Bean的配置方式:通过全类名(反射).通过工厂方法(静态工厂方法&实例工厂方法).FactoryBean -- ...

  5. [Spring学习笔记 4 ] AOP 概念原理以及java动态代理

    一.Spring IoC容器补充(1) Spring IoC容器,DI(依赖注入): 注入的方式:设值方法注入setter(属性注入)/构造子注入(构造函数传入依赖的对象)/字段注入Field(注解) ...

  6. Spring学习笔记2—AOP

    1.AOP概念 AOP(Aspect Oriented Programming):面向切面编程,AOP能够将那些与业务无关,却为业务模块所共同调用的应用(例如事务处理.日志管理.权限控制等)封装起来, ...

  7. Spring听课笔记(专题一)

    Spring入门课程:https://www.imooc.com/learn/196 第0章: Spring是为解决企业应用程序开发复杂性而创建的一个Java开源框架,应用非常广泛.业内非常流行的SS ...

  8. Spring学习笔记之AOP配置篇(一)

    [TOC] 1. 创建并声明一个切面 首先,创建一个类,添加@Component注解使其添加到IoC容器 然后,添加@Aspect注解,使其成为一个切面 最后,在配置文件里面,使用<aop:as ...

  9. Spring听课笔记(专题二下)

    第4章 Spring Bean基于注解的装配 4.1 Bean的定义及作用域的注解实现 1. Bean定义的注解 -- @Component是一个通用注解,可用于任何bean -- @Reposito ...

随机推荐

  1. Screaming Frog SEO Spider页面分析工具使用方法

    一.  下载地址:https://www.screamingfrog.co.uk/seo-spider/ 二.  使用教程 链接1: https://blog.csdn.net/a055350/art ...

  2. [leetcode712]204. Count Primes寻找范围内的素数

    厄拉多塞筛选法,就是哈希表记录素数的倍数 public int countPrimes(int n) { /* 牛逼哄哄的厄拉多塞筛选法 就是从2开始,每找到一个素数,就把n以内的这个数的倍数排除 记 ...

  3. javascript中如何截取字符串?

    JavaScript中截取字符串有三种方法,分别是substring(),substr(),split(). 方法1:使用substring() substring()方法用于提取字符串中介于两个指定 ...

  4. IDEA本地运行Hadoop程序配置环境变量

    1.首先到github上下载hadoop-common-2.2.0-bin-master 2.解压放到自定义目录下 再将hadoop.dll文件复制到windows/System32目录下 3.配置环 ...

  5. IT 界那些朗朗上口的“名言

    中国有很多古代警世名言,朗朗上口,凝聚了很多故事与哲理.硅谷的互联网公司里头也有一些这样的名言,凝聚了很多公司价值观和做事的方法,对于很多程序员来说,其影响潜移默化.这里收集了一些,如下. Stay ...

  6. python 文章集合

    Python profiling tools 我常用的 Python 调试工具

  7. Detectron2 快速开始,使用 WebCam 测试

    本文将引导快速使用 Detectron2 ,介绍用摄像头测试实时目标检测. Detectron2: https://github.com/facebookresearch/detectron2 环境准 ...

  8. Go 的定时任务模块 Cron 使用

    前言 新项目是Golang作为开发语言, 遇到了些新的坑, 也学到了新的知识, 收获颇丰 本章介绍在Go中使用Cron定时任务模块来实现逻辑 正文 在项目中, 我们往往需要定时执行一些逻辑, 举个例子 ...

  9. 【Java基础】基本语法-变量与运算符

    基本语法-变量与运算符 关键字和保留字 关键字定义:被 Java 语言赋予了特殊含义,用做专门用途的字符串(单词). 关键字特点:关键字中所有字母都为小写. 用于定义数据类型:class.interf ...

  10. MySQL学习Day01

    1.MySQL的层级关系 2.xampp的安装使用 如果之前安装过mysql那么就需要将原来的mysql完全卸载干净 1.卸载之前安装的MySQL 安装xampp需要先卸载之前的mysql,以及更改m ...