레이블이 #스프링AOP인 게시물을 표시합니다. 모든 게시물 표시
레이블이 #스프링AOP인 게시물을 표시합니다. 모든 게시물 표시

2022년 1월 10일 월요일

Spring AOP 동영상, 자바 스프링 AOP, HelloWorld, 충고, 포인트컷, Advice, Pointcut, 동영상강의, 자바학원, 스프링학원, 자바교육, 스프링교육, JAVA교육, 자바동영상, Spring

 Spring AOP 동영상, 자바 스프링 AOP, HelloWorld, 충고, 포인트컷, Advice, Pointcut, 동영상강의, 자바학원, 스프링학원, 자바교육, 스프링교육, JAVA교육, 자바동영상, Spring















#스프링AOP, #AOPHelloWorld, #스프링충고, #포인트컷, #Advice, #Pointcut, #자바동영상, #스프링동영상



2021년 11월 13일 토요일

Spring AOP(Spring Aspect Oriented Programming), 포인트컷, 충고, 어드바이스

 Spring AOP(Spring Aspect Oriented Programming), 포인트컷, 충고, 어드바이스

3. Spring AOP(Spring Aspect Oriented Programming)

3-1. AOP 개요

 

  • IoC를 이용하여 협력하는 객체를 쉽게 연결가능지만 때로는 전체 애플리케이션에 걸쳐 사용되어야 하는 공통적인 기능(횡단관심사)이 필요할 수도 있다. 

  • DI의 목적이 Application간 결합도를 떨어뜨리는 것이 목표라면 AOP의 목적은 횡단관심사(Cross-Cutting Concerns, 예외처리, 로깅, 인증, 보안, 트랜잭션)와 이에 영향을 받는 객체간에 결합도를 떨어뜨리는 것이다. 즉 AOP는 횡단관심사를 모듈화하는 프로그래밍 기법으로 공통적인 기능을 Aspect라 불리는 한곳에서 정의한다.

  • 새로운 기능을 적용하려고 하는 클래스를 수정할 필요 없이  그런 기능을 어디에 어떻게 적용할 것인지 선언적 또는 어노테이션으로 정의가 가능하다.

  • 각 횡단관심사에 관한 로직이 애플리케이션 전체에 걸쳐 관리되지 않고 Aspect라고 하는 곳에서 관리되고 어플리케이션은 고유의 비지니스로직 처리에 충실할 수 있다.

  • Logging은 시스템 내에서 거의 모든 경우에 적용하긴 하지만 특정한 비즈니스 로직과는 전혀 관련이 없다. 만일 AOP방식으로 구현한다면 실제 비즈니즈 로직의 구현을 담당하는 객체는 아무런 로그 관련 객체 정보가 없고 이 객체의 특정 메소드를 호출하는 시점에 지정된 다른 서비스(로깅)가 실행된다.

  • 횡단 관심사(Cross-Cuitting Concern) : 여러 개의 모듈에 걸치는 시스템 전체적인 부가적인 요구사항을 다룬다. 대표적인 횡단 관심사의 일부 예들 든다면 인증 ,로깅,트랜잭션  무결성, 오류검사, 정책시행

  • 핵심 관심사(Primary Concern) : 각 모듈에서 수행해야 하는  기본적이고 대표적인 업무처리 기능

  • Springdptj AOP 구현 방법

  • 소스코드에서 직접 AOP 구현 : ProxyFactory 기반 AOP

  • 선언적 AOP 설정 메커니즘

ProxyFactoryBean 사용 : 스프링 ApplicationContext를 XML에 선언적으로 선언하여 빈 정의를 기반으로 AOP 프록시 생성한다.

Spring aop 네임스페이스 : aop 네임스페이스를 이용하여 Aspect 및 DI 요구사항을 간단히 정의. Aop 네임스페이스도 내부적으로 ProxyFactoryBean 사용한다.

@ApectJ Annotation : @AspectJ방식의 어노테이션을 사용하여  클래스 내에서 AOP 설정이 가능하다, 이 방식은 AspectJ를 기반으로 하고 있으며 AspectJ 라이브러리가 필요하다. 이 역시 스프링에서는 프록시 메커니즘을 이용하는데 ApplicationContext를 부트스트랩 할 때 타겟에 대해 프록시를 생성한다.

Spring AOP는 메소드 가로채기로 제한, 만약 그이상의 기능

(생성자 또는 멤버변수에 대한 가로채기)을 필요로 하는 경우 Proxy 기반 AOP

대신 AspectJ를 이용해서 Aspect를 구현해야 한다.

  • 스프링의 충고(Advice)는 자바로 작성 : Pointcut의 경우 Advice를 어디에 적용할지를 정의하는 데 XML 설정 파일, AspectJ Expression으로 정의한다. AspectJ는 자바언어를 확장한 형태로 구현. 새로운 도구와 문법을 배워야 한다.

 

  • 스프링의 Aspect는 실행시간에 만들어진다. 빈을 감싸는 Proxy 객체를 실행시간에 생성하므로 Aspect가 Spring 관련 빈에 위빙(Weaving)된다. Proxy 객체는 타겟 객체로 위장해서 메소드 호출을 가로채고,  타겟 객체로 호출을 전달한다. 어플리케이션이 실제 Proxy 객체를 필요로 할 때까지 target를 생성하지 않으므로 즉 Proxy가 실시간으로 생성되므로 Aspect를 위빙 하기 위해 별도 컴파일러가 필요 없다.

 

0Rh21bJYSpJwyn6WAoaKTjjomenyxJFb_wTxNDqc

  • 스프링은 AOP연맹의  인터페이스를 구현하며 메소드 호출 결합점(Join Point)만 제공한다.

  • AOP 용어

 

rW4fJjtp7Up99sptIwfBvHzMz8rPMQ1tv15BoXqY

 

  • 스프링에서 Proxy 객체를 생성하는 방법은 2가지.

  

1. 대상 객체가 특정 메소드를 공개하는 인터페이스를 구현한다면 JDK의  java.lang.reflect.Proxy 클래스를 이용하며 이 클래스는 필요한 인터페이스를 구현한 새로운 Proxy 객체를 동적으로 생성할 수 있으며 target 객체의 인터페이스를 통한 호출은 모두 가로채서 Advice를 수행한다.

 

2. 대상 클래스가 어떤 인터페이스를 구현하고 있지 않다면 CGLIB이라는 라이브러리를 이용하여 대상클래스의 서브클래스를 생성 시킨다. 이 서브클래스를 생성시킴으로써 스프링은 충고를 엮을 수 있으며 서브클래스에 대한 호출을 대상 클래스에 위임 할 수 있다. 이 경우 final 메소드는 충고를 받을 수 없으며(final Method는 대상클래스의 서브클래스를 생성해서 메소드를 재정의 해야 하는데 final인 경우 곤란함) 애플리케이션이 좀 더 느슨하게 결합되게 하기 위해 이 방법보단 인터페이스를 통해 프록시를 만드는 것이 선호된다.

 

  • 스프링 어스팩트 : 스프링 AOP에서 애스팩트는 Advisor 인터페이스를 구현한 클래스의 인스턴스로 표시하는데 Advisor를 구현한 몇 개의 클래스를 제공한다. Advisor의 하위 인터페이스로는 IntroductionAdvisor, PointcutAdvisor등이 있는데 PointcutAdvisor 인터페이스는 포인트컷을 사용하여 조인포인트에 어드바이스를 적용하는 모든 Advisor가 구현한다.

 

3-2. AOP HelloWorld

3-2-1. AOP HelloWorld(프로그래밍을 통한 AOP 구현)

 

  • File -> New -> Spring Legacy Project -> Simple Spring Maven 선택 후 프로젝트명을 “aophello1” 이라고 입력 후 “Finish” 클릭

  • 단순히 "Hello AOP..." 이라고 출력하는 PrintMsg 클래스의 메소드 sayHello()가 있고 이 클래스에 주변충고(어라운드 어드바이스, Around Advice)를 추가해 sayHello() 메소드 실행 전/후 필요한 기능("메소드 실행전 안녕...", "메소드 실행후 안녕...")을 출력하고자 한다.

 

[IprintMsg.java]

package aophello1;

 

public interface IPrintMsg {

public void sayHello();

}

 

[PrintMsg.java]

