Spring AOP全面详解(超级详细)「建议收藏」

Spring AOP全面详解(超级详细)「建议收藏」AOP(AspectOrientProgramming),直译过来就是面向切面编程,AOP是一种编程思想,是面向对象编程(OOP)的一种补充。面向切面编程,实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术,如下图所示:AOP可以拦截指定的方法并且对方法增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离,比如Spring的事务,通过事务的注解配置,Spring会自动在业务方法中开启、提交业务,并且在业务处理失败时,执行相应的回滚策略。

如果说IOC 是 Spring 的核心,那么面向切面编程AOP就是 Spring 另外一个最为重要的核心@mikechen

AOP的定义

AOP (Aspect Orient Programming),直译过来就是 面向切面编程,AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。

面向切面编程,实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术,如下图所示:Spring AOP全面详解(超级详细)「建议收藏」

 

AOP可以拦截指定的方法并且对方法增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离,比如Spring的事务,通过事务的注解配置,Spring会自动在业务方法中开启、提交业务,并且在业务处理失败时,执行相应的回滚策略。

AOP的作用

AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。

主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。

简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。

AOP的应用场景

比如典型的AOP的应用场景:\Spring AOP全面详解(超级详细)「建议收藏」

 

  • 日志记录
  • 事务管理
  • 权限验证
  • 性能监测

AOP可以拦截指定的方法,并且对方法增强,比如:事务、日志、权限、性能监测等增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离。

Spring AOP的术语

在深入学习SpringAOP 之前,让我们先对AOP的几个基本术语有个大致的概念。

AOP核心概念Spring AOP全面详解(超级详细)「建议收藏」

 Spring AOP 通知分类Spring AOP全面详解(超级详细)「建议收藏」

 Spring AOP 织入时期Spring AOP全面详解(超级详细)「建议收藏」

 

Spring AOP三种使用方式

AOP编程其实是很简单的事情,纵观AOP编程,程序员只需要参与三个部分:

1、定义普通业务组件

2、定义切入点,一个切入点可能横切多个业务组件

3、定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作

所以进行AOP编程的关键就是定义切入点和定义增强处理,一旦定义了合适的切入点和增强处理,AOP框架将自动生成AOP代理,即:代理对象的方法=增强处理+被代理对象的方法。

方式1:使用Spring自带的AOP

public class LogAdvice implements MethodBeforeAdvice, AfterReturningAdvice,MethodInterceptor {
    @Override
    public void before(Method method, Object[] objects, Object target) throws Throwable {
        //前置通知
    }

    @Override
    public void afterReturning(Object result, Method method, Object[] objects, Object target) throws Throwable {
        //后置通知
    }
     @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        //环绕通知
        //目标方法之前执行
        methodInvocation.proceed();   //目标方法

        //目标方法之后执行
        return resultVal;
    }
}

配置通知时需实现org.springframework.aop包下的一些接口

  • 前置通知:MethodBeforeAdvice
  • 后置通知:AfterReturningAdvice
  • 环绕通知:MethodInterceptor
  • 异常通知:ThrowsAdvice

创建被代理对象

<bean id="orderServiceBean" class="com.apesource.service.impl.OrderServiceImpl"/>
<bean id="userServiceBean" class="com.apesource.service.impl.UserServiceImpl"/>

通知(Advice)

<bean id="logAdviceBean" class="com.apesource.log.LogAdvice"/>
<bean id="performanceAdviceBean" class="com.apesource.log.PerformanceAdvice"/>

切入点(Pointcut):通过正则表达式描述指定切入点(某些 指定方法)

<bean id="createMethodPointcutBean"         class="org.springframework.aop.support.JdkRegexpMethodPointcut">
    <!--注入正则表达式:描述那些方法为切入点-->
    <property name="pattern" value=".*creat.*"/>
</bean>

Advisor(高级通知) = Advice(通知) + Pointcut(切入点)

