5.4 Spring AOP

2019独角兽企业重金招聘Python工程师标准>>> hot3.png


5.4.1  从代理机制初探AOP

        来看一个简单的例子,当需要在执行某些方法时留下日志信息,可能会这样写:

import java.util.logging.*;
public class HelloSpeaker{
pirvate Logger logger=Logger.getLogger(this.getClass().getName());public void hello(String name){logger.log(Level.INFO, "hello method starts…");// 方法开始执行时留下日志Sytem.out.println("hello, "+name); // 程序的主要功能Logger.log(Level.INFO, "hello method ends…");// 方法执行完毕时留下日志}
}

        在HelloSpeaker类中,当执行hello()方法时,程序员希望该方法执行开始与执行完毕时都留下日志。最简单的做法是用上面的程序设计,在方法执行的前后加上日志动作。


        可以使用代理(Proxy)机制来解决这个问题,有两种代理方式:静态代理(static proxy)动态代理(dynamic proxy)

        在静态代理的实现中,代理类与被代理的类必须实现同一个接口。在代理类中可以实现记录等相关服务,并在需要的时候再呼叫被代理类。这样被代理类就可以仅仅保留业务相关的职责了。

        举个简单的例子,首先定义一个IHello接口,IHello.java代码如下:

public interface IHello{public void hello(String name);
}

        然后让实现业务逻辑的HelloSpeaker类实现IHello接口,HelloSpeaker.java代码如下:

public class HelloSpeaker implements IHello{public void hello(String name){System.out.println("hello,"+name);}
}

        可以看到,在HelloSpeaker类中没有任何日志的代码插入其中,日志服务的实现将被放到代理类中,代理类同样要实现IHello接口

HelloProxy.java代码如下:

public class HelloProxy implements IHello{private Logger logger=Logger.getLogger(this.getClass().getName());private IHello helloObject;public HelloProxy(IHello helloObject){this.helloObject=helloObject;}public void hello(String name){log("hello method starts…");// 日志服务helloObject.hello(name);// 执行业务逻辑log("hello method ends…");// 日志服务}private void log(String msg){logger.log(Level.INFO,msg);}
}

        在HelloProxy类的hello()方法中,真正实现业务逻辑前后安排记录服务,可以实际撰写一个测试程序来看看如何使用代理类。

public class ProxyDemo{public static void main(String[] args){IHello proxy=new HelloProxy(new HelloSpeaker());proxy.hello("Justin");}
}

        程序运行结果:

hello,Justin

5.4.2  动态代理

        要实现动态代理,同样需要定义所要代理的接口。

IHello.java代码如下:

public interface IHello{public void hello(String name);
}

        然后让实现业务逻辑的HelloSpeaker类实现IHello接口。

HelloSpeaker.java代码如下:

public class HelloSpeaker implements IHello{public void hello(String name){System.out.println("Hello,"+name);}
}

        与上例不同的是,这里要实现不同的代理类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LogHandler implements InvocationHandler{private Object sub;public LogHandler() {}public LogHandler(Object obj){sub = obj;}public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{System.out.println("before you do thing");method.invoke(sub, args);System.out.println("after you do thing");return null;}
}

        写一个测试程序,使用LogHandler来绑定被代理类

ProxyDemo.java代码如下:

import java.lang.reflect.Proxy;
public class ProxyDemo {public static void main(String[] args) {HelloSpeaker helloSpeaker=new HelloSpeaker();LogHandler logHandler=new LogHandler(helloSpeaker);Class cls=helloSpeaker.getClass();IHello iHello=(IHello)Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),logHandler);iHello.hello("Justin");}
}

        程序运行结果:

before you do thing
Hello, Justin
after you do thing


5.4.3  AOP术语与概念

1.cross-cutting concerns

        在DynamicProxyDemo例子中,记录的动作原先被横切(Cross-cutting)到HelloSpeaker本身所负责的业务流程中。类似于日志这类的动作,如安全检查、事务等服务,在一个应用程序中常被安排到各个类的处理流程之中。这些动作在AOP的术语中称为cross-cutting concerns。如图5.7所示,原来的业务流程是很单纯的。 

 

153307_Uc7K_220508.png


图5.7  原来的业务流程

        为了加入日志与安全检查等服务,类的程序代码中被硬生生地写入了相关的Logging、Security程序片段,如图5.8所示。 

 153308_0Xp8_220508.png

图5.8  加入各种服务的业务流程

