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

posted @ 2020-10-11 14:49  杜撰丶  阅读(173)  评论(0编辑  收藏  举报