zoukankan      html  css  js  c++  java
  • Spring5入门-11-AOP

    一、前言



    二、什么是AOP?

    AOP(Aspect Oriented Programming)意为:面向切面编程。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合降低,提高程序的可复用性,同时提高开发效率。

    image-20201002215657010



    三、AOP在Spring中的作用

    提供声明式事务;允许用户自定义切面。

    下面是一些名词:

    • 横切关注点:跨越应用程序多个模块的方法或功能。即,与业务逻辑无关,但是需要关注的部分,如:日志、安全、缓存、事务等......
    • 切面(Aspect):横切关注点被模块化的特殊对象,即,一个类。比如:Log类。
    • 通知(Advice):切面必须完成的工作,即,类中的一个方法。比如:Log类中的方法。
    • 目标(Target):被通知对象。比如:代理对象中的抽象角色(接口类)。
    • 代理(Proxy):向目标对象应用通知之后创建的对象。比如:代理类。
    • 切入点(PointCut):切面通知执行的“地点”的定义。
    • 连接点(JointPoint):与切入点匹配的执行点。

    image-20201002220238559

    在SpringAOP中,通过Advice定义横切逻辑,Spring中支持5中类型的Advice:

    通知类型 连接点 实现接口
    前置通知 方法前 org.springframework.aop.MethodBeforeAdvice
    后置通知 方法后 org.springframework.aop.MethodAfterAdvice
    环绕通知 方法前后 org.aopalliance.intercept.MethodInterceptor
    异常抛出通知 方法抛出异常 org.springframework.aop.ThrowsAdvice
    引介通知 类中增加新的方法属性 org.springframework.aop.IntroductionInterceptor


    四、maven依赖

    <!--Spring-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${org.springframework.version}</version>
    </dependency>
    
    <!--JUnit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    
    <!--AOP织入-->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
    


    五、Service

    路径

    image-20201011084855736

    代码

    这里用System.out.println("使用了XXX方法");去代表Service层的动作:

    Service:

    package com.duzhuan.service;
    
    /**
     * @Autord: HuangDekai
     * @Date: 2020/10/11 8:43
     * @Version: 1.0
     * @since: jdk11
     */
    public interface Service {
        void add();
        void delete();
        void update();
        void query();
    }
    

    ServiceImpl:

    package com.duzhuan.service;
    
    /**
     * @Autord: HuangDekai
     * @Date: 2020/10/11 8:45
     * @Version: 1.0
     * @since: jdk11
     */
    public class ServiceImpl implements Service {
        @Override
        public void add() {
            System.out.println("使用了add()方法");
        }
    
        @Override
        public void delete() {
            System.out.println("使用了delete()方法");
        }
    
        @Override
        public void update() {
            System.out.println("使用了update()方法");
        }
    
        @Override
        public void query() {
            System.out.println("使用了query()方法");
        }
    }
    


    六、前后Advice实现

    路径

    image-20201011085438417

    代码

    BeforeLog:

    package com.duzhuan.log;
    
    import org.springframework.aop.MethodBeforeAdvice;
    
    import java.lang.reflect.Method;
    
    /**
     * @Autord: HuangDekai
     * @Date: 2020/10/11 8:50
     * @Version: 1.0
     * @since: jdk11
     */
    public class BeforeLog implements MethodBeforeAdvice {
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println("[DEBUG]"+method.getClass().getName()+"执行了:  "+method.getName());
        }
    }
    

    AfterLog:

    package com.duzhuan.log;
    
    import org.springframework.aop.AfterReturningAdvice;
    
    import java.lang.reflect.Method;
    
    /**
     * @Autord: HuangDekai
     * @Date: 2020/10/11 8:52
     * @Version: 1.0
     * @since: jdk11
     */
    public class AfterLog implements AfterReturningAdvice {
        @Override
        public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
            System.out.println("[DEBUG]"+method.getClass().getName()+"执行了:  "+method.getName()+"    返回值为:  "+returnValue);
        }
    }
    

    补充说明

    如果有了解过动态代理的话,可以很快地知道public void before(Method method, Object[] args, Object target)中,args是被代理的方法的输入参数,target是被代理的接口Spring5入门-10-静态代理与动态代理略有提及。



    七、实现织入

    7.1 使用Spring的API接口

    路径

    image-20201011091632809

    代码

    <?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: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/aop
           https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <bean id="serviceImpl" class="com.duzhuan.service.ServiceImpl"/>
        <bean id="beforeLog" class="com.duzhuan.log.BeforeLog"/>
        <bean id="afterLog" class="com.duzhuan.log.AfterLog"/>
    
        <aop:config>
            <!--execution()执行器  具体操作可以自行查询   这里第一个*代表返回值的类型是所有类型
                第二个*代表ServiceImpl下的所有方法(..)表示所有的输入参数-->
            <aop:pointcut id="servicePointCut" expression="execution(* com.duzhuan.service.ServiceImpl.*(..))"/>
    
            <aop:advisor advice-ref="beforeLog" pointcut-ref="servicePointCut"/>
            <aop:advisor advice-ref="afterLog" pointcut-ref="servicePointCut"/>
        </aop:config>
    
    </beans>
    

    aop:pointcut,aop的切入点,aop:advisor则是执行的advisor。


    7.2 测试样例

    路径

    image-20201011093302787

    代码

    package com.duzhuan.service;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    /**
     * @Autord: HuangDekai
     * @Date: 2020/10/11 9:27
     * @Version: 1.0
     * @since: jdk11
     */
    public class ServiceTest {
        @Test
        public void serviceImplTest(){
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            
            // 注意,由于aop是用动态代理实现,此处必须使用 接口  才能实现织入
            Service serviceImpl = context.getBean("serviceImpl", Service.class);
            serviceImpl.add();
            serviceImpl.delete();
            serviceImpl.update();
            serviceImpl.query();
        }
    }
    
    

    结果

    image-20201011093517568


    7.3 自定义实现AOP

    路径

    image-20201011104526986

    配置文件

    <?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: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/aop
           https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <bean id="serviceImpl" class="com.duzhuan.service.ServiceImpl"/>
        <bean id="beforeLog" class="com.duzhuan.log.BeforeLog"/>
        <bean id="afterLog" class="com.duzhuan.log.AfterLog"/>
    
        <!--方法一: 使用原生Spring API接口-->
    <!--    <aop:config>
            &lt;!&ndash;execution()执行器  具体操作可以自行查询   这里第一个*代表返回值的类型是所有类型
                第二个*代表ServiceImpl下的所有方法(..)表示所有的输入参数&ndash;&gt;
            <aop:pointcut id="servicePointCut" expression="execution(* com.duzhuan.service.ServiceImpl.*(..))"/>
    
            <aop:advisor advice-ref="beforeLog" pointcut-ref="servicePointCut"/>
            <aop:advisor advice-ref="afterLog" pointcut-ref="servicePointCut"/>
        </aop:config>-->
    
        <!--方法二: 自定义类-->
        <bean id="logDiyPointCut" class="com.duzhuan.diyPointCut.LogDiyPointCut"/>
        <aop:config>
            <aop:aspect ref="logDiyPointCut">
                <aop:pointcut id="servicePointCut" expression="execution(* com.duzhuan.service.ServiceImpl.*(..))"/>
                <aop:before method="beforeAdvice" pointcut-ref="servicePointCut"/>
                <aop:after method="afterAdvice" pointcut-ref="servicePointCut"/>
            </aop:aspect>
        </aop:config>
    
    </beans>
    

    执行测试样例

    image-20201011104705546


    7.4 注解实现

    路径

    image-20201011123823739

    代码

    LogAnnotationPointCut

    package com.duzhuan.diyPointCut;
    
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    /**
     * @Autord: HuangDekai
     * @Date: 2020/10/11 10:53
     * @Version: 1.0
     * @since: jdk11
     */
    
    @Aspect
    public class LogAnnotationPointCut {
    
        @Before("execution(* com.duzhuan.service.ServiceImpl.*(..))")
        public void beforeAdvice(){
            System.out.println("===============!方法执行前!===============");
        }
    
        @After("execution(* com.duzhuan.service.ServiceImpl.*(..))")
        public void afterAdvice(){
            System.out.println("===============!方法执行后!===============");
        }
    }
    

    执行测试样例

    image-20201011144710537

  • 相关阅读:
    .net core 大型事务的处理办法
    .net Core把一个list集合里面的所有字段的数值汇总
    C#使用模板导出Excel
    JQuery滚动分页查询功能
    返回一个条件表达式树的拓展方法
    C++类的大小
    基数排序-八大排序汇总(8)
    归并排序-八大排序汇总(7)
    快速排序(交换排序)-八大排序汇总(6)
    希尔排序(插入排序)-八大排序汇总(5)
  • 原文地址:https://www.cnblogs.com/duzhuan/p/13797428.html
Copyright © 2011-2022 走看看