타겟 클래스(충고가 적용될 클래스, 횡단관심사 기능을 구햔한 클래스

본 예제에서는 간단히 로깅만 하기로 한다.

 

package aophello1;

 

public class PrintMsg implements IPrintMsg {

public void sayHello() {

System.out.println("Hello AOP...");

}

}

 

[MyAroundAdvice.java]

어라운드 어드바이스(AroundAdvice)의 구현체, 충고

MethodInterceptor는 메소드 호출용 어라운드 어드바이스의 표준 인터페이스이다.

MethodInvocation은 어드바이스를 추가하기 위한 메소드 호출을 나타내며 이 객체를 사용하면 메소드 호출이 실행되는 시점(메소드 실행 전 / 후)을 제어할 수 있다.

 

package aophello1;

 

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

 

public class MyAroundAdvice implements MethodInterceptor {

public Object invoke(MethodInvocation invocation) 

                        throws Throwable {

System.out.println("메소드 실행전 안녕...");

Object ret = invocation.proceed();

System.out.println("메소드 실행후 안녕...");

return ret;

}

}

 

[HelloMain.java]

Proxy 빈 객체를 생성후 MyAroundAdvice 및 타겟 객체인 PrintMsg를 위빙하여 프록시 생성

 

package aophello1;

 

import org.springframework.aop.framework.ProxyFactory;

 

public class HelloMain {

public static void main(String[] args) {

IPrintMsg target = new PrintMsg();

//Proxy 빈껍데기 생성

ProxyFactory pf = new ProxyFactory();

pf.addAdvice(new MyAroundAdvice());  //충고 add

pf.setTarget(target);                //타겟 add

PrintMsg proxy = (PrintMsg)pf.getProxy();

proxy.sayHello();

}

}

 

[실행결과]

메소드 실행전 안녕...

Hello AOP...

메소드 실행후 안녕...

3-2-2. AOP HelloWorld(XML Schema Based AOP 구현)

  • File -> New -> Spring Legacy Project -> Simple Spring Maven 선택 후 프로젝트명을 “aophello1” 이라고 입력 후 “Finish” 클릭

  • 스프링에서 제공하는 aop Namespace를 이용하는 방법으로 타겟클래스의 sayHello1(), sayHello2() 메소드중 sayHello1() 메소드만 주변충고가 적용되도록 구성한 예제이다.

 

[pom.xml]에 AOP를 위한 라이브러리 추가

<!-- Spring AOP + AspectJ -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-aop</artifactId>

<version>${spring-framework.version}</version>

</dependency>

 

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjrt</artifactId>

<version>1.6.11</version>

</dependency>

 

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</artifactId>

<version>1.6.11</version>

</dependency>

 

[IprintMsg.java]

package aophello1;

 

public interface IPrintMsg {

public void sayHello1();

public void sayHello2();

}

 

[PrintMsg.java]

package aophello1;

 

public class PrintMsg implements IPrintMsg {

public void sayHello1() {

System.out.println("Hello AOP1...");

}

public void sayHello2() {

System.out.println("Hello AOP2...");

}

}

 

[LogginAspect.java]

// XML Schema Based AOP 구현에서는 Aspect클래스에 충고용 메소드를 정의한다.

// 아래 myAdvice가 주변충고용 메소드로 어떤 메소드에 이 충고가 내려갈지는 XML에서 설정을 한다.

// pjp.procees() 메소드를 통해 원래 타겟클래스의 sayHello1() 메소드가 호출된다.

package aophello1;

 

import org.aspectj.lang.ProceedingJoinPoint;

 

public class LoggingAspect {

public void myAdvice(ProceedingJoinPoint pjp)

                                  throws Throwable{

System.out.println("메소드 실행전 안녕...");  //메소드 실행전

Object ret = pjp.proceed();

System.out.println("메소드 실행후 안녕...");  //메소드 실행후

}

}

 

[Src/main/resources/aophello1.xml]

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

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance

    xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans-4.1.xsd 

    http://www.springframework.org/schema/aop 

    http://www.springframework.org/schema/aop/spring-aop-4.1.xsd ">

 

   <aop:config>

      <aop:aspect id="helloAspect" ref="logging"> <!-- ref에는 충고를 담고있는 Aspect가 온다

         <aop:pointcut id="selectSayHello1" 

         expression="execution(* aophello1.PrintMsg.sayHello1(..))"/>

         <!—myAdvice는 Aspect 클래스(logging)의 충고용 메소드-->

         <aop:around pointcut-ref="selectSayHello1" method="myAdvice"/>         

      </aop:aspect>

   </aop:config>

 

   <!-- Definition for printMsg bean, 타겟클래스 -->

   <bean id="printMsg" class="aophello1.PrintMsg" />

 

   <!-- Definition for logging aspect  충고들이 오여있는 Aspect클래스-->

   <bean id="logging" class="aophello1.LoggingAspect"/> 

      

</beans>

 

[HelloMain.java]

package aophello1;

 

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

public class HelloMain {

public static void main(String[] args) {

ApplicationContext ctx = new ClassPathXmlApplicationContext("aophello1.xml");

IPrintMsg printMsg = (IPrintMsg) ctx.getBean("printMsg");

//sayHello1만 충고가 내려가도록 되어 있다.

printMsg.sayHello1();

printMsg.sayHello2();

}

}


 

3-2-3. AOP HelloWorld(@AspectJ Annotation Based AOP 구현)

  • File -> New -> Spring Legacy Project -> Simple Spring Maven 선택 후 프로젝트명을 “aophello2” 이라고 입력 후 “Finish” 클릭

 

[pom.xml]에 AOP를 위한 라이브러리 추가

<!-- Spring AOP + AspectJ -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-aop</artifactId>

<version>${spring-framework.version}</version>

</dependency>

 

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjrt</artifactId>

<version>1.6.11</version>

</dependency>

 

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</artifactId>

<version>1.6.11</version>

</dependency>

 

[IprintMsg.java]

package aophello2;

 

public interface IPrintMsg {

public void sayHello1();

public void sayHello2();

}

 

[PrintMsg.java]

package aophello2;

 

public class PrintMsg implements IPrintMsg {

public void sayHello1() {

System.out.println("Hello AOP1...");

}

public void sayHello2() {

System.out.println("Hello AOP2...");

}

}

 

[LogginAspect.java]

// Aspect클래스로 충고용 메소드 및 포인트컷을 정의한다.

// 아래 myAdvice가 주변충고용 메소드로 메소드 상단에 포인트컷을 정의했다.

// pjp.procees() 메소드를 통해 원래 타겟클래스의 sayHello1() 메소드가 호출된다.

package aophello2;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

 

@Aspect

public class LoggingAspect {

@Around("execution(* aophello2.PrintMsg.sayHello1())")

public void myAdvice(ProceedingJoinPoint pjp)

                                  throws Throwable{

System.out.println("메소드 실행전 안녕...");  //메소드 실행전

Object ret = pjp.proceed();

System.out.println("메소드 실행후 안녕...");  //메소드 실행후

}

}

 

[Src/main/resources/aophello1.xml]

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

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance

    xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans-4.1.xsd 

    http://www.springframework.org/schema/aop 

    http://www.springframework.org/schema/aop/spring-aop-4.1.xsd ">

 

   <aop:aspectj-autoproxy />

 

   <!-- Definition for printMsg bean, 타겟클래스 -->

   <bean id="printMsg" class="aophello2.PrintMsg" />

 

   <!-- Definition for logging aspect  충고들이 오여있는 Aspect클래스-->

   <bean id="logging" class="aophello2.LoggingAspect"/> 

      

</beans>

 

[HelloMain.java]

package aophello2;

 

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

public class HelloMain {

public static void main(String[] args) {

ApplicationContext ctx = new ClassPathXmlApplicationContext("aophello1.xml");

IPrintMsg printMsg = (IPrintMsg) ctx.getBean("printMsg");

//sayHello1만 충고가 내려가도록 되어 있다.

printMsg.sayHello1();

printMsg.sayHello2();

}

}

 

[결과]

메소드 실행전 안녕...

Hello AOP1...

메소드 실행후 안녕...

Hello AOP2...

 


 

3-3. AOP 충고(Advice)

스프링 AOP에서 충고(Advice)는 횡단관심사 기능을 구현한 것인데 다음과 같이 분류한다.

 

K-mIleb4TuaLDBfazFadocm6pVtNogWRZPWfUyhe

 

IJOVhPQtTYqP55KL_0G5tJrfYM0nXYi4Vqvp8ecm

 

Aroud Advice의 앞단 처리 부분과 Before Advice, Aroud Advice의 뒷단 처리부분과 AfterReturning Advice간 실행순서는 정해져 있지 않고 먼저 추가되는 Advice, Advisor가 먼저 실행된다.

3-4. 프로그래밍적인 방법을 통한 AOP 구현(ProxyFactory 이용)

 

프로그래밍적인 방법을 통해 Spring AOP의 기본원리를 이해하자.

 

STS에서

 

1. File -> New -> Spring Starter Project 

Name : demo-smallmart

Type  : MAVEN

Package : onj.hello.aop

 

2. Core -> AOP 체크

 

Gd2VDPV2ubV0vb_PUVox3-tSoZBY7LbQ3ERJwP3n

 

V28i693gkyDBjQ7tzc5yHMTOLnVo8CKUdaBiUyGF

 

[SmallMartInterface.java]

package onj.hello.aop;

 

public interface SmallMartInterface {

public void getProducts(String productName) throws Exception;

}

 

[SmallMart.java]

package onj.hello.aop;

 

public class SmallMart implements SmallMartInterface {

public void getProducts(String productName) throws Exception {

  System.out.println("[Target Method]getProduct()..." + productName);

  throw new Exception("error...");  //주석으로 막고 실행해 보자.

}

}

 

[BeforeLoggingAdvice.java]

package onj.hello.aop;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class BeforeLogginAdvice implements MethodBeforeAdvice{

   public void before(Method method, Object[] args, Object target) throws Throwable {

String findName = (String)args[0];

  System.out.println(method.getName() + "(" + findName + "):: 사전충고");

   }

}

 

/**

* 사전 충고용 인터페이스

* 이메소드는 대상메소드, 그 메소드에 전달할 인자, 대상객체에 대한 접근점 제공

* 메소드 인자에 접근 가능하므로 런타임중에 파라미터를 사용하여 충고 구현 가능

*/

public interface MethodBeforeAdvice {

   void before(Method method, Object[] args, Object target) throws Throwable ;

}

 

[AfterLoggingAdvice.java]

package onj.hello.aop;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

 

public class AfterLoggingAdvice implements AfterReturningAdvice {

public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {

System.out.println(method.getName() + "(" + args[0] + ") :: 사후중고" );

}

}

 

/* 사후 충고(AfterReturning Advice)용 인터페이스 */

public interface AfterReturningAdvice {

   void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;

}

 

[AroundLoggingAdvice.java]

package onj.hello.aop;

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

public class AroundLoggingAdvice implements MethodInterceptor {

   @Override

   public Object invoke(MethodInvocation invocation) throws Throwable {

      String findName = (String) invocation.getArguments()[0];

      String methodName = (String) invocation.getMethod().getName();

     System.out.println("[주변충고]" + methodName + "(" + findName + ") 메소드 실행전");

     Object obj = invocation.proceed(); // 타켓클래스의 메소드 호출

     System.out.println("[주변충고]" + methodName + "(" + findName + ") 메소드 실행후");

      return obj;

   }

}

 

/* 이전에 사전,사후 충고를 엮었는데 주변충고를 통해 둘 다를 엮을 수 있다.*/

public interface MethodInterceptor {

    Object invoke(MethodInvocation invocation) throws Throwable; 

}

 

MethodInterceptor 구현 클래스(주변충고용 클래스)는 대상 메소드의 실제 호출여부를 제어하며 proceed를 통해 호출 타겟 메소드 호출하며, 원래 메소드의 리턴을 대체가능하다.

 

[ThrowsLoggingAdvice.java]

package onj.hello.aop;

 

import org.springframework.aop.ThrowsAdvice;

 

public class ThrowsLoggingAdvice implements ThrowsAdvice {

public void afterThrowing(Throwable throwable) {

System.out.println("에러 발생...");

}

}

 

/* 예외가 발생했을 때의 행위를 정의, marker interface */

public interface ThrowsAdvice {   }

 

구현대상이 없지만 아래의 메소드중 하나를 포함해야 한다.

void afterThrowing(Throwable throwable);

void afterThrowing(Method method, Object[] args,  Object target, Throwable throwable);

 

[DemoSmallmartApplication.java]

package onj.hello.aop;

import org.springframework.aop.framework.ProxyFactory;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication

public class DemoSmallmartApplication {

   public static void main(String[] args) {

      SpringApplication.run(DemoSmallmartApplication.class, args);

      SmallMartInterface target = new SmallMart();

      ProxyFactory pf = new ProxyFactory();

      pf.addAdvice(new BeforeLoggingAdvice());

      pf.addAdvice(new AfterLoggingAdvice());

      pf.addAdvice(new AroundLoggingAdvice());

      pf.addAdvice(new ThrowsLoggingAdvice());

      pf.setTarget(target);

      try {

   SmallMartInterface proxy = (SmallMartInterface)pf.getProxy();

proxy.getProducts("생필품");

      }

      catch(Exception e) {e.printStackTrace(System.out);  }

   }

}

DemoSmallmartApplication 클래스에서 마우스 우측버튼 -> Run As -> Spring Boot App 또는 Java Application으로 실행 가능

 

2rWhYQzTF1cMJnWAkqIhP8kqbprpqi0m1nu2BbBg

 

3-5. 포인트컷(Pointcut)

 

  • Pointcut은 모든 Join Point중 Advice가 Weaving 되어야 할 Join Point의 집합을 정의한 것이다.

  • 교차점(PointCut)은 특정한 클래스의 특정한 메소드가 특정한 기준과 일치하는지를 판단한다. 만약 그 메소드가 실제로 일치한다면 충고가 적용된다.

  • 스프링은 충고를 받으려고 하는 클래스와 메소드의 관점에서 교차점을 정의하며 충고는 클래스의 이름과 메소드 시그네처(Method Signature)와 같은 특징에 기초하여 대상 클래스와 메소드에 엮인다.

  • 스프링의 교차점 프레임워크를 위한 핵심 인터페이스는 PointCut, PointCut은 메소드와 클래스에 기초하여 충고를 어디에 엮을지 결정한다.

  • Pointcut 구현체를 사용하려면 먼저 Advisor 인터페이스의 인스턴스를 생성하거나 좀 더 구체적으로 PointcutAdvisor 인터페이스의 인스턴스를 생성해야 한다.

8dK379XjTTEPdkTmnLL_3R1nX0GF8CN2iRj1yRT_


 

8P3mWajD9dOvgIciVZqSRd4tMF3TQwPWq2M1yPkl

 

  • PointCut Interface

public interface PointCut {

   ClassFilter getClassFilter();   //클래스의 특정메소드에 pointcut적용여부판단

   MethodMatcher getMethodMatcher(); 

}

 

  • ClassFilter Interface : 인자로 넘어온 Class가 충고를 받아야 하는지를 판단해야 한다.

public interface ClassFilter {

   boolean matches(Class class);

}

 

  • MethodMatcher

public interface MethodMatcher { 

  boolean matches(Method m, Class targetClass);

  public boolean isRuntime();

  public boolean matches(Method m, Class target, Object[] args);

}

matches(Method ,Class) 메소드는 대상 Class와 Method를 기초로 메소드가 충고를 받을 수 있는지의 여부를 판단한다. true가 리턴되면 어떤 유형의 MethodMatcher인지 판단하기 위해 isRuntime()이 호출된다. Static 교차점(pointcut)은 항상 실행되는 교차점을 정의하며 이 경우 isRuntime()은 false를 return하며 동적 교차점(Dynamic Pointcut)은 런타임시의 메소드 인자를 확인하여 충고가 실행되어야 하는지를 결정한다. matches 메소드와 마찬가지로 isRuntime()은 프록시 객체가 생성될 때 오직 한번만 호출된다.

 

  • 충고자(Advisor)

Aspect는 행동을 정의한 충고와 실행돼야 할 위치를 정의한 교차점(Pointcut)의 조합으로 이루어 진다. 스프링에서는 이를 위해 충고자라는 것을 제공한다. 충고자는 충고(Advice)와 교차점(Pointcut)을 하나의 객체로 합친것 이다.

 

public interface PointcutAdvisor { 

   Pointcut getPointcut();

   Advice getAdvice();

}

 

3-5-1. 포인트컷(Pointcut) - StaticMethodMatcherPointCut

 

  • StaticMethodMatcherPointcut을 상속할 때는 matches 메소드만 구현하면 되지만 올바른 타입의 메소드만 어드바이스가 적용되도록 할려면 getClassFilter() 메소드를 오버라이드 하는 것이 좋다.

 

File -> New -> Spring Legacy Project 

Project name : springonj

Simple Spring Maven 선택

 

[First.java]

package onj.edu.aop1;

 

public class First {

public void one() {

System.out.println("First One...");

}

public void two() {

System.out.println("First Two...");

}

}

 

[Second.java]

package onj.edu.aop1;

 

public class Second {

public void one() {

System.out.println("Second One...");

}

public void two() {

System.out.println("Second Two...");

}

}

 

[SimpleAdvice.java]

package onj.edu.aop1;

 

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

 

public class SimpleAdvice implements MethodInterceptor {

   public Object invoke(MethodInvocation invocation) throws Throwable {

System.out.println(invocation.getMethod().getName());

 

Object o = invocation.proceed();

System.out.println("... SimpleAdvice의 충고가 적용됨 ...");

 

return o;

   }

}

 

[SimpleStaticPointcut.java]

package onj.edu.aop1;

import java.lang.reflect.Method;

import org.springframework.aop.support.StaticMethodMatcherPointcut;

 

public class SimpleStaticPointcut extends StaticMethodMatcherPointcut {

// 아래는First.class의 one()의 실행 전/후 충고가 적용된다.

public boolean matches(Method method, Class<?> cls) {

return ("one".equals(method.getName()) && cls == First.class);

}

}

 

[StaticPointcutExam.java]

public class StaticPointcutExam {

public static void main(String[] args) {

First f = new First();

Second s = new Second();

First proxyFirst;

Second proxySecond;

 

//pointcut, advice, advisor 생성

Pointcut pc = new SimpleStaticPointcut();

Advice advice = new SimpleAdvice();

Advisor advisor = new DefaultPointcutAdvisor(pc, advice);

 

//First 프록시 생성

ProxyFactory pf = new ProxyFactory();

pf.addAdvisor(advisor);

pf.setTarget(f);  //First.class를 타겟으로

proxyFirst = (First)pf.getProxy();

 

proxyFirst.one();

proxyFirst.two();

 

//Second 프록시 생성

pf = new ProxyFactory();

pf.addAdvisor(advisor);

pf.setTarget(s);  //Second.class를 타겟으로

proxySecond = (Second)pf.getProxy();

 

proxySecond.one();

proxySecond.two();

}

}

 

[결과]

one

First One...

... SimpleAdvice의 충고가 적용됨 ...

First Two...

Second One...

Second Two...

 

KpgosoIwq8wOpqS0im2vo7RedfJZ-iKE7asYEjlj


 

3-5-2. 포인트컷(Pointcut) - DynamicMethodMatcherPointCut

 

  • DynamicMethodMatcherPointcut에서 충고가 적용되는 메소드는 모든 메소드를 검사하는 초기화 단계, 메소드가 처음 호출되는 시점에 걸쳐 두 번의 정적 검사를 받게 된다.  이 처럼 동적 포인트 컷은 정적 포인트 컷 보다 유연하게 적용될 수 있지만 성능 부담을 고려해 필요한 경우만 사용해야 한다.

 

[First.java]

package onj.edu.aop2;

 

public class First {

public void one(int i) {

System.out.println("First One... i = " + i);

}

public void two() {

System.out.println("First Two...");

}

 

[SimpleAdvice.java]

package onj.edu.aop2;

 

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

//주변충고

public class SimpleAdvice implements MethodInterceptor {

public Object invoke(MethodInvocation invocation) throws Throwable {

System.out.println(invocation.getMethod().getName());

Object o = invocation.proceed();

System.out.println("... SimpleAdvice의 충고가 적용됨 ...");

return o;

}

}

 

[SimpleDynamicPointcut.java]

package onj.edu.aop2;

 

import java.lang.reflect.Method;

 

import org.springframework.aop.ClassFilter;

import org.springframework.aop.support.DynamicMethodMatcherPointcut;

 

/* DynamicMethodMatcherPointcut은

 * matches(Method method, Class<?> cls, Object[] args) 반드시 구현해야함 

 * 정적체크를 위해서는 matches(Method method, Class<?> cls)도 같이 구현해야 한다.

 */

public class SimpleDynamicPointcut extends DynamicMethodMatcherPointcut {

 

//메소드 이름이 one 인 경우 true 즉 충고가 주입된다.아래는 정적체크

//스프링은 two  메소드에 대해서는 동적 검사를 진행 안함

public boolean matches(Method method, Class<?> cls) {

System.out.println("static check :: method.getName() :: " + method.getName());

return "one".equals(method.getName());

}

 

//동적 검사

public boolean matches(Method method, Class<?> cls, Object[] args) {

System.out.println("Dynamic Check : " + ((Integer)args[0]).intValue());

 

int i = ((Integer)args[0]).intValue();

return i > 100;

}

 

//First.class만 충고가 주입된다.

public ClassFilter getClassFilter() {

return new ClassFilter() {

public boolean matches(Class <?> cls) {

return (cls == First.class);

}

};

}

}

 

[DynamicPointcutExam.java]

package onj.edu.aop2;

import org.springframework.aop.Advisor;

import org.springframework.aop.framework.ProxyFactory;

import org.springframework.aop.support.DefaultPointcutAdvisor;

public class DynamicPointcutExam {

public static void main(String[] args) {

First target = new First();

//어드바이저 생성

Advisor advisor = new DefaultPointcutAdvisor(new SimpleDynamicPointcut(), new SimpleAdvice());

 

ProxyFactory pf = new ProxyFactory();

pf.setTarget(target);

pf.addAdvisor(advisor);

First proxy = (First)pf.getProxy();

 

proxy.one(99);

proxy.one(101);

proxy.two();

}

}

 

[결과]

F4VE6FCe99isFJLIV0UcgFWm1dHmQD-tMRuZH_r1

 

3-5-3. 포인트컷(Pointcut) - NameMatchMethodPointCut

 

  • 가끔은 메소드 시그네처, 리턴형은 무시하고 메소드 이름으로 적용여부를 판단하는 포인트 컷이 필요하다. 이때 사용되는 포인트 컷이  NameMatchMethodPointcut 이다.  이번에는 포인트 컷으로 사용할 클래스를 만들지 않아도 되며 직접 NameMatchMethodPointcut을 new  하면 된다.

 

[First.java]

package onj.edu.aop3;

 

public class First {

public void one() {

System.out.println("First One...");

}

public void two() {

System.out.println("First Two...");

}

public void three() {

System.out.println("First Three...");

}

}

 

[SimpleAdvice.java]

package onj.edu.aop3;

 

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

 

public class SimpleAdvice implements MethodInterceptor {

public Object invoke(MethodInvocation invocation) throws Throwable {

System.out.println(invocation.getMethod().getName());

Object o = invocation.proceed();

System.out.println("... SimpleAdvice의 충고가 적용됨 ...");

return o;

}

}

 

[NameMatchMethodPointcutExam.java]

package onj.edu.aop3;

import org.springframework.aop.Advisor;

import org.springframework.aop.framework.ProxyFactory;

import org.springframework.aop.support.DefaultPointcutAdvisor;

import org.springframework.aop.support.NameMatchMethodPointcut;

public class NameMatchMethodPointcutExam {

public static void main(String[] args) {

First target = new First();

//Advisor

NameMatchMethodPointcut pc = new NameMatchMethodPointcut();

pc.addMethodName("one");

pc.addMethodName("two");

Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice());

//Proxy

ProxyFactory pf = new ProxyFactory();

pf.setTarget(target);

pf.addAdvisor(advisor);

First f = (First)pf.getProxy();

f.one();

f.two();

f.three();

}

}

 

[결과]

rbBg_FPtA5IxAQj_92T_9d0m5vmuEAop4eAwcIbA

 

3-5-4. 포인트컷(Pointcut) - JdkRegexpMethodPointcut

 

  • 앞 예제 처럼 메소드 이름을 정확히 알지 못하는 경우, 이름대신 패턴을 사용하여 포인트 컷을 생성할 수 있는데 이때 사용되는 포인트 컷이 JdkRegexpMethodPointcut 이다.

 

[First.java]

package onj.edu.aop4;

 

public class First {

public void hello1() {

System.out.println("hello1 ... ");

}

public void hello2() {

System.out.println("hello2 ... ");

}

public void sayHello() {

System.out.println("sayHello ... ");

}

}

 

[RegExpExam.java]

public class RegExpExam {

public static void main(String[] args) {

First target = new First();

//Advisor

JdkRegexpMethodPointcut pc = new JdkRegexpMethodPointcut();

//스프링은 비교할 때 onj.edu.aop4.RegExam.hello1을 사용한다.

//.* 어떤패키지에있던지... hello를 담고 있으면  OK 

pc.setPattern(".*hello.*");

Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice());

//Proxy 

ProxyFactory pf = new ProxyFactory();

pf.setTarget(target);

pf.addAdvisor(advisor);

First f = (First)pf.getProxy();

f.hello1();

f.hello2();

f.sayHello();

}

}

 

