AspectJ를 사용하여 조건부로 다른 데이터를 기록하는 방법은 무엇입니까?

로버트 보웬

나는 일반적으로 AspectJ와 AOP에 꽤 익숙하다. AspectJ에는 메서드가 호출되기 전, 호출 된 후, 반환 된 후, 예외가 발생할 때 등의 코드를 실행하기위한 많은 주석 (After, AfterReturning 등)이 있다는 것을 알고 있습니다.

매우 일반적인 사용 사례 인 로깅에 사용하고 싶습니다. 나는이 기사를보고 있었고 그것이 내가 필요한 것의 대부분이라고 생각한다. 로깅을 수행하기 위해 AspectJ와 "jcambi aspect"를 사용합니다.

하지만 다음과 같은 작업을하고 싶습니다.

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>

모든 액터 유형, 작업 및 오류 코드는 열거 형에 포함됩니다.

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 ()를 볼 수있는 모든 곳에서 Aspect 로깅으로 대체합니다. 이것은 또한 내가 원하는만큼 모든 코드를 리팩토링하여 내 삶을 더 쉽게 만들 수 없다는 것을 의미합니다. 로그인에 예외를 사용하고 싶지만 내 작업 범위를 벗어납니다. 로깅 추가.

물론 각 방법에서 비즈니스 로직이 다르므로 로깅도 달라집니다. 그래서 내 질문은 다음과 같습니다. 이것을 수행하는 가장 좋은 방법은 무엇입니까? 내 말은, 각 메서드는 자체 논리, ifs를 가지며 조건부로 다른 것을 기록합니다. 그럼 계속해서 이러한 사용 사례 각각에 대해 aspect 클래스를 생성하고 기본적으로 동일한 "if"도 포함합니까?

예 (로그인이 아닙니다!) : 데이터를 가져 오는 방법.

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 ...");
        }  
      }  
    }

반복되는 코드 등이있는 쓰레기 예제라는 점을 염두에 두십시오. 그러나 아이디어를 얻었습니다. 또한이 메서드를 로깅하기 위해 "가져 오기"측면을 만들어야합니까? "ok", "problem", "exception"을 기록하는 모든 동반 "ifs"와 함께? 그리고 모든 사용 사례에 대해 이렇게합니까 ?

나는 침입 로깅 코드를 제거하기위한 것이지만 ... 원래 메서드에서 "if"등과 같은 논리를 가져야하는 코드 냄새 같은 것 같습니다 (메서드가 "이기 때문에" 로깅보다 더 많은 작업을 수행합니다.)뿐만 아니라 해당 Aspect ...

어쨌든, 둘 다 제 원래의 질문 답 해주셨는데 ... 제가 은 1 개 밖에 안될 수 있으니까 크리 게스가 많은 일을하신 것 같아서 받아 들일 게요 !

크리 게 엑스

예, 가능합니다. 하지만 내가 당신이라면 전체 이야기를 조금 다르게 모델링 할 것입니다. 우선, 알 수 없거나 비활성 사용자 또는 잘못된 암호로 인해 로그인 실패에 대한 예외를 던집니다. 또는 로그인 메서드가 부울 값을 반환 할 수 있습니다 (성공적인 로그인의 경우 true, 그렇지 않으면 false). 그러나 제 생각에는 이것은 현대 OOP보다 구식 C 스타일이 될 것입니다.

다음은 일관된 예입니다. UserDB많은 정적 멤버와 메서드가 있는 추악한 클래스에 대해 죄송합니다 . 그리고 실제로는 일반 텍스트 암호가 아니라 무작위 솔트와 솔트 해시를 저장합니다. 그러나 결국 이것은 측면 기반의 조건부 로깅에 대한 개념 증명 일뿐입니다.

로그인에 사용되는 사용자 빈 :

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;
    }
}

사용자 데이터베이스 :

단순성을 위해 하드 코딩 된 DB 항목, 정적 열거 형, 멤버 및 메서드와 정적 내부 클래스가 있습니다. 죄송합니다! 더 나은 디자인으로 똑같이하는 방법을 쉽게 상상할 수 있기를 바랍니다.

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)메서드가 로깅에 유용한 세부 정보와 함께 예외를 throw 하는 방법에 유의 하십시오.

