一、概念【AOP(Aspect Orient Programming)面向切面编程】
定义:在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
可以这样理解,面向对象编程(OOP)是从静态角度考虑程序结构,面向切面编程(AOP)是从动态角度考虑程序运行过程。
二、AOP的优点
1、降低模块的耦合度
2、使系统容易扩展
3、设计决定的迟绑定
4、更好的代码复用性
三、作用
常常通过 AOP 来处理一些具有横切性质的系统性服务,如事物管理、安全检查、缓存、对象池管理等,AOP 已经成为一种非常常用的解决方案。
四、AOP的基本概念
◆Aspect :切面,一个关注点的模块化,这个关注点可能会横切多个对象。在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @Aspect 注解(@AspectJ风格)来实现。
◆ Join point :连接点, 在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。 在Spring AOP中,一个连接点 总是 代表一个方法的执行。 通过声明一个 org.aspectj.lang.JoinPoint 类型的参数可以使通知(Advice)的主体部分获得连接点信息。;
◆ Advice :通知,处理逻辑。在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括“around”、“before”和“after”等通知。 通知的类型将在后面部分进行讨论。许多AOP框架,包括Spring,都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。
◆ Pointcut :切点,匹配连接点(Joinpoint)的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。 切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。
◆ Introduction:引入,也称为内部类型声明。(也被称为内部类型声明(inter-type declaration))。声明额外的方法或者某个类型的字段。 Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。 例如,你可以使用一个引入来使bean实现 IsModified接口,以便简化缓存机制。
◆ Target:目标,即被通知的对象, 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(advised) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。
◆ proxy:代理,应用通知的对象, AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。 注意:Spring 2.0最新引入的基于模式(schema-based)风格和@AspectJ注解风格的切面声明,对于使用这些风格的用户来说,代理的创建是透明的。
◆Weaving: 织入,把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象。 这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。 Spring和其他纯Java AOP框架一样,在运行时完成织入。
五、AOP的Advice类型
★前置增强(Before advice):在某连接点之前执行的增强,但这个增强不能阻止连接点前的执行(除非它抛出一个异常)。
★后置返回增强(After returning advice): 在某连接点正常完成后执行的增强:例如,一个方法没有抛出任何异常,正常返回。
★ 后置异常增强(After throwing advice): 在方法抛出异常退出时执行的增强。
★ 后置最终增强(After (finally) advice):当某连接点退出的时候执行的增强(不论是正常返回还是异常退出)。
★ 环绕增强(Around Advice):包围一个连接点的增强,如方法调用。这是最强大的一种增强类型。 环绕增强可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
六、示例
1、注解法
package com.xxx.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
*
* 功能描述: 监控接口调用时间
* @author yyx
*/
@Aspect
@Component
public class PerformenceMonitor {
private static final Logger logger = LoggerFactory.getLogger(getClass());
/**
* 监控com.xxx.assp.web.service包及其子包的所有public方法
* <功能详细描述>
* @see [类、类#方法、类#成员]
*/
@Pointcut("execution(* com.xxx.assp.web.service..*.*(..))")
private void pointCutMethod() {
}
//声明环绕通知
@Around("pointCutMethod()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.nanoTime();
Object o = pjp.proceed();
long end = System.nanoTime();
logger.debug("{}:{}",pjp.getTarget().getClass()+"."+pjp.getSignature().getName(),(end-begin)/1000000);
return o;
}
}
2、配置文件法
package com.xxx.assp.service.util;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.xxx.assp.common.AsspException;
import com.xxx.assp.common.MessageDTO;
import com.xxx.assp.common.ResponseDTO;
import com.xxx.assp.util.filter.LogFilter;
/**
* 异常处理的切面 <br>
*
* <p>
* 功能:
* </p>
* <ul>
* <li>打印日志</li>
* <li>消息转换及标准化</li>
* <li>报警(短信、邮件)</li>
* <li>接口频率控制</li>
* <li>接口鉴权</li>
* <li>性能监控</li>
* </ul>
*
* @author
*/
public class CommonInterceptor {
private static Logger logger = LoggerFactory.getLogger(CommonInterceptor.class);
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// 从请求参数中读取事务ID
String callId = LogFilter.getCallId();
MessageDTO message = new MessageDTO();
message.setCallId(callId);
Object result = null;
try {
long start = System.currentTimeMillis();
message.setTimestamp(start);
result = proceedingJoinPoint.proceed(); // 具体业务逻辑处理
long end = System.currentTimeMillis();
message.setCost(end - start);
ResponseDTO base = (ResponseDTO) result;
base.setMessage(message);
MessageUtil.wrapSuccess(base);
} catch (Exception e) {
ResponseDTO tmp = new ResponseDTO();
tmp.setMessage(message);
result = tmp;
//设置失败属性
if (e instanceof AsspException) {
MessageUtil.wrapFailure((ResponseDTO) result, (AsspException) e);
} else {
MessageUtil.wrapFailure((ResponseDTO) result, e, Constant.SERVICE_EXCEPTION_OF_AOP, new Object[] {
e.getMessage()
});
}
}
logger.info("cost={}ms", message.getCost());
return result;
}
}
配置文件
<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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- hessian统一异常处理 -->
<bean id="asspInterceptor" class="com.xxx.assp.service.util.CommonInterceptor"></bean><!--切面-->
<aop:config>
<aop:pointcut id="asspPointcut" expression="execution(public * com.xxx.assp.service.impl..*.*(..))" /><!--切点-->
<aop:aspect id="asspAspect" ref="asspInterceptor">
<aop:around method="around" pointcut-ref="asspPointcut" />
</aop:aspect>
</aop:config>
</beans>