Spring中AOP的运用_spring里面的aop怎么用-程序员宅基地

技术标签: spring  Java  Java框架  java  aop  

Spring中的AOP(面向切面编程)运用十分广泛和方便,我们常用于日志、事务的处理,其实能够用到的地方远远不止于此,这篇文章主要就介绍AOP的用法和一些运用实例以及思路。

一、AOP常用术语

1.通知(Advice):
通知定义了切面是什么以及何时使用。描述了切面要完成的工作和何时需要执行这个工作。(切面何时使用,即注有@Around、@Before、@After等注解的方法)

2.连接点(Joinpoint):
程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法被调用时、异常被抛出时等等。(在Spring中,所有的方法都可以认为是joinpoint,但是我们不希望所有的方法都添加Advice,而pointcut的作用就是提供一组规则来匹配joinpoint,给满足规则的joinpoint添加Advice。)

3.切入点(Pointcut)
通知定义了切面要发生的“故事”和时间,那么切入点就定义了“故事”发生的地点,例如某个类或方法的名称,Spring中允许我们方便的用正则表达式来指定。

4.切面(Aspect)
通知和切入点共同组成了切面:时间、地点和要发生的“故事”

5.引入(Introduction)
引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)

6.目标(Target)
即被通知的对象,如果没有AOP,那么它的逻辑将要交叉别的事务逻辑,有了AOP之后它可以只关注自己要做的事(AOP让他做爱做的事)

7.代理(proxy)
应用通知的对象,详细内容参见设计模式里面的代理模式

8.织入(Weaving)
把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:
(1)编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器
(2)类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码
(3)运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术

二、AOP 常用注解

1.@Aspect:
作用:把当前类声明为切面类。

2.@Before:
作用:把当前方法看成是前置通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用。

3.@After
作用:把当前方法看成是始终通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用。

4.@Around
作用:把当前方法看成是环绕通知。包围一个连接点的通知,可以在核心方法前后完成自定义的行为。这是最常用最重要的。这个注解需要传入参数ProceedingJoinPoint pjp,决定是否进入核心方法----调用pjp.proceed();如果不调的话将不进入核心方法!
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用。

5.@Pointcut
作用:指定切入点表达式
属性:
value:指定表达式的内容

6.@AfterReturning
作用:把当前方法看成是后置通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用。

7.@AfterThrowing
作用:把当前方法看成是异常通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用。

三、@BEFORE @AROUND @AFTER等执行顺序

@Before:核心代码执行前通知
@After:连接点执行退出时通知(不论正常返回还是异常退出)
@Around:在核心方法前后完成自定义的行为(这个注解需要传入参数ProceedingJoinPoint pjp,决定是否进入核心方法----调用pjp.proceed();如果不调的话将不进入核心方法!)
@AfterReturning:返回后通知,正常返回后通知
@AfterThrowing:返回后通知,正常返回后通知

注意:除了@Around传的参数是ProceedingJoinPoint pjp外,其它都是传的JoinPoint jp,也就是说能控制是否进入核心代码的只有Around,因为AOP走到核心代码就是通过调用ProceedingJoinPoint的proceed()方法,而JoinPoint没有这个方法

前后顺序:@Before==》@Around==》@After

四、AOP原理

动态代理(Proxy+InvocationHandler)
指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式。
简单解析动态代理:
用到Proxy+InvocationHandler。下面接用别人的代码简要说明:

public class HelloInvocationHandle implements InvocationHandler {
    
    private Object object;
    public HelloInvocationHandle(Object o) {
    
        this.object = o;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
        System.out.println("method: " + method.getName() + " is invoked");
        System.out.println("proxy: " + proxy.getClass().getName());
        Object result = method.invoke(object, args);
        // 反射方法调用
        return result;
    }
}
// HelloWorld 是一个接口,此处没有贴出来
Class<?> proxyClass = Proxy.getProxyClass(HelloWorld.class.getClassLoader(), HelloWorld.class);
Constructor cc = proxyClass.getConstructor(InvocationHandler.class);
InvocationHandler ihs = new HelloInvocationHandle(new HelloWorldImpl());
HelloWorld helloWorld = (HelloWorld) cc.newInstance(ihs);

套路就是先获取Proxy生成的class,然后获取其中使用了InvocationHandler作为参数的构造器,使用反射newInstance 实现代理对象helloWorld的生成。

当然Proxy也提供了更加方便的方法给我们使用:

final InvocationHandler in = new HelloInvocationHandle(new HelloWorldImpl());
HelloWorld helloWorld = (HelloWorld) Proxy.newProxyInstance(
    HelloWorld.class.getClassLoader(),    // 被代理对象的类加载器
    HelloWorld.class.getInterfaces(),    // 被代理对象的接口(数组,可保护多个)
    in);   // InvocationHandler实例对象

五、Spring提供了4种实现AOP的方式

1.经典的基于代理的AOP
2.@AspectJ注解驱动的切面
3.纯POJO切面
4.注入式AspectJ切面

具体可参考:
https://blog.csdn.net/J2EEWEIWEI/article/details/5844741?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control

四、运用场景