여러 사용자 / 암호 조합에 대한 로그인을 시뮬레이션하는 드라이버 응용 프로그램 :

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

로그인 로거 측면 :

애스펙트 로깅과 섞이지 않도록 먼저 두 System.out.printf(..)호출을 주석 처리하는 것이 좋습니다 Application.main(..).

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;
        }
    }
}

aspect에 대한 콘솔 로그 :

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

Et voilà! 나는 이것이 대략 당신이 원하는 것이기를 바랍니다.

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

분류에서Dev

DataTable의 데이터를 조건부로 업데이트하는 가장 빠른 방법은 무엇입니까?

분류에서Dev

AngularJS를 사용하여 데이터를 다른 페이지로 전달하는 방법은 무엇입니까?

분류에서Dev

데이터 바를 단색 채우기로 hackmodding 할 때 다른 조건부 서식이 사라지는 것을 방지하는 방법은 무엇입니까?

분류에서Dev

plsql을 사용하여 다른 열의 CSV로 데이터를 표시하는 방법은 무엇입니까?

분류에서Dev

R의 다른 열에있는 데이터를 기반으로 뺄셈을 조건부로 수행하는 방법은 무엇입니까?

분류에서Dev

UsersAdapter를 사용하여 데이터를 목록보기로 설정하는 방법은 무엇입니까?

분류에서Dev

R : tidyverse를 사용하여 길이가 다른 벡터 목록을 데이터 프레임으로 강제 변환하는 방법은 무엇입니까?

분류에서Dev

Observable의 데이터를 다른 방법으로 사용하는 방법은 무엇입니까?

분류에서Dev

한 필드 또는 다른 필드를 조건부로 반환하는 방법은 무엇입니까?

분류에서Dev

명명 된 목록을 사용하여 그룹별로 데이터 세트를 조건부로 자르는 방법은 무엇입니까?

분류에서Dev

한 GUI를 다른 GUI에 조건부로 연결하는 방법은 무엇입니까?

분류에서Dev

sed를 사용하여 줄 블록을 다른 위치로 이동하는 방법은 무엇입니까?

분류에서Dev

mysql을 사용하여 한 데이터베이스를 다른 데이터베이스로 덤프하는 방법은 무엇입니까?

분류에서Dev

다른 변수 이름으로 데이터를 사용하는 방법은 무엇입니까?

분류에서Dev

다른 테이블의 데이터로 트리거를 사용하여 필드를 업데이트하는 방법은 무엇입니까?

분류에서Dev

v-for를 사용하여 조건부로 아이콘을 표시하는 방법은 무엇입니까?

분류에서Dev

CupertinoTabBar를 사용하여 다른 탭의 다른 페이지로 이동하는 방법은 무엇입니까?

분류에서Dev

샘플이 다른 데이터 세트의 행에 나타나는 경우 조건부로 계산하고 기록하는 방법은 무엇입니까?

분류에서Dev

다른 데이터 프레임의 인덱스를 사용하여 데이터 프레임으로 인덱싱하는 방법은 무엇입니까?

분류에서Dev

jquery를 사용하여 JSON 데이터를 탐색하는 다른 방법은 무엇입니까?

분류에서Dev

linq를 사용하여 다른 테이블에서 일치하는 ID로 데이터를 주문하는 방법은 무엇입니까?

분류에서Dev

Ajax 응답 데이터를 다른 형식으로 사용하는 방법은 무엇입니까?

분류에서Dev

C # 드라이버를 사용하여 MongoDB의 다른 데이터베이스로 문서를 이동하는 방법은 무엇입니까?

분류에서Dev

devise를 사용하여 다른 사용자에게 권한을 부여하는 방법은 무엇입니까?

분류에서Dev

$ ref를 사용하여 다른 OpenAPI 파일의 경로를 참조하는 방법은 무엇입니까?

분류에서Dev

Thymeleaf를 사용하여 조건부로 형식을 구문 분석하는 방법은 무엇입니까?

분류에서Dev

셸을 사용하여 초과 문자를 조건부로 삭제하는 방법은 무엇입니까?