[결과]

FGdo99rcFY2O1U0tf6Y9i6dRnfKWLiRXsmXqXoAW

 

Pointcut은 Advice가 적용될 메소드를 골라야 하는데 Method Signature에 일치하는 패턴을 지정하는 방법을 주로 사용하는 포인트 컷이다.

 

org.springframework.aop.support.JdkregexpMethodPointcut JDK1.4이상 사용

 

<bean id=“smallMartPointcut” class=“org.springframework.aop.support.JdkregexpMethodPointcut”>

<!-- 어떤클래스든 관계없고 get으로 시작하는 메소드

    <property name=“pattern” value=“*.get*”/> 

</bean>

 

Pointcut을 정의했으면 이를 Advice에 결합시킬 수 있다.

 

<bean id=“smallMartAdvisor” class=“org.springframework.aop.support.DefaultPointcutAdvisor”>

    <property name=“advice” ref=“beforeLogging”/>

    <property name=“pointcut” ref= “smallMartPointcut”/>

</bean>

 

앞에서 pointcut을 만들고 advice와 결합을 시켰는데 하나의 빈에 pointcut과 advice를 동시에 정의할 수 있는 특별한 Advisor가 있다. 

(org.springframework.aop.support.RegexpMethodPointcutAdvisor)

 

<bean id=“smallMartAdvisor” class=“org.springframework.aop.support. RegexpMethodPointcutAdvisor”>

    <property name=“advice” ref=“beforeLogging”/>

    <property name=“pattern” value=“*.get*”/>

