본문 바로가기
Spring

[Spring] AOP(1) 세팅

by moca7 2024. 10. 22.

 

 

 

ㅁ AOP

- 지금까지 작성한 컨트롤러, 서비스, dao의 코드는 이 프로그램이 제대로 작동하기 위해 필수로 필요한 핵심 로직(비즈니스 로직)이다.

- 그런데 핵심로직 내에서 매번 공통적으로 처리해야 하는 로직(공통 로직)이 있을 수 있다.

대표적인게 로깅 처리와 트랜잭션 처리다. 

 

 

- 컨트롤러의 각 메소드에서 요청시 전달값을 받고 있다.

요청시 전달값, 조회된 결과값을 로그로 확인해보는 것이 좋다. 

- 각각의 url 요청 때마다 요청시 전달값, 어떤 클래스의 어떤 메소드가 동작하는지 등의 정보를 알기 위해 로그를 출력한다면 컨트롤러의 모든 메소드에 로그 출력문을 일일이 작성해야 한다.

 

- 그러지 않고 url 요청 때마다 컨트롤러 측의 메소드가 동작될 때 무조건 로그가 출력되게끔 공통 로직을 핵심 로직과 분리해서 작성해두고, 내가 원하는 시점에 반복 적용시킬 수 있는 기술이 AOP다.

- 핵심 로직은 최대한 건들지 않고 공통 로직만 따로 작성한 다음에, 내가 원하는 시점에 작동되게 주입할 수 있다. 

 

 

- AOP 기술을 안 써도 됨. 핵심 로직가서 일일이 다 쓰기만 하면 된다.

근데 불편하다. 개발이 끝나고 공통 로직을 수정할 때도 일일이 다 수정해야 함.

 

 

 

 

ㅁ AOP 사용

- AOP 개념이 어렵고 세팅할 것도 많아서 안쓰는 곳도 있다.

- 스프링을 사용한다면 트랜잭션 관리를 위한 AOP 설정을 기본적으로 많이 해두고 쓴다.

 

ㅁ 트랜잭션 처리

- 원래 트랜잭션 관리는 개발자가 직접 해야되는게 개발원칙중에 하나다.

- 그런데 스프링에서는 한 기능에 하나의 dml문 같은경우 AOP 설정을 안해도 
sqlsession이 자동으로 close 되면서 commit 까지 자동으로 진행된다.

- 한 기능에 여러 dml문일 때는 개발자가 직접 핸들링을 해야되고 이를 편하게 하기 위해 AOP 기술을 이용한다.

 

 

 

 

 

 

ㅁ AOP 

- Aspect Oriented Programming. 관점 지향 프로그래밍

- 핵심 로직(비즈니스 로직)마다 매번 반복해서 공통적으로 처리해야하는 로직(로깅, 트랜잭션 등)을 한번만 정의해두고 내가 원하는 시점에 반복 적용시키는 프로그래밍 방식.

 

- 핵심 로직과 공통 로직을 분리해서 개발할 수 있다.

- 개발이 다 끝난 후에 핵심 로직을 건들지 않고도 공통로직만을 따로 작성해서 원하는 시점에 실행시킬 수 있다.

 

 

 

 

 

ㅁ AOP 관련 용어

 

 

(1) JoinPoint

- AOP를 적용시킬 대상의 전체 메소드

- Controller 또는 ServiceImpl의 모든 메소드

 

(2) PointCut

- AOP를 적용시킬 특정 메소드(핵심로직)

- Controller 또는 ServiceImpl의 모든 메소드 중 로깅 또는 트랜잭션이 필요한 메소드

 

(3) Advice

- 공통로직(공통적으로 처리해야하는 구문)이다.

- AOP 동작하는 부분이다. AOP 동작 자체다. 

- ex) 로깅, 트랜잭션 처리

 

 

- Advice(공통로직)을 어떤 핵심 로직에 반영시킬건지 그 대상을 지정해줘야 한다.

그때 JoinPoint와 PointCut이 나온다.

- 중요한건 PointCut과 Advice다.

PointCut이 핵심로직이고 Advice가 공통 로직이다.

