好文: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. 如何快速学会git

    相信大多数入门者都对git的原理比较恍惚,今天我们来告诉大家如何快速学会git命令. 1.git init 这个命令会在当前目录里创建一个.git目录,也就是初始化本地仓库.git. 如图先创建文件夹 ...

  2. 云计算之4---Cockpit

    cockpit是一个简单可用的监控工具,你可以添加多个主机进行监控,上限是20台 .也可以使用cockpit来管理虚拟机/容器,也可以安装其他组件开启更多功能. 注意:cockpit没有告警功能,不适 ...

  3. 第七章节 BJROBOT 选择区域自主构建地图【ROS全开源阿克曼转向智能网联无人驾驶车】

    1.把小车平放在地板上,用资料里的虚拟机,打开一个终端 ssh 过去主控端启动roslaunch znjrobot bringup.launch 2.在虚拟机端再打开一个终端,ssh 过去主控端启动r ...

  4. git基础-git别名

    Git 并不会在你输入部分命令时自动推断出你想要的命令. 如果不想每次都输入完整的 Git 命令,可以通过 git config 文件来轻松地为每一个命令设置一个别名. 这里有一些例子你可以试试: $ ...

  5. [开源软件] 腾讯云Linux服务器一键安装LAMP/LNMP/LANMP环境 转

    本帖最后由 我本戏子 于 2015-8-13 22:00 编辑OneinStack是非常优秀的一键PHP/JAVA安装脚本,提供以下环境:lnmp(Linux + Nginx+ MySQL+ PHP) ...

  6. python3 处理列表嵌套字典去重

    def list_dict(dictlist): def function(date): return date['ip'] dictlist_new = [] for list_dict in di ...

  7. Label_table

    <table border(边框) = "" width = height = align = bordercolor(边框色) = cellspacing 表格边框与单元格 ...

  8. Dubbo 就是靠它崭露头角!(身为开源框架很重要的一点)

    Hola,我是 yes. 经过了 RPC 核心和 Dubbo 微内核两篇文章后,今天终于要稍稍深入一波 Dubbo 了. 作为一个通用的 RPC 框架,性能是很重要的一环,而易用性和扩展性也极为重要. ...

  9. 有哪些适合个人开发的微信小程序

    微信小程序提供了一个简单.高效的应用开发框架和丰富的组件及API,帮助开发者在微信中开发具有原生 APP 体验的服务. 微信小程序支持采用云开发模式,无需后台服务,十分的方便快捷,适合个人开发一些工具 ...

  10. PHP 爬取图片 保存本地

    public function getImage($url,$filename='') { if($url == ''){ return false; } if($filename == ''){ $ ...