Обучение Spring boot (6) Spring boot реализует АОП для записи журналов операций.

Spring Boot

предисловие

В реальных проектах, особенно в системах управления, мы обычно записываем журналы операций для этих важных операций. например база данныхCRUDОперация, мы будем записывать каждую важную операцию, обычной практикой является вставка записи в таблицу журнала, указанную в базе данных. Здесь возникает вопрос, должны ли мыCRUDВы вручную вставляете записи журнала, когда хотите? Это определенно неуместно, такая операция, несомненно, увеличивает объем разработки и непроста в обслуживании, поэтому она всегда используется в реальных проектах.AOP(Aspect Oriented Programming)То есть аспектно-ориентированное программирование — это методика записи журнала операций в системе.

Статья впервые опубликована в личном блоге: [www.xiongfrblog.cn

классификация бревен

Здесь я делю журналы на две категории в соответствии с различными объектами:

  • Журнал, ориентированный на пользователя: пользователь относится к человеку, который использует систему.Этот тип журнала обычно записывается в базу данных и обычно записывает некоторую информацию о базе данных.CRUDработать.
  • Журналы, ориентированные на разработчиков: как правило, этот тип журнала просматривают разработчики.Этот тип журнала обычно сохраняется в файле или распечатывается на консоли (консоль используется для разработки и сохраняется в файле после завершения проекта). Журналы классов в основном используются для обнаружения ошибок во время разработки разработчиком и последующего обслуживания.

Для разных объектно-ориентированных журналов мы используем разные стратегии записи. Легко видеть, что журналы, ориентированные на пользователя, очень гибкие, и разработчику необходимо контролировать, какие операции пользователя необходимо регистрировать в базе данных, поэтому мы используем этот тип журналов, хранящихся в базе данных.AOPПри записи используйте пользовательские аннотации для сопоставления, а для журналов, ориентированных на разработчиков, мы можем использовать выражения для сопоставления (это может быть немного расплывчато, но станет ясно, когда вы прочитаете пример ниже), которые будут представлены отдельно ниже. Реализация двух видов журналов.

Реализовать АОП для записи журналов, ориентированных на пользователя.

Далее шаг за шагомSpring bootкак добиться черезAOPЗапишите журнал операций.

добавить зависимости

существуетpom.xmlДобавьте в файл следующие зависимости:

<!-- aop依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

Изменить файл конфигурации

в проектеapplication.propertiesДобавьте в файл следующую конфигурацию:

spring.aop.auto=true

Вот специальное примечание, это предложение на самом деле может быть опущено, потому что по умолчаниюtrue, пока мыpom.xmlДостаточно добавить к нему зависимости.Это предлагается здесь, чтобы все знали, что есть эта конфигурация.

пользовательская аннотация

Как описано выше, поскольку этот тип журнала является более гибким, нам необходимо настроить аннотацию.При его использовании добавьте эту аннотацию в метод, который должен записывать журнал.Сначала создайте новый в пакете того же уровня стартовый классconfigпакет, создайте новый под этим отчетомnewимяLogизAnnotationфайл, содержимое файла следующее:

package com.web.springbootaoplog.config;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* @author Promise
* @createTime 2018年12月18日 下午9:26:25
* @description  定义一个方法级别的@log注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
	String value() default "";
}

используется здесьJavaДля получения соответствующих знаний о мета-аннотациях друзья, которые не знакомы с соответствующими концепциями, могут перейти в этот блог.getнемного【портал].

Подготовьте таблицы журналов базы данных и классы сущностей, интерфейсы sql и файлы xml.

Поскольку вы вставляете записи в базу данных, предпосылка заключается в том, что вам нужно создать таблицу для ведения журнала.Моя таблица приведена ниже.sql,Поскольку я пишу образец, я разработал эту таблицу очень просто, и вы можете разработать ее самостоятельно.