- 공통 로직을 어떤 핵심 로직에 반영시킬건지, PointCut 실행 전 후 전과 후 모두 언제 Advice를 수행할지 순서 정하기.

 

- 예를들어 서비스에는 삽입, 수정, 삭제, 조회 메소드가 있지만 트랜잭션이 필요한 메소드는 사입, 수정, 삭제 메소드 뿐이다.

 

 

 

 

 

ㅁ Advice 동작 시점

(1) @Before  : PointCut(핵심로직) 동작 이전

(2) @After     : PointCut(핵심로직) 동작 이후

(3) @Around : PointCut(핵심로직) 동작 이전과 이후

 

(4) @AfterReturning : PointCut(핵심로직) 정상 완료 이후

(5) @AfterThrowing : PointCut(핵심로직) 예외 발생 이후

 

 

ㅁ Advice 동작 순서

(1) Before Advice   :  Advice -> PointCut

(2) After Advice       : PointCut -> Advice

(3) Around Advice   :  Advice -> PointCut -> Advice

 



ㅁ Pointcut을 결정하는 표현식 작성법

 

(1) 형식

- execution (반환타입 패키지.클래스.메소드(매개변수))

- 클래스와 메소드에도 *을 쓸 수 있다. 모든을 의미한다.

 

(2) 반환타입

- *          : 모든 반환타입

- void     : void 타입

- !void    : void 제외한 타입

 

(3) 매개변수

- *     : 1개의 모든 매개변수       

- ..    : 모든 매개변수

 

 

 

 

ㅁ 로직 : 하나의 기능을 처리하기 위한 코드 묶음.

 

 

===================================================================================

 

 

ㅁ 이전 마이바티스 프로젝트 계속 씀.

 

 

 

ㅁ pom.xml

 

 
<?xml version="1.0" encoding="UTF-8"?>
 
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.br</groupId>
  <artifactId>sbatis</artifactId>
  <name>04_Spring_MyBatis_AOP</name>
  <packaging>war</packaging>
  <version>1.0.0-BUILD-SNAPSHOT</version>
 
  <properties>
    <java-version>11</java-version>
    <org.springframework-version>5.3.27</org.springframework-version>
    <org.aspectj-version>1.9.19</org.aspectj-version>
    <org.slf4j-version>2.0.7</org.slf4j-version>
  </properties>
 
  <dependencies>
 
    <!-- Spring -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${org.springframework-version}</version>
      <exclusions>
        <!-- Exclude Commons Logging in favor of SLF4j -->
        <exclusion>
          <groupId>commons-logging</groupId>
          <artifactId>commons-logging</artifactId>
         </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${org.springframework-version}</version>
    </dependency>
 
       
    <!-- AspectJ (AOP를 위한 라이브러리)-->
    <dependency> <!-- 원래 있던 것. 이게 있어야 "Advice 동작 시점"의 어노테이션들을 사용할 수 있다. -->
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>${org.aspectj-version}</version>
    </dependency>
   
    <!-- 위빙 : Advice(공통로직)를 PointCut(핵심로직)에 로딩되도록 함 -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>${org.aspectj-version}</version>
      <scope>runtime</scope>
    </dependency>  
   
   
   
   
    <!-- Logging (logback) -->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.4.14</version>
      <!-- <scope>test</scope> 이건 지워야 함 -->
    </dependency>

    <!-- (2) slf4j -->
    <dependency> <!-- 기존 dependency 중 이것만 남기고 다 지운다.-->
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${org.slf4j-version}</version>
    </dependency>
   
    <!-- (3) log4jdbc -->
    <dependency>
      <groupId>org.bgee.log4jdbc-log4j2</groupId>
      <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
      <version>1.16</version>
    </dependency>


   

    <!-- @Inject -->
    <dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <version>1</version>
    </dependency>
       
    <!-- Servlet -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.3</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
 
    <!-- Test -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
      <scope>test</scope>
    </dependency>        
   
   
    <!-- Lombok 라이브러리 추가 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
        <scope>provided</scope>
    </dependency>

    <!-- Jackson 라이브러리 추가 -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.14.2</version>
    </dependency>


    <!-- db관련 라이브러리 -->
    <!-- (1) ojdbc8 -->
    <dependency>
      <groupId>com.oracle.database.jdbc</groupId>
      <artifactId>ojdbc8</artifactId>
      <version>23.2.0.0</version>
    </dependency>

    <!-- (2) spring-jdbc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${org.springframework-version}</version>
    </dependency>

    <!-- (3) mybatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.14</version>
    </dependency>

    <!-- (4) mybatis-spring -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.6</version>
    </dependency>

    <!-- (5) commons-dbcp -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-dbcp2</artifactId>
      <version>2.9.0</version>
    </dependency>

   

  </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.9</version>
                <configuration>
                    <additionalProjectnatures>
                        <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
                    </additionalProjectnatures>
                    <additionalBuildcommands>
                        <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
                    </additionalBuildcommands>
                    <downloadSources>true</downloadSources>
                    <downloadJavadocs>true</downloadJavadocs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java-version}</source>
                    <target>${java-version}</target>
                    <compilerArgument>-Xlint:all</compilerArgument>
                    <showWarnings>true</showWarnings>
                    <showDeprecation>true</showDeprecation>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>org.test.int1.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
 

 

