四.springAOP
1.AOP概述
1) 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
2) AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
3) 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
4) AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
5) 经典应用:事务管理、性能监视、安全检查、缓存 、日志等【画图】
6) Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码
7) AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入
2.AOP实现原理
a. aop底层将采用代理机制进行实现。
b. 接口 + 实现类 :spring采用 jdk 的动态代理Proxy。
c. 实现类:spring 采用 cglib字节码增强。
3.AOP术语
1.target:目标类,需要被代理的类。例如:UserService
2.Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法。例如:所有的方法
3.PointCut 切入点:已经被增强的连接点。例如:addUser()
4.advice 通知/增强,增强代码。例如:after、before
5.Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.
6.proxy 代理类
7.Aspect(切面): 是切入点pointcut和通知advice的结合
一个切入点和一个通知,组成成一个特殊的面。
4.AspectJ 通知类型
before:前置通知(应用:各种校验)
在方法执行前执行,如果通知抛出异常,阻止方法运行
afterReturning:后置通知(应用:常规数据处理)
方法正常返回后执行,如果方法中抛出异常,通知无法执行
必须在方法执行后才执行,所以可以获得方法的返回值。
around:环绕通知(应用:十分强大,可以做任何事情)
方法执行前后分别执行,可以阻止方法的执行
必须手动执行目标方法
afterThrowing:抛出异常通知(应用:包装异常信息)
方法抛出异常后执行,如果方法没有抛出异常,无法执行
after:最终通知(应用:清理现场)
方法执行完毕后执行,无论方法中是否出现异常
5.切入点表达式
语法:execution(修饰符 返回值 包.类.方法名(参数) throws异常)
修饰符,一般省略
public 公共方法
* 任意
返回值,不能省略
void 返回没有值
String 返回值字符串
* 任意
包,[省略]
com.gyf.crm 固定包
com.gyf.crm.*.service crm包下面子包任意 (例如:com.gyf.crm.staff.service)
com.gyf.crm.. crm包下面的所有子包(含自己)
com.gyf.crm.*.service.. crm包下面任意子包,固定目录service,service目录任意包
类,[省略]
UserServiceImpl 指定类
*Impl 以Impl结尾
User* 以User开头
* 任意
方法名,不能省略
addUser 固定方法
add* 以add开头
*Do 以Do结尾
* 任意
(参数)
() 无参
(int) 一个整型
(int ,int) 两个
(..) 参数任意
6.示例
(1)导包
核心包: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
AOP包:com.springsource.org.aopalliance-1.0.0.jar AOP联盟规范
spring-aop-4.2.4.RELEASE.jar AOP实现包
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar weave包
spring-aspects-4.2.4.RELEASE.jar aspect包
日志包:com.springsource.org.apache.commons.logging-1.1.1.jar 日志规范
com.springsource.org.apache.log4j-1.2.15.jar 日志实现
同时日志还需要log4j.properties 配置文件
(2)前置通知,后置通知示例
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- userService -->
<bean id="userService" class="com.gyf.service.impl.UserServiceImpl"></bean>
<!-- 配置切面类对象 -->
<bean id="myAspect" class="com.gyf.aspect.MyAspect"></bean>
<aop:config>
<!-- aop:指定切面 -->
<aop:aspect ref="myAspect">
<!-- 定义一个切入点 -->
<aop:pointcut id="myPointcut" expression="execution(* com.gyf.service.*.*(..))"></aop:pointcut>
<!-- 配置前置通知 -->
<aop:before method="myBefore" pointcut-ref="myPointcut"></aop:before>
<!-- 配置后置通知 -->
<aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut"></aop:after-returning>
</aop:aspect>
</aop:config>
</beans>
MyAspect
package com.gyf.aspect;
public class MyAspect {
public void myBefore(JoinPoint joinPoint){
System.out.println("前置通知...");
}
// 当后置通知需要用到被切函数的返回值时
// 可以在通知方法中增加Object retValue参数接收返回值
// 同时还要在applicationContext.xml 中修改
// <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut"></aop:after-returning>为
// <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut" returning="retValue"></aop:after-returning>
public void myAfterReturning(JoinPoint joinPoint, Object retValue){
System.out.println("后置通知...");
}
}
UserServiceImpl
package com.gyf.service.impl;
import com.gyf.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("service 添加用户");
}
}
测试结果
@Test
public void test(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.add();
}
执行结果:
前置通知...
service 添加用户
后置通知...
(3)环绕通知
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- userService -->
<bean id="userService" class="com.gyf.service.impl.UserServiceImpl"></bean>
<!-- 配置切面类对象 -->
<bean id="myAspect" class="com.gyf.aspect.MyAspect"></bean>
<aop:config>
<!-- aop:指定切面 -->
<aop:aspect ref="myAspect">
<!-- 定义一个切入点 -->
<aop:pointcut id="myPointcut" expression="execution(* com.gyf.service.*.*(..))"></aop:pointcut>
<!-- 配置环绕通知 -->
<aop:around method="myAround" pointcut-ref="myPointcut"></aop:around>
</aop:aspect>
</aop:config>
</beans>
MyAspect
package com.gyf.aspect;
public class MyAspect {
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知...");
// 获取原方法名
System.out.println(proceedingJoinPoint.getSignature().getName());
System.out.println("开启事务...");
// 执行原方法,retObj为原方法的返回值
Object retObj = proceedingJoinPoint.proceed();
System.out.println("提交事务...");
return retObj;
}
}
UserServiceImpl
package com.gyf.service.impl;
import com.gyf.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("service 添加用户");
}
}
测试结果
@Test
public void test(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.add();
}
执行结果:
环绕通知...
add
开启事务...
service 添加用户
提交事务...
(4)异常通知,最终通知示例
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- userService -->
<bean id="userService" class="com.gyf.service.impl.UserServiceImpl"></bean>
<!-- 配置切面类对象 -->
<bean id="myAspect" class="com.gyf.aspect.MyAspect"></bean>
<aop:config>
<!-- aop:指定切面 -->
<aop:aspect ref="myAspect">
<!-- 定义一个切入点 -->
<aop:pointcut id="myPointcut" expression="execution(* com.gyf.service.*.*(..))"></aop:pointcut>
<!-- 配置异常通知 参数throwing中的e为切面类中myAfterThrowing接收的异常参数e-->
<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointcut" throwing="e"></aop:after-throwing>
<!-- 配置最终通知 -->
<aop:after method="myAfter" pointcut-ref="myPointcut"></aop:after>
</aop:aspect>
</aop:config>
</beans>
MyAspect
package com.gyf.aspect;
import org.aspectj.lang.JoinPoint;
public class MyAspect {
public void myAfterThrowing(JoinPoint joinPoint, Throwable e){
System.out.println("异常通知..." + joinPoint.getSignature().getName() + "===" + e.getMessage());
}
public void myAfter(JoinPoint joinPoint){
System.out.println("最终通知..." + joinPoint.getSignature().getName());
}
}
UserServiceImpl
package com.gyf.service.impl;
import com.gyf.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("service 添加用户");
int i = 5 / 0;
}
}
测试类
@Test
public void test5(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.add();
}
输出结果
service 添加用户
异常通知...add===/ by zero
最终通知...add