CREATE TABLE `sys_log` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `user_id` int(11) NOT NULL COMMENT '操作员id',
  `user_action` varchar(255) NOT NULL COMMENT '用户操作',
  `create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COMMENT='日志记录表';

Представлено в предыдущем блогеMBGСоздайте соответствующий класс сущности,sqlинтерфейсный файл иxmlДокумент больше не приводится здесь. Для тех, кто не понял, перейдите в [портал

Конечно нужно создатьserviceФайл интерфейса и класс реализации интерфейса, вот непосредственно код:

ISysLogServcie.java

package com.web.springbootaoplog.service;

import com.web.springbootaoplog.entity.SysLog;

/**
* @author Promise
* @createTime 2018年12月18日 下午9:29:48
* @description 日志接口
*/
public interface ISysLogService {

	/**
	 * 插入日志
	 * @param entity
	 * @return
	 */
	int insertLog(SysLog entity);
}

SysLogServiceImpl.java

package com.web.springbootaoplog.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.web.springbootaoplog.config.Log;
import com.web.springbootaoplog.dao.SysLogMapper;
import com.web.springbootaoplog.entity.SysLog;
import com.web.springbootaoplog.service.ISysLogService;


/**
* @author Promise
* @createTime 2018年12月18日 下午9:30:57
* @description 
*/
@Service("sysLogService")
public class SysLogServiceImpl implements ISysLogService{

	@Autowired
	private SysLogMapper sysLogMapper;
	
	@Override
	public int insertLog(SysLog entity) {
		// TODO Auto-generated method stub
		return sysLogMapper.insert(entity);
	}
}

Аспекты АОП и касательные

После подготовки соответствующих документов выше, давайте введем ключевой момент - созданиеAOPКласс реализации аспекта, также мы размещаем этот класс здесьconfigпакет, названныйLogAsPect.java, содержание следующее:

package com.web.springbootaoplog.config;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
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.aspectj.lang.reflect.MethodSignature;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;

import com.web.springbootaoplog.entity.SysLog;
import com.web.springbootaoplog.service.ISysLogService;


/**
* @author Promise
* @createTime 2018年12月18日 下午9:33:28
* @description 切面日志配置
*/
@Aspect
@Component
public class LogAsPect {
	
	private final static Logger log = org.slf4j.LoggerFactory.getLogger(LogAsPect.class);

	@Autowired
	private ISysLogService sysLogService;
	
	//表示匹配带有自定义注解的方法
	@Pointcut("@annotation(com.web.springbootaoplog.config.Log)")
	public void pointcut() {}
	
	@Around("pointcut()")
	public Object around(ProceedingJoinPoint point) {
		Object result =null;
		long beginTime = System.currentTimeMillis();
		
		try {
		    log.info("我在目标方法之前执行!");
			result = point.proceed();
			long endTime = System.currentTimeMillis();
			insertLog(point,endTime-beginTime);
		} catch (Throwable e) {
			// TODO Auto-generated catch block
		}
		return result;
	}
	
	private void insertLog(ProceedingJoinPoint point ,long time) {
		MethodSignature signature = (MethodSignature)point.getSignature();
		Method method = signature.getMethod();
		SysLog sys_log = new SysLog();
		
		Log userAction = method.getAnnotation(Log.class);
		if (userAction != null) {
			// 注解上的描述
			sys_log.setUserAction(userAction.value());
		}
		
		// 请求的类名
		String className = point.getTarget().getClass().getName();
		// 请求的方法名
		String methodName = signature.getName();
		// 请求的方法参数值
		String args = Arrays.toString(point.getArgs());
		
		//从session中获取当前登陆人id
//		Long useride = (Long)SecurityUtils.getSubject().getSession().getAttribute("userid");
		
		Long userid = 1L;//应该从session中获取当前登录人的id,这里简单模拟下
		
		sys_log.setUserId(userid);
		
		sys_log.setCreateTime(new java.sql.Timestamp(new Date().getTime()));
		
		log.info("当前登陆人:{},类名:{},方法名:{},参数:{},执行时间:{}",userid, className, methodName, args, time);
		
		sysLogService.insertLog(sys_log);
	}
}

Вот краткое введение вAOPНесколько важных замечаний:

  • @Aspect: Эта аннотация указывает, что текущий класс рассматривается как класс аспекта.
  • @Component: Указывает, что текущий класс переданSpringуправлять.
  • @Pointcut: выражение pointcut, определите наши правила сопоставления, выше мы используем@Pointcut("@annotation(com.web.springbootaoplog.config.Log)")Указывает методы сопоставления с нашей пользовательской аннотацией.
  • @Around: окружающий совет, который может выполнять некоторые действия до и после выполнения целевого метода, а также действия, которые необходимо выполнить, когда целевой метод выдает исключение.

Это аннотации, которые мы используем. Конечно, есть и другие аннотации, я не буду приводить их здесь по одной, я хочу узнать о них больше.AOPДрузья с соответствующими знаниями могут перейти к официальному документу [портал

Давайте посмотрим на ключевой фрагмент кода:

log.info("我在目标方法之前执行!");
result = point.proceed();
long endTime = System.currentTimeMillis();
insertLog(point,endTime-beginTime);

вresult = point.proceed();Это предложение означает выполнение целевого метода, видно, что мы печатаем лог перед выполнением этого кода и вызываем его после выполнения.insertLog()Метод вставки журнала, и в методе мы можем получить имя класса, имя метода, параметры и другую важную информацию целевого метода.

тестовый контроллер

существуетcontrollerСоздать новый под пакетомHomeCOntroller.java(имя необязательно), содержание следующее:

package com.web.springbootaoplog.controller;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.web.springbootaoplog.config.Log;
import com.web.springbootaoplog.entity.SysLog;
import com.web.springbootaoplog.service.ISysLogService;

/**
* @author Promise
* @createTime 2019年1月2日 下午10:35:30
* @description  测试controller
*/
@Controller
public class HomeController {

    private final static Logger log = org.slf4j.LoggerFactory.getLogger(HomeController.class);
	
	@Autowired
	private ISysLogService logService;

	@RequestMapping("/aop")
	@ResponseBody
	@Log("测试aoplog")
	public Object aop(String name, String nick) {
		Map<String, Object> map =new HashMap<>();
		log.info("我被执行了!");
		map.put("res", "ok");
		return map;
	}
}

Определяем тестовый метод с двумя параметрами и добавляем наш кастомный метод в метод@LogАннотации, стартовые проекты, доступ через браузерlocalhost:8080/aop?name=xfr&nick=eran, проверьте на этот разeclipseНекоторая информация о выводе консоли выглядит следующим образом:

2019-01-24 22:02:17.682  INFO 3832 --- [nio-8080-exec-1] c.web.springbootaoplog.config.LogAsPect  : 我在目标方法之前执行!
2019-01-24 22:02:17.688  INFO 3832 --- [nio-8080-exec-1] c.w.s.controller.HomeController          : 我被执行了!
2019-01-24 22:02:17.689  INFO 3832 --- [nio-8080-exec-1] c.web.springbootaoplog.config.LogAsPect  : 当前登陆人:1,类名:com.web.springbootaoplog.controller.HomeController,方法名:aop,参数:[xfr, eran],执行时间:6

Вы можете видеть, что мы успешно вставили некоторый логический код до и после выполнения целевого метода.Теперь посмотрим на данные в базе данных:

在这里插入图片描述

Часть данных была успешно записана.

Реализовать АОП для записи журналов, ориентированных на разработчиков.

Прежде всего, здесь я перечисляю сценарий применения с использованием этого метода, который появляется в проектеbug, мы хотим знать, поступил ли запрос с переднего плана в наш контроллер, и получение параметров.Начнем с шагов реализации.

По сути, принцип тот же, что и выше, но правила сопоставления pointcut изменились, и записывать лог в базу не нужно, достаточно распечатать.

первый вLogAsPect.javaОпределите новое выражение pointcut в , как показано ниже:

@Pointcut("execution(public * com.web.springbootaoplog.controller..*.*(..))")
public void pointcutController() {}

@Pointcut("execution(public * com.web.springbootaoplog.controller..*.*(..))")указывает на совпадениеcom.web.springbootaoplog.controllerВсе общедоступные методы пакета и его подпакетов.

Подробное использование этого выражения можно переместить сюда, [портал

Что мы должны сделать при добавлении совпадения в метод:

@Before("pointcutController()")
public void around2(JoinPoint point) {
	//获取目标方法
	String methodNam = point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName();
	
	//获取方法参数
	String params = Arrays.toString(point.getArgs());
	
	log.info("get in {} params :{}",methodNam,params);
}

@Before: указывает, что содержимое следующего тела метода выполняется до выполнения целевого метода.

Затем добавьте тестовый метод в контроллер:

@RequestMapping("/testaop3")
@ResponseBody
public Object testAop3(String name, String nick) {
	Map<String, Object> map = new HashMap<>();
	
	map.put("res", "ok");
	return map;
}

Вы можете видеть, что мы не добавили этот метод@LogАннотируйте, перезапустите проект и войдите в браузер на localhost:8080/testaop3?name=xfr&nick=eran.В настоящее время некоторая выходная информация консоли eclipse выглядит следующим образом:

2019-01-24 23:19:49.108  INFO 884 --- [nio-8080-exec-1] c.web.springbootaoplog.config.LogAsPect  : get in com.web.springbootaoplog.controller.HomeController.testAop3 params :[xfr, eran]

Распечатывается журнал ключей, чтобы мы могли узнать, ввели ли мы метод, правильно ли получен параметр, и другую ключевую информацию.

У некоторых друзей здесь могут быть вопросы о том, будет ли это добавлено с@LogМетод повторяется, так и есть, поэтому в проекте я обычно использую@LogАннотация используется вServiceС точки зрения послойного метода это также более разумно.

Эпилог

хорошо, оAopВот и все по содержанию журнала, увидимся в следующем блоге. пока~