gpt4 book ai didi

java - 如何使用 AspectJ 有条件地记录不同的数据?

转载 作者:太空宇宙 更新时间:2023-11-04 12:14:46 27 4
gpt4 key购买 nike

总的来说,我对 AspectJ 和 AOP 还很陌生。我确实知道 AspectJ 有很多注释(After、AfterReturning 等)来在调用方法之前、调用之后、返回之后、抛出异常时等执行代码。

我想用它来记录日志,这是一个非常典型的用例。我一直在看这篇文章,我认为这就是我所需要的。它使用 AspectJ 以及“jcambi方面”来执行日志记录。

但我想做如下的事情:

public void login(User user) {
String userType = user.getType();

if (!user.isActive()) {
// point cut 1 -- log inactive user
} else if (!user.isPasswordValid()) {
// point cut 2 -- log wrong password
} else {
// point cut 3 -- log successful login
}
}

我们有既定的日志格式。像这样的东西:

<actor>|<action_code>|<error_code>|<extra_info>

所有 Actor 类型、操作和错误代码都包含在枚举中。

有没有办法告诉 AspectJ:

在“ifs”内登录,并且根据发生的情况记录不同的信息?例如,在切入点 1 中记录以下内容之一:

admin|login|001|Admin user inactive
user|login|001|Normal user inactive

...并在切入点 2 中记录以下内容之一:

admin|login|002|Invalid Admin password
user|login|002|Invalid normal user password

...并在切入点 3 中记录以下内容之一:

admin|login|000|Successful Admin login
user|login|000|Successful Normal user login

有件事告诉我这是不可能的。或者至少不容易。但我不确定这是否值得尝试。所以我很伤心。一方面,我想“清理”所有日志记录的代码。另一方面,我不确定实现这个是否会需要太多工作。

有什么想法吗?

******************************************** 编辑********************************************

谢谢两位的回答!我现在意识到两件事: 1. 我还有很多工作要做。 2.我认为我过于强调“登录”示例。

登录只是一个微小的用例。我的任务是在任何地方添加日志记录......在许多很多类的一堆方法中。基本上在应用程序中的任何地方我都会看到 LOG.debug() 或 LOG.info(),以将其替换为方面日志记录。这也意味着,尽管我愿意,但我不能仅仅重构所有代码来让我的生活更轻松。我想让登录使用异常,但这超出了我的任务范围:添加日志记录。

当然,在每种方法中,业务逻辑都会不同,因此日志记录也会不同。所以我的问题是:执行此操作的最佳实践是什么?我的意思是,每个方法都有自己的逻辑,它的 ifs ...并且会有条件地记录不同的内容。那么我是否应该继续为这些用例中的每一个创建一个切面类,并且基本上也有相同的“ifs”?

示例(不是登录!):导入数据的方法。

public void import(String type) {
if (type.equals("people")) {
try {
int result = importPeople();
if (result > 0) {
// do some stuff
LOG.info("ok");
} else {
// do some stuff
LOG.info("problem");
}
} catch (Exception e) {
// do some stuff
LOG.debug("exception ...");
}
} else if (type.equals("places")) {
try {
int result = importPlaces();
if (result > 0) {
// do some stuff
LOG.info("ok");
} else {
// do some stuff
LOG.info("problem");
}
} catch (Exception e) {
// do some stuff
LOG.debug("exception ...");
}
}
}

请注意,这是一个糟糕的示例,包含重复的代码等。但您明白了。我是否还应该创建一个“导入”方面,用于记录此方法......以及所有随附的“ifs”来记录“ok”,“问题”,“异常”?并为每个用例执行此操作?

我完全赞成摆脱侵入式日志记录代码,但是......这似乎有点代码味道,必须在原始方法(因为该方法比日志记录“做更多的事情”)以及相应的方面中都必须具有逻辑,及其“ifs”等......

无论如何,你们都回答了我最初的问题...但我只能有 1 个答案,所以我将接受 kriegaex 的答案,因为他似乎投入了很多工作!

最佳答案

是的,这是可能的。但如果我是你,我会对整个故事进行稍微不同的建模。首先,我会抛出由于未知或不活动用户或错误密码而导致登录失败的异常。或者,登录方法可以返回一个 boolean 值(成功登录则返回 true,否则返回 false)。但在我看来,这更像是老式的 C 风格,而不是现代的 OOP。

这是一个自洽的例子。对于带有大量静态成员和方法的丑陋的 UserDB 类,我们深表歉意。实际上,您不会存储明文密码,而是存储随机盐和加盐哈希值。但毕竟它只是基于方面的条件日志记录的概念证明。

用于登录的用户 bean:

package de.scrum_master.app;

public class User {
private String id;
private String password;

public User(String id, String password) {
this.id = id;
this.password = password;
}

public String getId() {
return id;
}

public String getPassword() {
return password;
}
}

用户数据库:

为了简单起见,有硬编码的数据库条目、静态枚举、成员和方法以及静态内部类。对不起!我希望您可以轻松想象如何通过更好的设计来做到这一点。

package de.scrum_master.app;

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

public class UserDB {
public static enum Role { admin, user, guest }
public static enum Action { login, logout, read, write }
public static enum Error { successful_login, user_inactive, invalid_password, unknown_user }

private static class UserInfo {
String password;
Role role;
boolean active;

public UserInfo(String password, Role role, boolean active) {
this.password = password;
this.role = role;
this.active = active;
}
}

private static Map<String, UserInfo> knownUsers = new HashMap<>();

static {
knownUsers.put("bruce", new UserInfo("alm1GHTy", Role.admin, true));
knownUsers.put("john", new UserInfo("LetMe_in", Role.user, true));
knownUsers.put("jane", new UserInfo("heLL0123", Role.guest, true));
knownUsers.put("richard", new UserInfo("dicky", Role.user, false));
knownUsers.put("martha", new UserInfo("paZZword", Role.admin, false));
}

public static class UserDBException extends Exception {
private static final long serialVersionUID = 7662809670014934460L;

public final String userId;
public final Role role;
public final Action action;
public final Error error;

public UserDBException(String userId, Role role, Action action, Error error, String message) {
super(message);
this.userId = userId;
this.role = role;
this.action = action;
this.error = error;
}
}

public static boolean isKnown(User user) {
return knownUsers.get(user.getId()) != null;
}

public static boolean isActive(User user) {
return isKnown(user) && knownUsers.get(user.getId()).active;
}

public static boolean isPasswordValid(User user) {
return isKnown(user) && knownUsers.get(user.getId()).password.equals(user.getPassword());
}

public static Role getRole(User user) {
return isKnown(user) ? knownUsers.get(user.getId()).role : null;
}

public static void login(User user) throws UserDBException {
String userId = user.getId();
if (!isKnown(user))
throw new UserDBException(
userId, getRole(user), Action.login,
Error.unknown_user, "Unknown user"
);
if (!isActive(user))
throw new UserDBException(
userId, getRole(user), Action.login,
Error.user_inactive, "Inactive " + getRole(user)
);
if (!isPasswordValid(user))
throw new UserDBException(
userId, getRole(user), Action.login,
Error.invalid_password, "Invalid " + getRole(user) + " password"
);
}
}

请注意 login(User) 方法如何抛出异常,并提供有助于日志记录的详细信息。

驱动程序应用程序模拟多个用户/密码组合的登录:

package de.scrum_master.app;

import java.util.Arrays;
import java.util.List;

public class Application {
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("mr_x", "foobar"),
new User("bruce", "foobar"),
new User("bruce", "alm1GHTy"),
new User("john", "foobar"),
new User("john", "LetMe_in"),
new User("jane", "foobar"),
new User("jane", "heLL0123"),
new User("richard", "foobar"),
new User("richard", "dicky"),
new User("martha", "foobar"),
new User("martha", "paZZword")
);

for (User user : users) {
try {
UserDB.login(user);
System.out.printf("%-8s -> %s%n", user.getId(), "Successful " + UserDB.getRole(user) + " login");
} catch (Exception e) {
System.out.printf("%-8s -> %s%n", user.getId(), e.getMessage());
}
}
}
}

请注意,我们只是捕获并记录所有异常,以避免应用程序在登录尝试失败后退出。

控制台日志:

mr_x     -> Unknown user
bruce -> Invalid admin password
bruce -> Successful admin login
john -> Invalid user password
john -> Successful user login
jane -> Invalid guest password
jane -> Successful guest login
richard -> Inactive user
richard -> Inactive user
martha -> Inactive admin
martha -> Inactive admin

登录记录器方面:

我建议您首先注释掉 Application.main(..) 中的两个 System.out.printf(..) 调用,以免将它们与方面日志记录混淆。

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import de.scrum_master.app.User;
import de.scrum_master.app.UserDB;
import de.scrum_master.app.UserDB.Action;
import de.scrum_master.app.UserDB.Error;
import de.scrum_master.app.UserDB.UserDBException;

@Aspect
public class UserActionLogger {
@Around("execution(void de.scrum_master.app.UserDB.login(*)) && args(user)")
public void captureLogin(ProceedingJoinPoint thisJoinPoint, User user) throws Throwable {
try {
thisJoinPoint.proceed();
System.out.printf("%s|%s|%d03|%s%n",
user.getId(), Action.login, Error.successful_login.ordinal(),
"Successful " + UserDB.getRole(user) + " login"
);
} catch (UserDBException e) {
System.out.printf("%s|%s|%03d|%s%n",
e.userId, e.action, e.error.ordinal(),
e.getMessage()
);
throw e;
}
}
}

方面的控制台日志:

mr_x|login|003|Unknown user
bruce|login|002|Invalid admin password
bruce|login|003|Successful admin login
john|login|002|Invalid user password
john|login|003|Successful user login
jane|login|002|Invalid guest password
jane|login|003|Successful guest login
richard|login|001|Inactive user
richard|login|001|Inactive user
martha|login|001|Inactive admin
martha|login|001|Inactive admin

瞧!我希望这大致就是您想要的。

关于java - 如何使用 AspectJ 有条件地记录不同的数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39531538/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com