스프링 AOP
AOP(Aspect Oriented Programming)는 로깅과 같은 기본적인 기능에서부터 트랜잭션이나 보안과 같은 기능에 이르기까지 어플리케이션 전반에 걸쳐 적용되는 공통기능을 핵심 기능과 분리해서 모듈로 구현한 뒤 핵심 기능이 수행될 때 필요로 하는 곳에 공통기능이 적용되게 하는 기능이다. 공통의 관심사항을 적용해서 발생하는 의존 관계의 복잡성과 코드 중복을 해소해 주는 프로그래밍 기법이다.
쉬운설명 :
A,B,C 프로그램은 각자 공통 기능을 내장하고있다. 그치만 가지고있던 공통기능을 빼서 공통 기능을 따로 만든다.
그 후 A프로그램이 실행되면 공통기능을 사용하도록 설정하는 방식. 마찬가지로 B,C가 실행될 때 공통기능을 사용하도록 설정하는 방식이다.(설정파일과 어노테이션을 이용하여 설정한다.)
공통기능은 핵심기능을 기준으로 전(before)에 실행하는 기능과 후(after)에 실행하는 기능으로 나뉜다.
[공통기능] - [핵심기능] - [공통기능]
kr.spring.product == Product
공통기능을 가지고있는 product이다.
package kr.spring.product;
public class Product {
//핵심 기능 수행
public String launch() {
System.out.println("launch() 메서드 출력");
return "[상품 출시]";
}
}
kr.spring.ch20.pojo => MyFirstAdvice
package kr.spring.ch20;
public class MyFirstAdvice {
/*
* 구현 가능한 Advice(언제 공통 기능을 핵심 기능에 적용할 지를 정의) 종류
* 종류 설명
* Before Advice 대상 객체의 메서드 호출 전에 공통 기능을 실행 (공통기능은 핵심기능을 기준으로 전(before)에 실행하는 기능과 후(after)에 실행하는 기능으로 나뉜다.)
* After Returning Advice 대상 객체의 메서드가 예외없이 실행한 이후에 공통 기능을 실행
* After Throwing Advice 대상 객체의 메서드를 실행하는 도중 예외가 발생한 경우에 공통 기능을 실행
* After Advice 대상 객체의 메서드를 실행하는 도중 예외가 발생했는지의 여부와 상관없는 메서드 (예외가 발생하든 안하든 실행하는 메서드)
* 실행 후 공통 기능을 싱행(try~catch~finally중 finally 블럭과 비슷)
* Around Advice 대상 객체의 메서드 실행 전, 후 또는 예외 발생 시점에 공통 기능을 실행
*/
public void before() {
//메서드 시작 직전에 동작하는 어드바이스
System.out.println("Hello Before! **메서드가 호출되기전에 나온다!");
}
}
applicationContextAOP.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--
AspectJ의 Pointcut 표현식
execution(Integer kr.spring.ch01..WriteArticleService.write(..))
:리턴 타입이 Integer인 WriteArticleService의 파라미터가 0개 이상인 write()메서드
execution(public void set*(..))
:리턴 타입이 void이고 메서드의 이름이 set으로 시작하고, 파라미터가 0개 이상인 메서드를 호출
execution(* kr.spring.ch01.*.*())
:kr.spring.ch01 패키지의 파라미터가 없는 모든 메서드 호출
execution(* kr.spring.ch01..*.*())
:kr.spring.ch01 패키지 및 하위 패키치에 있는 파라미터가 0개 이상인 메서드 호출
execution(* get*(*))
:이름이 get으로 시작하고 1개의 파라미터를 갖는 메서드 호출
execution(* get*(*,*))
:이름이 get으로 시작하고 2개의 파라미터를 갖는 메서드 호출
execution(* read*(Integer,..))
:메서드 이름이 read로 시작하고, 첫 번째 파라미터 타입이 Integer이며, 1개 이상의 파라미터를 갖는 메서드 호출
-->
<!-- 공통기능을 갖고 있는 클래스 빈 객체 설정 -->
<bean id="myFirstAdvice" class="kr.spring.ch20.pojo.MyFirstAdvice"/>
<!-- 핵심 기능을 구현한 클래스 빈 객체 설정 -->
<bean id="product" class="kr.spring.product.Product"/>
<!--==============================================================================================================-->
<!-- AOP 설정 -->
<aop:config>
<!-- 공통 기능을 갖고 있는 클래스 지정 -->
<aop:aspect id="aspect" ref="myFirstAdvice">
<!-- 핵심기능을 실행할 때 어느 시점에 공통 기능을 적용할지 지정 -->
<!-- 핵심 기능 지정 -->
<aop:pointcut expression="execution(public String launch())" id="publicMethod"/>
<!-- 공통 기능 지정 --> <!-- before이기 떄문에 먼저 실행된다. -->
<aop:before method="before" pointcut-ref="publicMethod"/> <!-- 위에 id와 이름을 같게함. before가 실행된 후 launch가 실행 -->
</aop:aspect>
</aop:config>
</beans>
SpringMain
package kr.spring.ch20.pojo;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import kr.spring.product.Product;
public class SpringMain {
public static void main(String[] args) {
//스프링 컨테이너 생성
AbstractApplicationContext context = new ClassPathXmlApplicationContext("applicationContextAOP.xml");
//핵심기능을 수행하는 메서드 호출
Product p = (Product)context.getBean("product");
p.launch();
context.close();
}
}
Hello Before! **메서드가 호출되기전에 나온다!
launch() 메서드 출력
MyFirstAdvice
package kr.spring.ch20.pojo;
public class MyFirstAdvice {
/*
* 구현 가능한 Advice(언제 공통 기능을 핵심 기능에 적용할 지를 정의) 종류
* 종류 설명
* Before Advice 대상 객체의 메서드 호출 전에 공통 기능을 실행 (공통기능은 핵심기능을 기준으로 전(before)에 실행하는 기능과 후(after)에 실행하는 기능으로 나뉜다.)
* After Returning Advice 대상 객체의 메서드가 예외없이 실행한 이후에 공통 기능을 실행
* After Throwing Advice 대상 객체의 메서드를 실행하는 도중 예외가 발생한 경우에 공통 기능을 실행
* After Advice 대상 객체의 메서드를 실행하는 도중 예외가 발생했는지의 여부와 상관없는 메서드 (예외가 발생하든 안하든 실행하는 메서드)
* 실행 후 공통 기능을 싱행(try~catch~finally중 finally 블럭과 비슷)
* Around Advice 대상 객체의 메서드 실행 전, 후 또는 예외 발생 시점에 공통 기능을 실행
*/
public void before() {
//메서드 시작 직전에 동작하는 어드바이스
System.out.println("Hello Before! **메서드가 호출되기전에 나온다!");
}
public void afterReturning(String msg) {
//메서드 호출이 예외를 내보내지 않고 종료했을 때 동작하는 어드바이스
System.out.println("Hello AfterReturning! **메서드가 호출한 후에 나온다! 전달된 객체:"+ msg);
}
}
applicationContextAOP.xml 내용수정
<!-- AOP 설정 -->
<aop:config>
<!-- 공통 기능을 갖고 있는 클래스 지정 -->
<aop:aspect id="aspect" ref="myFirstAdvice">
<!-- 핵심기능을 실행할 때 어느 시점에 공통 기능을 적용할지 지정 -->
<!-- 핵심 기능 지정 -->
<aop:pointcut expression="execution(public String launch())" id="publicMethod"/>
<!-- 공통 기능 지정 --> <!-- before이기 떄문에 먼저 실행된다. -->
<!-- <aop:before method="before" pointcut-ref="publicMethod"/> --> <!-- 위에 id와 이름을 같게함. before가 실행된 후 launch가 실행 -->
<aop:after-returning method="afterReturning" pointcut-ref="publicMethod" returning="msg"/>
</aop:aspect>
</aop:config>
springMain 실행시
launch() 메서드 출력
Hello AfterReturning! **메서드가 호출한 후에 나온다! 전달된 객체:[상품 출시]
Product 수정
package kr.spring.product;
public class Product {
//핵심 기능 수행
public String launch() {
System.out.println("launch() 메서드 출력");
//에외 발생시 호출되는 공통 기능을 테스트하기 위해
System.out.println(20/0);
return "[상품 출시]";
}
}
Product 를 이렇게 수정하고springMain을 실행하면
launch() 메서드 출력
After Throwing Advice 방법
public void afterThrowing(Exception ex) {
//메서드 호출이 예외를 던졌을 때 동작하는 어드바이스
System.out.println("Hello AfterThrowing! **예외가 생기면 나온다! 예외 :"+ex);
}
<aop:after-throwing method="afterThrowing" pointcut-ref="publicMethod" throwing="ex"/>
launch() 메서드 출력
Hello AfterThrowing! **예외가 생기면 나온다! 예외 :java.lang.ArithmeticException: / by zero
After Advice
public void after() {
//메서드 종료 후에 동작하는 어드바이스(예외가 발생해도 실행됨)
System.out.println("Hello After! **메서드가 호출된 후에 나온다!");
}
<aop:after method="after" pointcut-ref="publicMethod"/>
launch() 메서드 출력
Hello After! **메서드가 호출된 후에 나온다!
에러내용
product의 예외를 주석처리하고 다시 실행을 하면 에러가 발생하지 않는다.
package kr.spring.product;
public class Product {
//핵심 기능 수행
public String launch() {
System.out.println("launch() 메서드 출력");
//에외 발생시 호출되는 공통 기능을 테스트하기 위해
/* System.out.println(20/0); */
return "[상품 출시]";
}
}
Around Advice
public String around(ProceedingJoinPoint joinPoint)throws Throwable { //ProceedingJoinPoint는 핵심기능을 가지고있기 때문에 핵심기능을 가지고 있는 메서드를 호출한다.
//메서드 호출 전후에 동작하는 어드바이스
System.out.println("Hello Around before! **메서드가 호출되기 전에 나온다.");
String s = null;
//try~catch~finally 구조로 명시해야 예외가 발생해도 메서드 실행 후 공통 기능을 수행
try {
//핵심 기능이 수행된 후 데이터 반환 s에 product에 있는 "[상품 출시]"가 담긴다.
s = (String)joinPoint.proceed();//핵심 기능을 수행하는 메서드 실행
}catch(Exception e) {
e.printStackTrace();
}finally {
System.out.println("Hello Around after! **메서드가 호출된 후에 나온다! 반환된 객체:" + s);
}
return s;
}
<aop:around method="around" pointcut-ref="publicMethod"></aop:around>
Hello Around before! **메서드가 호출되기 전에 나온다.
launch() 메서드 출력
Hello Around after! **메서드가 호출된 후에 나온다! 반환된 객체:[상품 출시]
kr.spring.ch21.annot => MyFirstAspect
package kr.spring.ch21.annot;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class MyFirstAspect {
/*
* 구현 가능한 Advice(언제 공통 기능을 핵심 기능에 적용할 지를 정의) 종류
* 종류 설명
* Before Advice 대상 객체의 메서드 호출 전에 공통 기능을 실행 (공통기능은 핵심기능을 기준으로 전(before)에 실행하는 기능과 후(after)에 실행하는 기능으로 나뉜다.)
* @Before
*
* After Returning Advice 대상 객체의 메서드가 예외없이 실행한 이후에 공통 기능을 실행
* @AfterReturning
*
* After Throwing Advice 대상 객체의 메서드를 실행하는 도중 예외가 발생한 경우에 공통 기능을 실행
* @AfterThrowing
*
* After Advice 대상 객체의 메서드를 실행하는 도중 예외가 발생했는지의 여부와 상관없는 메서드 (예외가 발생하든 안하든 실행하는 메서드)
* @After
* 실행 후 공통 기능을 싱행(try~catch~finally중 finally 블럭과 비슷)
* Around Advice 대상 객체의 메서드 실행 전, 후 또는 예외 발생 시점에 공통 기능을 실행
* @Around
*/
@Pointcut("execution(public String launch())")
//(주의)메서드명이 getPointcut으로 정해져있음.
public void getPointcut() {}//어노테이션을 동작시키기 위해서 형식적으로 만드는 것
@Before("getPointcut()") //메서드명을 적어줘야지 위에 메서드와 연결된다.
public void before() {
//메서드 시작 직전에 동작하는 어드바이스
System.out.println("Hello Before! **메서드가 호출되기전에 나온다!");
}
public void afterReturning(String msg) {
//메서드 호출이 예외를 내보내지 않고 종료했을 때 동작하는 어드바이스
System.out.println("Hello AfterReturning! **메서드가 호출한 후에 나온다! 전달된 객체:"+ msg);
}
public void afterThrowing(Exception ex) {
//메서드 호출이 예외를 던졌을 때 동작하는 어드바이스
System.out.println("Hello AfterThrowing! **예외가 생기면 나온다! 예외 :"+ex);
}
public void after() {
//메서드 종료 후에 동작하는 어드바이스(예외가 발생해도 실행됨)
System.out.println("Hello After! **메서드가 호출된 후에 나온다!");
}
public String around(ProceedingJoinPoint joinPoint)throws Throwable { //ProceedingJoinPoint는 핵심기능을 가지고있기 때문에 핵심기능을 가지고 있는 메서드를 호출한다.
//메서드 호출 전후에 동작하는 어드바이스
System.out.println("Hello Around before! **메서드가 호출되기 전에 나온다.");
String s = null;
//try~catch~finally 구조로 명시해야 예외가 발생해도 메서드 실행 후 공통 기능을 수행
try {
//핵심 기능이 수행된 후 데이터 반환 s에 product에 있는 "[상품 출시]"가 담긴다.
s = (String)joinPoint.proceed();//핵심 기능을 수행하는 메서드 실행
}catch(Exception e) {
e.printStackTrace();
}finally {
System.out.println("Hello Around after! **메서드가 호출된 후에 나온다! 반환된 객체:" + s);
}
return s;
}
}
applicationContextAOP2.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 어노테이션 방식으로 AOP를 구현할 때 명시 -->
<aop:aspectj-autoproxy/>
<!-- 공통 기능이 구현된 클래스 빈 객체 설정 -->
<bean id="myFirstAdvice" class="kr.spring.ch21.annot.MyFirstAspect"/>
<!-- 핵심 기능이 구현된 클래스 빈 객체 설정 -->
<bean id="product" class="kr.spring.product.Product"/>
</beans>
SpringMain
package kr.spring.ch21.annot;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import kr.spring.product.Product;
public class SpringMain {
public static void main(String[] args) {
//스프링 컨테이너 생성
AbstractApplicationContext context = new ClassPathXmlApplicationContext("applicationContextAOP2.xml");
//핵심 기능이 구현된 메서드 호출
Product p = (Product)context.getBean("product");
p.launch();
context.close();
}
}
Hello Before! **메서드가 호출되기전에 나온다!
launch() 메서드 출력
@AfterReturning(value="getPointcut()",returning="msg")
public void afterReturning(String msg) {
//메서드 호출이 예외를 내보내지 않고 종료했을 때 동작하는 어드바이스
System.out.println("Hello AfterReturning! **메서드가 호출한 후에 나온다! 전달된 객체:"+ msg);
}
launch() 메서드 출력
Hello AfterReturning! **메서드가 호출한 후에 나온다! 전달된 객체:[상품 출시]
@AfterThrowing(value="getPointcut()",throwing="ex")
public void afterThrowing(Exception ex) {
//메서드 호출이 예외를 던졌을 때 동작하는 어드바이스
System.out.println("Hello AfterThrowing! **예외가 생기면 나온다! 예외 :"+ex);
}
product주석을 푼다
package kr.spring.product;
public class Product {
//핵심 기능 수행
public String launch() {
System.out.println("launch() 메서드 출력");
//에외 발생시 호출되는 공통 기능을 테스트하기 위해
System.out.println(20/0);
return "[상품 출시]";
}
}
launch() 메서드 출력
Hello AfterThrowing! **예외가 생기면 나온다! 예외 :java.lang.ArithmeticException: / by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero
at kr.spring.product.Product.launch(Product.java:8)
at kr.spring.product.Product$$FastClassBySpringCGLIB$$b31c6156.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
at kr.spring.product.Product$$EnhancerBySpringCGLIB$$1005cef6.launch(<generated>)
at kr.spring.ch21.annot.SpringMain.main(SpringMain.java:15)
@After("getPointcut()")
public void after() {
//메서드 종료 후에 동작하는 어드바이스(예외가 발생해도 실행됨)
System.out.println("Hello After! **메서드가 호출된 후에 나온다!");
}
launch() 메서드 출력
Hello After! **메서드가 호출된 후에 나온다!
@Around("getPointcut()")
public String around(ProceedingJoinPoint joinPoint)throws Throwable { //ProceedingJoinPoint는 핵심기능을 가지고있기 때문에 핵심기능을 가지고 있는 메서드를 호출한다.
//메서드 호출 전후에 동작하는 어드바이스
System.out.println("Hello Around before! **메서드가 호출되기 전에 나온다.");
String s = null;
//try~catch~finally 구조로 명시해야 예외가 발생해도 메서드 실행 후 공통 기능을 수행
try {
//핵심 기능이 수행된 후 데이터 반환 s에 product에 있는 "[상품 출시]"가 담긴다.
s = (String)joinPoint.proceed();//핵심 기능을 수행하는 메서드 실행
}catch(Exception e) {
e.printStackTrace();
}finally {
System.out.println("Hello Around after! **메서드가 호출된 후에 나온다! 반환된 객체:" + s);
}
return s;
}
product주석을 다시 걸어준다
package kr.spring.product;
public class Product {
//핵심 기능 수행
public String launch() {
System.out.println("launch() 메서드 출력");
//에외 발생시 호출되는 공통 기능을 테스트하기 위해
/* System.out.println(20/0); */
return "[상품 출시]";
}
}
Hello Around before! **메서드가 호출되기 전에 나온다.
launch() 메서드 출력
Hello Around after! **메서드가 호출된 후에 나온다! 반환된 객체:[상품 출시]
'쌍용교육(JAVA) > Spring' 카테고리의 다른 글
쌍용교육 -JSP수업 80일차 - ch08_SpringMVC(2) (0) | 2024.06.18 |
---|---|
쌍용교육 -JSP수업 79일차 - ch08_SpringMVC(1) (0) | 2024.06.17 |
쌍용교육 -JSP수업 78일차 - ch07_SpringDI(3) (0) | 2024.06.14 |
쌍용교육 -JSP수업 77일차 - ch07_SpringDI(2) (0) | 2024.06.13 |
쌍용교육 -JSP수업 77일차 - ch07_SpringDI(1) (0) | 2024.06.12 |