- AspectJ가 AOP 관련 라이브러리다.

- AspectJ 주석이 붙은 dependency 태그가 이미 있다. 

- 홈피에서 가져와도 되는데 귀찮아서 어차피 버전도 같은걸 쓸거라 이미 있던 dependency 태그를 복붙한다.

2번째 AspectJ의 dependency 태그의 artifactId를 바꾸고 scope를 추가하면 홈피에 있는거랑 동일하게 된다.

 

 

 

 

ㅁ 패키지와 클래스 생성

- com.br.sbatis.aop.BeforeLoggingAdvice 

- com.br.sbatis.config.SpringBeanConfig

 

 

 

ㅁ BeforeLoggingAdvice 

 

 
 
package com.br.sbatis.aop;

import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class BeforeLoggingAdvice { // controller측 모든 메소드(PointCut)가 동작되기 전에 로그 출력되게끔(Advice)

 
  @Pointcut("execution (* com.br.sbatis.controller.*Controller.*(..))")   /* 포인트컷 표현식 */
  public void setPointCut() {}                      /* 포인트컷 서명 */
 
 
  // Before Advice 메소드 작성법 (즉, Pointcut 메소드 전에 실행시키고자 하는 메소드)
  // 1. 반환타입은 void로 해야 함.
  // 2. 메소드명은 마음대로.
  // 3. 매개변수 : JoingPoint 타입 객체 (생략 가능, 핵심 로직 정보를 알아내고자 할 경우 작성)
 
 
 
  // "setPointCut()"이라는 메소드가 실행되기 "전(@Before)"에 "loggingBeforeAdvice()"를 실행한다는 뜻.
  @Before("setPointCut()")
  public void loggingBeforeAdvice() {
    log.info("loggingBeforeAdvice 메소드 동작 ***************");
   
   
  }
 
 

 
 
}




 

- *Controller를 작성하면 ~Controller로 끝나는 모든 클래스가 다 선택된다.

- *(..)로 모든 메소드를 선택했다. 매개변수는 ..이 모든 것을 뜻한다.

 

- setPointCut() 메소드를 만들었다. 메소드 명은 아무거나 상관없다.

- 포인트컷 서명은 포인트컷 표현식을 지칭하는 이름이다.

- BeforeLoggingAdvice에서는 포인트컷 표현식과 포인트컷 서명을 분리해서 작성하고 있다.

 

- Logger 객체가 필요하다. 클래스 상단에 @Slf4j 어노테이션을 작성하면 내가 Logger 객체를 생성할 필요 없이 log.메소드()를 사용할 수 있다.

 

 

- PointCut과 Advice를 만들었다고 끝이 아니고 빈으로 등록해야 한다.

클래스 상단에 @Aspect 어노테이션을 작성한다. 이게 AOP를 등록하는 구문이다.

