博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring AOP中级——应用场景
阅读量:6659 次
发布时间:2019-06-25

本文共 7468 字,大约阅读时间需要 24 分钟。

  在中对AOP作了简要的介绍,以及一些专业术语的解释,同时写了一个简单的Spring AOPdemo。本文将继续探讨Spring AOP在实际场景中的应用。

  对用户操作日志的记录是很常见的一个应用场景,本文选取“用户管理”作为本文Spring AOP的示例。当然,该示例只是对真实场景的模拟,实际的环境一定比该示例更复杂。

  该示例的完整代码路径在。本文仅对Spring AOP相关的代码进行讲解。

  在这个示例中首次采用RESTful架构风格,对于以下RESTful API的设计可能并不完美,如果有熟悉、精通RESTful架构风格的朋友希望能够指出我的错误。

  

  使用RESTful的前后端分离架构风格后,我感受到了前所未有的畅快,所以此次示例并没有前端页面的展示,完全使用JUnit进行单元测试包括对HTTP请求的Mock模拟,这部分代码不会进行详细讲解,之后会继续深入JUnit单元测试的一些学习研究。

  数据库只有一张表:

  回到正题,我们回顾下切面由哪两个部分组成: 通知 切点 首先明确我们需要在何时记录日志:

  通知

  切点

  首先明确我们需要在何时记录日志:

  1. 查询所有用户时,并没有参数(此示例没有作分页),只有在返回时才会有数据的返回,所以对查询所有用户的方法采用返回通知(AfterReturning)。

  2. 新增用户时,会带有新增的参数,此时可采用前置通知(Before)。

  3. 修改用户时,也会带有新增的参数,此时同样采用前置通知(Before)。

  4. 删除用户时,通常会带有唯一标识符ID,此时采用前置通知(Before)记录待删除的用户ID。

  在明确了通知类型后,此时我们需要明确切点,也就是在哪个地方记录日志。当然上面实际已经明确了日志记录的位置,但主要是切面表达式的书写。 在有了的基础,相信对日志切面类已经比较熟悉了:

1 package com.manager.aspect; 2  3 import org.apache.log4j.Logger; 4 import org.aspectj.lang.JoinPoint; 5 import org.aspectj.lang.annotation.*; 6 import org.springframework.stereotype.Component; 7  8 import java.util.Arrays; 9 10 /**11  * 日志切面12  * Created by Kevin on 2017/10/29.13  */14 @Aspect15 @Component16 public class LogAspect {17     /**18      * 操作日志文件名19      */20     private static final String OPERATION_LOG_NAME = "operationLog";21     private static final String LOG_FORMATTER = "%s.%s - %s";22     Logger log = Logger.getLogger(OPERATION_LOG_NAME);23     /**24      * 对查询方法记录日志的切点25      */26     @Pointcut("execution(* com.manager..*.*Controller.query*(..))")27     public void query(){}28 29     /**30      * 对新增方法记录日志的切点31      */32     @Pointcut("execution(* com.manager..*.*Controller.add*(..))")33     public void add(){}34 35     /**36      * 对修改方法记录日志的切点37      */38     @Pointcut("execution(* com.manager..*.*Controller.update*(..))")39     public void update(){}40 41     /**42      * 对删除方法记录日志的切点43      */44     @Pointcut("execution(* com.manager..*.*Controller.delete*(..))")45     public void delete(){}46 47     @AfterReturning(value = "query()", returning = "rvt")48     public void queryLog(JoinPoint joinPoint, Object rvt) {49         String className = joinPoint.getTarget().getClass().getName();50         String methodName = joinPoint.getSignature().getName();51         String returnResult = rvt.toString();52         log.info(String.format(LOG_FORMATTER, className, methodName, returnResult));53     }54 55     @Before("add()")56     public void addLog(JoinPoint joinPoint) {57         String className = joinPoint.getTarget().getClass().getName();58         String methodName = joinPoint.getSignature().getName();59         Object[] params = joinPoint.getArgs();60         log.info(String.format(LOG_FORMATTER, className, methodName, Arrays.toString(params)));61     }62 63     @Before("update()")64     public void updateLog(JoinPoint joinPoint) {65         String className = joinPoint.getTarget().getClass().getName();66         String methodName = joinPoint.getSignature().getName();67         Object[] params = joinPoint.getArgs();68         log.info(String.format(LOG_FORMATTER, className, methodName, Arrays.toString(params)));69     }70 71     @Before("delete()")72     public void deleteLog(JoinPoint joinPoint) {73         String className = joinPoint.getTarget().getClass().getName();74         String methodName = joinPoint.getSignature().getName();75         Object[] params = joinPoint.getArgs();76         log.info(String.format(LOG_FORMATTER, className, methodName, Arrays.toString(params)));77     }78 }

  上面的日志切面类中出现了JointPoint类作为参数的情况,这个参数能够传递被通知方法的相信,例如被通知方法所处的类以及方法名等。在第47行中的Object rvt参数就是获取被通知方法的返回值。 上面的切面并没有关注被通知方法的参数,如果要使得切面和被通知方法参数参数关联可以使用以下的方式:

