文章目录
1 Spring AOP
1.1 spring AOP 的简介
- AOP思想最初由AOP联盟提出,Spring是使用这种思想最好的框架.
- Spring两套AOP开发方式
- Spring传统方式(弃用)
- Spring基于AspectJ的AOP开发(常用)
1.2 AOP开发的相关术语
1.2.1 Joninpoint(连接点)
所谓连接点是指那些被拦截到的点.在spring中,这些点是方法,因为spring只支持方法类型的连接点.
1.2.2 Pointcut(切入点):
所谓的切入点是指我们要对那些Joinpoint进行拦截点的定义.
1.2.3 Advice(通知/增强):
所谓的通知是指拦截到的Joinpoint之后要做的事情就是通知,通知分为前置通知,后置通知,环绕通知,异常通知,最终通知,通知就是要增强的功能
1.2.4 Introduction(引介):
引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态的添加一些方法或者属性.
1.2.5 Target(目标对象):
被代理的目标对象
1.2.6 Weaving(织入):
把增强应用到目标对象来创建新的代理对象的过程,spring采用动态代理织入,而AspectJ采用编译器织入和类加载器织入
1.2.7 Proxy(代理):
一个类被AOP织入增强后,就产生一个代理类.
1.2.8 Aspect(切面):
是切入点和通知(引介)的结合
1.3 spring使用AspectJ进行AOP开发(xml配置方式)
1.3.1 引入响应的jar包
- spring基础包
- com.springsource.org.apache.commons.logging-1.1.1.jar
- com.springsource.org.apache.log4j-1.2.15.jar
- spring-beans-4.2.4.RELEASE.jar
- spring-context-4.2.4.RELEASE.jar
- spring-core-4.2.4.RELEASE.jar
- spring-expression-4.2.4.RELEASE.jar
- spring aop开发包
- com.springsource.org.aopalliance-1.0.0.jar
- com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
- spring-aop-4.2.4.RELEASE.jar
- spring-aspects-4.2.4.RELEASE.jar
1.3.2 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:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- bean definitions here -->
</beans>
1.3.3 通知
1.3.3.1 前置通知:在目标方法执行之前
- 前置通知代码:可以使用无参,也可以获取切入点参数
- 优先执行无参方法
/**
* 前置通知,获取切入点
*/
public void before(JoinPoint point)
{
System.out.println("前置通知... " + point);
//输出结果 前置通知... execution(void com.zgd.learn.spring.demo02.dao.ProductDao.save())
}
/**
* 前置通知
*/
public void before()
{
System.out.println("前置通知... ");
}
- AOP配置
<!-- AOP配置 -->
<aop:config>
<!-- 使用execution表达式定位到哪个切入点 -->
<aop:pointcut expression="execution(* com.zgd.learn.spring.demo02.dao.impl.ProductDaoImpl.save(..))" id="pointcut1"/>
<!-- 配置切面 -->
<aop:aspect ref="aopAspect">
<aop:before method="before" pointcut-ref="pointcut1"/>
</aop:aspect>
</aop:config>
1.3.3.2 后置通知:在目标方法执行之后
- 后置通知代码:可无参可有参,有参则可获取切点的返回值
- 优先执行无参方法
- 方法参数中Object的值需要和AOP配置中returning的值一致
/**
* 后置通知,不需要结果值
*/
public void after()
{
System.out.println("后置通知...");
}
/**
* 后置通知,需要结果值
*/
public void after(Object result)
{
System.out.println("后置通知..." + result);
}
- AOP配置
<!-- AOP配置 -->
<aop:config>
<!-- 使用execution表达式定位到哪个切入点 -->
<aop:pointcut expression="execution(* com.zgd.learn.spring.demo02.dao.impl.ProductDaoImpl.delete(..))" id="pointcut2"/>
<!-- 配置切面 -->
<aop:aspect ref="aopAspect">
<!-- 配置后置通知,after为切面中的方法名,pointcut-ref为切点的ID,
returning为结果名,可随意,如需要在切面中使用返回则则可配置 -->
<aop:after-returning method="after" pointcut-ref="pointcut2" returning="result"/>
</aop:aspect>
</aop:config>
1.3.3.3 环绕通知:在目标方法执行前后
- 环绕通知代码
/**
* 环绕通知
*
* @throws Throwable
*/
public void around(ProceedingJoinPoint jointPoint) throws Throwable
{
System.out.println("环绕通知执行前...");
jointPoint.proceed();
System.out.println("环绕通知执行后...");
}
- AOP配置
<!-- AOP配置 -->
<aop:config>
<!-- 使用execution表达式定位到哪个切入点 -->
<aop:pointcut expression="execution(* com.zgd.learn.spring.demo02.dao.impl.ProductDaoImpl.update(..))" id="pointcut3"/>
<!-- 配置切面 -->
<aop:aspect ref="aopAspect">
<!-- 环绕通知,around为切面方法-->
<aop:around method="around" pointcut-ref="pointcut3" />
</aop:aspect>
</aop:config>
1.3.3.4 异常通知:在程序出现异常时执行
- 异常通知代码,可无参,可获取异常对象
- 都有则使用无参方法
- 方法参数值可随意,但是在aop配置中throwing的值需要和方法参数中Throwable值一致,否则无效
/**
* 异常通知
*/
public void execpt()
{
System.out.println("异常通知,出现异常了");
}
/**
* 异常通知
*/
public void execpt(Throwable ex)
{
System.out.println("异常通知,出现异常了" + ex);
}
- AOP配置
<!-- AOP配置 -->
<aop:config>
<!-- 使用execution表达式定位到哪个切入点 -->
<aop:pointcut expression="execution(* com.zgd.learn.spring.demo02.dao.impl.ProductDaoImpl.find(..))" id="pointcut4"/>
<!-- 配置切面 -->
<aop:aspect ref="aopAspect">
<!-- 异常通知 -->
<aop:after-throwing method="execpt" pointcut-ref="pointcut4" throwing="ex"/>
</aop:aspect>
</aop:config>
1.3.3.5 最终通知:不管是否有异常,最终都会执行
- 最终通知会在异常通知前执行.
- 最终通知代码
/**
* 最终通知
*/
public void fin()
{
System.out.println("最终通知... ");
}
- AOP配置
<!-- AOP配置 -->
<aop:config>
<!-- 使用execution表达式定位到哪个切入点 -->
<aop:pointcut expression="execution(* com.zgd.learn.spring.demo02.dao.impl.ProductDaoImpl.find(..))" id="pointcut4"/>
<!-- 配置切面 -->
<aop:aspect ref="aopAspect">
<!-- 最终通知 -->
<aop:after method="fin" pointcut-ref="pointcut4"/>
</aop:aspect>
</aop:config>
1.4 切入点表达式
- 基于execution的函数完成的
- 语法
- [访问修饰符] 方法返回值 包名.类名.方法名(参数)
- 访问修饰符可省略
- … 表示任意参数
- 返回值类型,包名,类名,方法名都可以使用*表示
- public void com.zgd.spring.Customer.*(…),切入点为Customer的任意返回值为void的方法中
- public * com.zgd.spring.Customer+.save(…),切入点为Customer及其子类的任意方法中
-
- com.zgd.spring….(…),切入点为spring包下所有类的所有方法
- [访问修饰符] 方法返回值 包名.类名.方法名(参数)
1.5 代码示例
1.5.1 切面类
/**
* 切面类
*/
public class AOPAspect
{
/**
* 前置通知,获取切入点
*/
public void before(JoinPoint point)
{
System.out.println("前置通知... " + point);
}
/**
* 前置通知
*/
// public void before()
// {
// System.out.println("前置通知... ");
// }
/**
* 后置通知,不需要结果值
*/
// public void after()
// {
// System.out.println("后置通知...");
// }
/**
* 后置通知,需要结果值
*/
public void after(Object result)
{
System.out.println("后置通知..." + result);
}
/**
* 环绕通知
*
* @throws Throwable
*/
public void around(ProceedingJoinPoint jointPoint) throws Throwable
{
System.out.println("环绕通知执行前...");
jointPoint.proceed();
System.out.println("环绕通知执行后...");
}
/**
* 异常通知
*/
// public void execpt()
// {
// System.out.println("异常通知,出现异常了");
// }
/**
* 异常通知
*/
public void execpt(Throwable ex)
{
System.out.println("异常通知,出现异常了" + ex);
}
/**
* 最终通知
*/
public void fin()
{
System.out.println("最终通知... ");
}
}
1.5.2 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:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here -->
<!-- 切入点所在的类 -->
<bean id="productDao" class="com.zgd.learn.spring.demo02.dao.impl.ProductDaoImpl"/>
<!-- 切面 -->
<bean id="aopAspect" class="com.zgd.learn.spring.demo02.AOPAspect"/>
<!-- AOP配置 -->
<aop:config>
<!-- 使用execution表达式定位到哪个切入点 -->
<aop:pointcut expression="execution(* com.zgd.learn.spring.demo02.dao.impl.ProductDaoImpl.save(..))" id="pointcut1"/>
<aop:pointcut expression="execution(* com.zgd.learn.spring.demo02.dao.impl.ProductDaoImpl.delete(..))" id="pointcut2"/>
<aop:pointcut expression="execution(* com.zgd.learn.spring.demo02.dao.impl.ProductDaoImpl.update(..))" id="pointcut3"/>
<aop:pointcut expression="execution(* com.zgd.learn.spring.demo02.dao.impl.ProductDaoImpl.find(..))" id="pointcut4"/>
<!-- 配置切面 -->
<aop:aspect ref="aopAspect">
<!-- 配置前置通知,before为切面中方法名,pointcut-ref为切点的ID -->
<aop:before method="before" pointcut-ref="pointcut1"/>
<!-- 配置后置通知,after为切面中的方法名,pointcut2为切点的ID,returning为结果名,可随意,如需要在切面中使用返回则则可配置 -->
<aop:after-returning method="after" pointcut-ref="pointcut2" returning="result"/>
<!-- 环绕通知,around为切面方法-->
<aop:around method="around" pointcut-ref="pointcut3" />
<!-- 异常通知 -->
<aop:after-throwing method="execpt" pointcut-ref="pointcut4" throwing="ex"/>
<!-- 最终通知 -->
<aop:after method="fin" pointcut-ref="pointcut4"/>
</aop:aspect>
</aop:config>
</beans>
1.5.3 测试代码
public class AOPTest
{
@Test
public void test()
{
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext-aop.xml");
ProductDao productDao = (ProductDao) ac.getBean("productDao");
System.out.println("==========");
productDao.save();
System.out.println("==========");
productDao.delete();
System.out.println("==========");
productDao.update();
System.out.println("==========");
productDao.find();
System.out.println("==========");
}
}
1.5.4 测试结果
==========
前置通知... execution(void com.zgd.learn.spring.demo02.dao.ProductDao.save())
添加商品
==========
删除商品
后置通知...success
==========
环绕通知执行前...
更新商品
环绕通知执行后...
==========
查找商品
最终通知...
异常通知,出现异常了java.lang.ArithmeticException: / by zero