предисловие
В реальных проектах, особенно в системах управления, мы обычно записываем журналы операций для этих важных операций. например база данных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
Вот и все по содержанию журнала, увидимся в следующем блоге. пока~