AOP的简单介绍

为什么使用AOP,一个简单的回答这个问题的方法是显示一个横切关注点的实现而不使用AOP。 考虑一个简单的服务及其实现:

public interface InventoryService {public Inventory create(Inventory inventory);public List<inventory> list();public Inventory findByVin(String vin);public Inventory update(Inventory inventory);public boolean delete(Long id);public Inventory compositeUpdateService(String vin, String newMake);
}

及其默认实现:

public class DefaultInventoryService implements InventoryService{@Overridepublic Inventory create(Inventory inventory) {logger.info("Create Inventory called");inventory.setId(1L);return inventory; }@Overridepublic List<inventory> list(){return new ArrayList<inventory>();}@Overridepublic Inventory update(Inventory inventory) {return inventory;}@Overridepublic boolean delete(Long id) {logger.info("Delete Inventory called");return true;}
....

这只是一项服务。 假设此项目中还有更多服务。

因此,现在,如果需要记录每种服务方法所花费的时间,则没有AOP的选项将遵循以下内容。 为服务创建一个装饰器:

public class InventoryServiceDecorator implements InventoryService{private static Logger logger = LoggerFactory.getLogger(InventoryServiceDecorator.class);private InventoryService decorated;@Overridepublic Inventory create(Inventory inventory) {logger.info("before method: create");long start = System.nanoTime();Inventory inventoryCreated = decorated.create(inventory);long end = System.nanoTime();logger.info(String.format("%s took %d ns", "create", (end-start)) );return inventoryCreated;}

该修饰器实质上将代表修饰的对象拦截该调用,记录将方法委派给修饰对象时该方法调用所花费的时间。

想象一下对项目中的所有方法和所有服务执行此操作。 这是AOP解决的方案,它为交叉问题(服务方法的记录时间要求例如)提供了一种模块化的方式–单独包装而不会污染类的核心。

为了结束会话,实现装饰器的另一种方法是使用Java的动态代理功能:

public class AuditProxy implements java.lang.reflect.InvocationHandler {private static Logger logger = LoggerFactory.getLogger(AuditProxy.class);private Object obj;public static Object newInstance(Object obj) {return java.lang.reflect.Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new AuditProxy(obj));}private AuditProxy(Object obj) {this.obj = obj;}public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {Object result;try {logger.info("before method " + m.getName());long start = System.nanoTime();result = m.invoke(obj, args);long end = System.nanoTime();logger.info(String.format("%s took %d ns", m.getName(), (end-start)) );} catch (InvocationTargetException e) {throw e.getTargetException();} catch (Exception e) {throw new RuntimeException("unexpected invocation exception: " + e.getMessage());} finally {logger.info("after method " + m.getName());}return result;}
}

因此,现在,当创建InventoryService实例时,我将通过AuditProxy动态代理创建它:

InventoryService inventoryService = (InventoryService)AuditProxy.newInstance(new DefaultInventoryService());

重写的java.lang.reflect.InvocationHandler调用方法将拦截以这种方式创建的对InventoryService的所有调用,其中记录了审核方法调用时间的交叉问题。 这样,将跨领域关注点模块化到一个位置(AuditProxy),但是在实例化InventoryService时,仍然需要InventoryService的客户端明确地知道它。

现在,我将展示如何使用Spring AOP来实现跨领域的关注– Spring提供了多种实现Aspects的方式–基于XML配置,基于@AspectJ。 在此特定示例中,我将使用基于XML配置文件的方式定义方面

Spring AOP在Spring容器的上下文中工作,因此在上一个会话中定义的服务实现必须是Spring Bean,我使用@Service批注定义了它:

@Service
public class DefaultInventoryService implements InventoryService{
...
}

现在,我想记录我的DefaultInventoryService的每个方法调用所花费的时间–我首先将其模块化为“建议”:

package org.bk.inventory.aspect;import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class AuditAdvice {private static Logger logger = LoggerFactory.getLogger(AuditAdvice.class);public void beforeMethod() {logger.info("before method");}public void afterMethod() {logger.info("after method");}public Object aroundMethod(ProceedingJoinPoint joinpoint) {try {long start = System.nanoTime();Object result = joinpoint.proceed();long end = System.nanoTime();logger.info(String.format("%s took %d ns", joinpoint.getSignature(), (end - start)));return result;} catch (Throwable e) {throw new RuntimeException(e);}}}

预计该建议将捕获DefaultInventoryService中的方法所花费的时间。 因此,现在将此建议连接到DefaultInventoryService Spring bean:

<bean id="auditAspect" class="org.bk.inventory.aspect.AuditAdvice" /><aop:config><aop:aspect ref="auditAspect"><aop:pointcut id="serviceMethods" expression="execution(* org.bk.inventory.service.*.*(..))" /><aop:before pointcut-ref="serviceMethods" method="beforeMethod" />  <aop:around pointcut-ref="serviceMethods" method="aroundMethod" /><aop:after-returning pointcut-ref="serviceMethods" method="afterMethod" /> </aop:aspect></aop:config>

这是通过首先定义“切入点”(即在本例中为服务方法的位置)添加横切关注点(在本例中为捕获方法执行时间)添加的。 在这里,我使用切入点表达式进行了定义–

execution(* org.bk.inventory.service.*.*(..))

,这实际上是选择org.bk.inventory.service包中所有类型的所有方法。 定义切入点后,它使用表达式定义围绕切入点(建议)要做的事情:

<aop:around pointcut-ref="serviceMethods" method="aroundMethod" />

这基本上就是说,围绕任何服务类型的每个方法,执行前面定义的AspectAdvice的aroundMethod。 现在,如果执行了服务方法,我将看到在方法执行期间调用建议,以下是如果调用DefaultInventoryService,createInventory方法的示例输出:

org.bk.inventory.service.InventoryService - Create Inventory called
org.bk.inventory.aspect.AuditAdvice - Inventory org.bk.inventory.service.InventoryService.create(Inventory) took 82492 ns

Spring的AOP实现通过在运行时基于定义的切入点为所有目标bean生成动态代理来工作。

定义方面的另一种方法是使用@AspectJ注释-Spring本身理解:

package org.bk.inventory.aspect;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;@Aspect
public class AuditAspect {private static Logger logger = LoggerFactory.getLogger(AuditAspect.class);@Pointcut("execution(* org.bk.inventory.service.*.*(..))")public void serviceMethods(){//}@Before("serviceMethods()")public void beforeMethod() {logger.info("before method");}@Around("serviceMethods()")public Object aroundMethod(ProceedingJoinPoint joinpoint) {try {long start = System.nanoTime();Object result = joinpoint.proceed();long end = System.nanoTime();logger.info(String.format("%s took %d ns", joinpoint.getSignature(), (end - start)));return result;} catch (Throwable e) {throw new RuntimeException(e);}}@After("serviceMethods()")public void afterMethod() {logger.info("after method");}    
}

类上的@Aspect批注将其标识为方面定义。 首先定义切入点:

@Pointcut("execution(* org.bk.inventory.service.*.*(..))")public void serviceMethods(){}

上面的代码基本上标识了org.bk.inventory.service包中所有类型的所有方法,该切入点通过放置注释的方法的名称(在本例中为“ serviceMethods”)进行标识。 接下来,使用@Before(serviceMethods()),@ After(serviceMethods())和@Around(serviceMethods())注释定义建议,而需要发生的细节是带有这些注释的方法的主体。 如果此Aspect被定义为bean,则Spring AOP可以自然地理解@AspectJ批注:

<bean id="auditAspect" class="org.bk.inventory.aspect.AuditAspect" />

Spring将创建一个动态代理,以将建议应用于所有标识为切入点符号的目标Bean。

定义方面的另一种方法–这次使用本机AspectJ表示法。

package org.bk.inventory.aspect;import org.bk.inventory.types.Inventory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public aspect AuditAspect {private static Logger logger = LoggerFactory.getLogger(AuditAspect.class);pointcut serviceMethods() : execution(* org.bk.inventory.service.*.*(..));pointcut serviceMethodsWithInventoryAsParam(Inventory inventory) : execution(* org.bk.inventory.service.*.*(Inventory)) && args(inventory);before() : serviceMethods() {logger.info("before method");}Object around() : serviceMethods() {long start = System.nanoTime();Object result = proceed();long end = System.nanoTime();logger.info(String.format("%s took %d ns", thisJoinPointStaticPart.getSignature(),(end - start)));return result;}Object around(Inventory inventory) : serviceMethodsWithInventoryAsParam(inventory) {Object result = proceed(inventory);logger.info(String.format("WITH PARAM: %s", inventory.toString()));return result;}after() : serviceMethods() {logger.info("after method");}
}

这映射到先前定义的 @AspectJ表示法

由于这是专门用于定义方面的DSL,因此Java编译器无法理解。 AspectJ提供了一个工具(ajc)来编译这些本机的Aspectj文件并将其编织到目标切入点中。 Maven提供了一个在编译时无缝调用ajc的插件:

<plugin><groupId>org.codehaus.mojo</groupId><artifactId>aspectj-maven-plugin</artifactId><version>1.0</version><dependencies><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>${aspectj.version}</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjtools</artifactId><version>${aspectj.version}</version></dependency></dependencies><executions><execution><goals><goal>compile</goal><goal>test-compile</goal></goals></execution></executions><configuration><outxml>true</outxml><aspectLibraries><aspectLibrary><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId></aspectLibrary></aspectLibraries><source>1.6</source><target>1.6</target></configuration></plugin>

这将是AOP简介的总结,并提供一个示例,该示例将全面应用前几节中介绍的概念。

用例很简单,我将定义一个自定义批注PerfLog,我希望对使用此批注进行批注的方法的调用进行计时和记录。 让我首先定义注释:

package org.bk.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PerfLog {}

现在使用此注释来注释一些服务方法:

@Service
public class DefaultInventoryService implements InventoryService{private static Logger logger = LoggerFactory.getLogger(InventoryService.class);@Overridepublic Inventory create(Inventory inventory) {logger.info("Create Inventory called");inventory.setId(1L);return inventory; }@Overridepublic List<Inventory> list() {return new ArrayList<Inventory>();}@Override@PerfLogpublic Inventory update(Inventory inventory) {return inventory;}@Overridepublic boolean delete(Long id) {logger.info("Delete Inventory called");return true;}@Override@PerfLogpublic Inventory findByVin(String vin) {logger.info("find by vin called");return new Inventory("testmake", "testmodel","testtrim","testvin" );}@Override@PerfLogpublic Inventory compositeUpdateService(String vin, String newMake) {logger.info("composite Update Service called");Inventory inventory = findByVin(vin);inventory.setMake(newMake);update(inventory);return inventory;}
}

在这里,已使用@PerfLog批注对DefaultInventoryService的三种方法进行了批注– update,findByVin,compositeUpdateService,它们在内部调用方法findByVin和update。

现在,对于Aspect,它将拦截对所有使用@PerfLog注释的方法的调用,并记录该方法调用所花费的时间:

package org.bk.inventory.aspect;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;@Aspect
public class AuditAspect {private static Logger logger = LoggerFactory.getLogger(AuditAspect.class);@Pointcut("execution(@org.bk.annotations.PerfLog * *.*(..))")public void performanceTargets(){}@Around("performanceTargets()")public Object logPerformanceStats(ProceedingJoinPoint joinpoint) {try {long start = System.nanoTime();Object result = joinpoint.proceed();long end = System.nanoTime();logger.info(String.format("%s took %d ns", joinpoint.getSignature(), (end - start)));return result;} catch (Throwable e) {throw new RuntimeException(e);}}
}

这里的切入点表达–

@Pointcut("execution(@org.bk.annotations.PerfLog * *.*(..))")

选择所有使用@PerfLog注释注释的方法,并且方面方法logPerformanceStats记录方法调用所花费的时间。

要对此进行测试:

package org.bk.inventory;import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;import org.bk.inventory.service.InventoryService;
import org.bk.inventory.types.Inventory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/testApplicationContextAOP.xml")
public class AuditAspectTest {@Autowired InventoryService inventoryService;@Testpublic void testInventoryService() {Inventory inventory = this.inventoryService.create(new Inventory("testmake", "testmodel","testtrim","testvin" ));assertThat(inventory.getId(), is(1L));assertThat(this.inventoryService.delete(1L), is(true));assertThat(this.inventoryService.compositeUpdateService("vin","newmake").getMake(),is("newmake"));}}

调用此测试时,输出如下:

2011-09-08 20:54:03,521 org.bk.inventory.service.InventoryService - Create Inventory called
2011-09-08 20:54:03,536 org.bk.inventory.service.InventoryService - Delete Inventory called
2011-09-08 20:54:03,536 org.bk.inventory.service.InventoryService - composite Update Service called
2011-09-08 20:54:03,536 org.bk.inventory.service.InventoryService - find by vin called
2011-09-08 20:54:03,536 org.bk.inventory.aspect.AuditAspect - Inventory org.bk.inventory.service.DefaultInventoryService.findByVin(String) took 64893 ns
2011-09-08 20:54:03,536 org.bk.inventory.aspect.AuditAspect - Inventory org.bk.inventory.service.DefaultInventoryService.update(Inventory) took 1833 ns
2011-09-08 20:54:03,536 org.bk.inventory.aspect.AuditAspect - Inventory org.bk.inventory.service.DefaultInventoryService.compositeUpdateService(String, String) took 1371171 ns

正确调用了findByVin,update和CompositeUpdateService的建议。

该示例可在以下位置获得:git://github.com/bijukunjummen/AOP-Samples.git

参考: all和其他博客中的JCG合作伙伴 Biju Kunjummen 对AOP的简单介绍 。


翻译自: https://www.javacodegeeks.com/2012/06/simple-introduction-to-aop.html

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

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

相关文章

java实现表达式求值_如何编写一个高效的Java表达式求值程序

虽然&#xff0c;这个题目是有一点夺人眼球&#xff0c;但我真实这么做了(关是以否信任基准测试效果&#xff0c;这是其他一个话题)。所以&#xff0c;上周我一贯在找一个小型、适用的竞赛争辩数学表达式的类库。有功夫我在stackoverflow上看到了一个帖子&#xff0c;里面举荐的…

两张表的笛卡尔积用sql语句

第一个表的行数乘以第二个表的行数等于笛卡尔积结果集的大小SELECT * FROM table1 CROSS JOIN table2转载于:https://www.cnblogs.com/henuyuxiang/p/6811717.html

[dpdk] 读官方文档(3)

续前节&#xff0c; 测试小程序 1. 想编译测试程序首先需要设置两个环境变量&#xff0c;为什么呢&#xff0c;因为测试程序的Makefile里用了。。。 rpm装了打包好的devel包&#xff0c;这个rpm也会自带这两个环境变量。就是说写第三方程序的时候&#xff0c;习惯上&#xff0c…

比较OpenDDR和WURFL

量身定制的Web内容通常受益于定制&#xff0c;以考虑多种因素&#xff0c;例如屏幕大小&#xff0c;标记语言支持和图像格式支持。 此类信息存储在“设备描述存储库”&#xff08;DDR&#xff09;中。 WURFL和OpenDDR项目都提供了访问DDR的API&#xff0c;以简化并促进适应其交…

逻辑表达式——黑纸白纸

一、问题描述 有A、B、C、D、E五个人&#xff0c;每人额头上都贴了一张黑色或白色的纸条。五人对坐&#xff0c;每人都可以看到其他人额头上的纸的颜色&#xff0c;但都不知道自己额头上的纸的颜色。五人互相观察后&#xff0c; A说&#xff1a;“我看见有三个人额头上贴的是白…

java 1.6u29 下载_jdk1.6 64位下载|JDK 6(Java SE Development Kit)下载6u43 64位官方版_java运行环境 IT猫扑网...

jdk1.6 64位适用于x64的系统安装的java运行环境&#xff0c;Java SE Development Kit6是java开发人员必备的产品&#xff0c;也叫做jdk6&#xff0c;欢迎下载使用。官方介绍适用于您的计算机(windows)的 Java 软件&#xff0c;即 Java Runtime Environment&#xff0c;也称为 J…

ZK 6中的MVVM初探

MVVM与MVC 在上一篇文章中&#xff0c;我们已经看到Ajax框架ZK如何采用CSS选择器启发的Controller来在View中连接UI组件并监听它们的事件。 在此ZK MVC模式下&#xff0c; View中的UI组件无需绑定到任何Controller方法或数据对象。 使用选择器模式作为将View状态和事件映射到Co…

消失循环的2023?你都做了什么? | 2023 年度总结

2023年度总结 -- 今年都做了什么事&#xff1f; 前言心态关键词感悟 记录申请软著独立游戏技术成长 共勉 前言 又到了一年一次年度总结的时候了。我们常常感叹时间飞逝&#xff0c;却又没办法让它放慢的脚步。那就将2023写下来&#xff0c;让它在时间的长河中留下一丝记忆。 心…

由于SSH配置文件的不匹配,导致的Permission denied (publickey)及其解决方法

读者如要转载&#xff0c;请标明出处和作者名&#xff0c;谢谢。 地址01&#xff1a;http://space.itpub.net/25851087 地址02&#xff1a;http://www.cnblogs.com/zjrodger/ 作者名&#xff1a;zjrodger 【问题发生环境和相关参数】(1)OS&#xff1a;Win7 32Bit. (2)Git&#…

java语言基本语法_Java语言基本语法

Java语言基本语法一、标识符和关键字标识符在java语言中&#xff0c;用来标志类名、对象名、变量名、方法名、类型名、数组名、包名的有效字符序列&#xff0c;称为“标识符”&#xff1b;标识符由字母、数字、下划线、美元符号组成&#xff0c;且第一个字符不能是数字&#xf…

Maven的鸟瞰图

我们每天要做的一件事是使用Maven通过发出诸如mvn install之类的构建命令来构建我们的项目。 然后&#xff0c;Maven查看我们项目的配置文件&#xff08;亲切地称为POM&#xff09;&#xff0c;神奇地找出要执行的操作&#xff0c;并且&#xff0c;嘿&#xff0c;您的构建已完成…

node源码详解(五)

本作品采用知识共享署名 4.0 国际许可协议进行许可。转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource5 本博客同步在https://cnodejs.org/topic/56ed6735b705742136388fa6 本博客同步在http://www.cnblogs.com/papertree/p/5295344.html 在上一篇博客&#xff…

分层图+最短路算法 BZOJ 2763: [JLOI2011]飞行路线

2763: [JLOI2011]飞行路线 Time Limit: 10 Sec Memory Limit: 128 MBDescription Alice和Bob现在要乘飞机旅行&#xff0c;他们选择了一家相对便宜的航空公司。该航空公司一共在n个城市设有业务&#xff0c;设这些城市分别标记为0到n-1&#xff0c;一共有m种航线&#xff0c;每…

java web 保护_java web项目请求控制及简单漏洞防范

背景&#xff1a;当时项目没用什么框架&#xff0c;过滤器&#xff0c;请求限制等都需要自己手写。1、请求加时间戳在后台过滤器中可以加判断&#xff0c;如果请求时间戳与服务器时间相差太大&#xff0c;可以返回异常&#xff0c;具体情况可以具体使用。请求中加时间戳的示例如…

Maven最佳实践

尽管Maven提供了“配置之上的约定”解决方案&#xff0c;但是仍然有足够多的必要配置引起严重的头痛。 在这篇文章中&#xff0c;我将与您分享一些最佳实践&#xff0c;以简化对POM文件的维护。 请勿使用已弃用的引用&#xff0c;例如$ {artifactId}或$ {pom.artifactId}。 使用…

51Nod - 1381 硬币游戏

51Nod - 1381 硬币游戏 有一个简单但是很有趣的游戏。在这个游戏中有一个硬币还有一张桌子&#xff0c;这张桌子上有很多平行线&#xff08;如下图所示&#xff09;。两条相邻平行线之间的距离是1&#xff0c;硬币的半径是R&#xff0c;然后我们来抛硬币到桌子上&#xff0c;抛…

Android中Activity和Fragment之间的通信

Android中Activity和Fragment之间的通信 Fragment启动Activity传数据到Fragment 举例&#xff1a;城市选择列表。一个Fragment启动Activity&#xff0c;Activity再把城市选择数据回传到Fragment中。Fragment中方法iv_city.setOnClickListener(new View.OnClickListener() {Ove…

NoSQLUnit 0.3.0发布

介绍 单元测试是一种验证应用程序中可测试的最小部分的方法。 单元测试必须遵循FIRST规则&#xff1b; 这些是快速&#xff0c;隔离&#xff0c;可重复&#xff0c;自我验证和及时的。 考虑到没有持久层&#xff08;典型的关系数据库或新的NoSQL数据库&#xff09;的JEE应用程…

proftpd java_Proftpd:编译安装

下载 proftpd# wget ftp://ftp.proftpd.org/distrib/source/proftpd-1.3.5a.tar.gz# wget https://github.com/proftpd/proftpd/archive/v1.3.5a.tar.gz# yum -y install gcc openssl-devel# ./configure --prefix/usr/local/proftpd/ \--sysconfdir/usr/local/proftpd/ \--ena…

javascript 相关小的知识点集合

本文主要是列出一些javascript 相关的&#xff0c;不限于javascript的&#xff0c;容易记错或者遗忘的小知识&#xff0c;小技巧。 1、javascript中的false 在 JavaScript&#xff0c;常见的 false 值&#xff1a; 0, 0, 0, -0, false, ,null,undefined,NaN 要注意空数组([])和…