다음과 같은 포인트컷에 어던 어드바이스를 작동시킬건지, 실행 시점은 언젠지 ~ 클래스를 ~ @Aspect 어노테이션

- 그리고 이 클래스도 빈으로 등록해야 한다.

내가 만든 클래스여서 상단에 어노테이션을 붙여서 간단하게 빈등록할 수 있지만, 스프링 부트 프로젝트할 때는 xml 파일이 다 사라지고 자바 방식으로 빈을 등록해야해서 자바 방식으로 자바 파일만들어서 빈을 등록해본다.

 

- @Aspect는 빈등록이 아니다. aop ~

 

 

 

 

ㅁ SpringBeanConfig

 

 
package com.br.sbatis.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import com.br.sbatis.aop.BeforeLoggingAdvice;

@EnableAspectJAutoProxy
@Configuration
public class SpringBeanConfig {
 
 
  @Bean
  public BeforeLoggingAdvice beforeLoggingAdvice() {
    return new BeforeLoggingAdvice();
  }
 

}

 

- 클래스 상단에 @Configuration ~

- @EnableAspectJAutoProxy ~

 

 

 

 

 

ㅁ NoticeController를 보면,

 

 
package com.br.sbatis.controller;

import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.br.sbatis.dto.NoticeDto;
import com.br.sbatis.service.NoticeService;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequestMapping("/notice")
@RequiredArgsConstructor
@Controller
public class NoticeController {

 
  private final NoticeService noticeService;
 
  @GetMapping("/list.do") // /notice/list.do
  public void noticeList(Model model) {
    List<NoticeDto> list = noticeService.selectNoticeList();
    log.debug("list: {}", list);
    model.addAttribute("list", list);
   
    // return "notice/list"; void여도 포워딩하려 한다. url mapping("/notice/list")를 가지고 포워딩한다.
  }
 
 
 
  @GetMapping("/detail.do") // /notice/detail.do
  public void noticeDetail(int no, Model model) {
    NoticeDto n = noticeService.selectNoticeByNo(no);
    model.addAttribute("n",n);
  }
 
 
  @GetMapping("/enroll.do") // /notice/enroll.do
  public void noticeEnroll() {}
  // 이렇게만 작성해도 페이지 이동이 된다.
 
 
  @PostMapping("/insert.do")
  public String noticeInsert(NoticeDto n) {
    int result = noticeService.insertNotice(n);
   
    if(result > 0) { // 성공시 다시 목록페이지
      return "redirect:/notice/list.do";
    }else { // 실패시 메인페이지
      return "redirect:/";
    }
  }
 
 
 
  @GetMapping("/modify.do") // /notice/modify.do
  public void noticeModify(int no, Model model) {
    model.addAttribute("n", noticeService.selectNoticeByNo(no));
  }
 
 
  @PostMapping("/update.do")
  public String noticeUpdate(NoticeDto n) {
    int result = noticeService.updateNotice(n);

    if(result > 0){ // 성공시 상세페이지
      return "redirect:/notice/detail.do?no=" + n.getNo();
    }else { // 실패시 목록페이지
      return "redirect:/notice/list.do";
    }
  }
 
  @PostMapping("/delete.do")
  public String noticeDelete(String[] deleteNo) {
    // String[] arr = request.getParameterValues("deleteNo") <- request 객체가 있었다면
   

      // 체크박스 선택 안 한 경우 예외 처리
      if (deleteNo == null || deleteNo.length == 0) {
          // 선택 항목이 없을 경우 에러 메시지와 함께 목록 페이지로 이동
          return "redirect:/notice/list.do?error=noSelection";
      }
   
   
   
    // 실행할 sql문 : delete from notice where no in (xx, xx, xx) <- 동적 쿼리
   
    int result = noticeService.deleteNotice(deleteNo);
   
    if(result == deleteNo.length){ // 성공시 목록페이지
      return "redirect:/notice/list.do";
    }else { // 실패시 메인페이지
      return "redirect:/";
    }
  }

}

 

 

- noticeList 메소드에는 이미 로그 출력문이 있다. 비교해본다. 

 

 

 

 