</bean>

 

3-5-5. 포인트컷(Pointcut) - AspectJExpressionPointcut

 

  • Jdk 정규 표현식외 AspectJ 포인트컷 표현식 언어를 통해 포인트 컷을 선언할 수도 있다. JDK 정규식 보다 많이 사용되며 스프링은 AspectJExpressionPointcut 클래스를 제공하며  aspectjrt.jar, aspectjweaver.jar 두 라이브러리 파일이 필요하다.

 

[pom.xml]

<dependency> 

<groupId>org.aspectj</groupId> 

<artifactId>aspectjweaver</artifactId> 

<version>1.8.10</version> 

</dependency> 

 

<dependency> 

<groupId>org.aspectj</groupId> 

<artifactId>aspectjrt</artifactId> 

<version>1.8.10</version> 

</dependency>

 

[AspectJPointcutExam.java]

package test;

 

public static void main(String[] args) {

First target = new First();

 

//Advisor

AspectJExpressionPointcut pc = new AspectJExpressionPointcut();

//인자, 반환 관계없이 hello로 시작하는...

pc.setExpression("execution(* hello*(..))");

Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice());

 

//Proxy

ProxyFactory pf = new ProxyFactory();

pf.setTarget(target);

pf.addAdvisor(advisor);

First f = (First)pf.getProxy();

 

f.hello1();

f.hello2();

f.sayHello();

}

 

XML 기반 사용예

 

[a.xml]

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

<beans xmlns="http://www.springframework.org/schema/beans

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context

xsi:schemaLocation="http://www.springframework.org/schema/beans 

http://www.springframework.org/schema/beans/spring-beans-4.2.xsd 

http://www.springframework.org/schema/context 

http://www.springframework.org/schema/context/spring-context-4.2.xsd"> 

<bean id="first" class="test.First" /> 

<bean id="simpleAdvice" class="test.SimpleAdvice" /> 

<bean id="main" class="test.AspectJPointcutExam"> 

 

<property name="first" ref="first"/> 

<property name="advisor" ref="advisor"/> 

</bean> 

 

<bean id="advisor" 

class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor"> 

<property name="advice" ref="simpleAdvice" /> 

<property name="expression" 

        value="execution(* test..hello*(..))" /> 

</bean> 

</beans>

 

[재작성된 main 클래스]

import org.springframework.aop.Advisor; 

import org.springframework.aop.aspectj.AspectJExpressionPointcut; 

import org.springframework.aop.framework.ProxyFactory; 

import org.springframework.aop.support.DefaultPointcutAdvisor; 

import org.springframework.context.ApplicationContext; 

import org.springframework.context.support.ClassPathXmlApplicationContext; 

 