2.Aspect

        将散落在各个业务类中的cross-cutting concerns收集起来,设计各个独立可重用的类,这种类称为Aspect。例如,在动态代理中将日志的动作设计为LogHandler类,LogHandler类在AOP术语中就是Aspect的一个具体实例。在需要该服务的时候,缝合到应用程序中;不需要服务的时候,也可以马上从应用程序中脱离。应用程序中的可重用组件不用做任何的修改。例如,在动态代理中的HelloSpeaker所代表的角色就是应用程序中可重用的组件,在它需要日志服务时并不用修改本身的程序代码。


5.4.4  通知Advice

        Spring提供了5种通知(Advice)类型:Interception Around、Before、After Returning、Throw 和Introduction。它们分别在以下情况被调用:

  • Interception Around Advice:在目标对象的方法执行前后被调用。

  • Before Advice:在目标对象的方法执行前被调用。

  • After Returning Advice:在目标对象的方法执行后被调用。

  • Throw Advice:在目标对象的方法抛出异常时被调用。

  • Introduction Advice:一种特殊类型的拦截通知,只有在目标对象的方法调用完毕后执行

创建一个Before Advice的Web项目,步骤如下。

① 创建一个Web项目,命名为“Spring_Advices”。

② 编写Java类。

        Before Advice会在目标对象的方法执行之前被呼叫。这个接口提供了获取目标方法、参数及目标对象。

MethodBeforeAdvice接口的代码如下:

import java.lang.ref.*;
import java.lang.reflect.Method;
public interface MethodBeforeAdvice{void before(Method method, Object[] args, Object target) throws Exception;
}

        用实例来示范如何使用Before Advice。首先要定义目标对象必须实现的接口IHello。

IHello.java代码如下:

public interface IHello{public void hello(String name);
}

        接着定义一个HelloSpeaker,实现IHello接口。

HelloSpeaker.java代码如下:

public class HelloSpeaker implements IHello{public void hello(String name){System.out.println("Hello,"+name);}
}

        在对HelloSpeader不进行任何修改的情况下,想要在hello()方法执行之前可以记录一些信息。有一个组件,但没有源代码,可对它增加一些日志的服务。

LogBeforeAdvice.java代码如下:

import java.lang.reflect.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.aop.MethodBeforeAdvice;
public class LogBeforeAdvice implements MethodBeforeAdvice{private Logger logger=Logger.getLogger(this.getClass().getName());public void before(Method method,Object[] args,Object target) throws Exception{logger.log(Level.INFO, "method starts…"+method);}
}

③ 添加Spring开发能力。

applicationContext.xml的代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"><bean id="logBeforeAdvice" class="LogBeforeAdvice" /><bean id="helloSpeaker" class="HelloSpeaker" /><bean id="helloProxy"class="org.springframework.aop.framework.ProxyFactoryBean"><property name="proxyInterfaces"><value>IHello</value></property><property name="target"><ref bean="helloSpeaker" /></property><property name="interceptorNames"><list><value>logBeforeAdvice</value></list></property></bean>
</beans>

④ 运行程序,测试结果。

写一个程序测试一下Before Advice的运作。

SpringAOPDemo.java代码如下:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class SpringAOPDemo{public static void main(String[] args){ApplicationContext context=new FileSystemXmlApplicationContext("/WebRoot/WEB-INF/classes/applicationContext.xml");IHello helloProxy=(IHello)context.getBean("helloProxy");helloProxy.hello("Justin");}
}

程序运行结果:

Hello,Justin


HelloSpeaker与LogBeforeAdvice是两个独立的类。对于HelloSpeaker来说,它不用知道LogBeforeAdvice的存在;而LogBeforeAdvice也可以运行到其他类之上。HelloSpeaker与LogBeforeAdvice都可以重复使用。


5.4.5  切入点Pointcut

创建一个切入点Pointcut项目,步骤如下。

① 创建一个Web项目,命名为“Spring_Pointcut”。

② 编写Java类。

IHello.java代码如下:

public interface IHello{public void helloNewbie(String name);public void helloMaster(String name);
}

HelloSpeaker类实现IHello接口。HelloSpeaker.java代码如下:

public class HelloSpeaker implements IHello{public void helloNewbie(String name){System.out.println("Hello, "+name+"newbie! ");}public void helloMaster(String name){System.out.println("Hello, "+name+"master! ");}
}

③ 添加Spring开发能力。

