切面,是面向对象编程中一个重要的术语,切面能够模块化系统当中横切关注点,这些横切关注点会横跨系统当中的多个组件,例如安全,事务,日志,缓存,如果在每个组件中都去自行完成这些关注点的代码编写,那么横切关注点的代码将会散步在系统的各个角落,从系统灵活性和维护性来说,造成了巨大的阻碍。所以将横切关注点进行模块化,形成切面,将核心的业务与切面进行分离,能够更加有限的对程序进行管理,使得开发人员只关注于核心业务的编写,并从横切关注点的逻辑中脱离出来。
AOP术语:
切面:系统当中的交叉业务,一般指安全,事务,日志,缓存这些横切关注点,切面是对这些横切关注点的模块化。
通知:横切关注点的具体实现,是切面具体实现的代码程序。
连接点:在系统当中能够插入切面的地点。
切入点:应用哪些切面到哪些连接点上。
目标对象:对哪些对象应用切面。
代理对象:将切面应用在目标对象上产生的对象。
织入:将切面应用在目标对象上产生代理对象的过程。织入发生的时期有三种:编译器,类加载期,运行期,编译期和类加载期要用特殊的编译器和特殊的类加载器,AspectJ可以完成,而spring AOP采用运行期的方式完成织入。
引入:向目标对象添加新的属性和方法。
目前AOP三足鼎立框架:
AspectJ |
JBOSS AOP |
Spring AOP |
其中AspectJ功能非常强大,spring AOP在此基础上借鉴了许多。
Spring对AOP的支持:
基于代理的经典AOP |
@AspectJ注解驱动的切面 |
纯POJO切面 |
注入式AspectJ切面 |
spring AOP只适用于方法的拦截,如果要考虑构造器或者属性的拦截,则应该考虑AspectJ的使用。
spring对AspectJ的指示器支持:
Aspect指示器 | 描述 |
arg() | 限制连接点匹配参数为指定类型的执行方法 |
@args() | 限制连接点匹配参数由指定注解标注的执行方法 |
execution() | 用于匹配连接点的执行方法 |
this() | 限制连接点匹配AOP代理的bean引用为指定类型的类 |
target() | 限制连接点匹配目标对象为指定类型的类 |
@target() | 限制连接点匹配特定的执行对象,这些对象对应的类要具备指定类型的注解 |
within() | 限制连接点匹配指定的类型 |
@within() | 限制连接点匹配指定注解所标注的类型(当使用spring AOP时,方法定义在由指定注解所标注的类里) |
@annotation | 限制匹配带有指定注解连接点 |
如果在spring中尝试使用AspectJ以外的指示器,将会抛出IllegalArgumentException异常。其中execution指示器是最常使用的,用于执行匹配,再配合其他的限制匹配指示器,从而定义切入点。
execution(* org.robbie..service..*.*(..))
上述指示器代表,匹配org.robbie包下任意子包(不一定是儿子包)任意类的任意方法(该方法不管参数,也不管返回类型),其中第一个星号是返回类型,第二个星号是类名,第三个星号是方法名,最后括号中的点代表方法参数。
配合其他指示器共同定义切入点:
execution(* org.robbie..service..*.*(..) and within(org.robbie.test.*))
代表既要满足前一个的执行匹配,又要满足在org.robbie.test下的包,才执行代理
使用bean()指示器,bean指示器是spring2.5后引入的:
execution(* org.robbie..service..Instrument.play() and bean(flute))
该例中展示的是匹配指定包下的Instrument类的play方法,并且该bean的名称为flute
在XML中声明切面(定义AOP):
传统的spring配置切面,需要使用proxyFactoryBean,但是配置起来十分的复杂,spring后来引入了命名空间的配置,用于简化切面的声明:
AOP配置元素 | 描述 |
<aop:advisor> | 定义AOP通知器 |
<aop:after> | 定义AOP后置通知(不管被通知的方法是否执行成功) |
<aop:after-returning> | 定义AOP 返回之后通知 |
<aop:after-throwing> | 定义AOP 抛出异常之后通知 |
<aop:around> | 定义AOP环绕通知 |
<aop:aspect> | 定义切面 |
<aop:aspectj-autoproxy> | 使用@AspectJ注解驱动的切面 |
<aop:before> | 定义AOP前置通知 |
<aop:config> | 顶层的AOP配置元素,大多数的<aop:*>元素必须包含在<aop:config>元素内 |
<aop:declare-parents> | 为被通知的对象引入额外的接口,并透明的实现 |
<aop:pointcut> | 定义切入点 |
目标对象:
package org.robbie.test.spring.beans; import java.util.Properties; public class Band { private Properties instruments; public Properties getInstruments() { return instruments; } public void setInstruments(Properties instruments) { this.instruments = instruments; } public void play(){ System.out.println("I am playing !!!"); } }
通知:
package org.robbie.test.spring; import org.aspectj.lang.ProceedingJoinPoint; public class MyAdvisor{ public void before(){ System.out.println("before"); } public void after(){ System.out.println("after"); } public void afterThrowing(){ System.out.println("afterThrowing"); } public void afterReturning(){ System.out.println("afterReturning"); } public void round(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("round before"); joinPoint.proceed(); System.out.println("round after"); } }
配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="myAdvisor" class="org.robbie.test.spring.MyAdvisor"></bean> <bean id="band" class="org.robbie.test.spring.beans.Band"></bean> <aop:config> <aop:aspect ref="myAdvisor"> <aop:pointcut expression="execution(* *..*.*(..))" id="performance"/> <aop:before method="before" pointcut-ref="performance"/> <aop:after method="after" pointcut-ref="performance"/> <aop:after-returning method="afterReturning" pointcut-ref="performance"/> <aop:after-throwing method="afterThrowing" pointcut-ref="performance"/> <aop:around method="round" pointcut-ref="performance"/> </aop:aspect> </aop:config> </beans>
关于通知调用的顺序(无异常),前置通知--->环绕通知的前置操作--->调用目标对象的方法--->后置通知--->方法返回通知--->环绕通知的后置操作
异常情况下:前置通知--->环绕通知的前置操作--->调用目标对象方法--->后置通知--->抛出通知
为通知传递参数:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="myAdvisor" class="org.robbie.test.spring.beans.MyAdvisor"></bean> <bean id="band" class="org.robbie.test.spring.beans.Band"></bean> <aop:config> <aop:aspect ref="myAdvisor"> <aop:pointcut expression="execution(* *..*.*(String)) and args(params)" id="performance"/> <aop:before method="before" pointcut-ref="performance" arg-names="params"/> </aop:aspect> </aop:config> </beans>
前置通知写法:
public void before(String params){ System.out.println("before " + params); }
目标方法:
public void play(String params){ System.out.println("I am playing " + params); }
这样配置之后目标方法的参数将会传递到前置通知里
为目标对象引入新功能:
因为JAVA不是动态语言,在没有修改类定义的情况下,是不允许新增方法和属性的(动态语言可以实现,例如ruby),一旦编译完成,就很难添加新的类和方法了,但是使用spring aop的引入功能可以实现,spring 引入的核心理念是代理对象既代理了目标对象,实现了目标对象的方法,又实现了新的接口,当调用新的接口方法时,代理对象把调用委托给了实现新接口的实现类,由新的实现类进行调用,实际上通过引入方式产生的代理是多个对象的结合体,既有目标对象,也有新的接口实现类对象。
定义新接口:
package org.robbie.test.spring.inf; public interface NewInterface { void newMethod(); }
定义新接口的实现类:
package org.robbie.test.spring.beans; import org.robbie.test.spring.inf.NewInterface; public class NewImpl implements NewInterface{ @Override public void newMethod() { System.out.println("new impl"); } }
配置:
<aop:config> <aop:aspect ref="myAdvisor"> <aop:declare-parents types-matching="*" implement-interface="org.robbie.test.spring.inf.NewInterface" default-impl="org.robbie.test.spring.beans.NewImpl"/> </aop:aspect> </aop:config>
declare-parent元素中types-matching指定要需要进行引入的类,implement-interface指定需要引入的类将要实现的新接口(添加新方法),default-impl指定新接口的实现类。当引入完成后,新完成的类既具有原来的方法同时也具有新接口的方法。default-impl也可以改写成delegate-ref,detegate-ref是引用的spring的bean
基于注解的切面:
package org.robbie.test.spring.beans; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; 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.springframework.stereotype.Component; @Aspect @Component public class MyAdvisor{ @Pointcut("execution(* *..*.*(..))") public void performance(){ } @Before("performance()") public void before(){ System.out.println("before "); } @After("performance()") public void after(){ System.out.println("after "); } @AfterThrowing("performance()") public void afterThrowing(){ System.out.println("afterThrowing"); } @AfterReturning("performance()") public void afterReturning(){ System.out.println("afterReturning"); } @Around("performance()") public void round(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("round before"); joinPoint.proceed(); System.out.println("round after"); } }
增加配置:
<aop:aspectj-autoproxy />
基于注解的切面传递参数:
package org.robbie.test.spring.beans; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Aspect @Component public class MyAdvisor{ @Pointcut("execution(* *..*.*.*(String)) && args(params)") public void performance(String params){ } @Before("performance(params)") public void before(String params){ System.out.println("before " + params); } }
基于注解的引入:
使用@DeclareParents:
package org.robbie.test.spring.beans; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.DeclareParents; import org.robbie.test.spring.inf.NewInterface; import org.springframework.stereotype.Component; @Aspect @Component public class MyAdvisor{ @DeclareParents(value="*", defaultImpl = NewImpl.class) private NewInterface newInterface; }
value属性为所要匹配的类,注解标注在一个接口上,该接口是对目标对象新增的接口,实现由defaultImpl属性提供
注入AspectJ切面:
相对于spring AOP,AspectJ的功能更加强大,不仅支持方法的拦截,还能拦截属性,构造器等,AspectJ是扩展了JAVA语言的AOP框架,详情见AspectJ项目,要把AspectJ的切面描述注入为spring bean需要进行如下配置:
<bean class="org.robbie.acpectj.MyAspect" factory-method="acpectOf"></bean>
其中MyAspect为AspectJ编写的切面,要声明成spring的bean,需要用factory-method指定工厂产生方法,aspectJ所有类默认都提供aspectOf方法用于返回实例,这样就完成了AspectJ在spring中的声明,之后就能像普通的spring bean一样进行操作了。
MyAspect示例:
public aspect MyAspect{ public MyAspect(){} pointcut performance() : execution(* *..*.*(..)) before() returning() : performance(){ System.out.println("aspect"); } }
相关推荐
5.Spring AOP 源码深度解析.mp4
aopalliance-1.0.jar,org.springframework.aop-3.0.0.RELEASE.jar,org.springframework.jdbc-3.0.0.RELEASEorg.springframework.beans-3.0.0.RELEASE.jar等
spring-aop-1.1.1.jar spring-aop-1.2.6.jar spring-aop-1.2.9.jar spring-aop-2.0.2.jar spring-aop-2.0.6.jar spring-aop-2.0.7.jar spring-aop-2.0.8.jar spring-aop-2.0.jar spring-aop-2.5.1.jar spring-aop-...
org.springframework.aop-3.1.0.M2
org.springframework.aop-3.0.4.RELEASE.jar org.springframework.asm-3.0.4.RELEASE.jar org.springframework.aspects-3.0.4.RELEASE.jar org.springframework.beans-3.0.4.RELEASE.jar org.springframework....
org.springframework.aop-3.0.0.M3.jar
org.springframework.aop-3.1.1.RELEASE org.springframework.asm-3.1.1.RELEASE org.springframework.aspects-3.1.1.RELEASE org.springframework.beans-3.1.1.RELEASE org.springframework.context.support-3.1.1....
**Spring AOP 实现机制详解** Spring AOP(面向切面编程)是Spring框架的核心特性之一,它允许程序员在不修改源代码的情况下,通过“切面”来插入额外的业务逻辑,如日志、事务管理等。AOP的引入极大地提高了代码的...
Spring AOP,全称为Aspect Oriented Programming,是Spring框架中的一个重要模块,主要负责处理系统中的...文件"5.SpringAOP_01"和"6.SpringAOP_02"很可能是课程的分阶段内容,涵盖了从基础概念到进阶实践的详细讲解。
4.Spring AOP 源码深度解析(一).mp4
org.springframework.aop-sources-3.0.5.release.jar
《Spring框架中的AOP模块详解》 在Java开发领域,Spring框架以其强大的功能和灵活性而备受推崇。在Spring框架中,AOP(Aspect Oriented Programming,面向切面编程)是其核心特性之一,它允许开发者将关注点从主...
5. **与其他Spring组件的交互**:Spring的其他组件,如Spring MVC(Web层)、Spring JDBC(数据访问层)等,都可以与AOP集成,利用其优势来简化代码并提高可维护性。 6. **配置与实践**:在Spring应用中,可以使用...
Spring AOP,全称Aspect-Oriented Programming(面向切面编程),是Spring框架的重要组成部分,主要用来解决传统面向对象编程中的横切关注点问题。在Java应用中,这些横切关注点通常包括日志记录、事务管理、权限...
Spring框架是Java开发者中最受欢迎的开源框架之一,其中AOP(面向切面编程)是Spring核心概念之一。AOP通过一种称为“横切”的技术,将业务逻辑中的横切关注点与业务逻辑相分离,以提高代码的模块性和重用性。在本...
该jar文件包含了Spring AOP的核心类和接口,如`org.springframework.aop.*`包下的`AspectJExpressionPointcut`、`MethodBeforeAdvice`、`AfterReturningAdvice`等。 3. spring-aspects-4.1.6.RELEASE.jar:这个jar...
org.springframework.aop-3.0.0.RELEASE.jar org.springframework.asm-3.0.0.RELEASE.jar org.springframework.aspects-3.0.0.RELEASE.jar org.springframework.beans-3.0.0.RELEASE.jar org.springframework....
org.springframework.aop-3.0.0.M4.jar
Spring AOP(面向切面编程)是Spring框架的核心特性之一,它允许开发者在不修改源代码的情况下,通过插入额外的代码来扩展或监控程序的行为。这一特性极大地增强了代码的灵活性和可维护性,使得我们可以实现如日志...
开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE...