详解spring面向切面aop拦截器

发布时间 - 2026-01-11 00:13:07    点击率:

spring中有很多概念和名词,其中有一些名字不同,但是从功能上来看总感觉是那么的相似,比如过滤器、拦截器、aop等。
过滤器filter、spring mvc拦截器Interceptor 、面向切面编程aop,实际上都具有一定的拦截作用,都是拦截住某一个面,然后进行一定的处理。

在这里主要想着手的是aop,至于他们的比较,我想等三个都一一了解完了再说,因此这里便不做过多的比较。

在我目前的项目实践中,只在一个地方手动显示的使用了aop,那便是日志管理中对部分重要操作的记录。

据我目前所知,aop拦截一般都是用在具体的方法上,或者说是具体的某一类方法,我所用过的实现方式有两种,一种是直接代码声明,一种是在xml文件中配置。

由于我目前实际开发的项目都是使用spring+spring mvc的架构,然后使用maven管理,然后junit测试。因此我自己几乎所有的个人项目也都是采用这些架构和项目管理工具,在这个理解aop的小项目中,自然也是这样,依赖包如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>springTest</groupId>
 <artifactId>aopTest</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.0.3.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>4.0.3.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>4.0.3.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.4</version>
  </dependency>
 </dependencies>
</project>

第一种方式,Java代码声明:

这里实例中,我要声明一个aop来拦截dao层中的get开头的所有方法,首先建一个dao以及简单的额imp实现:

dao接口如下:

package com.ck.aopTest.dao;
import com.ck.aopTest.model.UserModel;

public interface MyAopDao {
  public void getUser();
  public void getName(UserModel user);
  public void addUser();
}

简单的实现:

package com.ck.aopTest.dao.impl;
import org.springframework.stereotype.Repository;
import com.ck.aopTest.dao.MyAopDao;
import com.ck.aopTest.model.UserModel;

@Repository
public class MyAopDaoImpl implements MyAopDao {

  @Override
  public void getUser() {
    System.out.println("这是我的aop测试dao方法一");
  }
  @Override
  public void getName(UserModel userModel) {
    System.out.println("这是我的aop测试dao方法二");
  }
  @Override
  public void addUser() {
    System.out.println("这是我的aop测试dao方法三");
  }
}

然后声明一个aop:

package com.ck.aopTest.aop;
import java.util.Date;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import com.ck.aopTest.model.UserModel;

@Aspect
@Component
public class MyAop {

  @Pointcut("execution(public * com.ck.aopTest.dao.impl.*.get*(..))")
  private void aopTest() {

  }
  @Before("aopTest()")
  public void before() {
    System.out.println("调用dao方法前拦截" + new Date().getTime());
  }
  @After("aopTest()" + "&&args(user)")
  public void after(UserModel user) {
    System.out.println(user.getName());
    System.out.println("调用dao方法之后拦截" + new Date().getTime());
  }
  @Around("aopTest()")
  public void around(ProceedingJoinPoint pdj) {
    System.out.println("调用dao之前的环绕拦截" + new Date().getTime());
    try {
      pdj.proceed();
    } catch (Throwable e) {
      e.printStackTrace();
    }
    System.out.println("调用dao之后的环绕拦截" + new Date().getTime());
  }
}

上述代码便是用java声明aop的核心代码,其中注解@Aspect的作用的就是告诉spring这是一个aop类,然后@Component就不用多说了,告诉spring这是一个需要扫描的类。

再往下,@Pointcut(“execution(public * com.ck.aopTest.dao.impl..get(..))”)正式声明需要拦截的切面,@Pointcut以及后边的额execution是固定的写法,execution后括号内的内容便是具体的切面,这里的意思是拦截所有public的任何返回值或者void的、命名空间是com.ck.aopTest.dao.impl下边的所有的类中的所有get开头的拥有任意多个参数的方法。

简单点说也就是当任何调用了com.ck.aopTest.dao.impl这个包中任何类中的get开头的方法,便会激活这个aop。

而紧接着上边这一段,我们看到了一个private void aopTest() 空的方法,实际上这个方法的作用是为这个aop切面声明一个名字,便于使用,也便于在多个aop切面时正常区分。

再后边的@Before、@After、@Around便是三个可选拦截方式,见名之意,分别是在上边声明的切面指明的方法调用之前执行、调用之后执行、以及环绕执行,调用之前和调用之后比较好理解,环绕的意思是在调用之前和之后都执行一定的逻辑。
从代码中可以看出,pdj.proceed();之前和之后各打印了两行数据,pdj.proceed();就代表了继续执行,如果是了解filter的应该很容易想到这个方法实际上和chain.doFilter很像,可以理解成放行。

在这三个注解之后需要指定要使用的切面,即@Pointcut声明的切面,指定名称就行。

从代码中可以看到有一个地方后边加了“&&args(UserModel user)”,意思是指定形参,也就是说指定的切面中有效方法的参数,例如上边dao中的getName方法有一个UserModel类型的参数,这里便可以使用。

主要代码写好了,接下来还有个必不可少的步骤,既然是spring项目,是spring的aop,那么自然需要配置spring文件,指明需要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:context="http://www.springframework.org/schema/context" " 
  xsi:schemaLocation="http://www.springframework.org/schema/beans 
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
  http://www.springframework.org/schema/context 
  http://www.springframework.org/schema/context/spring-context-3.0.xsd"> 
  <context:component-scan base-package="com.ck.aopTest.*" />
</beans>

为了验证这里的aop是否真的有效,我写了一个junit测试:

package com.ck.aopTest.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.ck.aopTest.dao.MyAopDao;
import com.ck.aopTest.model.UserModel;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring.xml")
public class AopTest {
  @Autowired
  private MyAopDao myAopDao;

  @Test
  public void aopTest2() {
    UserModel user = new UserModel();
    myAopDao.getName(user);
  }
}

按理说,这里运行测试方法后应该打印出很多条输出,但是遗憾的是结果只是打印出了dao中的一条输出,原因是spring配置中并没有启用aop,正确的配置应该是下边这样:

<?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-3.0.xsd 
  http://www.springframework.org/schema/context 
  http://www.springframework.org/schema/context/spring-context-3.0.xsd
  http://www.springframework.org/schema/aop
  http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> 

  <aop:aspectj-autoproxy />
  <context:component-scan base-package="com.ck.aopTest.*" />  

</beans>

我们需要再文件头加入

xmlns:aop=”http://www.springframework.org/schema/aop”

以及http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
除此之外,还要启用aop:

<aop:aspectj-autoproxy />

再次运行测试方法会看到控制台如下:

由此证明这个aop是有效的。

第二种方式,配置文件配置:

同样的,这里还是用之前的dao以及对应的impl,因此这段代码便不再重复,不一样的是具体的aop类如下:

package com.ck.aopTest.aop;
public class MyAop2 {
  public void before2() {
    System.out.println("这是我的使用注解的aop,调用dao之前拦截");
  }
}

可以看到这个类实际上也是极致简单,普通类,普通方法,没有任何特别,然后我们要做的是在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: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-3.0.xsd 
  http://www.springframework.org/schema/context 
  http://www.springframework.org/schema/context/spring-context-3.0.xsd
  http://www.springframework.org/schema/aop
  http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> 

  <aop:aspectj-autoproxy />
  <bean id="myAop2" class="com.ck.aopTest.aop.MyAop2"></bean>
  <aop:config>
   <aop:pointcut expression="execution(public * com.ck.aopTest.dao.impl.*.add*(..))" id="aopTest1"/>
   <aop:aspect id="myAopTest2" ref="myAop2">
    <aop:before method="before2" pointcut-ref="aopTest1"/>
   </aop:aspect>
  </aop:config>
  <context:component-scan base-package="com.ck.aopTest.*" />

</beans>

至于这里配置内容的具体解释,我想通过我对第一种方式的解释后,也没有太大必要再说,稍微一对比就会一清二楚。