applicationContext.xml的代码。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"><bean id="logBeforeAdvice" class="LogBeforeAdvice" /><bean id="helloAdvisor"class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"><property name="mappedName"><value>hello*</value></property><property name="advice"><ref bean="logBeforeAdvice" /></property></bean><bean id="helloSpeaker" class="HelloSpeaker" /><bean id="helloProxy"class="org.springframework.aop.framework.ProxyFactoryBean"><property name="proxyInterfaces"><value>IHello</value></property><property name="target"><ref bean="helloSpeaker" /></property><property name="interceptorNames"><list><value>helloAdvisor</value></list></property></bean>
</beans>



④ 运行程序,测试结果。

SpringAOPDemo.java代码如下:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class SpringAOPDemo {public static void main(String[] args) {ApplicationContext context = new FileSystemXmlApplicationContext("/WebRoot/WEB-INF/classes/applicationContext.xml");IHello helloProxy = (IHello) context.getBean("helloProxy");helloProxy.helloNewbie("Justin");helloProxy.helloMaster("Tom");}
}

程序运行结果:

Hello, Justinnewbie! 
Hello, Tommaster!


附:目录《JavaEE基础实用教程》笔记说明

转载于:https://my.oschina.net/jerrypan/blog/631893

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/285508.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WPF 实现心电图曲线绘制

本文经原作者授权以原创方式二次分享&#xff0c;欢迎转载、分享。原文作者&#xff1a;流浪g原文地址&#xff1a;https://www.cnblogs.com/cong2312/p/16411637.html一、前言项目中之前涉及到胎儿心率图曲线的绘制&#xff0c;最近项目中还需要添加心电曲线和血样曲线的绘制功…

C语言试题110之利用条件运算符的嵌套来完成此题:学习成绩大于等于90分的同学用 A 表示,60到89 分之间的用 B 表示, 60 分以下的用 C 表示。

✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 1、题目 题目:C语言试题110之利用条件运算符的嵌套来完成此题:学习成绩大于等于90分的同学用 A 表…

如何让 Dapper 支持 DateOnly 类型

前言在上次的文章中&#xff0c;我们让 EF Core 6 支持了 DateOnly 类型。那么&#xff0c;Dapper 是否支持 DateOnly 类型呢&#xff1f;public class User {public int Id { get; set; }public string Name { get; set; }public DateOnly Birthday { get; set; } }using (var…

VB常用内部函数大全一览表(建议收藏)

VB提供了大量的内部函数供用户在编程时调用。内部函数按其功能分为数学运算函数、字符串函数、转换函数、日期与时间函数、判断函数和格式输出函数等。 文章目录 算术函数字符串函数日期和时间函数数据类型转换函数算术函数 字符串函数 日期和时间函数

数据库分类介绍

在当今的互联网中&#xff0c;最常见的数据库模型主要是两种&#xff0c;即“关系型数据库”和“非关系型数据库”。 一、关系型数据库 1、关系型数据库的由来 虽然网状数据库和层次数据库已经很好的解决了数据的集中和共享问题&#xff0c;但是在数据库独立性和抽象级别上扔有…

记一次 .NET 某物管后台服务 卡死分析

一&#xff1a;背景 1. 讲故事这几个月经常被朋友问&#xff0c;为什么不更新这个系列了&#xff0c;哈哈&#xff0c;确实停了好久&#xff0c;主要还是打基础去了&#xff0c;分析 dump 的能力不在于会灵活使用 windbg&#xff0c;而是对底层知识有一个深厚的理解&#xff0c…

C语言试题111之 s=a+aa+aaa+aaaa+aa...a 的值,其中 a 是一个数字。例如 2+22+222+2222+22222(此时 共有 5 个数相加),几个数相加有键盘控制。

✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 1、题目 题目: s=a+aa+aaa+aaaa+aa…a 的值,其中 a 是一个数字。例如 2+22+222+2222+22222(此时 共…

MathType插入带序号公式的两种方法

方法一&#xff1a; 由于我之前使用表格15% 70% 15%来布局的&#xff0c;所以最开始相的就是如何录入公示后插入公式序号&#xff0c;如下图所示 先设置序号格式 录好公式后点“Insert Number”就好了&#xff0c;这样的话需要紧挨着公式&#xff0c;用空格把他空到最右侧就好了…

【系统设计】邻近服务

在本文中&#xff0c;我们将设计一个邻近服务&#xff0c;用来发现用户附近的地方&#xff0c;比如餐馆&#xff0c;酒店&#xff0c;商场等。设计要求 从一个小明去面试的故事开始。面试官&#xff1a;你好&#xff0c;我想考察一下你的设计能力&#xff0c;如果让你设计一个…

[转]Redis持久化存储(AOF与RDB两种模式)

Redis中数据存储模式有2种&#xff1a;cache-only,persistence; cache-only即只做为“缓存”服务&#xff0c;不持久数据&#xff0c;数据在服务终止后将消失&#xff0c;此模式下也将不存在“数据恢复”的手段&#xff0c;是一种安全性低/效率高/容易扩展的方式&#xff1b;pe…

C语言试题112之一个数如果恰好等于它的因子之和,这个数就称为“完数”。例如 6=1+2+3.编程 找出 1000 以内的所有完数。

✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 1、题目 题目:一个数如果恰好等于它的因子之和,这个数就称为“完数”。例如 6=1+2+3.编程 找出 …

关于jstl.jar引用问题及解决方法

在前文SSM说到因为从MyEclipse换成了Eclipse。有些架包自动缺失。 造成&#xff1a;"org.apache.jasper.JasperException: This absolute uri (http://java.sun.com/jsp/jstl/core ) cannot be resolved in either web.xml or the jar files deployed with this applicati…

网络技术基础与计算思维实验教程_2.3_单交换机VLAN配置实验

2.3.1 实验内容 2.3.2实验目的 实验的目的一是验证交换机 VLAN 配置过程; 二是验证属于同一 VLAN的终端之间的通信过程; 三是验证每一个 VLAN 为独立的广播域; 四是验证属于不同 VLAN的两个终端之间不能通信; 五是验证转发项和 VLAN的对应关系。 2.3.3实验原理 默认情况下,交换…

dotnet-exec 0.5.0 released

dotnet-exec 0.5.0 releasedIntrodotnet-exec 是一个 C# 程序的小工具&#xff0c;可以用来运行一些简单的 C# 程序而无需创建项目文件&#xff0c;而且可以自定义项目的入口方法&#xff0c;支持但不限于 Main 方法Install/Updatedotnet-exec 是一个 dotnet tool&#xff0c;可…

C语言试题113之一球从 100 米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在 第 10 次落地时,共经过多少米?第 10 次反弹多高?

📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 ✅作者简介:大家好,我是码莎拉蒂,CSDN博客专家(全站排名Top 50),阿里云博客专家、51CTO博客专家、华为云享专家 1、题目 题目:一球从 100 米高度自由落下,…

超酷的 Vim 搜索技巧

尽管目前我们已经涉及[1] Vim 的多种特性&#xff0c;但此编辑器的特性集如此庞大&#xff0c;不管我们学习多少&#xff0c;似乎仍然远远不足。承接我们的 Vim 教程系列&#xff0c;本文我们将讨论 Vim 提供的多种搜索技术。 不过在此之前&#xff0c;请注意文中涉及到的所有…

对面的00后萌新看过来:浅析计算机编程在高等职业GIS专业中的重要性

文章目录什么是传说中的GIS&#xff1f;GIS必修哪些课程&#xff1f;学GIS到底何去何从&#xff1f;什么是计算机编程&#xff1f;编程在GIS中的地位如何&#xff1f;高等职业GIS如何教学&#xff1f;专科生怎样学好GIS&#xff1f;什么是传说中的GIS&#xff1f; GIS是“3S”之…

SQLServer Agent执行[分发清除: distribution] 无法删除快照文件

由于之前创建的发布订阅造成严重的性能压力&#xff0c;症状表现为发布订阅表查询产生CMEMTHREAD suspend等待&#xff0c;由于开发配置每隔十分钟会产生大量的SQLCOMMAND&#xff08;create table&#xff0c;create index大量的命令&#xff09;发布订阅 复制监视器 有Memor…

C语言试题114之猴子吃桃问题

📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 ✅作者简介:大家好,我是码莎拉蒂,CSDN博客专家(全站排名Top 50),阿里云博客专家、51CTO博客专家、华为云享专家 1、题目 题目:猴子吃桃问题:猴子第一天摘…

.NET 7 的 JWT 配置太方便了!

微软宣布 .NET 7 preview5 有一些较大的改进&#xff0c; 包括 JWT 身份验证的简化和自动配置。我安装了 preview 5 尝试了新的 JWT 身份配置。如果您想把现有的项目更新到 .Net 7 preview 5, 下面是一个快速更新的命令。Update all Microsoft.AspNetCore.* package references…