Spring中的AOP强大,
OOP编程中,多态继承等特性,使得方便纵向扩展,但是对于横向的业务需求无能为力。
比如日志模块,权限模块,等等。
Spring中可以通过xml来配置,也可以通过注解来实现。
一个简单项目如下
为了进行Spring开发,先编辑maven项目的pom.xml文件
<project Xmlns="http://maven.apache.org/POM/4.0.0" Xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 Http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.asus</groupId> <artifactId>xk</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>xk</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- Https://mvnrepository.com/artifact/aopalliance/aopalliance --> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.4</version> </dependency> </dependencies> </project>先定义接口
Package Cn.asus.xk; Public Interface HelloWorld { Void PrintHelloWorld(); Void DoPrint(); }然后,定义两个类实现接口
package cn.asus.xk; public class HelloWorldImpl1 implements HelloWorld { public void printHelloWorld() { System.out.println("Enter HelloWorldImpl1.printHelloWorld()"); } public void doPrint() { System.out.println("HelloWorldImpl1.doPrint()"); } }
Package Cn.asus.xk; Public Class HelloWorldImpl2 Implements HelloWorld { Public Void PrintHelloWorld() { System.out.println("Enter HelloWorldImpl2.printHelloWorld()"); } Public Void DoPrint() { System.out.println("HelloWorldImpl2.doPrint()"); } }
定义观察类
package cn.asus.xk; public class TimeHandler { public void printTime() { System.out.println("Current Time" + System.currentTimeMillis()); } }
然后,使用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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"> <!-- 被代理对象 --> <bean id="helloWorldImpl1" class="cn.asus.xk.HelloWorldImpl1" /> <bean id="helloWorldImpl2" class="cn.asus.xk.HelloWorldImpl2" /> <!-- 通知 --> <bean id="timeHandler" class="cn.asus.xk.TimeHandler" /> <!-- aop配置 --> <aop:config> <!-- 切面 --> <aop:aspect id="time" ref="timeHandler"> <!-- 切点 --> <aop:pointcut id="addAllMethod" expression="execution(* cn.asus.xk.HelloWorld.*(..))" /> <!-- 连接 通知方法和切点 --> <aop:before method="printTime" pointcut-ref="addAllMethod" /> <aop:after method="printTime" pointcut-ref="addAllMethod" /> </aop:aspect> </aop:config> </beans>
上面很好懂,就是id就是给个标识,后面用,然后定义了AOP的before和after两个切面。
然后,定义测试类。
package cn.asus.xk; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AopTest { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("aop.xml"); HelloWorld hw1 = (HelloWorld) ctx.getBean("helloWorldImpl1"); HelloWorld hw2 = (HelloWorld) ctx.getBean("helloWorldImpl2"); hw1.printHelloWorld(); System.out.println(); hw1.doPrint(); System.out.println(); hw2.printHelloWorld(); System.out.println(); hw2.doPrint(); } }
结果为
--------
几个重要的概念如下---
切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在springAOP中,切面可以使用基于模式)或者基于Aspect注解方式来实现。通俗点说就是我们加入的切面类(比如log类),可以这么理解。
连接点(Joinpoint):在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在SpringAOP中,一个连接点总是表示一个方法的执行。通俗的说就是加入切点的那个点
通知(Advice):在切面的某个特定的连接点上执行的动作。其中包括了“around”、“before”和“after”等不同类型的通知(通知的类型将在后面部分进行讨论)。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。
切入点(Pointcut):匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。
引入(Introduction):用来给一个类型声明额外的方法或属性(也被称为连接类型声明(inter-typedeclaration))。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用引入来使一个bean实现IsModified接口,以便简化缓存机制。
目标对象(TargetObject):被一个或者多个切面所通知的对象。也被称做被通知(advised)对象。既然SpringAOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。
AOP代理(AOPProxy):AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
织入(Weaving):把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯JavaAOP框架一样,在运行时完成织入。
通知类型:
前置通知(Beforeadvice):在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
后置通知(Afterreturningadvice):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
异常通知(Afterthrowingadvice):在方法抛出异常退出时执行的通知。
最终通知(After(finally)advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
环绕通知(AroundAdvice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。
环绕通知是最常用的通知类型。和AspectJ一样,Spring提供所有类型的通知,我们推荐你使用尽可能简单的通知类型来实现需要的功能。例如,如果你只是需要一个方法的返回值来更新缓存,最好使用后置通知而不是环绕通知,尽管环绕通知也能完成同样的事情。用最合适的通知类型可以使得编程模型变得简单,并且能够避免很多潜在的错误。比如,你不需要在JoinPoint上调用用于环绕通知的proceed()方法,就不会有调用的问题。
这个地方讲的很不错,可以参考