- 메인페이지요청시, 그리고 목록페이지 요청시 loggingBeforeAdvice 메소드 동작 ************** 로그가 찍힌다.

- DEBUG는 NoticeController의 noticeList 메소드의 로그 출력문에서 출력되는 로그다.

 

 

 

ㅁ 뭔가 잘 안될 땐 메이븐 - 프로젝트 업데이트.

 

 

 

 

================================================================================

 

 

ㅁ logback.xml 수정

 

 
<?xml version="1.0" encoding="UTF-8"?>
<configuration>


  <appender class="ch.qos.logback.core.ConsoleAppender" name="consoleLog">
    <encoder>
      <pattern>%-5level: [%date{yyyy-MM-dd HH:mm:ss}] [%logger:%line] - %msg%n</pattern>
    </encoder>
  </appender>



 
  <logger name="org.springframework" level="INFO" />
  <logger name="com.br.sbatis" level="DEBUG" />


  <logger name="jdbc.sqlonly" level="off" />          <!-- 쿼리문만 -->
  <logger name="jdbc.sqltiming" level="off" />        <!-- 쿼리문 + 실행시간 -->
  <logger name="jdbc.audit" level="off" />            <!-- JDBC 호출 정보  -->
  <logger name="jdbc.resultset" level="off" />          <!-- ResultSet 호출 정보 -->
  <logger name="jdbc.resultsettable" level="off" />    <!-- ResultSet 결과 (조회결과 테이블) -->
  <logger name="jdbc.conntection" level="off" />       <!-- Connection 호출 정보 -->
 

 
  <root level="WARN">
    <appender-ref ref="consoleLog" />
  </root>  
     
   

</configuration>


 

- 쿼리 로그 헷갈려서 sqliming을 off했다.

 

 

 

 

ㅁ BeforeLoggingAdvice 수정

 

 
package com.br.sbatis.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

import lombok.extern.slf4j.Slf4j;

@Aspect
@Slf4j
public class BeforeLoggingAdvice { // controller측 모든 메소드(PointCut)가 동작되기 전에 로그 출력되게끔(Advice)

 
  @Pointcut("execution (* com.br.sbatis.controller.*Controller.*(..))")   /* 포인트컷 표현식 */
  public void setPointCut() {}                      /* 포인트컷 서명 */
 
 
  // Before Advice 메소드 작성법 (즉, Pointcut 메소드 전에 실행시키고자 하는 메소드)
  // 1. 반환타입은 void로 해야 함.
  // 2. 메소드명은 마음대로.
  // 3. 매개변수 : JoingPoint 타입 객체 (생략 가능, 핵심 로직 정보를 알아내고자 할 경우 작성)
 
 
 
  // "setPointCut()"이라는 메소드가 실행되기 "전(@Before)"에 "loggingBeforeAdvice()"를 실행한다는 뜻.
  @Before("setPointCut()")
  public void loggingBeforeAdvice(JoinPoint joinPoint) {
    // log.info("loggingBeforeAdvice 메소드 동작 ***************");
   
    Object[] args = joinPoint.getArgs(); // 핵심로직이 실행될 때 요청시 전달값 (배열로 반환하는 메소드가 getArgs다.)
    for(Object arg : args) {
      log.info("arg: {}", arg);
    }
  }
 
 
 
}


 

 

 

- 모든 컨트롤러의 메소드 실행 전에 로그가 출력되게 되어있었다. 

- loggingBeforeAdvice 메소드에 JoinPoint 타입의 매개변수를 추가한다.

- 기존 로그 출력문은 잘 작동하는지 확인하기 위한 것었다. 주석처리한다. 

 

 

 

- 메인페이지 - 목록페이지 - 2번 게시글 상세 페이지 - 2번 게시글 수정 페이지 - 수정하고 다시 2번 게시글 상세 페이지

 

 

 

 

 

 

 

- NoticeController에 가보면 메소드 옆에 표시가 되어 있다.

 

 

 

 

================================================================

 

 

 BeforeLoggingAdvice 수정

 

 
package com.br.sbatis.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

import lombok.extern.slf4j.Slf4j;

