AOP切面前置后置环绕通知的讲解和运用之手写切面+详解

首先让我们先说一下切面需要我们关注哪些地方(组成部分),

1.切面点表达式[pointcut]:指定类的指定方法(就是为了定位指定类的指定方法)

2.方位:定义在指定方法的前,后,抛出异常,等等

3.执行横切业务逻辑:公共部分的代码,

注:那什么是公共部分的代码?如:日志记录,性能统计,安全控制,事务处理等方面

4.通知[advice] : 包涵方位,横切业务逻辑

5.连接点[JoinPoint] : 包涵切点表达式,方位

 

什么意思那??一张图告诉你,,,

接着我们要知道一个常识 ;

我们AOP可以实现的功能不用AOP也可以实现

 

那问题来了,我们不用AOP怎么实现这个日志的记录

那我们就举个Product的栗子:

比如写增加时(伪代码)

addProduct(){

    log.info("开始执行addProduct方法")

    添加产品的核心逻辑

    log.error("执行出错:某某某")

}

那删除也是一样的

deleteProduct(){

    log.info("开始执行deleteProduct方法")

    添加产品的核心逻辑

    log.error("执行出错:某某某")

}

 

之后的修改也一样,,,

那,问题来了,这样写方便吗???

是,现在写,感觉挺方便,但是有没有考虑到一个问题,

这是在写一个的增删改,那么我们在正常的项目里,有多少条增删改,,,

1百?一千?1一万???

那你就按1千算,1000×(3+2)=6000行代码

你这样就莫名其妙的写了6000行或者说N行代码,

你不会用AOP时,你又要必须写和用,你就得这样写

......

程序猿开发效率,代码冗余的问题,这都是很严重的问题

所以说,怎么解决现在这些非核心业务逻辑的代码,

那就用到切面...

那,一句话怎么总结这个切面,

在指定类的指定方法的前后进行特定的业务逻辑

为什么使用这个切面:

还是刚刚说的,为了将代码中的非核心代码提取出来,进行统一的处理,从而提高开发效率,让程序猿只需要关心核心业务逻辑代码

之后我们会说一说如何实现一个基本的前置通知与后置通知,让大家先了解了解

 

前置通知:

首先我们说说前置通知,

先来的简单的要求,要求:

在controller层的每个类的每个方法之前,打印出 '你好Java' 在说之前那我要强调的是,大家要知道,能用注解完成的,都可以用配置文件来完成,相反,用配置文件完成的也可以用注解来完成,

我就不用注解了,用注解的方式来完成

1、首先我们先建一个新的class文件,我的就叫 FHspect

2、直接输出 你好java

现在就写好了,就差配置它了,如果我不配置它,它就是一个普通的java类,如果我给它配置好,它就被称为切面类而不是普通的java类,这个类 叫 '切面类' ,这个方法就叫 '横切业务逻辑'

3、在spring-controller.cml里配置如下

我一一给大家解释,

①首先是 <bean> 标签里的 class 的路径,指向的是我们刚刚写的 class 类

②而 <bean> 标签的 id 指向的是下面的 <aop:config> 标签里面的 <aop:aspect> 标签下的 ref 的名称保持一致

③<aop:pointcut> 里的 expression 的意思是表达式的意思,那后面的是 execution(* 路径),而括号里的内容是说,这个路径下的controller下的,任意嵌套的子包 下的 任意 类 下的 任意 方法 ,而括号中的意思是代表 任意类型的参数

④<aop:pointcut> 标签里的 id 与 <aop:before> 标签里的 pointcut-ref 的名称保持一致

⑤要知道 <aop:pointcut> 标签代表的意思是方位 ,它里面的 method 跟的是你要执行的方法名

总结: 在这些 com.fh.shop.controller..*.*(..) 方法的前面执行 com.fh.shop.aspect.FHAspect 这个切面类中的 showInfo 的方法.

注: <aop:pointcut ref="fh123"> = com.fh.shop.aspect.FHAspect <aop:before> 标签里的 method = showInfo

这样就配置好了,那接下来就可以看效果了,,, ,,,

成功输出...

 

后置通知:

还是,再来说一个要求:

在controller层的每个类的每个方法之前/后,打印出 '你好Java_方法名()'

还是写一个普通的方法,

public void showInfo(JoinPoint jp){
    System.out.println("你好JAVA");
}

那怎么怎么获取方法名

public void showInfo(JoinPoint jp){
    String methodName = jp.getSignature.getName();
    System.out.println("你好JAVA");
}

注: getSignature 代表的是方法的签名,而 getName 代表的是方法名

最终的样子是如下的

//之前
public void showInfo(JoinPoint jp){
    String methodName = jp.getSignature.getName();
    System.out.println("前置:你好JAVA"+methodName +"()");
}
//之后
public void doInfo(JoinPoint jp){
    String methodName = jp.getSignature.getName();
    System.out.println("后置:你好JAVA"+methodName +"()");
}