<bean id="performanceAdvisorBean" class="org.springframework.aop.support.DefaultPointcutAdvisor">
        <!--注入切入点-->
        <property name="pointcut" ref="createMethodPointcutBean"/>

        <!--注入通知-->
        <property name="advice" ref="performanceAdviceBean"/>
    </bean>

创建自动代理

<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <!--Bean名称规则(数组):指定那些bean创建自动代理-->
        <property name="beanNames">
            <list>
                <value>*ServiceBean</value>
                <value>*TaskBean</value>
            </list>
        </property>

        <!--通知列表:需要执行那些通知-->
        <property name="interceptorNames">
            <list>
                <value>logAdviceBean</value>
                <value>performanceAdvisorBean</value>
            </list>
        </property>
</bean>

方式2:使用Aspectj实现切面(普通POJO的实现方式)

导入Aspectj相关依赖

<!--aop依赖1:aspectjrt -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.5</version>
</dependency>

<!--aop依赖2: aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>

通知方法名随便起,没有限制

public class LogAspectj {
    //前置通知
    public void beforeAdvice(JoinPoint joinPoint){
        System.out.println("========== 【Aspectj前置通知】 ==========");
    }

    //后置通知:方法正常执行后,有返回值,执行该后置通知:如果该方法执行出现异常,则不执行该后置通知 
    public void afterReturningAdvice(JoinPoint joinPoint,Object returnVal){
        System.out.println("========== 【Aspectj后置通知】 ==========");
    }
    public void afterAdvice(JoinPoint joinPoint){
        System.out.println("========== 【Aspectj后置通知】 ==========");
    }

    //环绕通知
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("##########【环绕通知中的前置通知】##########");
        Object returnVale = joinPoint.proceed();
        System.out.println("##########【环绕通知中的后置通知】##########");
        return returnVale;
    }

    /**
     * 异常通知:方法出现异常时,执行该通知
     */
    public void throwAdvice(JoinPoint joinPoint, Exception ex){
        System.out.println("出现异常:" + ex.getMessage());
    }

}

使用Aspectj实现切面,使用Spring AOP进行配置

<!--业务组件bean-->
<bean id="userServiceBean" class="com.apesource.service.impl.UserServiceImpl"/>

<!--日志Aspect切面-->
<bean id="logAspectjBean" class="com.apesource.log.LogAspectj"/>

<!--使用Aspectj实现切面,使用Spring AOP进行配置-->
<aop:config>
    <!--配置切面-->
    <!--注入切面bean-->
    <aop:aspect ref="logAspectjBean">
        <!--定义Pointcut:通过expression表达式,来查找 特定的方法(pointcut)-->
        <aop:pointcut id="pointcut"
                     expression="execution(* com.apesource.service.impl.*.create*(..))"/>

        <!--配置"前置通知"-->
        <!--在pointcut切入点(serviceMethodPointcut)查找到 的方法执行"前",
            来执行当前logAspectBean的doBefore-->
        <aop:before method="beforeAdvice" pointcut-ref="pointcut"/>

        <!--配置“后置通知”-->
        <!--returning属性:配置当前方法中用来接收返回值的参数名-->
        <aop:after-returning returning="returnVal" 
                             method="afterReturningAdvice" pointcut-ref="pointcut"/> 
        <aop:after method="afterAdvice" pointcut-ref="pointcut"/>
        
        <!--配置"环绕通知"-->
        <aop:around method="aroundAdvice" pointcut-ref="pointcut"/>
        
        <!--配置“异常通知”-->
        <!--throwing属性:配置当前方法中用来接收当前异常的参数名-->
        <aop:after-throwing throwing="ex" method="throwAdvice" pointcut-ref="pointcut"/>
    </aop:aspect>

</aop:config>

方式3:使用Aspectj实现切面(基于注解的实现方式)

//声明当前类为Aspect切面,并交给Spring容器管理
@Component
@Aspect
public class LogAnnotationAspectj {
    private final static String EXPRESSION = 
                            "execution(* com.apesource.service.impl.*.create*(..))";

    //前置通知   
    @Before(EXPRESSION)
    public void beforeAdvice(JoinPoint joinPoint){
        System.out.println("========== 【Aspectj前置通知】 ==========");
    }


