SpringAop(日志记录)的实现
基于xml
1、环境搭建,添加需要引入的依赖
- spring-aop(spring-context, 把aop加载类),
- spring-aspects
<!-- 导入了context,会自动导入先关依赖,包括spring-aop --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.3.RELEASE</version> </dependency> <!--aop实现的框架 Aspects--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.3.3.RELEASE</version> </dependency>
2、创建一个通知/增强类
//将advice包下的LogAdvice交给spring容器管理
@Component("logAdvice") public class LogAdvice { // 使用log4j日志框架进行日志的记录 // Logger Log4j进行日志输出的的类 private Logger log = Logger.getLogger(LogAdvice.class); // 编写增强方法、前置增强 // JoinPoint 连接点对象,就是正在运行的目标方法的对象 public void beforeLog(JoinPoint joinPoint) { /* * log.debug("debug"); // 对应debug输出级别 * log.info("info"); // 对应info输出级别 * log.warn("warn"); // 对应warn输出级别 * log.error("error"); // 对应error输出级别 */ //得到目标对象 Object target = joinPoint.getTarget(); //由对象获取这个对象所在类的类名 getName() 获取的 包.类名的字符串 getSimpleName() 获取类名 String className = target.getClass().getSimpleName(); //得到正在执行的目标方法的签名(例:public int findUserById(int id)) String methodName = joinPoint.getSignature().getName(); //得到调用目标方法传递参数 Object[] args = joinPoint.getArgs(); //输出日志 log.info("开始执行:"+className+"的"+methodName+",传递的参数"+Arrays.asList(args)); //打印结果:com.zl.spring.advice.LogAdvice - 开始执行:UserServiceImpl的findUserById,传递的参数[2] } /** * 后置增强方法 * 得到目标方法的返回值 * rs: 接收目标方法的返回值 如果目标方法没有返回值, 得到null * @param joinPoint */ public void afterLog(JoinPoint joinPoint,Object rs) { Object target = joinPoint.getTarget(); String className = target.getClass().getSimpleName(); String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); log.info("后置增强开始执行:"+className+"的"+methodName+",传递的参数"+Arrays.asList(args)+"目标方法的返回值:"+rs); } //最终增强方法 public void finallyLog(JoinPoint joinPoint) { Object target = joinPoint.getTarget(); String className = target.getClass().getSimpleName(); String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); log.info("最终增强开始执行:"+className+"的"+methodName+",传递的参数"+Arrays.asList(args)); } //异常增强方法 //指定异常(exception\SQLException public void throwLog(JoinPoint joinPoint,RuntimeException e) { Object target = joinPoint.getTarget(); String className = target.getClass().getSimpleName(); String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); log.info("异常增强开始执行:"+className+"的"+methodName+",传递的参数"+Arrays.asList(args) +"出现异常"); }
其中环绕增强应该新建一个类,一个类可以有以上四种增强
//环绕增强
public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable{ log.info(jp.getSignature().getName()+"方法开始执行"); try { //执行目标方法 Object rs = jp.proceed(); log.info(jp.getSignature().getName()+"方法正常执行完"); return rs; } catch (SQLException e) { log.error("调用"+jp.getTarget()+"的"+jp.getSignature().getName() +"方法发生异常"+e); throw e; } }
3、 进行织入, 在spring的配置文件进行织入
使用前缀为:aop 的标签
①、导入命名空间
xmlns:aop="http://www.springframework.org/schema/aop"
...
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
配置aop
②、切入点的配置(将service包下的所有类,运行方法进行增强)
<aop:config> <aop:pointcut expression="execution( * spring06.service..*.*(..) )" id="pointcut1"/>
语法:
<!-- 切入点 id: 唯一标示符 expression: 切入点的表达式 语法格式 execution([访问修饰符] 返回值类型 包名.类名.方法名(参数列表) ) execution(public void spring06.service.impl.UserServiceImpl.findById(int) ) 对一个方法的增强 通配符: * 任意, 返回值可以使用, 类名可以使用, 方法名可以使用 在类名前加两个点: 表示这个包以及这个包所有的子包下的类参数表示任意: .. execution( * spring06.service..*.*(..) ) -->
③、增强的配置
通知类(增强的方法) ref: 对应通知类的bean的id <aop:aspect ref="logAdvice">
④、织入(满足条件进行织入)
<!-- 增强策略 : 增强分类的调用的方法--> <!-- 配置一个前置增强 method: 前置增强的方法名 pointcut-ref: 引用的切入点 织入: 对符合切入点的方法进行前置增强, 增强的方法是logAdvice的beforeLog()的方法 --> <aop:before method="beforeLog" pointcut-ref="pointcut1"/>
<!-- 后置增强 public void afterLog(JoinPoint joinPoint, Object rs) returning="参数名" 告诉spring,把目标方法的返回值赋值给这个增强方法的哪一个参数上, 要求returning后面的参数,与方法的接收返回值的参数名一样 --> <aop:after-returning method="afterLog" pointcut-ref="pointcut1" returning="rs" />
<!-- 最终增强: 不管是否发生异常, 都会执行 --> <aop:after method="finallyLog" pointcut-ref="pointcut1"/>
<!-- 配置异常增强 throwing: 参数名, 告诉spring,把异常对象赋值给增强方法的那个异常参数上 --> <aop:after-throwing method="throwLog" pointcut-ref="pointcut1" throwing="e"/>
</aop:aspect>
</aop:config>
如果在通知类中没有使用注解的形式交给spring管理可以使用默认构造方法
<!-- 通知类交给spring管理 --> <bean id="logAdvice" class="spring06.advice.LogAdvice"></bean>
4、扫描注解
<!-- 扫描注解 base-package: 包名, 扫描这个包,以及这个包后代包下所有类上的注解 --> <context:component-scan base-package="spring06" />
基于注解的方式
通知类使用注解
@Aspect //定义为一个通知类
方式一:
@Component("logAdvice2") @Aspect //定义为一个通知类 public class LogAdvice2 { private Logger log = Logger.getLogger(LogAdvice2.class); //最终增强方法 @After("execution( * com.zl.spring.service..*.*(..) )") public void finallyLog(JoinPoint joinPoint) { Object target = joinPoint.getTarget(); String className = target.getClass().getSimpleName(); String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); log.info("最终增强开始执行2:"+className+"的"+methodName+",传递的参数"+Arrays.asList(args)); } //异常增强方法 //指定异常(exception\SQLException @AfterThrowing(value="execution( * com.zl.spring.service..*.*(..) )",throwing = "e") public void throwLog(JoinPoint joinPoint,RuntimeException e) { Object target = joinPoint.getTarget(); String className = target.getClass().getSimpleName(); String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); log.info("异常增强开始执行2:"+className+"的"+methodName+",传递的参数"+Arrays.asList(args) +"出现异常"); }
方式二:
@Component("logAdvice2") @Aspect //定义为一个通知类 public class LogAdvice2 { private Logger log = Logger.getLogger(LogAdvice2.class); //加在方法上 @Pointcut("execution( * spring06.service..*.*(..) )" ) private void pit() {} @Before("pit()") public void beforeLog(JoinPoint joinPoint) { Object target = joinPoint.getTarget(); String className = target.getClass().getSimpleName(); String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); log.info("开始执行2:"+className+"的"+methodName+",传递的参数"+Arrays.asList(args)); } //后置增强方法 @AfterReturning(value="pit()",returning="rs") public void afterLog(JoinPoint joinPoint,Object rs) { Object target = joinPoint.getTarget(); String className = target.getClass().getSimpleName(); String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); log.info("后置增强开始执行2:"+className+"的"+methodName+",传递的参数"+Arrays.asList(args)+"目标方法的返回值:"+rs); }
applicationContext.xml中扫描注解:
<!-- 扫描注解 base-package: 包名, 扫描这个包,以及这个包后代包下所有类上的注解 --> <context:component-scan base-package="com.zl.spring" /> <!-- 扫描通知类中的注解 --> <aop:aspectj-autoproxy/>