目录
前言
编程范式主要有以下几类
- AOP(Aspect Oriented Programming)面向切面编程
- OOP(Object Oriented Programming)面向对象编程
- POP(procedure oriented programming)面向过程编程
- FP(Functional Programming)面向函数编程
引入pom依赖
项目根目录 pom.xml
添加依赖 spring-boot-starter-aop
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
aop注解
@Aspect
: 切面,由通知和切入点共同组成,这个注解标注在类上表示为一个切面。@Joinpoint
: 连接点,被AOP拦截的类或者方法,在前置通知中有介绍使用@Joinpoint
获取类名、方法、请求参数。Advice
: 通知的几种类型@Before
: 前置通知,在某切入点@Pointcut
之前的通知@After
: 后置通知,在某切入点@Pointcut
之后的通知无论成功或者异常。@AfterReturning
: 返回后通知,方法执行return之后,可以对返回的数据做加工处理。@Around
: 环绕通知,在方法的调用前、后执行。@AfterThrowing
: 抛出异常通知,程序出错跑出异常会执行该通知方法。@Pointcut
: 切入点,从哪里开始。例如从某个包开始或者某个包下的某个类等。
实现日志分割功能
目录 aspect
下 新建 HttpAspect.java
类,在收到请求之后先记录请求的相关参数日志信息,请求成功完成之后打印响应信息,请求处理报错打印报错日志信息。
HttpAspect.java
package com.itaofly.aspect.aspect;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
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;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* @desctiption:
* @author: yinghuaYang
* @date: 2018/12/22
*/
@Aspect
@Component
public class HttpAspect {
// 打印日志模块
private final Logger logger = LoggerFactory.getLogger(HttpAspect.class);
// 下面慢慢介绍...
添加切入点
定义切入的入口在哪里,封装一个公共的方法实现复用
HttpAspect.java
/**
* 定义一个公共的方法
* 拦截UserController下面的所有方法
* 拦截UserController下面的userList方法里的任何参数(..表示拦截任何参数)
* 写法: @Pointcut("execution(public * com.itaofly.aspect.controller.UserController.*(..))")
*/
@Pointcut("execution(public * com.itaofly.aspect.controller.UserController.*(..))")
public void verify() {}
前置通知
拦截方法之前的一段业务逻辑,获取请求的一些信息,其中用到了Gson
处理对象转json输出
HttpAspect.java
@Before("verify()")
public void doBefore(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Map<String,Object> map = new HashMap<>();
// 获取请求的url
map.put("url",request.getRequestURL());
// 获取请求的方式
map.put("method",request.getMethod());
// 获取请求的ip地址
map.put("ip",request.getRemoteAddr());
// 获取类名
map.put("className",joinPoint.getSignature().getDeclaringTypeName());
// 获取类方法
map.put("classMethod",joinPoint.getSignature().getName());
// 请求参数
map.put("args",joinPoint.getArgs());
// 输出格式化后的json字符串
Gson gson = new GsonBuilder().setPrettyPrinting().create();
logger.info("request: {}",gson.toJson(map));
}
后置通知
拦截方法之后的一段业务逻辑
HttpAspect.java
@After("verify()")
public void doAfter() {
logger.info("doAfter!");
}
环绕通知
环绕通知是在方法的前后的一段逻辑操作,可以修改目标方法的返回值,第一个参数是org.aspectj.lang.ProceedingJoinPoint
类型,注意这里要调用执行目标方法proceed()
获取值返回,不然会造成空指针异常。在环绕通知里面也可以捕获错误返回。
HttpAspect.java
@Around("verify()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) {
try {
Object obj = proceedingJoinPoint.proceed();
System.out.println("方法环绕..., 结果是 : {}" + obj);
logger.info("doAround...1");
return obj;
}catch (Throwable e) {
logger.info("Throwable ...");
return null;
}
}
返回后通知
在切入点完成之后的返回通知,此时就不会抛出异常通知,除非返回后通知的业务逻辑报错。
HttpAspect.java
/**
* 获取响应返回值
* @param obj
*/
@AfterReturning(returning = "obj",pointcut = "verify()")
public void doAfterReturning(Object obj) {
// 会打印出一个对象,想打印出具体内容需要在定义模型处加上toString()
//logger.info("response: {}",obj);
logger.info("response: {}",obj.toString());
}
异常通知
抛出异常后的通知,此时返回后通知@AfterReturning
就不会执行。
HttpAspect.java
@AfterThrowing(pointcut = "verify()")
public void doAfterThrowing() {
logger.error("doAfterThrowing: {}"," 异常情况!!!!");
}
一段段伪代码读懂执行顺序
try {
// @Before 执行前通知
// 执行目标方法
// @Around 执行环绕通知 成功走finall,失败走catch
} finally {
// @After 执行后置通知
// @AfterReturning 执行返回后通知
} catch(e) {
// @AfterThrowing 抛出异常通知
}
测试正常异常两种情况
测试之前先对controller/UserController.java
文件的getUserList 方法增加了
exception`参数
/**
* 根据所有用户
* @param exception
* @return
*/
@RequestMapping(value = "/list/{exception}")
public List<User> getUserList(@PathVariable("exception") Boolean exception) {
if (exception) {
throw new Error("throw error!");
}
return repository.findAll();
}
- 测试正常情况
curl 127.0.0.1:8080/user/list/false
正常情况返回值如下所示:
- 测试异常情况
curl 127.0.0.1:8080/user/list/true
异常情况返回值如下所示:
通过以上两种情况测试可以看到环绕通知在正常、异常两种情况都可以执行到
完整示例代码
-- HttpAspect.java --
package com.itaofly.aspect.aspect;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
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;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* @desctiption:
* @author: yinghuaYang
* @date: 2018/12/22
*/
@Aspect
@Component
public class HttpAspect {
// 打印日志模块
private final Logger logger = LoggerFactory.getLogger(HttpAspect.class);
/**
* 定义一个公共的方法
* 拦截UserController下面的所有方法
* 拦截UserController下面的userList方法里的任何参数(..表示拦截任何参数)
* 写法: @Pointcut("execution(public * com.itaofly.aspect.controller.UserController.*(..))")
*/
@Pointcut("execution(public * com.itaofly.aspect.controller.UserController.*(..))")
public void verify() {}
@Before("verify()")
public void doBefore(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Map<String,Object> map = new HashMap<>();
// 获取请求的url
map.put("url",request.getRequestURL());
// 获取请求的方式
map.put("method",request.getMethod());
// 获取请求的ip地址
map.put("ip",request.getRemoteAddr());
// 获取类名
map.put("className",joinPoint.getSignature().getDeclaringTypeName());
// 获取类方法
map.put("classMethod",joinPoint.getSignature().getName());
// 请求参数
map.put("args",joinPoint.getArgs());
// 输出格式化后的json字符串
Gson gson = new GsonBuilder().setPrettyPrinting().create();
logger.info("request: {}",gson.toJson(map));
}
@Around("verify()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) {
try {
Object obj = proceedingJoinPoint.proceed();
System.out.println("方法环绕..., 结果是 : {}" + obj);
logger.info("doAround...1");
return obj;
}catch (Throwable e) {
logger.info("Throwable ...");
return null;
}
}
@After("verify()")
public void doAfter() {
logger.info("doAfter!");
}
/**
* 获取响应返回值
* @param obj
*/
@AfterReturning(returning = "obj",pointcut = "verify()")
public void doAfterReturning(Object obj) {
// 会打印出一个对象,想打印出具体内容需要在定义模型处加上toString()
//logger.info("response: {}",obj);
logger.info("response: {}",obj.toString());
}
@AfterThrowing(pointcut = "verify()")
public void doAfterThrowing() {
logger.error("doAfterThrowing: {}"," 异常情况!!!!");
}
}
-- UserController.java --
package com.itaofly.aspect.controller;
import com.itaofly.aspect.model.User;
import com.itaofly.aspect.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
/**
* @desctiption:
* @author: yinghuaYang
* @date: 2018/12/22
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserRepository repository;
/**
* 新增用户
* @param params
* @return
*/
@PostMapping(value = "/add")
public User addUesr(@RequestBody User params) {
User user = new User();
user.setUserName(params.getUserName());
user.setUserAge(params.getUserAge());
return repository.save(user);
}
/**
* 根据所有用户
* @param exception
* @return
*/
@GetMapping(value = "/list/{exception}")
public List<User> getUserList(@PathVariable("exception") Boolean exception) {
if (exception) {
throw new Error("throw error!");
}
return repository.findAll();
}
/**
* 根据Id查询用户
* @param id
* @return
*/
@GetMapping("/{id}")
public Optional<User> getUserById(@PathVariable("id") Integer id) {
return repository.findById(id);
}
/**
* 根据userName获取用户信息
* @param userName
* @return
*/
@GetMapping("/userName")
public List<User> getUserListByName(@RequestParam(name = "userName",defaultValue = "") String userName) {
return repository.findByUserName(userName);
}
/**
* 更新用户信息
* @param id
* @param userName
* @param userAge
* @return
*/
@PutMapping("/{id}")
public User updateUser(@PathVariable("id") Integer id,
@PathVariable("userName") String userName,
@PathVariable("userAge") Integer userAge){
User user = new User();
user.setId(id);
user.setUserAge(userAge);
user.setUserName(userName);
return repository.save(user);
}
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable("id") Integer id) {
repository.deleteById(id);
}
}
-- User.java --
package com.itaofly.aspect.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* @desctiption:
* @author: yinghuaYang
* @date: 2018/12/22
*/
@Entity
public class User {
@Id
@GeneratedValue
private Integer id;
private String userName;
private Integer userAge;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getUserAge() {
return userAge;
}
public void setUserAge(Integer userAge) {
this.userAge = userAge;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", userName='" + userName + '\'' +
", userAge=" + userAge +
'}';
}
}
-- UserReponsitory.java --
package com.itaofly.aspect.repository;
import com.itaofly.aspect.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
/**
* @desctiption:
* @author: yinghuaYang
* @date: 2018/12/22
*/
public interface UserRepository extends JpaRepository<User,Integer> {
/**
* 通过用户名查询
* @param userName
* @return
*/
List<User> findByUserName(String userName);
}
我的新博客地址链接:https://www.itaofly.com