    //后置通知:方法正常执行后,有返回值,执行该后置通知:如果该方法执行出现异常,则不执行该后置通知
    @AfterReturning(value = EXPRESSION,returning = "returnVal")
    public void afterReturningAdvice(JoinPoint joinPoint,Object returnVal){
        System.out.println("========== 【Aspectj后置通知】 ==========");
    }

    //后置通知
    @After(EXPRESSION)
    public void afterAdvice(JoinPoint joinPoint){
        System.out.println("========== 【Aspectj后置通知】 ==========");
    }

    //环绕通知
    @Around(EXPRESSION)
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("##########【环绕通知中的前置通知】##########");
        Object returnVale = joinPoint.proceed();
        System.out.println("##########【环绕通知中的后置通知】##########");
        return returnVale;
    }

    // 异常通知:方法出现异常时,执行该通知
    @AfterThrowing(value = EXPRESSION,throwing = "ex")
    public void throwAdvice(JoinPoint joinPoint, Exception ex){
        System.out.println("********** 【Aspectj异常通知】执行开始 **********");
        System.out.println("出现异常:" + ex.getMessage());
        System.out.println("********** 【Aspectj异常通知】执行结束 **********");
    }

}
<!-- 自动扫描器 -->
<context:component-scan base-package="com.apesource"/>

<!--配置Aspectj的自动代理-->
<aop:aspectj-autoproxy/>

Spring AOP的实现原理

Spring的AOP实现原理其实很简单,就是通过动态代理实现的。

Spring AOP 采用了两种混合的实现方式:JDK 动态代理和 CGLib 动态代理。

  • JDK动态代理:Spring AOP的首选方法。 每当目标对象实现一个接口时,就会使用JDK动态代理。目标对象必须实现接口
  • CGLIB代理:如果目标对象没有实现接口,则可以使用CGLIB代理。

JDK动态代理

Spring默认使用JDK的动态代理实现AOP,类如果实现了接口,Spring就会使用这种方式实现动态代理。

JDK实现动态代理需要两个组件,首先第一个就是InvocationHandler接口。

我们在使用JDK的动态代理时,需要编写一个类,去实现这个接口,然后重写invoke方法,这个方法其实就是我们提供的代理方法。

如下源码所示:

/**
 * 动态代理
 *
 * @author mikechen
 */
public class JdkProxySubject implements InvocationHandler {

    private Subject subject;

    public JdkProxySubject(Subject subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("before 前置通知");
        Object result = null;

        try {
            result = method.invoke(subject, args);
        }catch (Exception ex) {
            System.out.println("ex: " + ex.getMessage());
            throw ex;
        }finally {
            System.out.println("after 后置通知");
        }
        return result;
    }
}

然后JDK动态代理需要使用的第二个组件就是Proxy这个类,我们可以通过这个类的newProxyInstance方法,返回一个代理对象。

生成的代理类实现了原来那个类的所有接口,并对接口的方法进行了代理,我们通过代理对象调用这些方法时,底层将通过反射,调用我们实现的invoke方法。

public class Main { public static void main(String[] args) { 
   //获取InvocationHandler对象 在构造方法中注入目标对象 
   InvocationHandler handler = new JdkProxySubject(new RealSubject()); 

   //获取代理类对象 
  Subject proxySubject = (Subject)Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{Subject.class}, handler); 

   //调用目标方法
   proxySubject.request(); proxySubject.response();

}

运行结果:

before 前置通知
执行目标对象的request方法......
after 后置通知
before 前置通知
执行目标对象的response方法......
after 后置通知

JDK动态代理优缺

优点

JDK动态代理是JDK原生的,不需要任何依赖即可使用;

通过反射机制生成代理类的速度要比CGLib操作字节码生成代理类的速度更快;

缺点

如果要使用JDK动态代理,被代理的类必须实现了接口,否则无法代理;