public class AspectJPointcutExam { 

First first; 

Advisor advisor; 

 

public void setFirst(First first) { 

this.first = first; 

 

public void setAdvisor(Advisor advisor) { 

this.advisor = advisor; 

 

public static void main(String[] args) { 

 

ApplicationContext context = new ClassPathXmlApplicationContext("a.xml"); 

 

AspectJPointcutExam a = 

(AspectJPointcutExam)context.getBean("main"); 

// Proxy 

ProxyFactory pf = new ProxyFactory(); 

pf.setTarget(a.first); 

pf.addAdvisor(a.advisor); 

 

First f = (First) pf.getProxy(); 

f.hello1(); 

f.hello2(); 

f.sayHello(); 

}

 

 

 

3-5-6. 포인트컷(Pointcut) - AnnotationMatchingPointcut

  • Application이 어노테이션 기반이라면 커스텀 어노테이션을 사용해서 포인트컷을 지정하고 어드바이스를 특정 어노테이션이 적용된 모든 메소드 또는 타입에 적용하고 싶을 때가 있다. 스프링은 어노테이션을 사용해서 포인트컷을 정의할 수 있도록 해주는 AnnotationMatchingPointcut 클래스를 제공한다.

 

[AdviceRequired.java]

package onj.edu.aop6;

 

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

 

@Retention(RetentionPolicy.RUNTIME)

//이 어노테이션을 타입레벨과 메소드레벨에서 적용할수있도록

@Target({ElementType.TYPE, ElementType.METHOD})  public @interface AdviceRequired {  //interface를 어노테이션으로 선언

}

 

[First.java]

package onj.edu.aop6;

 

public class First {

@AdviceRequired  //어드바이스가 적용될 것

public void hello() {

System.out.println("hello1 ... ");

}

 

public void sayHello() {

System.out.println("sayHello ... ");

}

}

 

[SimpleAdvice.java]

package onj.edu.aop6;

 

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

 

public class SimpleAdvice implements MethodInterceptor {

public Object invoke(MethodInvocation invocation) throws Throwable {

System.out.println(invocation.getMethod().getName());

Object o = invocation.proceed();

System.out.println("... SimpleAdvice의 충고가 적용됨 ...");

return o;

}

}

 

[AnnotationPointcutExam.java]

public class AnnotationPointcutExam {

public static void main(String[] args) {

First target = new First();

 

//Advisor, 메소드를 호출하면서 지정한 어노테이션이 적용된 모든 메소드에 어드바이스를 적용

AnnotationMatchingPointcut pc = AnnotationMatchingPointcut.forMethodAnnotation(AdviceRequired.class);

Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice());

 

//Proxy

ProxyFactory pf = new ProxyFactory();

pf.setTarget(target);

pf.addAdvisor(advisor);

 

First f = (First)pf.getProxy();

 

f.hello();

f.sayHello();

}

}

 

[결과]

LKv2iqPG8aRGwDWcCpkCKIkUc5T7bwsNDt8X8Fzd

 

3-5-7. 포인트컷(Pointcut) - ControllFlowPointcut

 

  • 특정 메소드 하위의 모든 메소드 호출 또는 특정 클래스 하위의 모든 메소드 호출에 사용한다

 

[First.java]

package onj.edu.aop7;

 

public class First {

 

public void hello() {

// 특정 메소드인 myAdvice()에서 Call할 때 충고 적용함

System.out.println("hello ... ");

}

}

 

[SimpleBeforeAdvice.java]

package onj.edu.aop7;

 

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

 

public class SimpleBeforeAdvice implements MethodBeforeAdvice {

public void before(Method method, Object args[], Object target) throws Throwable {

System.out.println("Before Method ::" + method);

}

}

 

[ControlFlowExam.java]

public class CaontrolFlowExam {

public static void main(String[] args) {

ControlFlowExam c = new ControlFlowExam();

c.go();

}

void go() {

First target = new First();

//ControlFlowExam.class의 myAdvice() 가 호출하는 모든 메소드에 충고적용

Pointcut pc = new ControlFlowPointcut(ControlFlowExam.class, "myAdvice");

Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleBeforeAdvice());

 

//Proxy

ProxyFactory pf = new ProxyFactory();

pf.setTarget(target);

pf.addAdvisors(advisor);

 

First f = (First)pf.getProxy();

//Normal Invoke

f.hello();

//ControlFlowExam.myAdvice()

myAdvice(f);

}

void myAdvice(First f) {

f.hello();

}

}

 

[결과]

kZGVrE0AL4aql1yCTGUrSCmmzacRDvWF_ZVVG-4P

 

3-6. ProxyFactoryBean을 이용한 선언적 AOP 구현

 

  • ProxyFactoryBean 클래스는 빈에 타겟을 지정할 수 있게 해주는 FactoryBean의 구현체로 AOP 프록시에 적용할 빈의 어드바이스와 어드바이저를 제공한다.

  • ProxyFactoryBean을 이용하는 경우 어드바이저와 어드바이스를 모두 사용할 수 있으므로 어드바이스 뿐 아니라 포인트컷도 선언적으로 설정 할 수 있다.

  • 타겟을 정하고 ProxyFactoryBean을 사용해 애플리케이션이 접근 할 빈을 정의 후 타겟빈을 프록시 타겟으로 사용한다. 

kIQMO9Zgxd2xsiKDZnHGTe6d1YofEWd6gkcUuTBc

 

  • 대상클래스가 인터페이스 기반이고 proxyTargetClass 속성이 true라면 CGLIB 기반의 프록시가 생성되고 false라면 JDK Proxy 기반으로 프록시를 생성한다. 대상 클래스가 인터페이스 기반이 아니라면 proxyTargetClass 속성이 false라고 하더라도 CGLIB 기반의 프록시가 생성된다.

 

3-6-1. ProxyFactoryBean을 이용한 선언적 AOP 구현 - 예제

 

  • 프로그래밍적인 방법을 통해 구현한 이전 SmallMart 예제를 ProxyFactoryBean을 이용하여 구현해 보자. (Pointcut을 사용하여 getProducts2() 메소드만 충고를 받을 수 있도록 구현)

 

STS에서

 

1. File -> New -> Spring Legacy Project 

Project name : demo-smallmart1

Simple Spring Maven 선택

 

fVHy0IA01ZCp-a8VoC6-Wklr_R268aY2IVTwINbf

 

[pom.xml]

의존성 추가

 

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-aop</artifactId>

<version>${spring-framework.version}</version>

</dependency>

 

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjrt</artifactId>

<version>1.8.8</version>

</dependency>

 

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</artifactId>

<version>1.8.8</version>

</dependency>

 

 

[SmallMartInterface.java]

package onj.hello.aop1;

 

public interface SmallMartInterface {

public void getProducts(String productName) throws Exception;

public void getProducts2(String productName) throws Exception;

}

 

[SmallMart.java]

 

package onj.hello.aop1;

 

public class SmallMart implements SmallMartInterface{

public void getProducts(String productName) throws Exception{

System.out.println("getProduct()..." + productName);

throw new Exception("error");

}

 

public void getProducts2(String productName) throws Exception{

System.out.println("getProduct2()..." + productName);

throw new Exception("error");

}

}

 

[BeforeLoggingAdvice.java]

package onj.hello.aop1;

 

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

 

public class BeforeLoggingAdvice implements MethodBeforeAdvice{

public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable{

String findName = (String)arg1[0];

System.out.println(arg0.getName() + ";;사전충고");

}

}

 

[AfterLoggingAdvice.java]

package onj.hello.aop1;

 

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

 

public class AfterLoggingAdvice implements AfterReturningAdvice{

public void afterReturning(Object arg0, Method arg1, Object[] arg2,Object arg3) 

throws Throwable{

System.out.println(arg1.getName() +  ": 사후충고");

}

}

 

[AroundLoggingAdvice.java]

package onj.hello.aop1;

 

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

 

public class AroundLoggingAdvice implements MethodInterceptor{

public Object invoke(MethodInvocation arg0) throws Throwable{

String findName= (String)arg0.getArguments()[0];

if(findName== null) {

throw new Throwable("예외");

}

System.out.println("메소드실행전");

Object returnedObj= arg0.proceed();

System.out.println("메소드실행후");

return returnedObj;

}

}

 

[ThrowsLoggingAdvice.java]

package onj.hello.aop1;

 

import org.springframework.aop.ThrowsAdvice;

 

public class ThrowsLoggingAdvice implements ThrowsAdvice{

public void afterThrowing(Throwable throwable){

System.out.println("예외충고 발생...");

}

}

 

[SmallMartApp.java]

package onj.hello.aop1;

 

import org.springframework.aop.framework.ProxyFactory;

import org.springframework.context.support.GenericXmlApplicationContext;

 

public class SmallMartApp {

public static void main(String[] args) throws Exception{

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

ctx.load("smallmart1.xml");

ctx.refresh();

SmallMartInterface smallMart = (SmallMartInterface)ctx.getBean("smallMartProxy"); 

smallMart.getProducts("과자");

smallMart.getProducts2("과일");

ctx.close();

}

}

 

[/src/main/resources/smallmart1.xml]

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

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">

 

<bean id="smallMartProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

<property name="target">

<ref bean="smallMart" />

</property>

<property name="interceptorNames">

<list>

<value>smallMartAdvisor1</value>

<value>smallMartAdvisor2</value>

<value>smallMartAdvisor3</value>

<value>smallMartAdvisor4</value>

</list>

</property>

</bean>

<bean id="beforeAdvice" class onj.hello.aop1.BeforeLoggingAdvice" />

<bean id="afterAdvice" class=" onj.hello.aop1.AfterLoggingAdvice" />

<bean id="aroundAdvice" class=" onj.hello.aop1.AroundLoggingAdvice" />

<bean id="throwsAdvice" class=" onj.hello.aop1.ThrowsLoggingAdvice" />

<bean id="smallMartAdvisor1" class="org.springframework.aop.support.DefaultPointcutAdvisor">

<property name="advice">

<ref bean="beforeAdvice"></ref>

</property>

<property name="pointcut">

<!-- 포인트컷으로 익명 빈을 사용 -->

<bean class="org.springframework.aop.aspectj.AspectJExpressionPointcut">

<property name="expression">

<value>execution(* getProducts2*(..))</value>

</property>

</bean>

</property>

</bean>

<bean id="smallMartAdvisor2" class="org.springframework.aop.support.DefaultPointcutAdvisor">

<property name="advice">

<ref bean="afterAdvice"></ref>

</property>

<property name="pointcut">

<!-- 포인트컷으로 익명 빈을 사용 -->

<bean class="org.springframework.aop.aspectj.AspectJExpressionPointcut">

<property name="expression">

<value>execution(* getProducts2*(..))</value>

</property>

</bean>

</property>

</bean>

<bean id="smallMartAdvisor3" class="org.springframework.aop.support.DefaultPointcutAdvisor">

<property name="advice">

<ref bean="aroundAdvice"></ref>

</property>

<property name="pointcut">

<!-- 포인트컷으로 익명 빈을 사용 -->

<bean class="org.springframework.aop.aspectj.AspectJExpressionPointcut">

<property name="expression">

<value>execution(* getProducts2*(..))</value>

</property>

</bean>

</property>

</bean>

<bean id="smallMartAdvisor4" class="org.springframework.aop.support.DefaultPointcutAdvisor">

<property name="advice">

<ref bean="throwsAdvice"></ref>

</property>

<property name="pointcut">

<!-- 포인트컷으로 익명 빈을 사용 -->

<bean class="org.springframework.aop.aspectj.AspectJExpressionPointcut">

<property name="expression">

<value>execution(* getProducts2*(..))</value>

</property>

</bean>

</property>

</bean>

<bean id="smallMart" class=" onj.hello.aop1.SmallMart" />

</beans>

 

3-7. Schema-Based Spring AOP(<AOP> 네임스페이스 이용)

 

