前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >实现 AOP 的三种方式

实现 AOP 的三种方式

作者头像
村雨遥
发布2020-08-27 10:08:33
1.5K0
发布2020-08-27 10:08:33
举报
文章被收录于专栏:JavaParkJavaParkJavaPark

目录

  • 1. 什么是 AOP
  • 2. 利用 Spring API 实现 AOP
  • 3. 自定义类实现 AOP
  • 4. 利用注解实现 AOP

1. 什么是 AOP

AOP(Aspect Oriented Programming),即面向切面编程,就是程序运行过程中在不改变程序源码的情况下,通过 预编译运行期动态代理 来增强方法。利用 AOP 能够对业务逻辑的各部分进行隔离,从而降低业务逻辑各部分之间的耦合度,提高程序的可重用性,提高开发效率。

在 AOP 中,常见概念有如下几点:

  • 横切关注点:横跨于应用程序多个模块的方法或功能,大多与我们的业务逻辑无关,但我们需要关注的部分,如日志、安全、缓存、事务等;
  • 切入点(PointCut):即要添加代码的地方;
  • 通知(Advice):即向切点动态添加的代码;
  • 切面(ASPECT):切点 + 通知,是一个类;
  • 连接点(JointPoint):和切点一样,是要添加代码的地方;
  • 代理(Proxy):向目标对象应用通知后创建的对象;
  • 目标(Target):被通知的对象;

Spring AOP 中,通过通知(Advice)来定义横切逻辑,支持 5 种类型的 Advice;

通知类型

连接点

接口

前置通知

方法前

org.springframework.aop.MethodBeforeAdvice

后置通知

方法后

org.springframework.aop.AfterReturningAdvice

环绕通知

方法前后

org.springframework.aop.MethodInterceptor

异常抛出通知

方法抛出异常

org.springframework.aop.ThrowsAdvice

引介通知

类中添加新的方法属性

org.springframework.aop.IntroductionInterceptor

AOP 实际上是基于 Java 动态代理来实现,因此主要有以下三种实现方式:

  1. 使用 Spring API 实现
  2. 自定义类来实现 AOP
  3. 使用注解实现

2. 利用 Spring API 实现 AOP

  1. 准备业务接口
package com.cunyu.service;

/**
 * @InterfaceName : UserService
 * @Author : cunyu
 * @Date : 2020/7/20 8:36
 * @Version : 1.0
 * @Description : 业务接口
 **/

public interface UserService {
    public void add();

    public void delete();

    public void update();

    public void search();
}

  1. 实现业务接口
package com.cunyu.service;

/**
 * @author : cunyu
 * @version : 1.0
 * @className : UserServiceImpl
 * @date : 2020/7/20 8:37
 * @description : 业务接口实现类
 */

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("add user");
    }

    @Override
    public void delete() {
        System.out.println("delete user");
    }

    @Override
    public void update() {
        System.out.println("update user");
    }

    @Override
    public void search() {
        System.out.println("search user");
    }
}
  1. 编写增强类
package com.cunyu.service;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

/**
 * @author : cunyu
 * @version : 1.0
 * @className : StrenthUSerAfter
 * @date : 2020/7/20 8:48
 * @description : 后置增强
 */

public class StrenthUSerAfter implements AfterReturningAdvice {
    /**
     * @param o       返回值
     * @param method  被调用的方法
     * @param objects 被调用方法的对象的参数
     * @param o1      被调用的目标对象
     * @return
     * @description TODO
     * @date 2020/7/20 8:49
     * @author cunyu1943
     * @version 1.0
     */
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println(o1.getClass().getName() + " 类的 " + method.getName() + " 方法被执行,返回值是 " + o);
    }
}
  1. 配置 Spring 配置文件
<?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 注册 -->
    <bean id="userService" class="com.cunyu.service.UserServiceImpl"/>
    <bean id="userBefore" class="com.cunyu.service.StrenthUSerBefore"/>
    <bean id="userAfter" class="com.cunyu.service.StrenthUSerAfter"/>

    <!--  AOP 配置  -->
    <aop:config>
        <!--切入点 expression:表达式匹配要执行的方法-->
        <aop:pointcut id="pointcut" expression="execution(* com.cunyu.service.UserServiceImpl.*(..))"/>
        <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
        <aop:advisor advice-ref="userAfter" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

  1. 进行单元测试
