学而时习之,不亦说乎!

                             --《论语》

看这一篇之前最好先看前面关于AOP的两篇。

http://www.cnblogs.com/zby9527/p/6945756.html (JDK代理和CGLIB代理)

http://www.cnblogs.com/zby9527/p/6946952.html (Spring的AOP)

AspectJ:

1.AspectJ是一个基于Java语言的AOP框架。

2.Spring2.0以后新增了对AspectJ切点表达式支持。

3.@AspectJ是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面新版本Spring框架,建议使用AspectJ方式来开发

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的方式,最主要的是要注意切面表达式的书写和方法参数传入,以及怎么使用这些参数。

最新文章

  1. mina中游戏客户端服务端数据交互流程
  2. Linux内核笔记--网络子系统初探
  3. MVC4做网站后台:栏目管理1、添加栏目-续
  4. Server Tomcat v7.0 Server at localhost was unable to&amp;amp;nbs 报错问题解决
  5. android异常: java.net.ConnectException: localhost/127.0.0.1:8080 - Connection refused
  6. CHECKBOX_CHECKED built-in in Oracle D2k Forms
  7. IIS报错,App_global.asax.&#215;&#215;&#215;.dll拒绝访问
  8. 类似于QQ游戏百万人同时在线的服务器架构实现
  9. [原]《打造未来的Java》视频笔记
  10. BNUOJ-26580 Software Bugs KMP匹配,维护
  11. window对象BOM
  12. PHP转码函数
  13. Octave Tutorial(《Machine Learning》)之第五课《控制语句和方程及向量化》
  14. 我读&lt;代码整洁之道&gt;--读书笔记整理
  15. vscode前端常用插件推荐,搭建JQuery、Vue等开发环境
  16. flink源码编译(windows环境)
  17. Vue中的状态管理器 - Vuex
  18. h5的video下载按钮如何隐藏
  19. DEV中右键菜单如何只在非空单元格上显示?
  20. mysql日期操作

热门文章

  1. 基于django rest framework做认证组件
  2. SQL 批量插入有标识列的数据
  3. Go程序设计3——并发编程
  4. select样式调整
  5. oracle数据库查询全系整理
  6. 【转】SSH指南
  7. Ubuntu在用root账户使用xftp连接时提示拒绝连接
  8. xmlreader与xmlwriter里的几个坑与解决方案
  9. go tcp使用
  10. MultiTrigger