  • 선언적인 스프링 AOP 설정을 간단하게 해 준다. <Beans> 태그 내에 aop 네임스페이스를 선언해야 한다.

  • 모든 스프링 aop 설정을 <aop:config></aop:config> 안에 넣어야 하는데 포인트컷, 어스펙트, 어드바이저 등을 정의하고 다른 스프링 빈을 참조 가능 하다.

  • aop:config 태그안에서 aspect를 정의하는 데 aspect=advisor=advice + pointcut 이므로 충고와 포인트컷을 aop:config안에서 정의해야 한다. 아래는 충고 클래스인 aBean을 외부에 선언하고 aop:config 태그안에서 ref로 참조 한 경우이다.

<aop:config>

    <aop:aspect id="myAspect" ref="aBean">

        ...

    </aop:aspect>

</aop:config>

 

<bean id="aBean" class="...">

    ...

</bean>

 

  • aop:aspect 내에 pointcut을 정의할 수도 있다.    

<aop:config>

    <aop:aspect id="myAspect" ref=“aBean“>

       <aop:pointcut id=“onjPointcut“ expression="execution(* x.y.z.service.*.*(..))"/>

         ……

    </aop:aspect>

</aop:config>

<bean id="aBean" class="...">

    ...

</bean>

 

  • pointcut을 정의하고 사전충고에서 포인트컷을 참조하고 충고메소드를 정의할 수도 있다.

<aop:config>

    <aop:aspect id="myAspect" ref="aBean">

       <aop:pointcut id=“onjPointcut“ 

expression=“execution(* x.y.z.service..*(..))"/> 

<!– pointcut-ref 대신 pointcut 속성을 사용하여 직접 포인트컷 표현식을 써도 된다

<!—myLogic() 메소드가 사전 충고용 메소드임을 정의

        <aop:before pointcut-ref="onjPointcut " method="myLogic"/>

    </aop:aspect>

</aop:config>

   

<bean id="aBean" class="x.y.z.advice.MyAdvice">

    ...

</bean>

 

[MyAdvice.java]

// 충고 클래스인 MyAdvice.java 에서 myLogic이라는 메소드가 정의되어 있어야 한다.

package x.y.z.advice;

public class MyAdvice {

   public void myLogic(Object service) {  }

   …

}

 

  • 포인트컷 조합을 and, or , not을 사용할 수 있다.

<aop:config>

    <aop:aspect id="myAspect" ref="aBean“>

       <aop:pointcut id=“onjPointcut“ 

expression=“execution(* x.y.z.service..*(..)) 

**and** bean(smallmart)"/>

        <aop:before pointcut-ref="onjPointcut " method="myLogic"/>

    </aop:aspect>

</aop:config>

<bean id=”smallmart” class=”x.y.z.service.SmallMart”/> <!—타겟 클래스

<bean id="aBean" class="x.y.z.advice.MyAdvice">

    ...

</bean>

 

[MyAdvice.java]

// 충고 클래스인 MyAdvice.java 에서 myLogic이라는 메소드가 정의되어 있어야 한다.

package x.y.z.advice;

public class MyAdvice {

   public void myLogic(Object service) {  }

   …

}

 

 

  • 충고 작성 예문

// 포인트컷을 AspectJ Expression으로 직접 정의

<aop:before pointcut="execution(* x.y.z.dao.*.*(..))" method="insert"/>

 

// 포인트컷을 외부에 만들고 pointcut-ref로 참조

<aop:after-returning pointcut-ref="onjPointcut " method=“myLogic1"/>

<aop:after-throwing pointcut-ref="onjPointcut " method=“myLogic2”/>

<aop:after pointcut-ref="onjPointcut " method="myLogic3"/>

<aop:around pointcut-ref="onjPointcut " method="myLogic4"/> 

<aop:around pointcut-ref="onjPointcut " method="myLogic4“ arg-names=“name, age”/>

 

  • Advisor 작성 예문

 <aop:config>

    <aop:pointcut id=“onjPointcut"

                 expression="execution(* x.y.z.service.*.*(..))"/>

    <aop:advisor pointcut-ref="onjPointcut" advice-ref="tx-advice"/>

</aop:config>

 

// 스프링 AOP를 이용하여 트랜잭션도 하나의 충고로 주입 가능하다.

// Pointcut 클래스(x.y.z.service 패키지 아래의 모든 클래스)의 모든 메소드가 

// 트랜잭션(충고)이 적용됨을 정의한다.

<tx:advice id="tx-advice">

    <tx:attributes>

        <tx:method name="*" propagation="REQUIRED"/>

    </tx:attributes>

</tx:advice>

 

3-7-1. Schema-Based Spring AOP(<AOP> 네임스페이스 이용) – 예제

 

  • 이전에 프로그래밍적인 AOP/ProxyFactoryBean을 이용하여 작성한 SmallMart 예제를 aop Namespace를 이용한 방식으로 변경해 보자.

 

STS에서

 

1. File -> New -> Spring Starter Project

Name : demo-smallmart2

Type : MAVEN

Package : onj.hello.aop2

 

2. Core -> AOP 체크

 

[SmallMartInterface.java]

package onj.hello.aop2;

 

public interface SmallMartInterface {

public void getProducts(String productName) throws Exception;

}

 

[SmallMart.java]

package onj.hello.aop2;

 

public class SmallMart implements SmallMartInterface {

public void getProducts(String productName) throws Exception {

  System.out.println("[Target Method]getProduct()..." + productName);

  throw new Exception("error...");  //주석으로 막고 실행해 보자.

}

}

 

[OraclejavaAdvice.java]

package onj.hello.aop2;

 

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.ProceedingJoinPoint;

 

public class OracleJavaAdvice {

public void afterReturning(JoinPoint joinPoint) {

System.out.println(joinPoint.getSignature().getName() + "(" + joinPoint.getArgs()[0] + ") : 사후충고");

}

 

public void before(JoinPoint joinPoint) {

System.out.println(joinPoint.getSignature().getName() + "(" + joinPoint.getArgs()[0] + ") : 사전충고");

}

 

public void afterThrowing() {

System.out.println("예외충고 발생...");

}

 

public Object around(ProceedingJoinPoint pjp) throws Throwable {

String findName = (String)pjp.getSignature().getName();

String methodName = (String)pjp.getArgs()[0];

System.out.println("[주변충고]"+ findName + "(" + methodName +") 메소드 실행전");

//타켓클래스의 메소드 호출

Object obj = pjp.proceed();  

System.out.println("[주변충고]" + findName + "(" + methodName +") 메소드 실행후");

return obj;

}

}

 

[DemoSmallmart2Application.java]

package onj.hello.aop2;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.AbstractApplicationContext;

import org.springframework.context.support.GenericXmlApplicationContext;

 

@SpringBootApplication

public class DemoSmallmart2Application {

   public static void main(String[] args) throws Exception {

SpringApplication.run(DemoSmallmart2Application.class, args);

ApplicationContext ctx = new GenericXmlApplicationContext();

((GenericXmlApplicationContext) ctx).load("smallmart2.xml");

((AbstractApplicationContext) ctx).refresh();

SmallMartInterface smallMart = (SmallMartInterface)ctx.getBean("smallMart");

smallMart.getProducts("과자");

((AbstractApplicationContext) ctx).close();

   }

}

 

[src/main/resources/smallmart2.xml]

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

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">

 

<aop:config>

<aop:pointcut expression="execution(* onj.hello.aop2..getProducts*(String)) and bean(smallMart*)"

id="onjpointcut" />

<aop:aspect ref="advice">

<aop:before method="before" pointcut-ref="onjpointcut" />

<aop:after-returning method="afterReturning"

pointcut-ref="onjpointcut" />

<aop:around method="around" pointcut-ref="onjpointcut" />

<aop:after-throwing method="afterThrowing"

pointcut-ref="onjpointcut" />

</aop:aspect>

</aop:config>

 

<bean id="advice" class="onj.hello.aop2.OracleJavaAdvice" />

<bean id="smallMart" class="onj.hello.aop2.SmallMart" />

</beans>

 

[결과]

p3uHgrmPLzwzduMuSDw0webhSOr3H0oAMcxs1P9O

 

 

3-8. @AspectJ Annotation을 이용한 AOP

 

  • Spring AOP와 JDK 1.5 이상인 경우 어노테이션을 이용하여 어드바이스를 선언 할 수 있다.

  • @AspectJ 방식은 AspectJ 5 버전에서 소개되었으며, Spring은 2.0 버전부터 AspectJ 5 어노테이션을 지원한다

  • 타겟 메소드에 어드바이스를 적용할 때는 AspectJ의 위빙 메커니즘이 아니라 자체 프록시 메커니즘을 이용한다.

  • @AspectJ를 사용하기 위해서 XML설정 파일에 <aop:aspectj-autoproxy/> 태그를 설정에 추가해야하며 클래스에@Aspect 어노테이션을 추가하여 Aspect를 생성해야 한다.

  • @Pointcut으로 포인트컷을 정의하거나 @Before, @After등 충고를 정의할 때 AspectJ Expression을 정의하여 포인트컷을 지정할 수 있다.

 

3-8-1. @AspectJ Annotation을 이용한 AOP – Aspect 선언

 

