@Aspect / @Component / @Before / @After / Aspectj Expression
Java project를 만들고 옆의 사진 처럼 패키지와 클래스를 만들었다.
[PenAspect.java] Aspect 메소드가 있는 클래스
[Mainclass] main() 메소드가 있는 클래스
[init.xml] Spring으로 객체를 생성해서 관리할 클래스들을 정의하는 문서
[WritingUtil] main()메소드 수행시 실행될 메소드가 있는 클래스
[WritingUtil]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package test.mypac;
import org.springframework.stereotype.Component;
@Component
public class WritingUtil {
public void write1() {
System.out.println("편지를 써요");
}
public void write2() {
System.out.println("일기를 써요");
}
public void write3() {
System.out.println("소설을 써요");
}
}
|
cs |
메인 메소드가 실행될 때 사용될 메소드들을 모아 놓은 클래스 이다.
5행 : 메소드를 사용하기 위해서는 WritingUtil 의 객체가 필요하다.
Component scan시 bean으로 만들어 주기 위 해 @Component 어노테이션을 작성해주었다.
[init.xml]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!-- 컴포넌트 스캔을 해서 bean 으로 만들 객체는 만들어 준다. -->
<!-- 패키지 하위까지 모두 스캔하고, bean을 만든다. -->
<context:component-scan base-package="test.mypac" />
<context:component-scan base-package="test.aspect" />
<aop:aspectj-autoproxy />
</beans>
|
cs |
12, 13 행 : base-package 속성의 값으로 명시한 패키지에 있는 클래스들을 스캔해서 bean으로 만들으라고 명시함.
어노테이션(@)으로 표시 해준 클래스들은 bean으로 만들라고 명시.
14 행 : <aop:aspectj-autoproxy /> 선언
AspectJ를 위한 태그이며, 먼저 Spring AOP 때 이용한 ProxyFactoryBean에 해당하는 것을 자동으로 생성하는 태그이다. 이를 기술하게 되면 ProxyFactoryBean으로 준비된 기능이 자동으로 포함된다.
[PenAspect Class]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package test.aspect;
import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component;
@Aspect @Component public class PenAspect {
@Before("execution(void write*())") public void prepare() { System.out.println("Pen을 준비해요!"); }
@After("execution(void write*())") public void after() { System.out.println("Pen을 마무리해요!"); }
} |
cs |
8행 : Aspect 역할을 할 수 있도록 정의해준다.
9행 : 객체를 생성해야 해당 메소드들을 사용할 수 있으므로 init.xml 문서에서
Component scan시 bean이 될 수 있도록 명시해준다.
12행 : @Before [Mainclass] 의 main() 메소드가 수행되기 전에 실행될 메소드라는 것을 명시해준다.
("execution(void write*())")의 의미는
(1) spring 이 관리 하는 객체의 메소드 중에서 리턴 type 은 void - void
(2) 메소드 명은 write로 시작 - write*
(3) 전달되는 인자가 없는 - ( )
이다.
17행 : @After [Mainclass] 의 main() 메소드가 수행된 후에 실행될 메소드라는 것을 명시해준다.
[Mainclass]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package test.main;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
import test.mypac.WritingUtil;
public class Mainclass { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("test/main/init.xml"); WritingUtil util2=context.getBean(WritingUtil.class); util2.write1(); util2.write2(); util2.write3(); } } |
cs |
10행 : test/main/init.xml 문서를 읽으면서 Spring이 bean을 생성하도록 작성
11행 : spring bean context로 부터 'WritingUtil' type을 찾아서 해당 객체의 참조값을 리턴해준다
12-14행 : 객체의 참조값을 사용해서 메소드 사용
Run 해서 console을 찍어 보면 아래와 같이 결과가 표시 된다.
Run을 하면 [WritingUtil]와 [PenAspect]를 합쳐서 aop가 적용된 새로운 클래스를 만든다.
[Mainclass]에서는 새로만들어진 클래스의 메소드를 사용한다.
따라서 왼쪽과 같이 콘솔에 찍히게 되는 것이다.
@Around / Aspectj Expression
위의 예제를 위해 만들었던 java project 클래스를 추가하였다.
[MessengerAspect] Aspect 메소드가 있는 클래스
[Mainclass2] main() 메소드가 있는 클래스
[Mainclass3] main() 메소드가 있는 클래스
[init.xml] Spring으로 객체를 생성해서 관리할 클래스들을 정의하는 문서
[Messenger] main()메소드 수행시 실행될 메소드가 있는 클래스
[Messenger]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package test.mypac;
import org.springframework.stereotype.Component;
@Component public class Messenger { public void sendGreeting(String msg) { System.out.println("오늘의 인사 : "+msg); }
public String getMessage() { return "힘을 내자!"; } } |
cs |
메인 메소드가 실행될 때 사용될 메소드들을 모아 놓은 클래스 이다.
5행 : 메소드를 사용하기 위해서는 WritingUtil 의 객체가 필요하다.
Component scan시 bean으로 만들어 주기 위 해 @Component 어노테이션을 작성해주었다.
6-9행 : String type의 인자가 오면 그 문자열을 콘솔에 출력하는 메소드
11-13행 : 메소드를 호출하면 String type의 문자열을 리턴하는 메소드
[init.xml]
1 2 3 4 5 6 7 8 9 10 11 12 |
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<context:component-scan base-package="test.aspect" /> <aop:aspectj-autoproxy /> </beans> |
cs |
10 행 : base-package 속성의 값으로 명시한 패키지에 있는 클래스들을 스캔해서 bean으로 만들으라고 명시함.
어노테이션(@)으로 표시 해준 클래스들은 bean으로 만들라고 명시.
11 행 : <aop:aspectj-autoproxy /> 선언
AspectJ를 위한 태그이며, 먼저 Spring AOP 때 이용한 ProxyFactoryBean에 해당하는 것을 자동으로 생성하는 태그이다. 이를 기술하게 되면 ProxyFactoryBean으로 준비된 기능이 자동으로 포함된다.
[MessengerAspect]
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 33 34 35 36 37 38 39 40 41 42 43 44 45 |
package test.aspect;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component;
@Aspect @Component public class MessengerAspect {
@Around("execution(* send*(..))") public void around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("---수행이전---");
Object[] args=joinPoint.getArgs(); //반복문 돌면서 찾고 싶은 type을 찾는다. for(Object tmp:args) { if(tmp instanceof String) { String msg=(String)tmp; System.out.println("aop 에서 읽어낸 내용 : "+msg); // 특정 메서드는 호출하지 않기 if(msg.contains("바보")) { System.out.println("바보라고 하기 없기!"); return; } } }
Object obj=joinPoint.proceed();
System.out.println("---수행직후---"); }
@Around("execution(String getMessage())") public Object around2(ProceedingJoinPoint joinPoint) throws Throwable { //aop가 적용된 메소드를 수행하고 리턴되는 값을 얻어낸 Object obj=joinPoint.proceed();
obj="공부 안 해!";
return obj; } } |
cs |
8행 : Aspect 역할을 할 수 있도록 정의해준다.
9행 : 객체를 생성해야 해당 메소드들을 사용할 수 있으므로 init.xml 문서에서
Component scan시 bean이 될 수 있도록 명시해준다.
12행 : @Around 는 메서드의 실행 전/후에 공통로직을 적용하고 싶을 때 사용한다.
("execution(* send*(..))")의 의미는
(1) spring 이 관리 하는 객체의 메소드 중에서 리턴 type 상관 없이 - *
(2) 메소드 명은 send로 시작 - send*
(3) 전달되는 메소드 인자 상관 없음 - (..)
이다.
14행 : 매소드가 수행되기 전에 콘솔에 출력될 내용
16행 : ProceedingJoinPoint 객체를 사용하여 getArgs()메서드를 호출하면
("execution(* send*(..))") 를 충족하는 메소드 호출시 전달된 인자들을 모두 Object type으로 배열에 담는다.
16-28행 : 반복문을 돌면서 배열을 살펴본다.
이때, instanceof 예약어를 사용해서 배열에 있는 data들 중 원래 type이 String인를 data 찾고, casting 해준다.
만약, 전달된 인자들 중 "바보"라는 문자열이 있으면 메소드 실행을 종료한다.
30행 : proceed()를 사용해서 aop가 적용된 메소드 수행하고 리턴되는 값 받아온다.(void 면 null 이다.)
32행 : 매소드가 수행된 후에 콘솔에 출력될 내용
41행 : proceed()를 사용해서 리턴 되는 결과값을 수정한다.
[Mainclass2]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package test.main;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
import test.mypac.Messenger;
public class MainClass2 { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("test/main/init.xml"); Messenger m=context.getBean(Messenger.class); m.sendGreeting("좋은 아침!"); m.sendGreeting("바보야!좋은 아침!"); } }
|
cs |
10행 : test/main/init.xml 문서를 읽으면서 Spring이 bean을 생성하도록 작성
11행 : spring bean container로 부터 'Messenger' type을 찾아서 해당 객체의 참조값을 리턴해준다
12-13행 : 객체의 참조값을 사용해서 메소드 사용
Run 해서 console을 찍어 보면 아래와 같이 결과가 표시 된다.
Run을 하면 [Messenger]와 [MessengerAspect]를 합쳐서 aop가 적용된 새로운 클래스를 만든다.
[Mainclass2]에서는 새로만들어진 클래스의 메소드를 사용한다.
sendGreeting("좋은 아침!");
인자로 "좋은 아침!" 이라는 문자열을 보낸경우,
Object[]에 있는 data들 중 원래 type이 String인를 data 찾아서 console에 출력하고,
proceed( ) 메소드를 사용하여 aop가 적용된 메소드 수행하고 리턴되는 값 받아온다.
sendGreeting("바보야!좋은 아침!");
인자로 "바보야!좋은 아침!" 이라는 문자열을 보낸경우,
Object[]에 있는 data들 중 원래 type이 String인를 data 찾아서 console에 출력하였다.
문자열 중에 "바보"가 포함되어 있었으므로 "바보라고 하기 없기!" 문자열을 출력한 뒤 메소드가 종료되었다.
[Mainclass3]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package test.main;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
import test.mypac.Messenger;
public class MainClass3 { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("test/main/init.xml"); Messenger m=context.getBean(Messenger.class);
String result=m.getMessage();
System.out.println("result : "+result); } } |
cs |
10행 : test/main/init.xml 문서를 읽으면서 Spring이 bean을 생성하도록 작성
11행 : spring bean container로 부터 'Messenger' type을 찾아서 해당 객체의 참조값을 리턴해준다
13행 : 객체의 참조값을 사용해서 메소드 사용
15행 : 수행한 결과를 콘솔에 출력.
Run 해서 console을 찍어 보면 아래와 같이 결과가 표시 된다.
Run을 하면 [Messenger]와 [MessengerAspect]를 합쳐서 aop가 적용된 새로운 클래스를 만든다.
[Mainclass3]에서는 새로만들어진 클래스의 메소드를 사용한다.
콘솔창을 보면 getMessage()메소드에서 리턴되는 "힘을 내자!" 대신 "공부 안 해! 라는 문자열이 찍힌것을 확인할 수 있다. 그 이유는 [MessengerAspect] 클래스의 41번행 때문이다.
41번행을 삭제하면 위의 결과가 나온다.
'스프링' 카테고리의 다른 글
[Eclips] Eclips 속도 개선 (메모리 용량 수정하기, close project) (0) | 2020.02.05 |
---|---|
Spring Security Core 다운 (0) | 2020.02.04 |
[Spring / AOP] Filter, Interceptor, AOP(스프링의 대표개념) (0) | 2020.01.29 |
[Spring/MVC] SpringMVC 프로젝트 구조 분석 / Front Controller 패턴 / 예시 (0) | 2020.01.28 |
[MVC] Spring + MVC 프로젝트 만들기 / 설정 세팅하기 (0) | 2020.01.28 |