AOP的使用步骤
1)将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect)
2)在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
3)开启基于注解的aop模式;@EnableAspectJAutoProxy

我们都知道Spring两大核心思想:DI/IOC和AOP,但要是问到你怎么用?哪里用?还是会有些含糊,这里就讲讲我理解的AOP能在那些地方用到。
(1)场景一:日志记录
(2)场景二:异常处理
(3)场景三:SQL记录,可能我们需要将执行的SQL语句,执行时间等信息记录到数据库表中,这时候AOP就能起到重要作用。
示例代码:
这里就是在执行数据库的增删改查时我们用到了AOP,其中process()方法就是封装了SQL语句以及执行时间的插入对应的表中。

@Around(value = "execution(public * org.mybatis.spring.SqlSessionTemplate.insert(..))")
    public Object insertProcess(ProceedingJoinPoint pjp) throws Throwable {
    
        return process(pjp);
}

@Around(value = "execution(public * org.mybatis.spring.SqlSessionTemplate.update(..))")
public Object updateProcess(ProceedingJoinPoint pjp) throws Throwable {
    
        return process(pjp);
}

@Around(value = "execution(public * org.mybatis.spring.SqlSessionTemplate.delete(..))")
public Object deleteProcess(ProceedingJoinPoint pjp) throws Throwable {
    
        return process(pjp);
}

@Around(value = "execution(public * org.mybatis.spring.SqlSessionTemplate.selectList(..))")
public Object selectListProcess(ProceedingJoinPoint pjp) throws Throwable {
    
        return process(pjp);
}

(4)场景四:释放Redis锁相关内容。

/**
     * 事件增删改完成后释放当前线程拥有的锁
     */
    @After("pigEventHandle() || pigBatchEventHandle() || pigRollback() || pigModify()" +
            "|| newGroupHandle() || batchNewGroupHandle() || groupEventHandle() || groupBatchEventHandle() " +
            "|| groupRollback() || groupModify()")
    public void delAllKey(final JoinPoint point){
    
        doctorConcurrentControl.delAll();
    }

    @Pointcut(value = "execution(* io.terminus.doctor.event.manager.DoctorPigEventManager.eventHandle(..))")
    private void pigEventHandle(){
    
    }

    @Pointcut(value = "execution(* io.terminus.doctor.event.manager.DoctorPigEventManager.batchEventsHandle(..))")
    private void pigBatchEventHandle(){
    
    }

    @Pointcut(value = "execution(Long io.terminus.doctor.event.manager.DoctorGroupManager.createNewGroup(..))")
    private void newGroupHandle(){
    
    }

    @Pointcut(value = "execution(Long io.terminus.doctor.event.manager.DoctorGroupManager.batchNewGroupEventHandle(..))")
    private void batchNewGroupHandle(){
    
    }

    @Pointcut(value = "execution(* io.terminus.doctor.event.manager.DoctorGroupEventManager.handleEvent(..))")
    private void groupEventHandle(){
    
    }

    @Pointcut(value = "execution(* io.terminus.doctor.event.manager.DoctorGroupEventManager.batchHandleEvent(..))")
    private void groupBatchEventHandle(){
    
    }

    @Pointcut(value = "execution(* io.terminus.doctor.event.manager.DoctorRollbackManager.rollbackPig(..))")
    private void pigRollback(){
    
    }

    @Pointcut(value = "execution(* io.terminus.doctor.event.manager.DoctorPigEventManager.modifyPigEventHandle(..))")
    private void pigModify(){
    
    }

    @Pointcut(value = "execution(* io.terminus.doctor.event.manager.DoctorRollbackManager.rollbackGroup(..))")
    private void groupRollback(){
    
    }

    @Pointcut(value = "execution(* io.terminus.doctor.event.manager.DoctorGroupEventManager.modifyGroupEventHandle(..))")
    private void groupModify(){
    
    }

暂时先写了些基础,后续继续更行

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_16268979/article/details/115059049

智能推荐

【Linux :编辑文件的方式】_linux 非交互方式修改文件-程序员宅基地

文章浏览阅读815次。一般可以把文件用sftp方式下载下来,在本地电脑修改好后再上传上去, 但有时候可能必须在linux里面修改,在Linux里面修改涉及到两种方式第一种:vi/vim两个常用linux端文本编辑工具, vi/vim 编辑工具1.vi/vim是什么?①vi是Visual Interface的缩写,即 可视化接口②vim是vi iMprove的缩写,即 vi的增强版(具有语法着色功能)2.vim模式有哪些?如何切换?①三种模式命令模式(默认):刚进入vim的时候,默认就是 命令模式,可以复制行,删除_linux 非交互方式修改文件

Imagination先进半导体IP引领汽车智能化发展 | IC China2020-程序员宅基地

文章浏览阅读155次。10月14-16日,由中国半导体行业协会、中国电子信息产业发展研究院主办的第三届全球IC企业家大会暨第十八届中国国际半导体博览会(IC China 2020)在上海新举行。本届大会暨博览..._imagination的半导体接口ip

Linux基础入门_linux license agreement-程序员宅基地

文章浏览阅读251次。Linux基础入门及安装虚拟机、操作系统_linux license agreement