@Aspect
@Slf4j
public class BeforeLoggingAdvice { // controller측 모든 메소드(PointCut)가 동작되기 전에 로그 출력되게끔(Advice)

 
  @Pointcut("execution (* com.br.sbatis.controller.*Controller.*(..))")   /* 포인트컷 표현식 */
  public void setPointCut() {}                      /* 포인트컷 서명 */
 
 
  // Before Advice 메소드 작성법 (즉, Pointcut 메소드 전에 실행시키고자 하는 메소드)
  // 1. 반환타입은 void로 해야 함.
  // 2. 메소드명은 마음대로.
  // 3. 매개변수 : JoingPoint 타입 객체 (생략 가능, 핵심 로직 정보를 알아내고자 할 경우 작성)
 
 
 
  // "setPointCut()"이라는 메소드가 실행되기 "전(@Before)"에 "loggingBeforeAdvice()"를 실행한다는 뜻.
  @Before("setPointCut()")
  public void loggingBeforeAdvice(JoinPoint joinPoint) {
    // log.info("loggingBeforeAdvice 메소드 동작 ***************");
   
    Object[] args = joinPoint.getArgs(); // 핵심로직이 실행될 때 요청시 전달값 (배열로 반환하는 메소드가 getArgs다.)
    for(Object arg : args) {
      // log.info("arg: {}", arg);
    }
   
   
    // 현재 실행되는 핵심로직 메소드 정보
    Signature signature = joinPoint.getSignature();
    log.info("method: {}", signature.getName());     // 메소드명만
    log.info("short: {}", signature.toShortString());    // 클래스명 + 메소드명
    log.info("long: {}", signature.toLongString());    // 반환형 + 클래스명 + 메소드명


   
  }
 
 
}


 

- org.aspectj.lang.Signature를 임포트한다.

- 요청시 전달값 출력하는 로그("log.info("arg: {}", arg)")는 헷갈리니 주석처리 했다.

 

 

 

 

- 메인페이지 - 목록페이지 - 2번 게시글 상세 페이지 - 2번 게시글 수정 페이지 - 수정하고 다시 2번 게시글 상세 페이지

- 컨트롤러 수행 시점 이전에 실행되었다.

 

 

 

 

===================================================================================

 

 

ㅁ 이번엔 before에서 after 로 바꿔본다.

 

ㅁ before 했던거는 끈다. 

- BeforeLoggingAdvice 클래스 상단의 @Aspect를 주석처리만 하면 된다. 등록을 아예 없앴다.   

 

 

ㅁ After Advice 메소드 작성법

- 반환타입 : void

- 메소드명 : 마음대로

- 매개변수 : JoinPoint (생략 가능)

 

 

 

ㅁ com.br.sbatis.aop 패키지에 AfterLoggingAdvice를 생성한다.

 

 
package com.br.sbatis.aop;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Aspect
public class AfterLoggingAdvice {


  @After("execution(* com.br.sbatis.controller.*Controller.*(..))")
  public void afterLoggingAdvice() {
    log.info("afterLoggingAdvice 메소드 동작 ************************");
  }
 
 
 
 
}

- before 때는 포인트컷 표현식과 포인트컷 서명을 따로 작성했었다.

 

 

 

 

ㅁ SpringBeanConfig 수정

 

 
package com.br.sbatis.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import com.br.sbatis.aop.AfterLoggingAdvice;
import com.br.sbatis.aop.BeforeLoggingAdvice;

@EnableAspectJAutoProxy
@Configuration
public class SpringBeanConfig {

 
  @Bean
  public BeforeLoggingAdvice beforeLoggingAdvice() {
    return new BeforeLoggingAdvice();
  }
 
  @Bean
  public AfterLoggingAdvice afterLoggingAdvice() {
    return new AfterLoggingAdvice();
  }

}

 

 

 

- 메인페이지 - 목록페이지.

- 첫번재 줄은 메인페이지 요청시 출력되는 로그문이다.

- sql문은 지금 껐다. 

 

 

- NoticeController의 noticeList 메소드 실행 다음에 실행되었다.

 

 

 

 

 

ㅁ 폴더 구조