1. AOP(Aspect Oriented Programming : 관점지향프로그래밍)
1) AOP 개념설명
[횡단관심사]
모든 영역에 공통적으로 적용되는 코딩
어플리케이션 여러부분에 영향을 주는 기능.
그림처럼 1XX3을 찾으라고 하면 앞이 1인 15,10의 3번을 모두 검사한다.
그림처럼 횡단으로 검사하는 모습을 볼 수 있다.
[JointPoints]
애플리케이션 실행의 특정 지점을 의미
AOP에서 Aspect(횡단 관심사)를 적용하는 위치를 표시하는데 사용
[Advice]
특정 조인포인트에 적용(실행)할 코드
실행시점에 따라 Before Advice, After Advice 등으로 구현
[Pointcut]
여러 조인포인트의 집합으로 언제 어드바이스를 실행하는 위치 표시
[Aspect]
어드바이스(동작)와 포인트컷(동작을적용하는조건)을 조합해서
횡단 관심사에 대한 코드와 그것을 적용할 지점을 정의한 것
Aspect :무엇을(Advice) 언제(Point) 할지 정의
2) 프로젝트 생성
① SpringAOP 프로젝트 만든다.
New>Spring Legacy Project>Simple Spring Utility Project(자바 콘솔에서 실행)
② Maven Repository에서 lib 다운
링크 : https://mvnrepository.com/
1) Spring AOP
2) AspectJ Runtime
3) AspectJ Weaver
4) CGLib
위의 4가지의 코드를 가져와 pom.xml에 붙여넣자.
-pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <!-- spring-aop --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.framework.version}</version> </dependency> <!-- aspectjrt --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.10</version> </dependency> <!-- aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.10</version> </dependency> <!-- cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.4</version> </dependency> | cs |
③ targetA, targetB 생성 및 작성
-targetA.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package com.exe.aop; import org.springframework.stereotype.Component; @Component("targetA") public class TargetA { public void doSomething1() { //advice System.out.println("TargetA.doSomething1"); } public void doSomething2() { System.out.println("TargetA.doSomething2"); } public void doAnother1() { System.out.println("TargetA.doAnother1"); } public void doAnother2() { System.out.println("TargetA.doAnother2"); } } | cs |
-targetB.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package com.exe.aop; import org.springframework.stereotype.Component; @Component("targetB") public class TargetB { public void doSomething1() { //advice System.out.println("TargetB.doSomething1"); } public void doSomething2() { System.out.println("TargetB.doSomething2"); } public void doAnother1() { System.out.println("TargetB.doAnother1"); } public void doAnother2() { System.out.println("TargetB.doAnother2"); } } | cs |
④ MyBeforeAdvice.java 생성 및 작성
1 2 3 4 5 6 7 8 | package com.exe.aop; public class MyBeforeAdvice { public void beforeMethodCall() { System.out.println("Jointpoint 메소드가 실행되기 전에..."); } } | cs |
⑤ app-context.xml 작성
위의 코드 추가해준다.
app-context.xml에 위의 코드 써준다.
beforeAdvice라는 객체를 생성후 aspect의 매개변수로 넘겨준다.
beforePointcut이라는 이름을 임의로 정해서 포인트컷을 생성해주고
before의 기능을하는 aop:before에 매개변수로 생성한 포인트컷을 넘겨준다.
before는 beforeAdvice라는 객체의 메소드인 beforeMethodCall()이라는 함수를 메소드로 사용한다.
-app-context.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <?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" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <description>Example configuration to get you started.</description> <context:component-scan base-package="com.exe.aop" /> <!-- 여기에 객체 생성 --> <!-- Advice --> <bean id="beforeAdvice" class="com.exe.aop.MyBeforeAdvice"/> <!-- Pointcut --> <!-- execution(public void com..aop.*.*(..)) --> <!-- execution(메소드접근지정자 반환값 패키지.클래스.메소드(매개변수)) --> <aop:config> <aop:aspect ref="beforeAdvice"> <aop:pointcut id="beforePointcut" expression="execution(public void com..aop.*.*(..))"/> <aop:before method="beforeMethodCall" pointcut-ref="beforePointcut"/> </aop:aspect> </aop:config> </beans> | cs |
⑥ AopMain.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package com.exe.aop; import org.springframework.context.support.GenericXmlApplicationContext; public class AopMain { public static void main(String[] args) { GenericXmlApplicationContext context= new GenericXmlApplicationContext("app-context.xml"); TargetA ta=(TargetA)context.getBean("targetA"); ta.doAnother1(); ta.doAnother2(); ta.doSomething1(); ta.doSomething2(); TargetB tb=(TargetB)context.getBean("targetB"); tb.doAnother1(); tb.doAnother2(); tb.doSomething1(); tb.doSomething2(); } } | cs |
-실행화면
⑦ after도 추가해보자
그 전 before는 주석처리하고 after만 실행해보자.
-MyAfterAdvice
1 2 3 4 5 6 7 8 | package com.exe.aop; public class MyAfterAdvice { public void afterMethodCall() { System.out.println("Jointpoint 메소드가 실행된 후에..."); } } | cs |
-app-context.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <!-- Pointcut --> <!-- execution(public void com..aop.*.*(..)) --> <!-- execution(메소드접근지정자 반환값 패키지.클래스.메소드(매개변수)) --> <aop:config> <aop:aspect ref="beforeAdvice"> <aop:pointcut id="beforePointcut" expression="execution(public void com..aop.*.*(..))"/> <aop:before method="beforeMethodCall" pointcut-ref="beforePointcut"/> </aop:aspect> <aop:aspect ref="afterAdvice"> <aop:pointcut id="afterPointcut" expression="execution(public void com..aop.*.*(..))"/> <aop:after method="afterMethodCall" pointcut-ref="afterPointcut"/> </aop:aspect> </aop:config> | cs |
aop:after로 형식에 맞취 써준다.
그리고 메인에서 실행
-실행화면
⑧ around 추가
-MyAroundAdvice.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package com.exe.aop; import org.aspectj.lang.ProceedingJoinPoint; public class MyAroundAdvice { public Object aroundMethodCall(ProceedingJoinPoint jointPoint) { Object result=null; try { System.out.println("JointPoint 메소드 실행전..(Around)"); result=jointPoint.proceed();//메소드 실행 System.out.println("JointPoint 메소드 실행후..(Around)"); } catch (Throwable e) { System.out.println(e.toString()); } return result; } } | cs |
-app-context.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <!-- Advice --> <bean id="aroundAdvice" class="com.exe.aop.MyAroundAdvice"/> <!-- Pointcut --> <!-- execution(public void com..aop.*.*(..)) --> <!-- execution(메소드접근지정자 반환값 패키지.클래스.메소드(매개변수)) --> <aop:config> <aop:aspect ref="aroundAdvice"> <aop:pointcut id="aroundPointcut" expression="execution(public void com..aop.*.*Some*(..))"/> <aop:around method="aroundMethodCall" pointcut-ref="aroundPointcut"/> </aop:aspect> </aop:config> | cs |
메소드의 형식을 *Some*으로 바꿔주었다.
-AopMain.java에서 똑같이 실행하면
-실행화면
*Some* 때문에 메소드의 이름에 Some이 들어가는 메소드에만 실행되는 모습을 볼 수 있다.
⑨ after-returning 추가
-MyAfterReturningAdvice.java
1 2 3 4 5 6 7 8 | package com.exe.aop; public class MyAfterReturningAdvice { public void afterReturningMethodCall() { System.out.println("JointPoint 메소드가 정상 실행한 뒤에.."); } } | cs |
-app-context
1 2 3 4 5 6 7 8 9 | <bean id="afterReturningAdvice" class="com.exe.aop.MyAfterReturningAdvice"/> <aop:config> <aop:aspect ref="afterReturningAdvice"> <aop:pointcut id="afterReturningPointcut" expression="execution(public void com..aop.*.*A*1(..))"/> <aop:after-returning method="afterReturningMethodCall" pointcut-ref="afterReturningPointcut"/> </aop:aspect> </aop:config> | cs |
메소드의 형식을 *A*1으로 바꾸고
-AopMain.java에서 똑같이 실행하면
-실행화면
⑩ after-throwing 추가
: 에러가 있을 때 실행됨
-MyAfterThrowingAdvice.java
1 2 3 4 5 6 7 8 9 | package com.exe.aop; //에러가 났을때 public class MyAfterThrowingAdvice { public void afterThrowingMethodCall() { System.out.println("JointPoint 메소드가 비정상 실행한 뒤에.."); } } | cs |
-app-context.xml
1 2 3 4 5 6 7 8 9 10 11 12 | <bean id="afterThrowingAdvice" class="com.exe.aop.MyAfterThrowingAdvice"/> <!-- Pointcut --> <!-- execution(public void com..aop.*.*(..)) --> <!-- execution(메소드접근지정자 반환값 패키지.클래스.메소드(매개변수)) --> <aop:config> <aop:aspect ref="afterThrowingAdvice"> <aop:pointcut id="afterThrowingPointcut" expression="execution(public void com..aop.*.*(..))"/> <aop:after-throwing method="afterThrowingMethodCall" pointcut-ref="afterThrowingPointcut"/> </aop:aspect> </aop:config> | cs |
-AopMain.java에서 똑같이 실행하면
-실행화면
-모두주석 풀고 패키지.클래스.메소드명 똑같이 맞춰준뒤 실행하면
아래처럼 전체메소드(Advice들)가 실행된다.
2. 전의 AOP코드를 Annotation으로 바꾸기
app-context에 썻던코드들 다 주석처리하고 annotation으로 바꿔준다.
-MyBeforeAdvice.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package com.exe.aop; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class MyBeforeAdvice { @Before("execution(* com..aop.*.*(..))") public void beforeMethodCall() { System.out.println("Jointpoint 메소드가 실행되기 전에..."); } } | cs |
-app-context.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?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" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <description>Example configuration to get you started.</description> <context:component-scan base-package="com.exe.aop" /> <aop:aspectj-autoproxy/> </beans> | cs |
<aop:aspectj-autoproxy/>를 써주면 annotation을 쓸 수 있게 된다.
-나머지 파일도 before처럼 다 annotation써준뒤 메인에서 실행하면
아까 마지막처럼 전체 메소드들이 실행된다.
-실행화면