@Pointcut("execution(* com.xxx.demo.Demo.method(int)) && args(arg)")public void aspectMethod(int arg){}@Before(“aspectMedhot(arg)”)    public void method(int arg) {    //此时arg参数就是被通知方法的参数}

  本例中最主要的切面部分就完成了。注意在结合Spring时需要在applicationContext.xml中加入以下语句:

  示例中关于log4j、pom.xml依赖、JUnit如何结合Spring进行单元测试等等均可可以参考完整代码。特别是JUnit是很值得学习研究的一部分,这部分在将来慢慢我也会不断学习推出新的博客,在这里就只贴出JUnit的代码,感兴趣的可以浏览一下:

1 package com.manager.user.controller; 2  3 import com.fasterxml.jackson.databind.ObjectMapper; 4 import com.manager.user.pojo.User; 5 import org.junit.Before; 6 import org.junit.Test; 7 import org.junit.runner.RunWith; 8 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.http.MediaType;10 import org.springframework.test.context.ContextConfiguration;11 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;12 import org.springframework.test.context.web.WebAppConfiguration;13 import org.springframework.test.web.servlet.MockMvc;14 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;15 import org.springframework.test.web.servlet.result.MockMvcResultMatchers;16 import org.springframework.test.web.servlet.setup.MockMvcBuilders;17 import org.springframework.web.context.WebApplicationContext;18 19 import static org.junit.Assert.assertNotNull;20 21 /**22  * UserController单元测试23  * Created by Kevin on 2017/10/26.24  */25 @RunWith(SpringJUnit4ClassRunner.class)26 @ContextConfiguration({"classpath*:applicationContext.xml", "classpath*:spring-servlet.xml"})27 @WebAppConfiguration28 public class UserControllerTest {29     @Autowired30     private WebApplicationContext wac;31     private MockMvc mvc;32 33     @Before34     public void initMockHttp() {35         this.mvc = MockMvcBuilders.webAppContextSetup(wac).build();36     }37 38     @Test39     public void testQueryUsers() throws Exception {40         mvc.perform(MockMvcRequestBuilders.get("/users"))41                 .andExpect(MockMvcResultMatchers.status().isOk());42     }43 44     @Test45     public void testAddUser() throws Exception {46         User user = new User();47         user.setName("kevin");48         user.setAge(23);49         mvc.perform(MockMvcRequestBuilders.post("/users")50                 .contentType(MediaType.APPLICATION_JSON_UTF8)51                 .content(new ObjectMapper().writeValueAsString(user)))52                 .andExpect(MockMvcResultMatchers.status().isOk())53                 .andExpect(MockMvcResultMatchers.jsonPath("$.name").value("kevin"))54                 .andExpect(MockMvcResultMatchers.jsonPath("$.age").value(23));55     }56 57     @Test58     public void testQueryUserById() throws Exception {59         User user = new User();60         user.setId(8);61         mvc.perform(MockMvcRequestBuilders.get("/users/" + user.getId()))62                 .andExpect(MockMvcResultMatchers.status().isOk())63                 .andExpect(MockMvcResultMatchers.jsonPath("$.name").value("kevin"))64                 .andExpect(MockMvcResultMatchers.jsonPath("$.age").value(23));65 66     }67 68     @Test69     public void testUpdateUserById() throws Exception {70         User user = new User();71         user.setId(9);72         user.setName("tony");73         user.setAge(99);74         mvc.perform(MockMvcRequestBuilders.put("/users/" + user.getId())75                 .contentType(MediaType.APPLICATION_JSON_UTF8)76                 .content(new ObjectMapper().writeValueAsString(user)))77                 .andExpect(MockMvcResultMatchers.status().isOk());78     }79 80     @Test81     public void testDeleteUserById() throws Exception {82         long id = 10;83         mvc.perform(MockMvcRequestBuilders.delete("/users/" + id))84                 .andExpect(MockMvcResultMatchers.status().isOk());85     }86 }

  有了初级和中级,接下来必然就是Spring AOP高级——源码实现。

 

 

这是一个能给程序员加buff的公众号 

转载地址:http://xvjto.baihongyu.com/

你可能感兴趣的文章
Ue4的GitHUB版本版本管理探索
查看>>
日常随笔
查看>>
Jquery ajax上传文件到服务器
查看>>
CentOS 6.7安装Mahout 0.11.2
查看>>
Matlab计算《概率论与数理统计辅导讲义》中与答案不一致的定积分
查看>>
c++ fstream用法(2)
查看>>
postgresql----索引失效
查看>>
Jquery获取Url的参数
查看>>
MVC过滤器详解
查看>>
js超简单日历
查看>>
Jmeter 获取系统时间,和对系统时间进行增减时间
查看>>
2014华为机试(3)
查看>>
spring之jdbcTemplate的增删改查操作--不使用配置文件
查看>>
rectify vuforia video background
查看>>
洛谷4245:【模板】任意模数NTT——题解
查看>>
POJ3630:Phone List——题解
查看>>
[AR]高通Vuforia之Frame Markers
查看>>
查看硬盘负载情况:iostat命令
查看>>
Onmouseover被调用多次
查看>>
科普帖:线程与进程
查看>>