Spring-AOP

目录

一、引入AOP

二、核心AOP概念和术语

三、切点表达式

四、Spring实现AOP

(一)@AspectJ的支持

[1 、基于注解开发](#1 、基于注解开发)

[1.1 引入依赖](#1.1 引入依赖)

[1.2 实现目标类](#1.2 实现目标类)

[1.3 定义切面类(日志管理)](#1.3 定义切面类(日志管理))

[1.4 将目标类和切面类纳入Spring容器](#1.4 将目标类和切面类纳入Spring容器)

[1.5 开启组件扫描、自动代理](#1.5 开启组件扫描、自动代理)

[1.6 测试](#1.6 测试)

2、基于XML方式开发

[(二) 基于Schema的AOP支持](#(二) 基于Schema的AOP支持)

[1.1 声明Aspect](#1.1 声明Aspect)

[1.2 编写Spring配置文件(目标类使用组件扫描)](#1.2 编写Spring配置文件(目标类使用组件扫描))

[1.3 测试](#1.3 测试)

五、总结


AOP (Aspect Oriented Programming)意为面向切面编程,通过预编译和运行期间动态代理实现程序功能的一种技术。Spring中通过JDK动态代理和Cglib动态代理技术实现AOP。Spring可以灵活切换这两种动态代理方式,如果代理接口会默认使用JDK动态代理,如果代理某个类,这个类没有实现接口,就会切换Cglib。此外,我们也可以配置Spring选择。


一、引入AOP

系统开发中不仅要做核心业务, 还需要做一些系统业务,例如:事务管理、运行记录、安全日志等,这些系统业务被称为交叉业务。

如果在每一个业务处理过程当中,都掺杂这些交叉业务进去的话,存在两方面问题:

  • 交叉业务代码在多个业务流程中反复出现,显然这些交叉代码没有得到复用。并且修改这些交叉代码,修改起来很麻烦。
  • 程序员无法专注核心业务代码的编写,在编写核心代码的同时还需要处理这些交叉业务。

见上图,方便理解AOP思想。将非核心业务与核心业务进行分离,同时把这些非核心业务切入到业务流程当中的过程称为AOP。

二、核心AOP概念和术语

  • JoinPoint(连接点)程序执行过程中的一个点,例如一个方法的执行或一个异常的处理。在Spring AOP中,一个连接点总是代表一个方法的执行。

  • 切点(Pointcut)真正切入切面的方法,指定哪些方法需要切入

  • 通知(Advice)一个切面在特定连接点采取的行动,根据通知类型确定执行流程。通知包括前置通知、后置通知、环绕通知等。

  • Aspect(切面)通知 + 切面

  • Target object(目标对象) 被一个或多个切面所 advice 的对象。也被称为 "advised object"。由于Spring AOP是通过使用运行时代理来实现的,这个对象总是一个被代理的对象。

  • AOP proxy(代理对象) 一个由AOP框架创建的对象,以实现切面契约(advice 方法执行等)。在Spring框架中,AOP代理是一个JDK动态代理或CGLIB代理。

  • Weaving(织入)将通知应用到目标对象的过程。

三、切点表达式

切点表达式用来定义通知往哪些方法切入。

切点表达式语法格式

execution( [访问权限修饰符] 返回值类型 [全限定类名] 方法名(形式参数列表)[异常] )

构成 @Pointcut 注解的值的 pointcut 表达式是一个常规的AspectJ pointcut表达式。关于AspectJ的 pointcut 语言的全面讨论,请参见 《AspectJ编程指南》(以及 AspectJ 5开发者笔记 的扩展)或关于AspectJ的书籍之一(如Colyer等人的《Eclipse AspectJ》,或Ramnivas Laddad的《AspectJ in Action》)。

四、Spring实现AOP

(一)@AspectJ的支持

1 、基于注解开发

1.1 引入依赖
XML 复制代码
<!--spring context依赖-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.1.9.RELEASE</version>
</dependency>
<!--spring aop依赖-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>5.1.9.RELEASE</version>
</dependency>
<!--spring aspects依赖-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
  <version>5.1.9.RELEASE</version>
</dependency>
1.2 实现目标类
java 复制代码
@Service("userService")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMongoDao userMongoDao;
    @Override
    public User login(String name, String password) {
        Document doc = new Document();
        doc.put("name", name);
        doc.put("password", password);
        ArrayList<Document> list = userMongoDao.queryAll(doc);
        User user = null;
        if (! list.isEmpty()) {
            Document document = list.get(0);
            user = new User();
            user.setId((ObjectId) document.get("_id"));
            user.setName((String) document.get("name"));
        }
        return user;
    }
}
1.3 定义切面类(日志管理)
java 复制代码
package com.zookin.service.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LoggerAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(LoggerAspect.class);

    @Pointcut("execution(public * com.zookin.service.*.*(..))")
    public void logPointcut() {}

    @Before("logPointcut()")
    public void beforeAdvice(JoinPoint joinPoint) {
        LOGGER.info("before advice:" + joinPoint.getSignature().getName());
    }

    @Around("logPointcut()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        LOGGER.info("Method:" + joinPoint.getSignature().getName() + " execution time:" + (endTime - startTime) + "ms");
        return result;
    }

    @AfterReturning(pointcut = "logPointcut()", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        LOGGER.info("After returning from method: " + joinPoint.getSignature().getName() + ", result: " + result);
    }

    @AfterThrowing(pointcut = "logPointcut()", throwing = "exception")
    public void afterThrowingAdvice(JoinPoint joinPoint, Exception exception) {
        LOGGER.error("Exception thrown from method: " + joinPoint.getSignature().getName() + ", exception: " + exception.getMessage());
    }

}
1.4 将目标类和切面类纳入Spring容器

目标类添加@Service注解,切面类添加@Component、@Aspect注解。

1.5 开启组件扫描、自动代理
XML 复制代码
<?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:context="http://www.springframework.org/schema/context"
       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/context  http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.zookin.service"/>
    <aop:aspectj-autoproxy proxy-target-class="true"/>

</beans>
1.6 测试
java 复制代码
@Test
    public void loginTest() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = applicationContext.getBean("userService", UserService.class);
        User login = userService.login("zookin", "zookin");
        System.out.println(login);
}

2、基于XML方式开发

略....

(二) 基于Schema的AOP支持

如果喜欢基于XML的格式,Spring也提供了对使用 aop 命名空间标签定义切面的支持。它支持与使用 @AspectJ 风格时完全相同的 pointcut 表达式和advice种类。要使用本节描述的 aop 命名空间标签,你需要导入 spring-aop schema,如 基于XML schema的配置 中所述。

在你的Spring配置中,所有的aspect和 advisor元素都必须放在一个 <aop:config> 元素(你可以在一个应用上下文配置中拥有多个 <aop:config> 元素)。一个 <aop:config> 元素可以包含 pointcut、 advisor 和 aspect 元素(注意这些必须按顺序声明)。

1.1 声明Aspect

通过使用 <aop:aspect> 元素来声明一个切面,并通过使用 ref 属性来引用支持 Bean,如下例所示。

XML 复制代码
<aop:config>
    <aop:aspect id="myAspect" ref="aBean">
        ...
    </aop:aspect>
</aop:config>

<bean id="aBean" class="...">
    ...
</bean>

支持切面的Bean(本例中的 aBean)当然可以像其他Spring Bean一样被配置和依赖注入。

1.2 编写Spring配置文件(目标类使用组件扫描)

XML 复制代码
<?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:context="http://www.springframework.org/schema/context"
       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/context  http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.zhj.service"/>
    <aop:aspectj-autoproxy proxy-target-class="true"/>

    <bean id="timerAspect" class="com.zhj.service.aspect.TimerAspect"></bean>

    <aop:config>
        <aop:pointcut id="aa" expression="execution(* com.zhj.service.CommodityService.*(..))"/>
        <aop:aspect ref="timerAspect">
            <aop:around method="time" pointcut-ref="aa"/>
        </aop:aspect>
    </aop:config>
    
</beans>

1.3 测试

java 复制代码
    @Test
    public void commodityTest() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        CommodityService commodityService = applicationContext.getBean("commodityService", CommodityService.class);
        ArrayList<Commodity> commodities = commodityService.queryCommodity("自行车", 0, null);
    }

五、总结

AOP是Spring框架的核心组件,AOP蕴含的底层原理十分值得了解和挖掘。Spring 框架自5.0版本以来,集成了Log4j、SLF4j框架,本文我简单地整合日志框架来说明AOP的实现,此外我们可以使用AOP实现事务管理,见上一篇博客。上述内容如果有错误的地方,希望大佬们可以指正。我一直在学习的路上,您的帮助使我收获更大!觉得对您有帮助的话,还请点赞支持!我也会不断更新文章! Spring-事务支持https://mp.csdn.net/mp_blog/creation/editor/134687195

相关推荐
昱禹1 小时前
使用spring-ws发布webservice服务
xml·java·spring boot·spring·webservice·spring-ws
荆州克莱2 小时前
腾讯二面:MySQL的半同步是什么?不是MySQL的两阶段提交,那是什么?
spring boot·spring·spring cloud·css3·技术
栗豆包14 小时前
w148基于spring boot的文档管理系统的设计与实现
java·spring boot·后端·spring·tornado
杂货铺的小掌柜17 小时前
spring mvc源码学习笔记之七
学习·spring·mvc
杂货铺的小掌柜17 小时前
spring mvc源码学习笔记之九
学习·spring·mvc
孇雙17 小时前
SpringBoot之核心配置
java·spring boot·spring
胡尔摩斯.19 小时前
Mybatis
java·后端·spring·mybatis
ccmjga19 小时前
升级 Spring Boot 3 配置讲解 — 新版本的秒杀系统怎么做?
java·spring boot·后端·spring·gradle·maven·spring security
杂货铺的小掌柜20 小时前
spring mvc源码学习笔记之十
学习·spring·mvc
十秒耿直拆包选手21 小时前
spring:xml声明bean的多种方式。
xml·java·spring