用aop实现登陆日志、和操作日志持久化
在 Java 中,可以使用 AOP(Aspect-Oriented Programming)来实现登录日志和操作日志的持久化功能。通过 AOP,我们可以将日志记录这类 **横切关注点(cross-cutting concerns)**从业务逻辑中分离出来,实现更好的代码结构和可维护性。下面是一个简单的示例代码,演示如何使用 AOP 在登录和操作时记录日志并将日志持久化到数据库中。
-
定义日志实体类:
首先定义登录日志和操作日志的实体类,包括相关的字段,如用户名、登录时间、操作内容、操作时间等。 public class LoginLog { private String username; private Date loginTime; // 省略其他字段和getter/setter方法 } public class OperationLog { private String username; private String operation; private Date operationTime; // 省略其他字段和getter/setter方法 }
-
编写持久化层(DAO)
编写持久化层的代码,负责将日志数据存储到数据库中。
public interface LogDao { void saveLoginLog(LoginLog loginLog); void saveOperationLog(OperationLog operationLog); } public class LogDaoImpl implements LogDao { // 实现持久化方法 @Override public void saveLoginLog(LoginLog loginLog) { // 将登录日志信息保存到数据库中的具体实现代码 System.out.println("Saving login log to the database: " + loginLog); } @Override public void saveOperationLog(OperationLog operationLog) { // 将操作日志信息保存到数据库中的具体实现代码 System.out.println("Saving operation log to the database: " + operationLog); } }
-
编写AOP切面
编写AOP切面,定义切点和通知,用于捕获登录和操作的相关行为,并在切点处插入通知以实现日志记录。
@Aspect @Component public class LogAspect { @Autowired private LogDao logDao; // 登录切点 @Pointcut("execution(* com.example.service.UserService.login(..))") public void loginPointcut() {} // 操作切点 @Pointcut("execution(* com.example.service.*Service.*(..))") public void operationPointcut() {} // 登录日志通知 @AfterReturning(pointcut = "loginPointcut()", returning = "result") public void logLogin(JoinPoint joinPoint, Object result) { String username = (String) joinPoint.getArgs()[0]; LoginLog loginLog = new LoginLog(); loginLog.setUsername(username); loginLog.setLoginTime(new Date()); logDao.saveLoginLog(loginLog); } 注解: 这段代码使用了 @AfterReturning 注解来定义一个通知方法 logLogin,该方法在切点 loginPointcut() 匹配的目标方法正常返回后执行。下面是对这段代码的详细解释: @AfterReturning(pointcut = "loginPointcut()", returning = "result"):使用 @AfterReturning 注解表示定义的是一个返回通知,在切点 loginPointcut() 匹配的方法正常返回后执行,returning 属性指定了方法返回值将被传递给通知方法的参数。 public void logLogin(JoinPoint joinPoint, Object result):logLogin 方法接收两个参数,一个是 JoinPoint 对象,提供了关于连接点(即目标方法)的信息;另一个是方法的返回值,即 result 对象。 String username = (String) joinPoint.getArgs()[0];:通过 joinPoint 对象的 getArgs() 方法获取目标方法的参数数组,然后取第一个参数作为用户名,并强制转换为字符串类型赋值给 username 变量。 LoginLog loginLog = new LoginLog();:创建一个 LoginLog 对象,用于存储登录日志信息。 loginLog.setUsername(username);:将用户名设置到 LoginLog 对象中。 loginLog.setLoginTime(new Date());:设置登录时间为当前时间。 logDao.saveLoginLog(loginLog);:调用 logDao 对象的 saveLoginLog 方法保存登录日志信息到数据库。 ----------------------------------------------------------------------------------------------- // 操作日志通知 @After("operationPointcut()") public void logOperation(JoinPoint joinPoint) { String username = SecurityContextHolder.getContext().getAuthentication().getName(); String methodName = joinPoint.getSignature().getName(); String operation = "execute " + methodName; OperationLog operationLog = new OperationLog(); operationLog.setUsername(username); operationLog.setOperation(operation); operationLog.setOperationTime(new Date()); logDao.saveOperationLog(operationLog); } 注解: 在这段代码中,使用了 @After 注解定义了一个通知方法 logOperation,该方法会在切点 operationPointcut() 匹配的目标方法执行后执行。下面是这段代码的详细解释: @After("operationPointcut()"):这里使用 @After 注解表示定义的是一个后置通知,当切点 operationPointcut() 匹配的方法执行完成后,logOperation 方法将会被调用。 public void logOperation(JoinPoint joinPoint):logOperation 方法接收一个 JoinPoint 对象作为参数,该对象提供了关于连接点(即目标方法)的信息,如方法签名、参数等。 String username = SecurityContextHolder.getContext().getAuthentication().getName();:通过 SecurityContextHolder 获取当前登录用户的认证信息,并获取用户名赋值给 username 变量。 String methodName = joinPoint.getSignature().getName();:使用 joinPoint 对象的 getSignature().getName() 方法获取目标方法的名称,赋值给 methodName 变量。 String operation = "execute " + methodName;:构造一个操作描述字符串,包含执行的方法名,赋值给 operation 变量。 OperationLog operationLog = new OperationLog();:创建一个 OperationLog 对象,用于存储操作日志信息。 operationLog.setUsername(username);:将用户名设置到 OperationLog 对象中。 operationLog.setOperation(operation);:将操作描述设置到 OperationLog 对象中。 operationLog.setOperationTime(new Date());:设置操作时间为当前时间。 logDao.saveOperationLog(operationLog);:调用 logDao 对象的 saveOperationLog 方法保存操作日志信息到数据库。 }
-
配置AOP
在Spring配置文件中配置AOP,指定切面和通知的织入方式。
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com.example.aspect"/>
-
在以上代码中,使用了Spring AOP的相关注解和配置来实现AOP的功能:
@Aspect
:声明该类是一个切面类。@Component
:将该切面类作为一个Spring的Bean进行管理。@Pointcut
:定义切点,指定需要拦截的方法。@AfterReturning
:定义一个后置通知,表示在目标方法返回结果后执行。@AfterThrowing
:异常通知,在目标方法抛出异常时执行。@After
:定义一个后置通知,表示在目标方法执行后执行。@Before
:前置通知,在目标方法执行前执行。@Around
:环绕通知,在目标方法执行前后都可以执行自定义的行为。JoinPoint
:提供了对目标方法和目标对象的访问。LogDao
:日志持久化接口,定义了保存登录日志和操作日志的方法。LogDaoImpl
:日志持久化接口的实现类,负责将日志数据存储到数据库中。LogAspect
:AOP切面类,包含了登录日志和操作日志的相关通知方法。@DeclareParents
:引入增强,允许向现有的类添加新的方法和属性。- 配置文件中的
<aop:aspectj-autoproxy/>
用于启用自动代理功能,<context:component-scan>
用于扫描并加载AOP切面类。