AOP — 面向切面编程
1.什么是AOP?
面向切面编程,能够让我们在不影响原有功能的前提下,为软件横向扩展 功能 ,是指在程序运行期间,将某段代码切入到指定方法的指定位置运行的编程方式
2.AOP通知类型
- 前置通知 — 目标方法执行前通知
- 后置通知 — 目标方法执行后通知,无论方法正常结束还是异常结束
- 返回通知 — 在目标方法正常返回之后通知
- 异常通知 — 在目标方法出现异常的情况下通知
- 环绕通知 — 动态代理,手动执行目标方法
图解:
3.AOP中的一些术语
- target:目标类,需要被代理的类。例如:UserService
- Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法。例如:所有的方法
- PointCut 切入点:已经被增强的连接点。例如:addUser()
- advice 通知/增强,增强代码。例如:after、before
- Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.
- proxy 代理类
- Aspect(切面): 是切入点pointcut和通知advice的结合
一个线是一个特殊的面。
一个切入点和一个通知,组成成一个特殊的面。
4.spring中使用注解制作切面类
a. 使用 @Aspect注解告诉spring容器,这是一个切面类
b. 可以抽取一个方法来写公共的切入点表达式
*号代表任意类型的返回值,然后是所在的类和方法名,*号同样代表任意,(…)表示任意参数
@Aspect
@Component
public class LogAspect {
/**
* 切面类
*/
private Logger logger = LoggerFactory.getLogger(getClass());
//切入点
@Pointcut("execution(public * com.kevin.library.controller.*.*(..))")
public void webLog() {
}
//前置通知
@Before("webLog()")
public void before(JoinPoint joinPoint)throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
logger.info("URL : " + request.getRequestURL().toString());
logger.info("HTTP_METHOD : " + request.getMethod());
logger.info("IP : " + request.getRemoteAddr());
logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
}
//后置通知
@AfterReturning(returning = "ret", pointcut = "webLog()")
public void afterReturning(Object ret) throws Throwable {
// 处理完请求,返回内容
logger.info("方法的返回值 : " + ret);
}
//后置异常通知
@AfterThrowing("webLog()")//JoinPoint一定要出现在参数表的第一位
public void returnThrowing(JoinPoint joinPoint) {
logger.info("方法异常时执行");
}
//最终通知
@After("webLog()")
public void after(JoinPoint joinPoint) {
logger.info("方法最终执行");
}
//环绕通知
@Around("webLog()")
public Object arround(ProceedingJoinPoint pjp) {
logger.info("方法环绕start.....");
try {
Object o = pjp.proceed();
logger.info("方法环绕proceed,结果是 :" + o);
return o;
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}
}
原来我们需要在配置文件中使用标签
这也是我们spring aop的一种写法
在xml里面配置文件中aop
i.将切面类交给spring管理
ii.声明Aop配置
<aop:config>
iii.定义切入点
aop:pointcut
iiii.定义通知类型
<aop:aspect ref = "切面类的引用">
前置通知
<aop:before method="切面类的方法名" pointcut-ref="切入点表达式是引用"/>
后置通知<aop:afterRunturning method="切面类的方法名" pointcut-ref="切入点表达式是引用"/>
异常通知<aop:after-throwing method="切面类的方法名" pointcut-ref="切入点表达式是引用"/>
最终通知<aop:after method="切面类的方法名" pointcut-ref="切入点表达式是引用"/>
注意:异常通知只有出现了异常了 才会有通知
</aop:aspect>
xml配置版
<!--在源码中,只要proxy-target-class="true" expose-proxy="true" 其中一个为true,则强制使用cglib字节码增强
如果不配置,默认都为false,则根据接口情况选择
如果目标类是接口 则使用 jdk动态代理
如果不是 则使用ObjensesisCglibAopProxy
-->
<aop:config proxy-target-class="true" expose-proxy="true">
<aop:aspect ref="aspect">
<aop:pointcut id="pointCut" expression="execution(public * main.java.config.*.*(..))"></aop:pointcut>
<aop:before method="before" pointcut-ref="pointCut"></aop:before>
<aop:after method="after" pointcut-ref="pointCut"></aop:after>
<aop:after-returning method="afterReturning" pointcut-ref="pointCut"></aop:after-returning>
<aop:after-throwing method="returnThrowing" pointcut-ref="pointCut"></aop:after-throwing>
<aop:around method="arround" pointcut-ref="pointCut"></aop:around>
</aop:aspect>
</aop:config>
上边提到的源码
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
return CglibProxyFactory.createCglibProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
怎样开启注解AOP
<!-- 开启对aop注解的支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
也可以直接在配置类使用注解
@EnableAspectJAutoProxy