uniapp - 实现日期选择器(年月日)组件,兼容 H5、App、小程序,提供组件源码可直接复制运行!_uni-app封装日期时间选择器-程序员宅基地

文章浏览阅读4.8k次,点赞3次,收藏3次。uniapp日期选择器,uniapp兼容h5小程序app的年月日选择器功能组件,uniapp日期选择组件插件源码,uniapp日期选择弹框示例代码,uniapp年月日的日期选择器组件,uniapp日期选择器(年月日),Uniapp实现了日期选择器,uniapp实现日期选择效果,uni-app 自定义选择日期,uniapp实现日期的选择,uni-app中组件picker的日期选择器,uniapp里怎么做日期选择功能,uniapp如何实现选择日期的弹框,uniapp不引组件库怎么实现日期选择,uniapp日期组_uni-app封装日期时间选择器

ACE2005语料库文件格式及内容解析_ace05-程序员宅基地

文章浏览阅读1.6k次。前提ACE2005语料库是语言数据联盟(LDC)发布的由实体,关系和事件注释组成的各种类型的数据,包括英语,阿拉伯语和中文培训数据,目标是开发自动内容提取技术,支持以文本形式自动处理人类语言。ACE语料解决了五个子任务的识别:entities、values、temporal expressions、relations and events。这些任务要求系统处理文档中的语言数据,然后为每个文档输出有关其中提到或讨论的实体,值,时间表达式,关系和事件的信息。ACE语料库的获取链接:https://cata_ace05

yolov3训练自己的数据_windows yolov3训练自己的数据-程序员宅基地

文章浏览阅读878次,点赞3次,收藏4次。谈谈如何实现用yolov3训练自己的数据前言:早就听说yolov3算法识别率高,“快如闪电,可称目标检测之光”,最近在研究yolov3算法,动手训练了自己的数据集,其间出现很多问题,所以写下这篇博客记录一下。一.编译源码并运行demo运行demo的话,我主要是采用YunYang1994的代码,github上的代码链接为:https://github.com/YunYang1994/tens..._windows yolov3训练自己的数据

随便推点

如何使用promise封装 原生ajax,$.ajax(),以及fetch?(使用promise封装之后,使用封装好的方法请求接口,三种方法都可以好使)_new promise如何套用原生ajax-程序员宅基地

文章浏览阅读203次。文章目录1.如何使用promise封装原生ajax2.如何使用promise封装$ajax()3.fetch的使用1.fetch请求数据2.Response对象1.如何使用promise封装原生ajaxget function getRequest(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest() xhr.open(_new promise如何套用原生ajax

聊聊通用的架构设计_thoughtwork 技术全景-程序员宅基地

文章浏览阅读8.6k次,点赞2次,收藏12次。本文通过从业务架构到IT架构,以及IT架构下的应用架构、数据架构、技术架构和基础设施架构的介绍,配合一些示例的展示,描绘了架构设计中一般包含哪些内容。_thoughtwork 技术全景

你了解这些常用器件封装及其名字来历么?-程序员宅基地

文章浏览阅读1.7k次,点赞2次,收藏30次。关注、星标公众号,不错过精彩内容1、BGA|ball grid array也称CPAC(globe top pad array carrier)。球形触点陈列,表面贴装型封装之一。在印刷基板的背面按陈列方式制作出球形凸点用以代替引脚,在印刷基板的正面装配LSI 芯片,然后用模压树脂或灌封方法进行密封。也称为凸点陈列载体(PAC)。引脚可超过200,是多引脚LSI用的一种封装。封装本体也可做得比QF..._弹针封装的命名

IDEA 当前项目jdk版本查看_idea查看项目jdk版本-程序员宅基地

文章浏览阅读8.2k次,点赞3次,收藏2次。File->Project Structure->project->SDKmaven打包JDKFile->Settings->runner->jreFile->Settings->Importing->jdk_idea查看项目jdk版本

Chrome等浏览器下出现net::ERR_BLOCKED_BY_CLIENT的解决办法-程序员宅基地

文章浏览阅读1.1w次,点赞4次,收藏2次。1.在开发过程中,打开网页的调试检查页面,有时候我们会看到 net::ERR_BLOCKED_BY_CLIENT这样的错误。2.这种情况是因为我们在浏览器扩展中安装了广告过滤插件,关闭广告过滤插件即可。3.关闭后结果..._net::err_blocked_by_client

ubuntu20.04——hdaudioC0D2: unable to bind the codec-程序员宅基地

文章浏览阅读1.1w次,点赞3次,收藏48次。问题不久前尝试双系统,win10+ubuntu20.04,安装比较顺利,能够正常进入系统。但是,当我更新了显卡驱动后(专有 nvidia-drivers-390),重启就无法进入系统的图形界面,一直卡在下面这个页面。如下图:解决之后,选择ubunru 高级选项,选择低版本的内核启动,却可以进入图形界面。但是,这样让我很不爽。。。最后,我找到某位大佬说进入/etc/default/grub编辑:可以使用低版本的内核启动图形界面进入编辑,也可以 Ctr+Alt+F2 进入命令行编辑;# 原_unable to bind the codec