이번에는 component-scan을 이용해서 AOP를 적용해보도록 하겠습니다.
바로 패키지 만들러 가볼까요 ~
Student.java (interface)
package com.test06;
public interface Student {
void classWork();
}
StudentA.java
package com.test06;
public class StudentA implements Student {
@Override
public void classWork() {
System.out.println("컴퓨터를 켜서 뉴스를 본다.");
}
}
StudentB.java
package com.test06;
public class StudentB implements Student {
@Override
public void classWork() {
System.out.println("컴퓨터를 켜서 주식을 본다.");
}
}
MyAspect.java
package com.test06;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class MyAspect {
@Pointcut("execution(public * *(..))")
public void myClass() {
}
@Before("myClass()")
public void before() {
System.out.println("출석한다.");
}
@After("myClass()")
public void after() {
System.out.println("집에 간다.");
}
}
MTest.java
package com.test06;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MTest {
public static void main(String[] args) {
ApplicationContext factory = new ClassPathXmlApplicationContext("com/test06/applicationContext.xml");
Student a = factory.getBean("studentA", Student.class);
Student b = (Student) factory.getBean("studentB");
System.out.println("A입장");
a.classWork();
System.out.println("-----------------");
System.out.println("B입장");
b.classWork();
}
}
applicationContext.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"
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.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<aop:aspectj-autoproxy />
<context:component-scan base-package="com.test06" />
</beans>
코드 실행결과
지금 위에 적어놓은 코드 그대로 실행하게 되면 에러가 발생합니다. 왜 에러가 발생할까요???
지금 MTest에서 불러올 객체가 생성되지 않아서 그렇습니다.
applicationContext에서 <autoproxy>태그를 이용하여 @Aspect에 있는 코드들을 aspect로 등록한 후
<component-scan>을 통해서 이를 각각 객체화 시켜주어야합니다.
** component-scan에 대한 설명은 아래를 참고해주세요
https://darmk.tistory.com/entry/Spring-11-Annotation-Component?category=945098
따라서 위의 출력 결과대로 출력하려면 StudentA, StudentB, MyAspect에 각각 @Component를 적어주면 이 어노테이션이 알아서 객체화시켜줍니다.
그렇다면 여기서는 어떤 게 joinpoint인지, pointcut인지 감이 오시나요?
MTest에서 a.classWork() 이런 부분이 타겟을 호출하는 시점이므로 Joinpoint
이러한 Joinpoint에서 어떤 Joinpoint에 프록시가 만들어져서 요청, 응답을 대신해줄 건지를 나타내 주는 부분이 MyAspect에서 myClass부분이므로 여기가 Pointcut ...
어차피 코드의 흐름은 위에서 아래로 흘러가니까 이렇게 하나하나 찾아보시면 됩니다.
이 부분은 좀 응용한 버전인데 이런 식으로도 사용하는구나 보기만 하셔도 됩니다.
앞에 부분이 이해가 잘돼서 응용이 필요하다 하시는 분은 따라 해 보셔도 좋을 것 같아요 ㅎㅎ
패키지부터 만들겠습니다.
MessageBean.java (interface)
package com.test07;
public interface MessageBean {
void sayHello();
}
MessageBeanImpl.java
package com.test07;
public class MessageBeanImpl implements MessageBean {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public void sayHello() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello, " + name);
}
}
LoggingAdvice.java (MethodInterceptor 잘 보고 상속받아주세요)
package com.test07;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.util.StopWatch;
public class LoggingAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
String method = invocation.getMethod().getName();
StopWatch timer = new StopWatch();
timer.start(method);
System.out.println("[Log] method : " + method + " is calling");
Object target = invocation.proceed();
timer.stop();
System.out.println("[LOG] method : " + method + " was called");
System.out.println("[LOG] method : " + timer.getTotalTimeSeconds() + " sec");
return target;
}
}
MTest.java
package com.test07;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MTest {
public static void main(String[] args) {
ApplicationContext factory = new ClassPathXmlApplicationContext("com/test07/applicationContext.xml");
MessageBean bean = factory.getBean("proxy", MessageBean.class);
bean.sayHello();
}
}
applicationContext.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.xsd">
<!-- target -->
<bean id="targetBean" class="com.test07.MessageBeanImpl">
<property name="name" value="Pengsoo" />
</bean>
<!-- advice -->
<bean id="loggingAdvice" class="com.test07.LoggingAdvice" />
<!-- advisor(aspect) -->
<bean id="myAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="loggingAdvice" />
<property name="pointcut">
<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern">
<value>.*sayHello.*</value>
</property>
</bean>
</property>
</bean>
<!-- proxy -->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="targetBean" />
<property name="interceptorNames">
<list>
<value>myAdvisor</value>
</list>
</property>
</bean>
</beans>
코드 실행결과
[Log] method : sayHello is calling 가 출력되고 2초 후에 나머지 부분이 출력되어야 합니다.
여기는 applicationContext부분에 어떤 코드들이 어떤 것을 생성하는지 적어뒀습니다.
target부분은 실제로 실행될 코드 부분을 나타내므로 CC
advice부분은 실제로 실행될 코드 부분에 붙어주는 코드이므로 CCC
proxy는 target과 target사이에서 연결해줘야 하는 코드입니다.(인터셉터할 수 있게 만들어줌)
Thread.sleep(2000); 는 2000 millisecond(2초) 동안 일시정지입니다. 이 코드 때문에 결과에서 2초의 딜레이가 발생합니다.
.*sayHello.* 부분은 정규표현식 형태입니다. (나중에 따로 공부하는 것을 추천드립니다.)
'Java > Spring' 카테고리의 다른 글
[Spring] 13 -* Spring MVC ( Task tag를 이용한 TODO주석 표시) (0) | 2021.07.13 |
---|---|
[Spring] 13. Spring MVC (4) | 2021.07.08 |
[Spring] 12 -2 AOP 적용하기 ( Annotation - Before, After, AfterReturning, AfterThrowing) (0) | 2021.07.05 |
[Spring] 12 -1 AOP 적용하기 ( Namespace - aop) (0) | 2021.07.04 |
[Spring] 12. AOP - 기본 개념 ( CC, CCC, Joinpoint, ...) (0) | 2021.07.03 |