同样的,这里只演示了before,至于after和round的配置应该也很容易就可以根据before推理出来。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


# spring  # aop  # 切面  # 方法切面  # 拦截器  # Spring中IOC和AOP的深入讲解  # Spring框架学习之AOP详解  # Spring 面向切面编程AOP实现详解  # Spring AOP面向切面编程实现原理方法详解  # Spring框架AOP面向切面编程原理全面分析  # 都是  # 这是  # 是在  # 的是  # 我想  # 多个  # 这是一个  # 可以看到  # 第一种  # 有一个  # 类中  # 他们的  # 就会  # 我要  # 在这里  # 在我  # 好了  # 有个  # 在这个  # 出了 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 如何在香港服务器上快速搭建免备案网站?  如何彻底卸载建站之星软件?  php打包exe后无法访问网络共享_共享权限设置方法【教程】  网站制作企业,网站的banner和导航栏是指什么?  网站制作壁纸教程视频,电脑壁纸网站?  如何用西部建站助手快速创建专业网站?  详解jQuery中的事件  黑客如何通过漏洞一步步攻陷网站服务器?  网站制作大概多少钱一个,做一个平台网站大概多少钱?  如何在万网开始建站?分步指南解析  猪八戒网站制作视频,开发一个猪八戒网站,大约需要多少?或者自己请程序员,需要什么程序员,多少程序员能完成?  Laravel Artisan命令怎么自定义_创建自己的Laravel命令行工具完全指南  Edge浏览器怎么启用睡眠标签页_节省电脑内存占用优化技巧  如何快速生成专业多端适配建站电话?  Laravel如何生成URL和重定向?(路由助手函数)  在线教育网站制作平台,山西立德教育官网?  百度输入法ai面板怎么关 百度输入法ai面板隐藏技巧  阿里云网站搭建费用解析:服务器价格与建站成本优化指南  Laravel如何处理异常和错误?(Handler示例)  七夕网站制作视频,七夕大促活动怎么报名?  Laravel如何发送邮件_Laravel Mailables构建与发送邮件的简明教程  HTML5建模怎么导出为FBX格式_FBX格式兼容性及导出步骤【指南】  如何在腾讯云服务器快速搭建个人网站?  西安市网站制作公司,哪个相亲网站比较好?西安比较好的相亲网站?  Laravel的.env文件有什么用_Laravel环境变量配置与管理详解  Laravel如何生成PDF或Excel文件_Laravel文档导出工具与使用教程  如何挑选最适合建站的高性能VPS主机?  Laravel怎么多语言本地化设置_Laravel语言包翻译与Locale动态切换【手册】  Laravel如何实现用户注册和登录?(Auth脚手架指南)  Laravel怎么为数据库表字段添加索引以优化查询  Swift开发中switch语句值绑定模式  Python3.6正式版新特性预览  Laravel的契約(Contracts)是什么_深入理解Laravel Contracts与依赖倒置  Laravel如何实现模型的全局作用域?(Global Scope示例)  Laravel如何配置任务调度?(Cron Job示例)  如何用PHP快速搭建CMS系统?  Laravel如何使用Sanctum进行API认证?(SPA实战)  如何在局域网内绑定自建网站域名?  iOS验证手机号的正则表达式  如何登录建站主机?访问步骤全解析  如何用PHP工具快速搭建高效网站?  Laravel如何生成API文档?(Swagger/OpenAPI教程)  php 三元运算符实例详细介绍  如何安全更换建站之星模板并保留数据?  JavaScript如何实现路由_前端路由原理是什么  Laravel如何使用软删除(Soft Deletes)功能_Eloquent软删除与数据恢复方法  焦点电影公司作品,电影焦点结局是什么?  Laravel请求验证怎么写_Laravel Validator自定义表单验证规则教程  Laravel如何使用Blade组件和插槽?(Component代码示例)  HTML5段落标签p和br怎么选_文本排版常用标签对比【解答】