  • 자바설정을 이용하여 AspectJ를 사용하는 방법

     @Configuration

     @EnableAspectJAutoProxy

     public class AppConfig {    }

 

@Aspect

public class UserAspect {

        // USerService+ : 인스턴스의 타입이 UserService 이거나 하위타입을 의미

@Before("execution(* x.y.z.UserService+.*(..))")

public void userAdvice(){

System.out.println("find my advice before your task.");

}

}

 

@Configuration

@EnableAspectJAutoProxy

public class AspectJAutoProxyConfig {

  @Bean

  public UserService userService(){

return new UserService();

  }

  @Bean

  public UserAspect userAspect(){

return new UserAspect();

  }

}

 

  • XML설정을 이용하여 AspectJ를 사용하는 방법

     <aop:aspectj-autoproxy/>

 

<aop:aspectj-autoproxy/>

<bean id="myAspect" class=“ojc.aop.OjcAspect“/>

 

 

package ojc.aop;

import org.aspectj.lang.annotation.Aspect;

@Aspect  

public class OjcAspect {  ……  }

 

위 XML절정은 @Component를 이용하면 아래처럼 가능하다.

(XML에서 빈으로 정의하지 않을 경우 @Component, @Service, @Named와 같은 Annotation을 

기술하면 된다.)

 

<aop:aspectj-autoproxy/>

 

package ojc.aop;

import org.aspectj.lang.annotation.Aspect;

@Aspect  

@Component

public class OjcAspect {  ……  }

 

3-8-2. @AspectJ Annotation을 이용한 AOP – Advice 선언

 

Before advice : Before advice는 @Before 어노테이션을 사용한다.

 

After returning advice : After returing 충고는 정상적으로 메소드가 실행될 때 수행된다. After returning 충고는 @AfterReturing 어노테이션을 사용한다.

 

After throwing advice : After throwing 충고는 메소드가 수행 중 예외사항을 반환하고 종료하는 경우 수행된다. After throwing 충고는 @AfterThrowing 어노테이션을 사용한다.

 

After (finally) advice : After (finally) 충고는 메소드 수행 후 무조건 수행된다. After (finally) 충고는 @After 어노테이션을 사용한다.

 

Around advice : Around 충고는 메소드 수행 전후에 수행된다. Around 충고는 @Around 어노테이션을 사용한다.

 

@Around 충고는 대상 메소드의 반환 값(return value)를 변경 가능하지만, After returning 충고는 반환 값을 참조 가능하지만 변경할 수는 없다.

 

  • 사전충고(@Before)

@Before 어노테이션을 사용하며 포인트컷 메소드가 실행되기 전에 충고가 적용된다.

 

@Aspect

public class BeforeExample {

 

    @Before(“x.y.MyClass.dataAccessOperation()")

    public void doAccessCheck1() {

        // ...

    }

 

    @Before("execution(* x.y.MyClass.*(..))")

    public void doAccessCheck2() {

        // ...

    }

}

 

  • 사후충고(@AfterReturning)

@AfterReturning 어노테이션을 사용하며 포인트컷 메소드가 리턴(정상종료)된 후 충고가 적용된다.

 

@Aspect

public class AfterReturningExample {

 

    @AfterReturning(“x.y.MyClass.dataAccessOperation()")

    public void doAccessCheck1() {

        // ...

    }

 

   // 리턴값을 retVal로 받음, returning 속성의 값은 어드바이스 메소드의 파라미터와 이름이 

   // 같아야 한다.

   @AfterReturning(

        pointcut=“x.y.MyClass.dataAccessOperation()",

        returning="retVal")

    public void doAccessCheck2(Object retVal) {

        // ...

    }

}


 

  • 예외충고(@AfterThrowing)

@AfterThrowing 어노테이션을 사용하며 포인트컷 메소드에서 예외가 발생할 때 충고가 적용된다.

 

@Aspect

public class AfterThrowingExample {

 

    @AfterThrowing(“x.y.MyClass.dataAccessOperation()")

    public void doRecoveryActions1() {

        // ...

    }

   // 리턴값을 retVal로 받음, throwing 속성의 값은 어드바이스 메소드의 파라미터와 이름이 

   // 같아야 하며 예외가 그 변수로 넘어온다.

   @AfterThrowing(

        pointcut=“x.y.MyClass.dataAccessOperation()",

        throwing="ex")

    public void doRecoveryActions2(DataAccessException ex) {

        // ...

    }

}

 

  • 사후충고(@After)

@After 어노테이션을 사용한다. 포인트컷 메소드가 실행된 후(정상종료 여부와 관계없이) 충고가 적용된다.

 

@Aspect

public class AfterExample {

    @After(“x.y.MyClass.dataAccessOperation()")

    public void doReleaseLock() {

        // ...

    }

}

 

  • 주변충고(@Around)

@Around 어노테이션을 사용하며 포인트컷 메소드가 실행 되기 전, 리턴 된 후에 충고가 적용된다.

 

어드바이스 메소드의 첫번째 파라미터는 ProceedingJoiinPoint가 되어야 한다. proceed() 메소드를 통해 타겟 클래스의 원래 메소드를 호출하고, proceed() 메소드를 호출하면서 Object[] 형태로 파라미터를 전달할 수도 있다. proceed() 메소드는 기술 안할수도 있고, 한번, 여러 번 호출할 수도 있으므로 원래 메소드에 대한 여러 형태의 제어가 가능하다. 물론 원래 메소드의 리턴 값을 바꿔치기 하는 것도 가능하다.

 

@Aspect

public class AroundExample {

 

     @Around(“x.y.MyClass.businessService()")

    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {

        // start stopwatch

        Object retVal = pjp.proceed();

        // stop stopwatch

        return retVal;

    }

}

 

[참고] StopWatch 사용법

1.System 클래스의 currentTimeMillis() 메소드 이용

1970년 1월 1일 자정부터 현재까지 카운트된 시간을 ms(milliseconds) 단위로 표시한다.

 

long startTime = System.currentTimeMillis();

// 특정 로직이나 메소드 호출

long elapsedTime = System.currentTimeMillis() - startTime;

System.out.println(elapsedTime + " ms");

 

2. System 클래스의 nanoTime() 메소드 이용

nanoTime 메서드는 현재 Java 가상머신의 high-resolution 시간값을 ns(nano sec.) 단위로 반환한다.

 

long startTime = System.nanoTime();

// 특정 로직이나 메소드 호출

long endtime = System.nanoTime();

long elapsedTime = startTime - endtime;

System.out.println(elapsedTime + " ns");

 

3. Common-lang의 StopWatch 클래스 start(), stop() 메소드 이용

 

import org.apache.commons.lang.time.StopWatch;

 

stopWatch.start();

doSomeTask(5000);

stopWatch.stop();

 

System.out.println("Time: " + stopWatch.getTime());   // Time: 5000

 

4. 스프링 프레임워크의 StopWatch 클래스의 start(), stop() 이용

 

import org.springframework.util.StopWatch;

 

stopWatch.start();

List<Tuple> emps = empService.getEnameDnameTop5(deptno);

stopWatch.stop();

log.info(">>>>> getEnameDnameTop5(Time) : " + stopWatch.getTotalTimeSeconds());

 

 

3-8-3. @AspectJ Annotation을 이용한 AOP – Pointcut 선언

 

  • @Pointcut 어노테이션을 사용한다.

@Pointcut("execution(* onj.aop.*.*(..))")      // pointcut expression

private void mypointcut() {   }                // pointcut signature

 

@Before("mypointcut ()")

public void myBeforeAdvice(JoinPointjoinPoint, int intValue) { ... }

 

 

  • 포인트컷 지정자(Pointcut Designators)

execution: 메소드 실행 결합점(join points)과 일치시키는데 사용된다.

within: 특정 타입에 속하는 결합점을 정의한다.

this: 빈 참조가 주어진 타입의 인스턴스를 갖는 결합점을 정의한다.

target: 대상 객체가 주어진 타입을 갖는 결합점을 정의한다.

args: 인자가 주어진 타입의 인스턴스인 결합점을 정의한다.

 

@target: 타겟 클래스가 주어진 타입의 어노테이션을 갖는 결합점을 정의한다.

@args: 전달된 인자의 런타임 타입이 주어진 타입의 어노테이션을 갖는 결합점을 정의한다.

@within: 주어진 어노테이션을 갖는 타입 내 결합점을 정의한다.

@annotation: 실행 메소드가 인자로 주어진 어노테이션을 갖는 결합점을 정의한다.

 

//메소드가 어떤 패키지에 있던지 public 메소드라면 충고적용

@Pointcut("execution(public * *(..))")

private void anyPublicOperation() {}

 

//메소드가 onj 패키지 또는 부 패키지안의 타입이라면 충고적용

@Pointcut("within(onj..*)")

private void inTrading() {}

 

//위 두포인트컷의 AND 조건을 만족하는 경우 충고가 적용

@Pointcut("anyPublicOperation() && inTrading()")

private void tradingOperation() {}

 

//메소드가 onj.dao 패키지 또는 부 패키지안의 타입이라면 충고적용

@Pointcut("within(onj.dao..*)")

 public void inDataAccessLayer() {}

 

[Aspect 클래스 작성 예]

@Aspect //애스팩트 클래스임을 표시

public class MyAdvice {

   @Pointcut("execution(* onj.edu.aop11..hello*(int)) && args(intValue)")

   public void helloExec(int intValue) {}

 

   @Pointcut("bean(myDependency*)")

   public void inMyDependency() {}

 

   //@AspectJ 는 &&, aop 네임스페이스는 and 

   @Before("helloExec(intValue) && inMyDependency()")

   public void myBeforeAdvice(JoinPointjoinPoint, int intValue) { ... }

 

   //Around Advice

   @Around("helloExec(intValue)")