JDK动态代理无法为没有在接口中定义的方法实现代理,假设我们有一个实现了接口的类,我们为它的一个不属于接口中的方法配置了切面,Spring仍然会使用JDK的动态代理,但是由于配置了切面的方法不属于接口,为这个方法配置的切面将不会被织入。

JDK动态代理执行代理方法时,需要通过反射机制进行回调,此时方法执行的效率比较低;

CGLib代理

CGLIB组成结构

Cglib是一个强大的、高性能的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截,如下图所示Cglib与Spring等应用的关系:Spring AOP全面详解(超级详细)「建议收藏」

 

  • 最底层的是字节码 Bytecode ,字节码是Java为了保证“一次编译、到处运行”而产生的一种虚拟指令格式,例如iload_0、iconst_1、if_icmpne、dup等
  • 位于字节码之上的是 ASM ,这是一种直接操作字节码的框架,应用ASM需要对Java字节码、Class结构比较熟悉
  • 位于 ASM 之上的是 CGLIB 、 Groovy 、 BeanShell ,后两种并不是Java体系中的内容而是脚本语言,它们通过ASM框架生成字节码变相执行Java代码,这说明 在JVM中执行程序并不一定非要写Java代码—-只要你能生成Java字节码,JVM并不关心字节码的来源 ,当然通过Java代码生成的JVM字节码是通过编译器直接生成的,算是最“正统”的JVM字节码
  • 位于 CGLIB 、 Groovy 、 BeanShell 之上的就是 Hibernate 、 Spring AOP 这些框架了,这一层大家都比较熟悉
  • 最上层的是Applications,即具体应用,一般都是一个Web项目或者本地跑一个程序

所以,Cglib的实现是在字节码的基础上的,并且使用了开源的ASM读取字节码,对类实现增强功能的。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由思创斯整理,转载请注明出处: https://ispacesoft.com/92881.html

(0)
思创斯忠实用户-ss思创斯忠实用户-ss
0 0

