前言
记录学习历程
AOP(Aspect Oriented Programming 面向切面编程)是一种编程范式,通过允许横切关注点的分离,提高模块化,AOP提供切面来将跨越对象关注点模块化
简介
日志、事务、安全验证等散布在系统各处的需要在实现业务逻辑时关注的事情称为“切面”、“关注点”,AOP要做的就是从系统中分离出“切面”,然后集中实现,从而独立地编写业务代码和方面代码,在系统运行时,在将方面“织入”到系统中。
使用AOP时,涉及到切面、通知、切入点、目标对象、代理对象、织入等概念
- 切面
是共有功能的实现,如日志切面、事务切面,在实际应用中通常是存放共有功能实现的普通Java类,要被AOP容器识别为切面,需要在配置中通过bean标记指定 - 通知
是切面的具体实现,以目标方法为参照点,根据放置的位置不同,可分为前置通知、后置通知、异常通知、环绕通知和最终通知,切面类中某个方法具体属于哪类通知,需要在配置中指定 - 切入点
用于定义通知应该织入哪些连接点 - 目标对象
指将要织入切面的对象,即那些被通知的对象 - 代理对象
将通知应用到目标对象之后,被动态创建的对象 - 织入
将切面应用到目标对象,从而创建一个新的代理对象的过程
基于xml配置文件的AOP实现
基于xml配置文件的方式实现前置通知
前置通知
前置通知在连接点(所织入的业务方法)前面执行,不会影响连接点的执行,除非此处抛出异常
例:
(1)创建接口MealService,添加方法browse(),模拟用户浏览商品的业务
package com.BeforeAdvice.Service;
public interface MealService {
public void browse(String LoginName,String mealName);
}
(2)创建MealService的实现类MealServiceImpl
package com.BeforeAdvice.Impl;
import com.BeforeAdvice.Service.MealService;
import org.springframework.stereotype.Service;
@Service("mealService")
public class MealServiceImpl implements MealService {
@Override
public void browse(String LoginName, String mealName) {
System.out.println("执行业务方法browse");
}
}
(3)创建日志通知类LogAdvice,编写生成日志记录的方法
(要将Spring包配置完整,不然会飘红)
package com.BeforeAdvice.Aop;
import org.springframework.stereotype.Controller;
import org.aspectj.lang.JoinPoint;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.text.SimpleDateFormat;
@Controller("logAdvice")
public class LogAdvice {
//此方法将作为前置通知
public void MyBeforeAdvice(JoinPoint joinpoint){
//获得业务方法参数
List<Object> list= Arrays.asList(joinpoint.getArgs());
//日志格式字符串
String logInfo="前置通知:"+
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+
" "+list.get(0).toString()+" 浏览商品: "+list.get(1).toString();
System.out.println(logInfo);
}
}
(4)编辑配置文件xml
采用AOP配置方法将日志类LogAdvice与业务组件MealService原本是两个互不相关的类和接口通过AOP元素进行配置,实现将日志通知类LogAdvice中的日志通知织入MealService中
<?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:context="http://www.springframework.org/schema/context"
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/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">
<!--配置指定扫描的基包-->
<context:component-scan base-package="com"></context:component-scan>
<!--配置aop-->
<aop:config>
<!--配置日志切面-->
<aop:aspect id="logaop" ref="logAdvice">
<!--定义切入点、切入点采用正则表达式-->
<aop:pointcut id="logpointcut" expression="execution(* com.BeforeAdvice.Service.MealService.*(..))"/>
<!--将日志通知类中MyBeforeAdvice方法指定为前置通知-->
<aop:before method="MyBeforeAdvice" pointcut-ref="logpointcut"/>
</aop:aspect>
</aop:config>
</beans>
通过aop:config元素进行AOP的配置,在配置时,通过 < aop:aspect > 子元素配置日志切面;在配置日志切面时,先通过< aop:pointcut >子元素定义切入点,切入点采用正则表达式,含义是对com.BeforeAdvice.Service.MealService中的所有方法都进行拦截,再通过< aop:before >子元素将日志通知类中的MyBeforeAdvice方法指定为前置通知
(5)测试类TestBeforeAdvice
package com.BeforeAdvice;
import com.BeforeAdvice.Service.MealService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestBeforeAdvice {
public static void main(String[] args){
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
MealService mealService=(MealService) context.getBean("mealService");
mealService.browse("zhangsan","麻辣香锅");
}
}
可以看出在业务方法browse执行前,先输出了日志通知类LogAdvice方法产生的日志记录