一. Aop
Aspect Oriented Programming:面向切面编程 ,通过预编译或和运行期动态代理实现程序的统一维护的一种技术。如果多个类中出现重复的代码,可以考虑定义一个共同的抽象类,将这些共同的代码提取到抽象类中。AOP就是希望将这些分散在各个业务逻辑代码中的相同代码,通过横向切割的方式抽取到一个独立的模块中,让业务逻辑类依然保存最初的单纯。
主要的功能是:日志记录,性能统计,安全控制,事务处理和异常处理等等
AOP实现方式
.预编译 -AspectJ
.运行期动态代理(JDK动态代理、CGLib动态代理)
-SpringAOP 、JbossAOP
AOP的概念
1.切面(Aspect):一个关注点的模块化,这个模块可以横切多个对象。切面是通知和切点的结合,它既包括了横切逻辑的定义,也包括了连接点的定义
2.连接点(Joinpoint):程序执行过程的某个特定的点,如某个方法调用前,调用后,方法抛出异常后,这些代码中的特定点称为连接点。简单来说,就是在哪加入你的逻辑增强。连接点表示具体要拦截的方法,下面切点是定义一个范围,而连接点是具体到某个方法
3.切入点(Pointcut):匹配连接点的断言,每个程序的连接点有多个,如何定位到某个具体的连接点,就需要通过切点来定位。通常我们采用表达式的方式来设置,所以关键词是范围
4.通知(Advice):AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
就是切面的某个特定的连接点上执行的动作,切面中的某个方法。
5.引入(Introduction):在不修改类代码的前提下,为类添加新的方法和属性
6.目标对象(Taret Objectt):被一个或多个切面所通知的对象,需要被加强的业务对象
7.织入(Weaving):将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。织入就是将增强添加到对目标类具体连接点上的过程。
织入是一个形象的说法,具体来说,就是生成代理对象并将切面内容融入到业务流程的过程。
织入分为:编译时织入,类加载时织入、执行时织入
8.AOP代理(AOP Proxy)一个类被AOP织入增强后,就产生了一个代理类。是AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方式执行等功能)
二 。切面配置
Spring 中所有的切面和通知器都必须放在一个<aop:config>中(可以配置多个<aop:config>元素)每个<aop:config>中可以包含 Pointcut 、Advisors和aspect。当必须按顺序声明。
切入点配置
当<aop:pointcut>元素作为<aop:config>元素的子元素定义时,表示该切入点是全局切入点,它可被多个切面所共享;当<aop:pointcut>元素作为<aop:aspect>元素的子元素时,表示该切入点只对当前切面有效。
定义<aop:pointcut>元素时,通常会指定id和expression两个属性。
属性名称 | 解释 |
---|---|
id | 用于指定切入点的唯一标识名称。 |
expression | 用于指定切入点关联的切入点表达式。 |
通知配置
使用<aop:aspect>的子元素可以配置5种常用通知,这5个子元素不支持使用子元素,但在使用时可以指定一些属性,其常用属性及其描述如下:
pointcut: 该属性用于指定一个切入点表达式,Spring将在匹配该表达式的连接点时织入该通知。
pointcut-ref : 该属性指定一个已经存在的切入点名称,通常pointcut和pointcut-ref两个属性只需要使用其中之一。
method: 该属性指定一个方法名,指定将切面Bean中的该方法转换为增强处理。
throwing : 该属性只对<after-throwing>元素有效,用于指定一个形参名,异常通知方法可以通过该形参访问目标方法所抛出的异常。
returning: 该属性只对<after-returning>元素有效,用于指定一个形参名,后置通知方法可以通过该形参访问目标方法的返回值。
例子:创建用户管理接口UserDao接口以及实现类UserDaoImpl类
package com.example.aspectjxml;
public interface UserDao {
public void addUser();
public void delUser();
}
package com.example.aspectjxml;
public class UserDaoImpl implements UserDao{
@Override
public void addUser() {
// TODO Auto-generated method stub
System.out.println("添加用户");
}
@Override
public void delUser() {
// TODO Auto-generated method stub
System.out.println("删除用户");
}
}
创建切面类MyAspect类。
package com.example.aspectjxml;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect {
//前置通知
public void myBefore(JoinPoint joinPoint){
System.out.println("前置通知:"+joinPoint);
System.out.println("被植入增强处理的目标方法:"+joinPoint.getSignature().getName());
}
//后置通知
public void myAfterReturrning(JoinPoint joinPoint){
System.out.println("后置通知:"+joinPoint);
System.out.println("被植入增强处理的目标方法:"+joinPoint.getSignature().getName());
}
/*
* 环绕通知
* proceedingJoinPoint是JoinPoint子接口,表示可以执行目标方法
* 1.必须是Object类型的返回值
* 2.必须接收一个参数,类型为ProceedingJoinPoint
* 3.必须throws Throwable
*/
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("环绕前通知");
//执行当前目标方法
Object obj = proceedingJoinPoint.proceed();
System.out.println("环绕后通知");
return obj;
}
//异常通知
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("异常通知:"+e.getMessage());
}
//最终通知
public void myAfter(){
System.out.println("最终通知");
}
}
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 目标类 -->
<bean id="userDao" class="com.example.aspectjxml.UserDaoImpl"></bean>
<!-- 切面 -->
<bean id="myAspect" class="com.example.aspectjxml.MyAspect"></bean>
<aop:config>
<!-- 配置切面 -->
<aop:aspect ref="myAspect">
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* com.example.aspectjxml.*.*(..))" id="myPointCut"/>
<!-- 前置通知 -->
<aop:before method="myBefore" pointcut-ref="myPointCut"/>
<!-- 后置通知 returning属性:用于设置后置通知的第二个参数的名称,类型是Object -->
<aop:after-returning method="myAfterReturrning" pointcut-ref="myPointCut" returning="returnVal"/>
<!-- 环绕通知 -->
<aop:around method="myAround" pointcut-ref="myPointCut"/>
<!-- 异常通知:如果没有异常,将不会执行增强
throwing属性:用于设置通知第二个参数的的名称、类型-->
<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
<!-- 最终通知 -->
<aop:after method="myAfter" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>
</beans>
环绕通知,目标方法参数传入环绕通知方法中
注意:如果在同一个连接点有多个通知需要执行,那么在同一切面中,目标方法之前的前置通知和环绕通知的执行顺序是未知的,目标方法之后的后置通知和环绕通知的执行顺序是未知的。