相关推荐

  • 如何提取字符串中括号里面的内容_最长有效括号字符串

    如何提取字符串中括号里面的内容_最长有效括号字符串

    如何提取字符串中括号里面的内容_最长有效括号字符串从字符串中截取第一个英文左括号之前的字符串_python从字符串中截取第一个英文左括号之前的字符串

    思创斯忠实用户-ss 思创斯忠实用户-ss
    2023年9月27日
  • 安卓开发者手册_安卓开发「建议收藏」

    安卓开发者手册_安卓开发「建议收藏」

    安卓开发者手册_安卓开发「建议收藏」Class索引-Android中文版-API参考文档Android类索引这些是API类。查看全部APIpackages。ABCDEFGHIJKLMNOPQRSTUVWXYZAAbsListView 可用于实现项目虚拟化列表的基类。 AbsListView.LayoutParams AbsListView…_安卓开发手册

    思创斯忠实用户-ss 思创斯忠实用户-ss
    2023年10月17日
  • 千锋Java微服务实战Spring Cloud Alibaba For MyShop源码视频[通俗易懂] Java

    千锋Java微服务实战Spring Cloud Alibaba For MyShop源码视频[通俗易懂]

    千锋Java微服务实战Spring Cloud Alibaba For MyShop源码视频[通俗易懂]微服务是一种用于设计复杂软件的架构解决方案,将其分解为可独立部署的小型模块化服务。Java微服务可以给你提供独立部署、重建、重新部署和管理软件模

    思创斯忠实用户-ss 思创斯忠实用户-ss
    2024年5月26日
  • directx 3d engine_3d文件后缀名是什么「建议收藏」

    directx 3d engine_3d文件后缀名是什么「建议收藏」

    directx 3d engine_3d文件后缀名是什么「建议收藏」泡泡网显卡频道2月19日近年来3D图形技术的发展势头非常迅猛,软件方面游戏的画面和逼真度有了长足进步,硬件方面显卡的更新换代越来越频繁。但始终存在这样一个现象,无论显卡的性能翻多少倍,游戏玩家们总感觉还是不够强大,GPU庞大的运算能力到底被谁吃掉了呢?答案很简单,游戏为了提高画面质量、更加接近于

    思创斯忠实用户-ss 思创斯忠实用户-ss
    2024年2月17日
  • 《Access2003中文版应用基础教程》学习笔记

    《Access2003中文版应用基础教程》学习笔记

    《Access2003中文版应用基础教程》学习笔记0723VBA:visalBasicforApplicationsub…endsub过程Function_EndFunction函数Dim变量名称as类型Vb中变量可不声明,但是无法确定类型,故用时,先声明。SELECTCASE表达式Case表达式1Case表达式2

    思创斯忠实用户-ss 思创斯忠实用户-ss
    2024年2月1日
  • 安卓页面跳转_安卓如何点击按钮跳转页面

    安卓页面跳转_安卓如何点击按钮跳转页面

    安卓页面跳转_安卓如何点击按钮跳转页面文章浏览阅读93次。安卓页面跳转前面学习了一些安卓的基础知识并能够编写一个简单的安卓程序,那么我们要如何显示多个页面之间的跳转呢,下面就是我们要学习的内容1.普通的页面跳转2.带有参数的页面跳转一.普通的页面跳转Intent函数的引用1.s

    思创斯忠实用户-ss 思创斯忠实用户-ss
    2023年11月1日
  • 亚马逊扫号器扫出来账号质量_亚马逊账号挂了谁承担责任「建议收藏」

    亚马逊扫号器扫出来账号质量_亚马逊账号挂了谁承担责任「建议收藏」

    亚马逊扫号器扫出来账号质量_亚马逊账号挂了谁承担责任「建议收藏」成功率在提升,时效在缩短,虽然比不上去年处理侵权的成功率和时效,但至少这类问题不是无解,卖家仍然是有很大概率能拿回账号和资金

    思创斯忠实用户-ss 思创斯忠实用户-ss
    2023年12月22日
  • 等价类划分三角形问题_计算器等价类划分

    等价类划分三角形问题_计算器等价类划分

    等价类划分三角形问题_计算器等价类划分任意输入3个整数作为三角形的3条边的长度,判断三角形的类型:第一步:划分有效等价类和无效等价类输入条件有效等价类无效等价类是否能构成三角形的三条边a>0              (1)a              (7)b>0              (2)b  

    思创斯忠实用户-ss 思创斯忠实用户-ss
    2022年12月24日

发表回复

您的电子邮箱地址不会被公开。

思创斯知道您想要的工具

  • pycharm2019激活工具_系统激活工具

    pycharm2019激活工具_系统激活工具

    2024年7月26日

  • idea2020.2.2激活码_Pycharm激活码

    idea2020.2.2激活码_Pycharm激活码

    2024年7月19日

联系我们

关注“Java架构师必看”公众号

回复4,添加站长微信。

附言:ispacesoft.com网而来。

关注微信

玻璃钢生产厂家扬州人物卡通玻璃钢雕塑广东商场美陈怎么样汕头玻璃钢卡通雕塑艺术品玻璃钢花盆好吗园林玻璃钢动物雕塑出厂价格四川不锈钢玻璃钢雕塑厂杭州环保玻璃钢雕塑定制阜阳学校玻璃钢雕塑哪家便宜商场美陈 待遇河北玻璃钢雕塑定做厂延安玻璃钢雕塑施工玻璃钢公园主题雕塑玻璃钢交警卡通雕塑中原区城市玻璃钢雕塑中山玻璃钢动物雕塑尺寸厦门玻璃钢雕塑产品西藏城市几何玻璃钢雕塑商丘玻璃钢花盆厂玻璃钢雕塑制作工艺标书写法广东省玻璃钢雕塑生产武汉南海玻璃钢人物雕塑甘肃主题公园玻璃钢雕塑银川玻璃钢植物雕塑价格商场美陈毛巾山东大型商场美陈销售广东中庭商场美陈批发价南京商场美陈怎么样信阳玻璃钢浮雕抽象景观雕塑公司韶关玻璃钢景观雕塑价格甘肃玻璃钢雕塑制作厂家香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化