Spring 3 AOP 概念及完整示例
本文内容纲要:
- AOP概念- AOP术语
- Advice的类型
- 实现机制
- Pointcut expression
- 完整示例:
AOP概念
AOP(Aspect Oriented Programming),即面向切面编程(也叫面向方面编程,面向方法编程)。其主要作用是,在不修改源代码的情况下给某个或者一组操作添加额外的功能。像日志记录,事务处理,权限控制等功能,都可以用AOP来“优雅”地实现,使这些额外功能和真正的业务逻辑分离开来,软件的结构将更加清晰。AOP是OOP的一个强有力的补充。
AOP术语
AOP的术语不太直观,Spring文档中也没有给一个确切的定义,所以重在理解。
- Join Point: Spring AOP中,join point就是一个方法。(通俗来讲就是起作用的那个方法)。
- Pointcut: 用来指定join point(通俗来讲就是描述的一组符合某个条件的join point)。通常使用pointcut表达式来限定joint point,Spring默认使用AspectJ pointcut expression language。
- Advice: 在join point上特定的时刻执行的操作,Advice有几种不同类型,下文将会讨论(通俗地来讲就是起作用的内容和时间点)。
- Introduction:给对象增加方法或者属性。
- Target object: Advice起作用的那个对象。
- AOP proxy: 为实现AOP所生成的代理。在Spring中有两种方式生成代理:JDK代理和CGLIB代理。
- Aspect: 组合了Pointcut与Advice,在Spring中有时候也称为Advisor。某些资料说Advisor是一种特殊的Aspect,其区别是Advisor只能包含一对pointcut和advice,但是aspect可以包含多对。AOP中的aspect可以类比于OOP中的class。
- Weaving:将Advice织入join point的这个过程。
Advice的类型
- Before advice: 执行在join point之前的advice,但是它不能阻止joint point的执行流程,除非抛出了一个异常(exception)。
- After returning advice: 执行在join point这个方法返回之后的advice。
- After throwing advice: 执行在join point抛出异常之后的advice。
- After(finally) advice: 执行在join point返回之后或者抛出异常之后的advice,通常用来释放所使用的资源。
- Around advice: 执行在join point这个方法执行之前与之后的advice。
实现机制
Spring AOP是基于代理机制的,通过JDK Proxy和CGLIB Proxy两种方法实现代理。
如果target object没有实现任何接口,那么Spring将使用CGLIB来实现代理。CGLIB是一个开源项目,它是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
如果target object实现了一个以上的接口,那么Spring将使用JDK Proxy来实现代理,因为Spring默认使用的就是JDK Proxy,并且JDK Proxy是基于接口的。这也是Spring提倡的面向接口编程。当然,你也可以强制使用CGLIB来进行代理,但是这样可能会造成性能上的下降。
Pointcut expression
Pointcut可以有下列方式来定义或者通过&& || 和!的方式进行组合.
args()@args() execution() this() target() @target() within() @within() @annotation
其中execution 是用的最多的,其格式为:
ret-type-pattern,name pattern, 和 parameters pattern是必须的.
- ret-type-pattern:可以为*表示任何返回值,全路径的类名等.
- name-pattern:指定方法名,*代表所以,set*,代表以set开头的所有方法.
- parameters pattern:指定方法参数(声明的类型),(..)代表所有参数,(*)代表一个参数,(*,String)代表第一个参数为任何值,第二个为String类型.
举例说明:
任意公共方法的执行:
execution(public * *(..))
任何一个以“set”开始的方法的执行:
execution(* set*(..))
AccountService 接口的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
定义在service包里的任意方法的执行:
execution(* com.xyz.service..(..))
定义在service包和所有子包里的任意类的任意方法的执行:
execution(* com.xyz.service...(..))
定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行:
execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))
pointcutexp包里的任意类.
within(com.test.spring.aop.pointcutexp.*)
pointcutexp包和所有子包里的任意类.
within(com.test.spring.aop.pointcutexp..*)
实现了Intf接口的所有类,如果Intf不是接口,限定Intf单个类.
this(com.test.spring.aop.pointcutexp.Intf)
带有@Transactional标注的所有类的任意方法.
@within(org.springframework.transaction.annotation.Transactional) @target(org.springframework.transaction.annotation.Transactional)
带有@Transactional标注的任意方法.
@annotation(org.springframework.transaction.annotation.Transactional)
参数带有@Transactional标注的方法.
@args(org.springframework.transaction.annotation.Transactional)
参数为String类型(运行是决定)的方法.
args(String)
完整示例:
首先新建一个maven项目,在项目的pom.xml中添加spring aop相关的依赖项:
如下是完整的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.outofmemory</groupId> <artifactId>spring-aop-aspect</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>spring-aop-aspect</name> <url>http://maven.apache.org</url> <properties> <spring.version>3.1.1.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.12</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.12</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2</version> </dependency> </dependencies> </project>
在maven中我们引入了spring aop相关的依赖,aspectj相关的包有两个,cglib是必须的。
下面我们在项目中新建一个Service类PersonService,它是业务代码,是我们AOP注入的目标类:
package cn.outofmemory.spring_aop_aspect; import org.springframework.stereotype.Service; @Service public class PersonService { public void addPerson(String personName) { System.out.println("add person " + personName); } public boolean deletePerson(String personName) { System.out.println("delete person " + personName) ; return true; } public void editPerson(String personName) { System.out.println("edit person " + personName); throw new RuntimeException("edit person throw exception"); } }
PersonService类中定义了三个方法,addPerson方法无返回值,而deletePerson方法有返回值,而editPerson方法抛出运行时的异常。
下面我们看Aspect类:
package cn.outofmemory.spring_aop_aspect; import org.aspectj.lang.JoinPoint; 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; @Component @Aspect public class SimpleAspect { @Pointcut("execution(* cn.outofmemory.spring_aop_aspect.*Service*.*(..))") public void pointCut() { } @After("pointCut()") public void after(JoinPoint joinPoint) { System.out.println("after aspect executed"); } @Before("pointCut()") public void before(JoinPoint joinPoint) { //如果需要这里可以取出参数进行处理 //Object[] args = joinPoint.getArgs(); System.out.println("before aspect executing"); } @AfterReturning(pointcut = "pointCut()", returning = "returnVal") public void afterReturning(JoinPoint joinPoint, Object returnVal) { System.out.println("afterReturning executed, return result is " + returnVal); } @Around("pointCut()") public void around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("around start.."); try { pjp.proceed(); } catch (Throwable ex) { System.out.println("error in around"); throw ex; } System.out.println("around end"); } @AfterThrowing(pointcut = "pointCut()", throwing = "error") public void afterThrowing(JoinPoint jp, Throwable error) { System.out.println("error:" + error); } }
SimpleAspect类中的第一个方法是pointCut()方法,此方法无任何执行内容,只有一个@Pointcut的注解,其他方法都会引用这个注解中指定的pointcut表达式。
其他几个方法是各种Advice类型的方法实现,在这些方法中都可以通过JoinPoint实例来获得方法执行的上下文信息,参数信息。需要注意AfterReturning和AfterThrowing,After三种不同Advice的执行顺序。
有了spring框架和aspectj以及cglib的支持,我们只需要实现上面两个类就可以使用spring aop的功能了,下面我们看下如何在spring的配置文件中配置spring aop。
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> <aop:aspectj-autoproxy /> <context:component-scan base-package="cn.outofmemory" /> </beans>
注意在beans节点中需要指定aop命名空间,一家chemaLocation地址,启用aop只需要添加<aop:aspectj-autoproxy/>
就可以了。context:component-scan/节点用来指定自动扫描bean的命名空间。
最后我们要测试下aop的效果,需要新建一个App类作为程序的入口类。
package cn.outofmemory.spring_aop_aspect; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Hello world! * */ public class App { public static void main( String[] args ) { ApplicationContext appContext = new ClassPathXmlApplicationContext("/appContext.xml"); PersonService personService = appContext.getBean(PersonService.class); String personName = "Jim"; personService.addPerson(personName); personService.deletePerson(personName); personService.editPerson(personName); ((ClassPathXmlApplicationContext)appContext).close(); } }
这个类中我们初始化了ApplicationContext,然后从中得到PersonService的实例,并执行其addPerson,deletePerson,editPerson方法,最后关闭ApplicationContext。
其执行结果如下:
before aspect executingaround start..
add person Jim
after aspect executed
around end afterReturning executed, return result is null ------------------------------------- before aspect executing around start.. delete person Jim after aspect executed around end afterReturning executed, return result is null ---------------------------------------- before aspect executing around start.. edit person Jim after aspect executed error in around error:java.lang.RuntimeException: edit person throw exception Exception in thread ...
从上面执行结果可以看到我们要执行的Before,around,after以及around,afterReturning,afterThrowing都正常的执行了。
AOP 配置详解
http://blog.csdn.net/lipei1220/article/details/52152430
Spring AOP 完成日志记录
http://hotspan.iteye.com/blog/1330046
http://blog.csdn.net/baidu_25958185/article/details/43764467
http://www.oschina.net/code/snippet_1159320_49213
Spring Aop自定义注解拦截Controller实现日志管理
http://blog.csdn.net/itnik/article/details/38542373
本文内容总结:AOP概念,AOP术语,Advice的类型,实现机制,Pointcut expression,完整示例:,
原文链接:https://www.cnblogs.com/nsw2018/p/5881723.html
以上是 Spring 3 AOP 概念及完整示例 的全部内容, 来源链接: utcz.com/z/362974.html