这只是写完了一个普通方法,还是和刚刚一样,要去配置

在刚刚的配置后加上

<aop:after method="doInfo" pointcut-ref="fhPointCut">

实际效果如下:

这就好了,,,你就可以看看效果了

 

环绕通知

首先我们肯定是先要有一个类

我的就叫LogAspect

然后我需要导入相关的jar包了,

 

好了我的jar包也导入了然后我们就要该配置文件了,这总要有log4j来做为支持

log4j是放到resources文件夹下即可

我们一般项目上线后将log4j设置为 INFO 级别或比 INFO 级别更高的级别, debug 级别的太多了,所以一般不用,当然在开发的时候,当然随便了...

我们先设置成DeBug级别试一下 然后我们就启动,并访问以下,看它与正常情况下有什么区别,

就如你看到的一样,会将你执行的出你所用到的 SQL 语句,但是!!!毕竟 Debug打出来的数据太庞大了,大部分又不用,设置成 INFO 级别又不给打印 sql 语句,那我们就需要在log4j里配置一下了

log4j.rootLogger=info,名

log4j.logger.路径=debug

实际如下:

这两行的意思分别说说,

首先是第一行,log4j.rootLogger=info,名:

这是说,其他包下的(非项目中的包下的)都设置成 INFO 级别,

然后是第二行,log4j.logger.路径=debug:

只要是我这个项目路径下的全部都设置成 debug 级别

记住!现在是debug,是方便你做修改,但是!!!项目上线后,切记切记要将两个都改为INFO级别,或INFO更高的级别!!!

然后我们就要对所有的日志进行统一的处理,找到我们刚刚建的 LogAspect

我们就要先声明一个 log 的日志类

private static final Logger LOG = LoggerFactory.getLogger(LogAspect.class);

 

注:要注意的是Logger 用的是 org.slf4j 资源包,因为 slf 公司对 log 的资源做了封装,知道就行了...

接下来写一个 doLog 的普通方法,额,等等..

我们先要思考一个东西, 要知道,我们最后的要求是在控制台打印出:

执行XX类.方法名() 花费了XXX时间

执行XX类.方法名()报错:错误为XXX

好了,知道了要求,我们就可以用环绕通知了 ProceedingJoinPoint 实际代码为:

然后我们首先获取类名:

然后是方法名:

然后打印出来

打印有两种方式:

LOG.info("执行"+canonicalName+"类名为:"+methodName)

或者

LOG.info("执行{}类{}方法()",canonicalName, methodName)

注:这是用大括号当做占位符,然后用后面的别名替换占位符,小括号还是小括号

这些还是不够的,这样就需要讲到另一个东西了,

如果只有上面的这些代码,你是不能跳转,最后你跳转的结构是404,还需要写一个东西,

Object result = pjp.proceed()

还要捕获一下,而这句话的意思是:执行真正的业务逻辑

什么叫执行真正的业务逻辑?你可以理解为,执行了就不会出现刚刚的404

但是,光这样你还是会被环绕通知给拦截掉,所以,还要返回 result

在最上面先声明一个全局变量,再在下面返回 result

完整代码如下:

当然,这个方法也要改,它是有返回值的,它的返回值是 Object 类型,要不然,你还是会被环绕通知的切面类里面的方法拦截的

当然了,代码写完了,配置文件还是要配置的...

在配置再来个 <bean> 标签

再来个 <aop:aspect> 标签,你的 <aop:aspect> 标签可以写在之前的 <aop:config> 里面,然后...

后面的被遮住的 id 还是与 pointcut-ref 保持一致

然后就写完了,你就可以访问页面来看看效果了...

之后我们还要加一些东西,花费的时间,

在执行方法之前加如下内容,

这意思是:记录当前时间

然后在执行完后,再写一个:

意思是:记录结束时间

最后,在之前的info里面再加个如下内容:

而这个end-start是说:用最后的结束时间,减去开始时记录的时间最后等于中间执行一共花费的时间

注:两个所记录的时间最小到毫秒值,所以,最后减出来的也是到毫秒值

实际代码如下:

那,出现异常了又改怎么办?

还是和刚刚的一样也是写一个

那我们对于异常的处理,都是往外抛

最后整体的代码如下:

最后我们执行出来的效果也有了:

错误的打印我也有

错误级别就是error级别,因为我们给的级别比较低,是 debug 级别,所以只要比debug级别高的都会显示,哪怕我们最后项目上线,我们把级别改成 INFO 级别的,但是它还是会在控制台打印出 INFO 级别和 error 级别

在这里那我就说一下我们最常用几个级别,最常用的不是所有,

error>warn>info>debug

就行了....

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

槐序二十四

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值