package com.cunyu.service;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author : cunyu
 * @version : 1.0
 * @className : UserServiceTest
 * @date : 2020/7/20 8:58
 * @description : 测试类
 */


public class UserServiceTest {
    @Test
    public void testAdd() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.add();
    }
}

3. 自定义类实现 AOP

  1. 准备业务接口(同第一种方法)
  2. 实现业务接口(同第一种方法)
  3. 准备我们的自定义切入类
package com.cunyu.service;

/**
 * @author : cunyu
 * @version : 1.0
 * @className : MyPointCut
 * @date : 2020/7/20 9:08
 * @description : 自定义切入类
 */

public class MyPointCut {
    public void after() {
        System.out.println("方法执行后...");
    }
}
  1. Spring 配置文件中去配置
<?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 注册 -->
    <bean id="userService" class="com.cunyu.service.UserServiceImpl"/>
    <bean id="myPointCut" class="com.cunyu.service.MyPointCut"/>

    <!--  AOP 配置  -->
    <aop:config>
        <aop:aspect ref="myPointCut">
            <aop:pointcut id="mypointcut1" expression="execution(* com.cunyu.service.UserServiceImpl.*(..))"/>
            <aop:after pointcut-ref="mypointcut1" method="after"/>
        </aop:aspect>
    </aop:config>
</beans>

  1. 单元测试
package com.cunyu.service;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author : cunyu
 * @version : 1.0
 * @className : UserServiceTest
 * @date : 2020/7/20 8:58
 * @description : 测试类
 */


public class UserServiceTest {

    @Test
    public void testDelete() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.delete();
    }
}

4. 利用注解实现 AOP

  1. 准备业务接口(同第一种方法)
  2. 实现业务接口(同第一种方法)
  3. 编写一个使用注解实现的增强类
package com.cunyu.config;

import org.aspectj.lang.annotation.After;

/**
 * @author : cunyu
 * @version : 1.0
 * @className : AnnotationPointCut
 * @date : 2020/7/20 9:39
 * @description : 基于注解实现的增强类
 */

public class AnnotationPointCut {
    @After("execution(* com.cunyu.service.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("注解实现,方法执行后...");
    }
}
  1. 配置 Spring 配置
<?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 注册,并增加支持注解的配置 -->
    <bean id="userService" class="com.cunyu.service.UserServiceImpl"/>
    <bean id="annotationPointCut" class="com.cunyu.config.AnnotationPointCut"/>
    <aop:aspectj-autoproxy/>
</beans>

  1. 单元测试
package com.cunyu.service;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author : cunyu
 * @version : 1.0
 * @className : UserServiceTest
 * @date : 2020/7/20 8:58
 * @description : 测试类
 */


public class UserServiceTest {

    @Test
    public void testUpdate() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.update();
    }
}

注意: <aop:aspectj-autoproxy/> 声明会自动为 Spring 容器中配置 @Aspect 切面的 Bean 创建代理,植入切面。但在 Spring 内部,依然采用AnnotationAwareAspectJAutoProxyCreator 进行自动代理的创建工作,只是具体实现的细节已经被 <aop:aspectj-autoproxy /> 隐藏起来了。此外,还有一个proxy-target-class 属性,默认为false,表示使用 Jdk 动态代理注入增强,此时如果目标类没有声明接口,则 Spring 将自动使用CGLib 动态代理。当为 true 时,表示使用 CGLib 动态代理技术注入增强。

- END -

往期精选

Spring 代理模式知多少

利用注解进行 Spring 开发

Spring 中的自动装配

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-08-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 村雨遥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • 1. 什么是 AOP
  • 2. 利用 Spring API 实现 AOP
  • 3. 自定义类实现 AOP
  • 4. 利用注解实现 AOP
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档