学而时习之,不亦说乎!html
--《论语》java
看这一篇以前最好先看前面关于AOP的两篇。web
http://www.cnblogs.com/zby9527/p/6945756.html (JDK代理和CGLIB代理)spring
http://www.cnblogs.com/zby9527/p/6946952.html (Spring的AOP)express
AspectJ:apache
1.AspectJ是一个基于Java语言的AOP框架。app
2.Spring2.0之后新增了对AspectJ切点表达式支持。框架
3.@AspectJ是AspectJ1.5新增功能,经过JDK5注解技术,容许直接在Bean类中定义切面新版本Spring框架,建议使用AspectJ方式来开发maven
AspectJ最强大的地方在于他的切入点表达式:测试
语法:execution(修饰符 返回值 包.类.方法名(参数) throws异常)
修饰符,通常省略
public 公共方法
* 任意
返回值,不能省略
void 返回没有值
String 返回值字符串
* 任意
包
com.zby.service 固定包
com.zby.oa.*.service oa包下面子包 (例如:com.zby.oa.flow.service)
com.zby.oa.. oa包下面的全部子包(含本身)
com.zby.oa.*.service.. oa包下面任意子包,固定目录service,service目录任意包
类
UserServiceImpl 指定类
*Impl 以Impl结尾
User* 以User开头
* 任意
方法名,不能省略
addUser 固定方法
add* 以add开头
*Do 以Do结尾
* 任意
(参数)
() 无参
(int) 一个整型
(int ,int) 两个
(..) 参数任意
throws ,可省略,通常不写。
固然,execution也是能够变得,可是通常用这个就够了,更详细的表达式用法,固然是查看专业文档。
AspectJ和aopalliance通知的区别:
AOP联盟的通知类型具备特性接口,必须实现,从而肯定方法名称,而AspectJ的通知类型只定义了类型名称和方法格式,这意味着,咱们的切面不须要实现任何方法!!!。
AspectJ通知:
before:前置通知(应用:各类校验)
在方法执行前执行,若是通知抛出异常,阻止方法运行
afterReturning:后置通知(应用:常规数据处理)
方法正常返回后执行,若是方法中抛出异常,通知没法执行,必须在方法执行后才执行,因此能够得到方法的返回值。
around:环绕通知(应用:十分强大,能够作任何事情)
方法执行先后分别执行,能够阻止方法的执行,必须手动执行目标方法
afterThrowing:抛出异常通知(应用:包装异常信息)
方法抛出异常后执行,若是方法没有抛出异常,没法执行
after:最终通知(应用:清理现场)
方法执行完毕后执行,不管方法中是否出现异常
固然,最重要也最经常使用的仍是环绕通知,由于环绕通知必须手动执行目标方法,因此,能够代替其余几个通知。
使用XML配置Spring整合AspectJ的AOP:
1)项目总体结构以下:
2)建立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>com.zby</groupId> <artifactId>aop</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.8.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.3.8.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.3.8.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-test --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.3.8.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.10</version> </dependency> </dependencies> </project>
3)建立目标类UserService:
package com.zby.service; public class UserService { public void saveUser(String username, String password) { System.out.println("save user[username=" + username + ",password=" + password + "]"); } }
4)建立切面类:
package com.zby.interceptor; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; public class MyAspect { public void myBefore(JoinPoint joinPoint) { System.out.println("前置通知 : " + joinPoint.getSignature().getName()); } public void myAfterReturning(JoinPoint joinPoint, Object ret) { System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret); } public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("环绕通知执行方法前"); // 手动执行目标方法 Object obj = joinPoint.proceed(); System.out.println("环绕通知执行方法后"); return obj; } public void myAfterThrowing(JoinPoint joinPoint, Throwable e) { System.out.println("抛出异常通知 : " + e.getMessage()); } public void myAfter(JoinPoint joinPoint) { System.out.println("最终通知"); } }
切面类没有实现接口,可是有几种方法参数,这些不是必须的。这些传入的对象是什么?固然是咱们在切面点须要的信息!用脑袋想,在给一个方法进行加强的时候,前置方法,或者后置方法,或者环绕方法,有可能须要获得原方法的哪些信息,这里面都有。
5)编写配置文件applicationContext.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" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 目标类 --> <bean id="userService" class="com.zby.service.UserService"></bean> <!-- 切面类 --> <bean id="myInterceptor" class="com.zby.interceptor.MyAspect"></bean> <aop:config> <aop:aspect ref="myInterceptor"> <aop:pointcut expression="execution(* com.zby.service.UserService.*(..))" id="myPointcut" /> <!--环绕通知 <aop:around method="" pointcut-ref=""/> 通知方法格式:public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ } 返回值类型:Object 方法名:任意 参数:org.aspectj.lang.ProceedingJoinPoint 抛出异常 执行目标方法:Object obj = joinPoint.proceed(); 例如: <aop:around method="myAround" pointcut-ref="myPointCut"/> --> <aop:around method="myAround" pointcut-ref="myPointcut" /> <!-- 最终通知 --> <aop:after method="myAfter" pointcut-ref="myPointcut" /> <!--后置通知 ,目标方法后执行,得到返回值 <aop:after-returning method="" pointcut-ref="" returning=""/> returning 通知方法第二个参数的名称 通知方法格式: public void myAfterReturning(JoinPoint joinPoint,Object ret){} 参数1:链接点描述 参数2:类型Object,参数名 returning="ret" 配置的 例如: <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" /> --> <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut" returning="ret" /> <!--抛出异常 <aop:after-throwing method="" pointcut-ref="" throwing=""/> throwing :通知方法的第二个参数名称 通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ } 参数1:链接点描述对象 参数2:得到异常信息,类型Throwable ,参数名由throwing="e" 配置 例如: <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/> --> <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointcut" throwing="e" /> <!--前置通知 <aop:before method="" pointcut="" pointcut-ref=""/> method : 通知,及方法名 pointcut :切入点表达式,此表达式只能当前通知使用。 pointcut-ref : 切入点引用,能够与其余通知共享切入点。 通知方法格式:public void myBefore(JoinPoint joinPoint){} 参数1:org.aspectj.lang.JoinPoint 用于描述链接点(目标方法),得到目标方法名等 例如: <aop:before method="myBefore" pointcut-ref="myPointCut"/> --> <aop:before method="myBefore" pointcut-ref="myPointcut" /> </aop:aspect> </aop:config> </beans>
6)编写测试类:
package com.zby.test; 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; import com.zby.service.UserService; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:applicationContext.xml" }) public class AOPTest { @Autowired private UserService userService; @Test public void testProxy() { System.out.println("After Proxy......"); userService.saveUser("zby", "1234567890"); } }
7)控制台打印结果:
六月 09, 2017 2:07:56 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper getDefaultTestExecutionListenerClassNames 信息: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener] 六月 09, 2017 2:07:56 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners 信息: Could not instantiate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute] 六月 09, 2017 2:07:56 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners 信息: Could not instantiate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource] 六月 09, 2017 2:07:56 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners 信息: Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext] 六月 09, 2017 2:07:56 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper getTestExecutionListeners 信息: Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@6e983d8d, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@4cf12cb4, org.springframework.test.context.support.DirtiesContextTestExecutionListener@6dae04e2] 六月 09, 2017 2:07:56 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [applicationContext.xml] 六月 09, 2017 2:07:56 下午 org.springframework.context.support.GenericApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.GenericApplicationContext@24024ad8: startup date [Fri Jun 09 14:07:56 CST 2017]; root of context hierarchy After Proxy...... 环绕通知执行方法前 前置通知 : saveUser save user[username=zby,password=1234567890] 后置通知 : saveUser , -->null 最终通知 环绕通知执行方法后
这个DEMO就是一个大杂烩,其实使用时使用一个环绕通知就够了。再环绕通知里面必须手动执行方法,所以咱们用try-catch把方法执行包裹起来,而后在执行前和执行后写加强代码便可。
使用注解配置Spring整合AspectJ的AOP:
1)上面的一二步骤不变。
2)编写目标类UserService:
package com.zby.service; import org.springframework.stereotype.Service; @Service public class UserService { public void saveUser(String username, String password) { System.out.println("save user[username=" + username + ",password=" + password + "]"); } }
3)编写切面类,使用注解:
package com.zby.interceptor; 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 MyAspect { // 多个方法须要使用这个切入点表达式,定义为一个公用的 @Pointcut("execution(* com.zby.service..*(..))") public void myPointCut() { } // 这里注解里面的值为上面的方法名 @Before("myPointCut()") public void myBefore(JoinPoint joinPoint) { System.out.println("前置通知 : " + joinPoint.getSignature().getName()); } // 当你只有一个方法,或者只在这儿用,能够直接写切入点表达式 @AfterReturning(value = "execution(* com.zby.service..*(..))", returning = "ret") public void myAfterReturning(JoinPoint joinPoint, Object ret) { System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret); } // @Around("myPointCut()") public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("环绕通知执行方法前"); // 手动执行目标方法 Object obj = joinPoint.proceed(); System.out.println("环绕通知执行方法后"); return obj; } @AfterThrowing(value = "myPointCut()", throwing = "e") public void myAfterThrowing(JoinPoint joinPoint, Throwable e) { System.out.println("抛出异常通知 : " + e.getMessage()); } @After("myPointCut()") public void myAfter(JoinPoint joinPoint) { System.out.println("最终通知"); } }
4)编写配置文件applicationContext.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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <context:component-scan base-package="com.zby"></context:component-scan> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
5)编写测试类:
package com.zby.test; 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; import com.zby.service.UserService; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:applicationContext.xml" }) public class AOPTest { @Autowired private UserService userService; @Test public void testProxy() { System.out.println("After Proxy......"); userService.saveUser("zby", "1234567890"); } }
6)控制台打印结果:
六月 09, 2017 2:29:21 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper getDefaultTestExecutionListenerClassNames 信息: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener] 六月 09, 2017 2:29:21 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners 信息: Could not instantiate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute] 六月 09, 2017 2:29:21 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners 信息: Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext] 六月 09, 2017 2:29:21 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners 信息: Could not instantiate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource] 六月 09, 2017 2:29:21 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper getTestExecutionListeners 信息: Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@6dae04e2, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@3bc2c9af, org.springframework.test.context.support.DirtiesContextTestExecutionListener@71471ecf] 六月 09, 2017 2:29:21 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [applicationContext.xml] 六月 09, 2017 2:29:21 下午 org.springframework.context.support.GenericApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.GenericApplicationContext@69f31d: startup date [Fri Jun 09 14:29:21 CST 2017]; root of context hierarchy After Proxy...... 环绕通知执行方法前 前置通知 : saveUser save user[username=zby,password=1234567890] 环绕通知执行方法后 最终通知 后置通知 : saveUser , -->null
总结:对比起来,能够看出来使用最后一种方式开发AOP很方便,这也是咱们最经常使用的,至于spring原生的AOP,大多在一些框架里面看到。使用整合AspectJ的方式,最主要的是要注意切面表达式的书写和方法参数传入,以及怎么使用这些参数。