- 浏览: 17534 次
- 性别:
- 来自: 南京
文章分类
最新评论
基于AspectJ的AOP编程
AspectJ的切点函数非常精妙,基本上可以覆盖我们编程中可以遇到的所有连接点,因为Spring仅支持方法级别的切点,所以Spring中的切点函数是AspectJ中的一个子集。掌握切点函数即掌握了AspectJ语法的基础。
首先是在切点函数中需要使用的通配符的概念:
*:匹配任意字符
..:匹配任意字符,表示类的时候必须和*联合使用,表示入参时可以单独使用
+:表示匹配目标类以及目标类的子类
execution(),within()可以使用全部通配符。
args(),target(),this()仅能使用+通配符,不过使用和不使用的效果是一样的。
其他的切点函数不能使用通配符。
切点函数的分类:
在AspectJ中我们使用注解来定义增强类型(即增强逻辑的织入位置)
在使用AspectJ配置文件的时候,需要在位置文件中加入
proxy-target-class默认是false,表示采用JDK动态代理生成目标类,如果设为true,则使用CGLib代理。
********这两者有很大区别:JDK动态代理仅能生成目标类的接口类型,而CGLib则生成目标类的代理类型。这对于切点函数的意义重大,这点一定需要牢记!!
切点函数详解:
1.@annotation()
表示当方法标注类设定的注解,则该方法匹配次切点
输出:
NaiveWaiter.greet to John
NaughtyWaiter: greet to Tom
needTestFun() executed
2.execution()
表示执行方法匹配切点函数,这个切点函数是最常用的。
2.1 通过方法签名定义切点
execution(public * *(..)):匹配public方法。
execution(* *To(..)):匹配所有以To结尾的方法。
2.2 通过类定义切点
execution(com.firethewhole.maventest08.Waiter.*(..)):匹配Waiter接口的所有方法,所以NaughtyWaiter和NaiveWaiter中greetTo和serveTo方法都会被织入增强。
execution(com.firethewhole.maventest08.Waiter+.*(..)):不光匹配Waiter接口中定义的增强,还匹配NaiveWaiter的smile方法和NaughtyWaiter中的joke方法。
2.3 通过类包定义切点
execution(* com.firethewhole.maventest08.*(..)):匹配com.firethewhole.maventest08包下面所有类的所有方法。
execution(* com.firethewhole.maventest08..*(..)):不光包括上面的方法,还包括类这个包里面的子孙包中的类的所有方法。
execution(* com.firethewhole.maventest08..*.*Dao.find*(..)):匹配com.firethewhole.maventest08这个包,包括子孙包中以Dao结尾的类中,以find开头的方法。
2.4 通过方法入参定义切点
execution(* joke(String,int)):匹配joke方法,且第一个参数类型必须是String,第二个参数类型必须是int,有且只有这两个参数,比如NaughtyWaiter中的joke方法。
execution(* joke(String,*)):第二个参数可以是任意类型,但是必须是两个参数。
execution(* joke(String,..)):第一个参数必须是String类型,可以只有一个参数,也可以有多个参数。
execution(* joke(Object+)):匹配Object类型或者Object的子类,即所有的引用类型变量,是否包含基本类型,需要测试才知道。execution(* joke(Object))则仅匹配Object,其他入参类型不匹配。
3.args()
表示入参类型匹配,注意这里是实际入参类型动态匹配,所以也包括类子类。
execution(com.firethewhole.maventest08.Waiter):如果入参是Waiter或者NavieWaiter或者NaughtyWaiter,则匹配该切点。
4.@args()
如果入参标注类该切点,则匹配该切点。
这里涉及到类3个概念,切点函数所标识的注解类型,入参类型,运行时实际入参类型类型。
需要记住:标注注解的类型如果在继承关系上是入参类型的子类,则该类型及其子类都匹配切点,如果标注注解的类型在继承关系上是入参类型法父类,则都不匹配。
5.within()
属于execution()切点函数的子集,连接点仅能指定到类级别。
6.@within()
匹配标注类指定注解的类,包括该指定注解类的子类。
7.@target()
仅匹配标注类指定注解的类,不包括该标注注解类的子类!
8.target()
匹配切点函数所指定的类型及其子类的所有方法
9.this()
如果该代理对象按类型匹配目标类,则匹配该切点。这个函数和target是有区别的,在使用引介增强是,为NaiveWaiter加入SmartSeller的增强后,this(com.firethewhole.maventest08.Seller)将匹配NaiveWaiter类。
输出:
NaiveWaiter.greet to John
this Test() executed
NaiveWaiter.serve to John
this Test() executed
SmartSeller: sell Beer to John
*****这里和书上不一样,书上最后一个sell方法也被织入增强,但是实际上是没有。
切点的复合运算
使用&& || !或者and or not组合各个切点函数
命名切点
直接在AspectJ类中使用的切点函数是匿名切点,我们可以在一个专门的类中,将所有切点函数都命名然后使用。
增强织入顺序
不同的切面类靠实现Order接口来决定,相同的切面类中的增强是按照定义的顺序织入的。
访问连接点信息
输出:
-----joinPointAccess-----
arg[0]:John
signature:class com.firethewhole.maventest08.NaiveWaiter
NaiveWaiter.greet to John
-----JoinPointAccess-----
绑定连接点方法入参
这个例子一定要用CGLib代理
输出:
-----bindJoinPointParams-----
name:John
num:2
-----bindJoinPointParams-----
NaiveWaiter.smile to John 2 times
绑定代理对象
输出:
-----bindProxyObj-----
com.firethewhole.maventest08.NaiveWaiter$$EnhancerBySpringCGLIB$$6f6d5c3b
-----bindProxyObj-----
NaiveWaiter.greet to John
可以看到实际生成的代理对象的类型。
绑定类注解对象
在使用@target或者@args时指定类注解类型,可以在增强逻辑中使用。
绑定返回值
输出:
SmartSeller: sell Beer to John
-----bindReturnValue-----
returnValue:100
-----bindReturnValue-----
绑定抛出异常
AspectJ的切点函数非常精妙,基本上可以覆盖我们编程中可以遇到的所有连接点,因为Spring仅支持方法级别的切点,所以Spring中的切点函数是AspectJ中的一个子集。掌握切点函数即掌握了AspectJ语法的基础。
首先是在切点函数中需要使用的通配符的概念:
*:匹配任意字符
..:匹配任意字符,表示类的时候必须和*联合使用,表示入参时可以单独使用
+:表示匹配目标类以及目标类的子类
execution(),within()可以使用全部通配符。
args(),target(),this()仅能使用+通配符,不过使用和不使用的效果是一样的。
其他的切点函数不能使用通配符。
切点函数的分类:
- 方法切点函数
- 方法入参切点函数
- 目标类切点函数
- 代理类起点函数
在AspectJ中我们使用注解来定义增强类型(即增强逻辑的织入位置)
- @Before:前置增强
- @AfterReturning:后置增强
- @Around:环绕增强
- AfterThrowing:抛出异常增强
- After:不管是否抛出异常,都会执行,相当于Final增强
- DeclareParents:引介增强
在使用AspectJ配置文件的时候,需要在位置文件中加入
<aop:aspectj-autoproxy proxy-target-class="true"/>
proxy-target-class默认是false,表示采用JDK动态代理生成目标类,如果设为true,则使用CGLib代理。
********这两者有很大区别:JDK动态代理仅能生成目标类的接口类型,而CGLib则生成目标类的代理类型。这对于切点函数的意义重大,这点一定需要牢记!!
切点函数详解:
1.@annotation()
表示当方法标注类设定的注解,则该方法匹配次切点
//标注了NeedTes注解的方法,在返回时将被注入以下增强 @Aspect public class TestAspect { @AfterReturning("@annotation(com.firethewhole.maventest08.aspectj.anno.NeedTest)") public void needTestFun() { System.out.println("needTestFun() executed"); } }
// 目标类 public class NaughtyWaiter implements Waiter { @NeedTest public void greetTo(String clientName) { System.out.println("NaughtyWaiter: greet to " + clientName); } }
<aop:aspectj-autoproxy proxy-target-class="true"/> <bean id="naiveWaiter" class="com.firethewhole.maventest08.NaiveWaiter"/> <bean id="naughtWaiter" class="com.firethewhole.maventest08.NaughtyWaiter"/> <bean class="com.firethewhole.maventest08.aspectj.fun.TestAspect"/>
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/firethewhole/maventest08/aspectj/example/beans.xml"); NaiveWaiter naiveWaiter = (NaiveWaiter) ctx.getBean("naiveWaiter"); NaughtyWaiter naughtWaiter = (NaughtyWaiter) ctx.getBean("naughtWaiter"); naiveWaiter.greetTo("John"); naughtWaiter.greetTo("Tom");
输出:
NaiveWaiter.greet to John
NaughtyWaiter: greet to Tom
needTestFun() executed
2.execution()
表示执行方法匹配切点函数,这个切点函数是最常用的。
2.1 通过方法签名定义切点
execution(public * *(..)):匹配public方法。
execution(* *To(..)):匹配所有以To结尾的方法。
2.2 通过类定义切点
execution(com.firethewhole.maventest08.Waiter.*(..)):匹配Waiter接口的所有方法,所以NaughtyWaiter和NaiveWaiter中greetTo和serveTo方法都会被织入增强。
execution(com.firethewhole.maventest08.Waiter+.*(..)):不光匹配Waiter接口中定义的增强,还匹配NaiveWaiter的smile方法和NaughtyWaiter中的joke方法。
2.3 通过类包定义切点
execution(* com.firethewhole.maventest08.*(..)):匹配com.firethewhole.maventest08包下面所有类的所有方法。
execution(* com.firethewhole.maventest08..*(..)):不光包括上面的方法,还包括类这个包里面的子孙包中的类的所有方法。
execution(* com.firethewhole.maventest08..*.*Dao.find*(..)):匹配com.firethewhole.maventest08这个包,包括子孙包中以Dao结尾的类中,以find开头的方法。
2.4 通过方法入参定义切点
execution(* joke(String,int)):匹配joke方法,且第一个参数类型必须是String,第二个参数类型必须是int,有且只有这两个参数,比如NaughtyWaiter中的joke方法。
execution(* joke(String,*)):第二个参数可以是任意类型,但是必须是两个参数。
execution(* joke(String,..)):第一个参数必须是String类型,可以只有一个参数,也可以有多个参数。
execution(* joke(Object+)):匹配Object类型或者Object的子类,即所有的引用类型变量,是否包含基本类型,需要测试才知道。execution(* joke(Object))则仅匹配Object,其他入参类型不匹配。
3.args()
表示入参类型匹配,注意这里是实际入参类型动态匹配,所以也包括类子类。
execution(com.firethewhole.maventest08.Waiter):如果入参是Waiter或者NavieWaiter或者NaughtyWaiter,则匹配该切点。
4.@args()
如果入参标注类该切点,则匹配该切点。
这里涉及到类3个概念,切点函数所标识的注解类型,入参类型,运行时实际入参类型类型。
需要记住:标注注解的类型如果在继承关系上是入参类型的子类,则该类型及其子类都匹配切点,如果标注注解的类型在继承关系上是入参类型法父类,则都不匹配。
5.within()
属于execution()切点函数的子集,连接点仅能指定到类级别。
6.@within()
匹配标注类指定注解的类,包括该指定注解类的子类。
7.@target()
仅匹配标注类指定注解的类,不包括该标注注解类的子类!
8.target()
匹配切点函数所指定的类型及其子类的所有方法
9.this()
如果该代理对象按类型匹配目标类,则匹配该切点。这个函数和target是有区别的,在使用引介增强是,为NaiveWaiter加入SmartSeller的增强后,this(com.firethewhole.maventest08.Seller)将匹配NaiveWaiter类。
// 引介增强,会NaiveWaiter织入Seller增强,实现类为SmartSeller @Aspect public class EnableSellerAspect { @DeclareParents(value="com.firethewhole.maventest08.NaiveWaiter", defaultImpl=SmartSeller.class) public Seller seller; }
// 代理增强切点 @Aspect public class TestAspect { @AfterReturning("this(com.firethewhole.maventest08.Seller)") public void thisTest() { System.out.println("this Test() executed"); } }
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/firethewhole/maventest08/aspectj/example/beans.xml"); Waiter naiveWaiter = (Waiter) ctx.getBean("naiveWaiter"); naiveWaiter.greetTo("John"); naiveWaiter.serveTo("John"); ((Seller)naiveWaiter).sell("Beer", "John");
输出:
NaiveWaiter.greet to John
this Test() executed
NaiveWaiter.serve to John
this Test() executed
SmartSeller: sell Beer to John
*****这里和书上不一样,书上最后一个sell方法也被织入增强,但是实际上是没有。
切点的复合运算
使用&& || !或者and or not组合各个切点函数
@Aspect public class TestAspect { @After("within(com.firethewhole.maventest08.*) && execution(* greetTo(..))") public void greetToFun() { System.out.println("--greetToFun() executed!--"); } @Before("!target(com.firethewhole.maventest08.NaiveWaiter) && execution(* serveTo(..))") public void notServeInNaiveWaiter() { System.out.println("--notServeInWaiveWaiter() executed!--"); } @AfterReturning("target(com.firethewhole.maventest08.Waiter) || target(com.firethewhole.maventest08.Seller)") public void waiterOrSeller() { System.out.println("--waiterOrSeller() executed!--"); } }
命名切点
直接在AspectJ类中使用的切点函数是匿名切点,我们可以在一个专门的类中,将所有切点函数都命名然后使用。
// 注意这里的访问修饰符,private表示仅能在该类中使用,和正常的修饰符是一样的 public class TestNamePointcut { @Pointcut("within(com.firethewhole.maventest08.*)") private void inPackage() { } @Pointcut("execution(* greetTo(..))") protected void greetTo() { } @Pointcut("inPackage() and greetTo()") public void inPkgGreetTo() { } }
@Aspect public class TestAspect { @Before("com.firethewhole.maventest08.aspectj.advanced.TestNamePointcut.inPkgGreetTo()") public void pkgGreetTo() { System.out.println("--pkgGreetTo() executed!--"); } @Before("!target(com.firethewhole.maventest08.NaiveWaiter) && com.firethewhole.maventest08.aspectj.advanced.TestNamePointcut.inPkgGreetTo()") public void pkgGreetToNoNaiveWaiter() { System.out.println("--pkgGreetToNoNaiveWaiter() executed!--"); } }
增强织入顺序
不同的切面类靠实现Order接口来决定,相同的切面类中的增强是按照定义的顺序织入的。
访问连接点信息
// @Around增强需要使用ProceedingJoinPoint,其他的增强类型使用JoinPoint @Aspect public class TestAspect { @Around("execution(* greetTo(..)) && target(com.firethewhole.maventest08.NaiveWaiter)") public void joinPointAccess(ProceedingJoinPoint pjp) throws Throwable { System.out.println("-----joinPointAccess-----"); System.out.println("arg[0]:" + pjp.getArgs()[0]); System.out.println("signature:" + pjp.getTarget().getClass()); pjp.proceed(); System.out.println("-----JoinPointAccess-----"); } }
输出:
-----joinPointAccess-----
arg[0]:John
signature:class com.firethewhole.maventest08.NaiveWaiter
NaiveWaiter.greet to John
-----JoinPointAccess-----
绑定连接点方法入参
这个例子一定要用CGLib代理
// args()会根据参数中的name,num自动去增强逻辑bindJoinPointParams中查找实际的类型信息 @Aspect public class TestAspect { @Before("target(com.firethewhole.maventest08.NaiveWaiter) && args(name,num,..)") public void bindJoinPointParams(int num, String name) { System.out.println("-----bindJoinPointParams-----"); System.out.println("name:" + name); System.out.println("num:" + num); System.out.println("-----bindJoinPointParams-----"); } }
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/firethewhole/maventest08/aspectj/example/beans.xml"); NaiveWaiter naiveWaiter = (NaiveWaiter) ctx.getBean("naiveWaiter"); naiveWaiter.smile("John", 2);
输出:
-----bindJoinPointParams-----
name:John
num:2
-----bindJoinPointParams-----
NaiveWaiter.smile to John 2 times
绑定代理对象
@Aspect public class TestAspect { @Before("this(waiter)") public void bindProxyObj(Waiter waiter) { System.out.println("-----bindProxyObj-----"); System.out.println(waiter.getClass().getName()); System.out.println("-----bindProxyObj-----"); } }
输出:
-----bindProxyObj-----
com.firethewhole.maventest08.NaiveWaiter$$EnhancerBySpringCGLIB$$6f6d5c3b
-----bindProxyObj-----
NaiveWaiter.greet to John
可以看到实际生成的代理对象的类型。
绑定类注解对象
在使用@target或者@args时指定类注解类型,可以在增强逻辑中使用。
绑定返回值
// 在各种增强类型中,如果有返回值相关的信息可以指定 @Aspect public class TestAspect { @AfterReturning(value="target(com.firethewhole.maventest08.SmartSeller)",returning="retVal") public void bindReturnValue(int retVal) { System.out.println("-----bindReturnValue-----"); System.out.println("returnValue:" + retVal); System.out.println("-----bindReturnValue-----"); } }
输出:
SmartSeller: sell Beer to John
-----bindReturnValue-----
returnValue:100
-----bindReturnValue-----
绑定抛出异常
@Aspect public class TestAspect { @AfterThrowing(value="target(com.firethewhole.maventest08.SmartSeller)", throwing="iae") public void bindException(IllegalArgumentException iae) { System.out.println("-----bindException-----"); System.out.println("exception:" + iae.getMessage()); System.out.println("-----bindException-----"); } }
- maventest08.zip (33.1 KB)
- 下载次数: 0
发表评论
-
Spring基础:数据访问(3)
2017-01-15 09:29 441在开源世界里,有很多ORM框架使用,比如Hibernate,还 ... -
Spring基础:数据访问(2)
2016-12-31 10:55 535上一篇主要将了Spring JDB ... -
Spring基础:数据访问(1)
2016-12-27 08:22 385Spring JDBC通过模板和回调机制大大降低了使用JDBC ... -
Spring基础:AOP编程(5)
2016-11-30 07:35 382基于Schema的AOP编程 基于AspectJ的AOP编程已 ... -
Spring基础:AOP编程(3)
2016-11-19 10:44 383基于切面的AOP编程 通过Advice,可以创建方法前,后, ... -
Spring基础:AOP编程(2)
2016-11-15 23:40 413基于ProxyFactory的AOP编程 Spring只支持 ... -
Spring基础:AOP编程(1)
2016-11-14 01:08 421Java编程中的代理 Spring以IoC为基础,发展了另外 ... -
Spring基础:IoC容器(2)
2016-11-12 10:00 451容器注入类型 最常见的注入类型是字面值注入,像String和 ... -
Spring基础:IoC容器(1)
2016-11-10 08:15 411在IoC容器中装配Bean 4.1.2.RELEASE版本的 ... -
Spring基础:稍显复杂的Spring Hello World
2016-11-01 00:59 390本文参考《Spring 3.x企业应用开发》这本书完成,作为自 ... -
使用Eclipse创建基于Maven Web工程
2016-06-06 19:19 451第一步:创建一个maven-webapp类型的工程 完 ...
相关推荐
这篇博客“Spring基础:AOP编程(1)”可能介绍了AOP的基础知识及其在Spring中的实现。 面向切面编程(AOP)是一种编程范式,旨在减少代码的重复性,提高模块间的解耦度。AOP通过将横切关注点(如日志、事务管理、...
在本篇关于“Spring基础:AOP编程(2)”的文章中,我们将深入探讨Spring框架中的面向切面编程(Aspect-Oriented Programming, AOP),这是一种强大的设计模式,它允许我们分离关注点,尤其是那些横切关注点,如日志、...
在本篇博文中,我们将深入探讨Spring框架中的一个核心特性——面向切...通过以上介绍,我们对Spring中的AOP编程有了基本了解。在实际开发中,AOP可以帮助我们更好地组织代码,降低复杂度,提高代码的可维护性和可读性。
**Spring AOP编程详解** 在Java开发中,Spring框架因其强大的功能和易用性而备受推崇,其中AOP(Aspect-Oriented Programming,面向切面编程)是其核心特性之一。AOP允许开发者将关注点从核心业务逻辑中分离出来,...
Spring AOP是在Spring框架的基础上实现的一种面向方面编程机制。 1. **方面(Aspect)**:这是AOP的核心概念之一,指代一个关注点的模块化,该关注点可能会横切多个对象。例如事务管理就是一个典型的横切关注点,...
Spring AOP,全称Aspect Oriented Programming(面向切面编程),是Spring框架的重要组成部分,它扩展了传统的面向对象编程(OOP),使得开发者能够更好地处理系统中的横切关注点,如日志、事务管理、权限控制等。...
在讨论Spring AOP(面向切面编程)时,首先需要理解几个核心概念。Spring AOP 是Spring框架提供的一个功能模块,它允许开发者将横切关注点(cross-cutting concerns)从业务逻辑中解耦出来,通过在方法调用前后进行...
根据提供的文件内容,可以提取出以下知识点: ...文档中提到的实践示例,例如前置通知、后置通知、返回通知、异常通知和环绕通知的具体编码实现,都是通过具体的代码示例来说明如何在Spring中应用AspectJ进行AOP编程。
Spring Boot AOP(面向切面编程)是一种强大的设计模式,它允许我们在不修改现有代码的情况下,插入额外的功能或监控代码。在Spring框架中,AOP主要用于日志记录、事务管理、性能统计等场景。本示例是关于如何在...
Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架的重要组成部分,它扩展了传统的面向对象编程,使得开发者可以方便地实现横切关注点,如日志、事务管理、性能监控等。在Spring中,AOP通过代理...
其中,面向切面编程(Aspect Oriented Programming,简称AOP)是Spring的重要特性之一,它极大地简化了代码中的横切关注点,如日志、事务管理等。本文将深入Spring源码,探索AOP的实现原理。 首先,我们需要理解AOP...
为了在Spring中进行AOP编程,我们需要一些特定的JAR包。以下是对这些关键组件的详细说明: 1. **Spring核心包**: - `spring-core.jar`: 这是Spring框架的基础,包含了IoC(Inversion of Control,控制反转)容器...
Spring AOP,全称Aspect-Oriented Programming(面向切面编程),是Spring框架的一个重要特性,主要用于解决程序中的横切关注点,如日志、事务管理、安全性等。AOP通过将这些关注点与核心业务逻辑分离,实现了代码的...
面向切面编程(AOP)是一种编程范式,它旨在减少代码中的重复部分,特别是那些与核心业务逻辑无关但又必须处理...同时,通过分析和操作`springAOP`压缩包中的示例代码,可以更直观地了解AOP在Spring框架中的具体实现。
在Spring框架中,AOP(面向切面编程)是一种强大的设计模式,它允许开发者将关注点从核心业务逻辑中分离出来,例如日志记录、事务管理等。本教程将通过模拟Spring AOP来阐述如何实现一个简单的切面编程。我们将讨论...
Spring 的核心特性包括依赖注入(Dependency Injection, DI)和面向切面编程(Aspect-Oriented Programming, AOP),这些特性有助于提高代码的可维护性和可扩展性。 #### 二、面向切面编程 (AOP) 原理与实践 ##### ...
在IT领域,Spring框架是一个广泛使用的Java应用框架,它提供了许多功能,包括依赖注入、面向切面编程(AOP)等。"spring-aop-jar"这个主题涉及到Spring框架中的核心组件之一——Spring AOP。这里我们将深入探讨...
为了说明Spring的AOP原理,本人使用代理模式中的动态代理完成演示AOP编程的原理的演示。相信,如果你耐心看完整个程序(几乎一行注释一行代码),那么你对Spring这个东西就不是觉得有什么神秘了! 阅读对象:凡是喜爱...
Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架的重要组成部分,它提供了一种在不修改源代码的情况下,对程序进行功能增强的技术。AOP的核心概念是切面(Aspect)、通知(Advice)、连接点...