분류에서Dev

MongoDB에서 find ()에서 $ out 연산자를 사용하여 데이터를 다른 컬렉션으로 복사하는 방법은 무엇입니까?

분류에서Dev

greenDAO를 사용하여 다른 개체 내부에 개체를로드하는 방법은 무엇입니까?

Related 관련 기사

  1. 1

    DataTable의 데이터를 조건부로 업데이트하는 가장 빠른 방법은 무엇입니까?

  2. 2

    AngularJS를 사용하여 데이터를 다른 페이지로 전달하는 방법은 무엇입니까?

  3. 3

    데이터 바를 단색 채우기로 hackmodding 할 때 다른 조건부 서식이 사라지는 것을 방지하는 방법은 무엇입니까?

  4. 4

    plsql을 사용하여 다른 열의 CSV로 데이터를 표시하는 방법은 무엇입니까?

  5. 5

    R의 다른 열에있는 데이터를 기반으로 뺄셈을 조건부로 수행하는 방법은 무엇입니까?

  6. 6

    UsersAdapter를 사용하여 데이터를 목록보기로 설정하는 방법은 무엇입니까?

  7. 7

    R : tidyverse를 사용하여 길이가 다른 벡터 목록을 데이터 프레임으로 강제 변환하는 방법은 무엇입니까?

  8. 8

    Observable의 데이터를 다른 방법으로 사용하는 방법은 무엇입니까?

  9. 9

    한 필드 또는 다른 필드를 조건부로 반환하는 방법은 무엇입니까?

  10. 10

    명명 된 목록을 사용하여 그룹별로 데이터 세트를 조건부로 자르는 방법은 무엇입니까?

  11. 11

    한 GUI를 다른 GUI에 조건부로 연결하는 방법은 무엇입니까?

  12. 12

    sed를 사용하여 줄 블록을 다른 위치로 이동하는 방법은 무엇입니까?

  13. 13

    mysql을 사용하여 한 데이터베이스를 다른 데이터베이스로 덤프하는 방법은 무엇입니까?

  14. 14

    다른 변수 이름으로 데이터를 사용하는 방법은 무엇입니까?

  15. 15

    다른 테이블의 데이터로 트리거를 사용하여 필드를 업데이트하는 방법은 무엇입니까?

  16. 16

    v-for를 사용하여 조건부로 아이콘을 표시하는 방법은 무엇입니까?

  17. 17

    CupertinoTabBar를 사용하여 다른 탭의 다른 페이지로 이동하는 방법은 무엇입니까?

  18. 18

    샘플이 다른 데이터 세트의 행에 나타나는 경우 조건부로 계산하고 기록하는 방법은 무엇입니까?

  19. 19

    다른 데이터 프레임의 인덱스를 사용하여 데이터 프레임으로 인덱싱하는 방법은 무엇입니까?

  20. 20

    jquery를 사용하여 JSON 데이터를 탐색하는 다른 방법은 무엇입니까?

  21. 21

    linq를 사용하여 다른 테이블에서 일치하는 ID로 데이터를 주문하는 방법은 무엇입니까?

  22. 22

    Ajax 응답 데이터를 다른 형식으로 사용하는 방법은 무엇입니까?

  23. 23

    C # 드라이버를 사용하여 MongoDB의 다른 데이터베이스로 문서를 이동하는 방법은 무엇입니까?

  24. 24

    devise를 사용하여 다른 사용자에게 권한을 부여하는 방법은 무엇입니까?

  25. 25

    $ ref를 사용하여 다른 OpenAPI 파일의 경로를 참조하는 방법은 무엇입니까?

  26. 26

    Thymeleaf를 사용하여 조건부로 형식을 구문 분석하는 방법은 무엇입니까?

  27. 27

    셸을 사용하여 초과 문자를 조건부로 삭제하는 방법은 무엇입니까?

  28. 28

    MongoDB에서 find ()에서 $ out 연산자를 사용하여 데이터를 다른 컬렉션으로 복사하는 방법은 무엇입니까?

  29. 29

    greenDAO를 사용하여 다른 개체 내부에 개체를로드하는 방법은 무엇입니까?

뜨겁다태그

보관