   public myAroundAdvice(ProceedingJoinPoint joinPoint, int intValue) throws Throwable { ... }

   

   //......

}

 

3-8-4. @AspectJ Annotation을 이용한 AOP – aspectj expression

 

execution(public * *(..)) : public 메소드가 포인트 컷

 

execution(* onj.spring.aop.*.*(..)) : onj.spring.aop 패키지의 모든 메소드가 포인트 컷

 

execution(* onj.spring.aop..*.*(..)) : onj.spring.aop 패키지와 하위 패키지의 모든 메소드가 포인트 컷

 

execution(public void insert*(..)) : public에 리턴값, 패키지명 없고 메서드 이름은 insert로 시작, 인자 값은 0개 이상인 메서드가 포인트 컷

 

execution(* onj.spring.aop.*.*()) : 리턴형 관계없고 onj.spring.aop 패키지의 모든 클래스, 인자 값이 없는 모든 메서드가 포인트 컷

 

execution(* onj.spring.aop..*.*(..)) : 리턴형 관계없고 onj.spring.aop 패키지 및 하위 패키지에 있는 모든 클래스,  인자값이 0개 이상인 메서드가 포인트 컷

 

execution(* delete*(*)) : 메서드 이름이 delete으로 시작하는 인자 값이 1개인 메서드가 포인트 컷

 

execution(* delete*(*,*)) : 메서드 이름이 delete로 시작하는 인자 값이 2개인 메서드가 포인트 컷

 

execution(* onj*(Integer, ..)) : 메서드 이름이 onj로 시작하고 첫번째 인자 값의 타입이 Integer, 1개 이상의 매개변수를 갖는 메서드가 포인트 컷

 

within(onj.spring.aop.*) : onj.spring.aop 패키지 내의 모든 메소드가 포인트 컷

 

within(onj.spring.aop..*) : onj.spring.aop패키지 및 하위 패키지의 모든 메소드가 포인트 컷

 

bean(oraclejava*) : 이름이 oraclejava로 시작되는 모든 빈의 메소드가 포인트 컷

 

bean(*dataSource) || bean(*DataSource) : 빈 이름이 “dataSource” 나 “DataSource” 으로 끝나는 모든 빈의 메소드가 포인트 컷

 

!bean(onjoraclejava) : onjoraclejava빈을 제외한 모든 빈의 메소드가 포인트

 

this(onj.aop.SmallMartInterface) : 현재 실행중인 인스턴스가  SmallMartInterface 이름의 빈과 타입이 같은 경우 포인트컷, SmallMart인터페이스를 구현했다면 모든 메소드가 포인트컷   

                         

target(onj.aop.SmallMartInterface) : 타겟 오브젝트가 SmallMartInterface를 구현했다면 모든 메소드가 포인트컷

 

args(java.io.Serializable) : 메소드가 매개변수가 하나이고 Serializable인터페이스를  타입이라면 포인트컷

 

@target(org.springframework.transaction.annotation.Transactional) : 타겟 오브젝트가 org.springframework.transaction.annotation.Transactional  어노테이션(@Transactional)을 가진다면 포인트컷

 

@within(org.springframework.transaction.annotation.Transactional) : 타겟 오브젝트의 선언된 타입이 @Trtansactional 어노테이션을 가진다면 포인트컷

 

@annotation(org.springframework.transaction.annotation.Transactional) : 실행 메소드가  @Transactional 어노테이션을 가진다면 포인트컷

 

@args(x.y.AnnotationRequired) : 파라미터를 하나 가지며 넘어오는 아규먼트의 런타임 타입이 @AnnotationRequired 타입이라면  포인트컷

 

3-8-5. @AspectJ Annotation을 이용한 AOP – 예제 

 

  • 이전에 작성한 AOP NameSpace를 이용한 SmallMart 예제를 @AspectJ Annotation을 이용하여 변경해 보자.

 

STS에서 

 

1. File -> New -> Spring Starter Project 

Name : demo-smallmart3

Type  : MAVEN

Package : onj.hello.aop3

 

다음화면에서 Core -> AOP 체크

 

2. @Inject를 사용하기 위해 의존성 추가

 

<dependency>

<groupId>javax.inject</groupId>

<artifactId>javax.inject</artifactId>

<version>1</version>

</dependency>

 

 

[SmallMartInterface.java]

package onj.hello.aop3;

 

public interface SmallMartInterface {

public String getProducts(String productName) throws Exception;

}

 

[SmallMart.java]

package onj.hello.aop3;

 

@Component

public class SmallMart implements SmallMartInterface {

public String getProducts(String productName) throws Exception {

return "[Target Method]getProduct()..." + productName;

//throw new Exception("error...");

}

}

 

[SmallMartAspect.java]

package onj.hello.aop3;

 

@Component

@Aspect
public class SmallMartAspect {

@Pointcut("execution(* onj.hello.aop3.SmallMartInterface.getProducts(..))")

public void getProduct1() { }

@Pointcut("args(String)")

public void getProduct2() { }

// 사전충고 : 타겟클래스의 메소드 실행 전 충고실행 

@Before("getProduct1() && getProduct2()")

public void logBefore(JoinPoint joinPoint) {

System.out.println("Before Advice --> logBefore()...");

joinPoint.getSignature().getName();

}

 

// 사후충고(after) : 타겟클래스의 메소드 실행후 충고 실행, 오류가 발생해도 실행

@After("getProduct1()")

public void logAfter(JoinPoint joinPoint) {

System.out.println("After Advice --> logAfter()...");

joinPoint.getSignature().getName();

}

 

// 사후충고(after returning) : 타겟클래스의 메소드  정상 리턴된 후 실행

//  returning 속성으로 리턴값을 받을 수 있다.

@AfterReturning(pointcut = "getProduct1()", returning = "result")

public void logAfterReturning(JoinPoint joinPoint, Object result) {

System.out.println("AfterReturning Advice --> logAfterReturning()...");

joinPoint.getSignature().getName();

System.out.println("return value is " + result);

}

 

// 주변충고 : 타겟클래스의 메소드 실행 전후에 충고 실행

@Around("getProduct1()")

public String logAround(ProceedingJoinPoint joinPoint) throws Throwable {

System.out.println("Around Advice[전] --> logAround()...");

joinPoint.getSignature().getName();

Arrays.toString(joinPoint.getArgs());

// 타겟 객체의 원래 메소드 실행

String s = (String) joinPoint.proceed();

System.out.println("Around Advice[후] --> logAround()...");

// 원래 타겟클래스의 메소드 리턴값을 대체시킴

return "이문장으로원래 타겟메소드(getProducts) 리턴이 대체됨!!";

          }

 

// 예외충고 : 예외가 발생될때 충고 실행

@AfterThrowing(pointcut = " getProduct1()", throwing = "error")

// error는 실행되는 메소드에서 던지는 예외객체

public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {

System.out.println("예외충고 --> logAfterThrowing()...");

joinPoint.getSignature().getName(); System.out.println("Exception " + error);

}

}

 

[DemoSmallMart3Application.java]

package onj.hello.aop3;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.CommandLineRunner;

import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.EnableAspectJAutoProxy;

 

@SpringBootApplication

public class DemoSmallmart3Application implements CommandLineRunner {

   public static void main(String[] args) throws Exception { SpringApplication.run(DemoSmallmart3Application.class, args);

   }

 

  @Inject

   private SmallMartInterface smallMart;

   public void run(String... args) throws Exception { 

this.smallMart.getProducts("과자");  

}

}

 

3-9. 스프링 선언적 AOP에 대한 고려사항(@AspectJ vs XML)

 

  • 스프링 애플리케이션이 XML 기반이라면 aop 네임스페이스를 이용 하는것이 적절하다. 이렇게 하면 DI, AOP 설정 방식을 일관되게 유지할 수 있기 때문이다.

  • 애플리케이션이  어노테이션 기반이라면 @AspectJ 어노테이션을 사용 한다. @AspectJ 어노테이션을 사용하는 경우 모듈 안에 어스펙트 관련 정보를 캡슐화 할 수 있기 때문에 유지보수가 용이하다.

  • aop 네임스페이스와 @AspectJ 어노테이션의 차이

  • 포인트컷 구문이 조금 다르다. (aop 네임스페이스는 “and”, @AspectJ 어노테이션에서는 “&&”)

  • aop 네임스페이스에서는 ‘싱글톤’ 방식의 애스펙트 인스턴스화 모델만 지원

  • @AspectJ 어노테이션 방식에서는 두 개의 포인트컷 정의 (helloExec(intValue) && inMyDependency()) 를 사전충고, 주변 충고에서 조합할 수 있지만 aop 네임스페이스에서는 조건을 조합한 포인트 컷을 새로 생성해야 한다.

 

  • @AspectJ or XML for Spring AOP?

 

  • AspectJ Annotation 방식

 

@Pointcut(execution(* get*()))

public void a() {}

 

@Pointcut(execution(org.xyz.Account+ *(..))

public void b() {}

 

@Pointcut(a() && b())

public void complex() {}

 

  • aop NameSpace를 이용한 XML 방식()

위에서 정의한 complext Pointcut은 정의가 불가능 하다.

 

<aop:pointcut id=“a" expression="execution(* get*())"/>

<aop:pointcut id=“b" expression="execution(org.xyz.Account+ *(..))"/>

 

#스프링AOP, #포인트컷, #충고, #스프링어드바이스, #스프링교육, #스프링강의, #스프링AOP

(C#교육동영상)C# ADO.NET 실습 ODP.NET/ODAC 설치 오라클 함수 호출 실습, C#학원, WPF학원, 닷넷학원, 자바학원

  (C#교육동영상)C# ADO.NET 실습  ODP.NET/ODAC 설치  오라클 함수 호출 실습, C#학원, WPF학원, 닷넷학원, 자바학원 https://www.youtube.com/watch?v=qIPU85yAlzc&list=PLxU-i...