Apect Orientd Programming
관점 지향 프로그래밍
공통 관심 (Cross-Cutting Concern)
➡ 보안 / 무결성 검사
핵심 관심 (Core Concern)
➡ 실행해야될 메서드
적용 시점(이전, 혹은 이후)
Point = 위치
따라서 Joinpoint는 적용할 위치라는 뜻이 된다.
어디에? 메서드에.
앞에할 것인가? 뒤에할 것인가?
Advice가 어떤 JoinPoint에 사용될 것인지를 지정하는 PointCut 표현식
정규표현식 ➡ 문자열로 조회
참고 블로그 : Spring AOP PointCut 표현식 정리
적용시키는 행위
AOP는 용어가 어렵다...
사유? AspectJ 라는 라이브러리가 학자들이 만든 것이기 때문이라고 한다...
org.springframework.aop.framework.ProxyFactoryBean
ProxyFactoryBean
은 자바 클래스를 인터셉트 해서 전처리 및 후처리를 할때 사용한다.
그런데 한 가지 특이사항이 있었다.
인터셉트 하는 클래스가 인터페이스 구현 받았다면? java.lang.ClassCastException
이 밸생하게 된다.
Exception in thread "main" java.lang.ClassCastException: class jdk.proxy2.$Proxy5 cannot be cast to class com.exam.aop01.WriteAction (jdk.proxy2.$Proxy5 is in module jdk.proxy2 of loader 'app'; com.exam.aop01.WriteAction is in unnamed module of loader 'app')
at com.exam.aop01.App.main(App.java:17)
해당 Exception이 발생한 코드는 다음과 같다.
package com.exam.aop01;
public interface BoardAction {
void execute();
}
package com.exam.aop01;
public class WriteAction implements BoardAction {
private String writer;
public WriteAction() {
// TODO Auto-generated constructor stub
System.out.println("WriteAction() 실행");
}
public void setWriter(String name) {
this.writer = name;
}
@Override
public void execute() {
// TODO Auto-generated method stub
System.out.println("execute() 시작");
System.out.println("execute() 종료");
}
public void execute1() {
// TODO Auto-generated method stub
System.out.println("execute1() 실행");
}
public void execute2() {
// TODO Auto-generated method stub
System.out.println("execute2() 실행");
}
}
package com.exam.aop01.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class BasicAdvice1 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// TODO Auto-generated method stub
System.out.println("전처리 : 공통 관심 1");
Object rtnObj = invocation.proceed();
System.out.println("후처리 : 공통 관심 1");
return null;
}
}
package com.exam.aop01.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class BasicAdvice2 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// TODO Auto-generated method stub
System.out.println("전처리 : 공통 관심 2");
Object rtnObj = invocation.proceed();
System.out.println("후처리 : 공통 관심 2");
return null;
}
}
<?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.3.xsd">
<bean id="basicAdvice1" class="com.exam.aop01.advice.BasicAdvice1" />
<bean id="pointcutAdvisor1" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="basicAdvice1" />
<property name="pointcut">
<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*execute*.*"></property>
</bean>
</property>
</bean>
<bean id="basicAdvice2" class="com.exam.aop01.advice.BasicAdvice2" />
<bean id="pointcutAdvisor2" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="basicAdvice2" />
<property name="pointcut">
<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*execute*.*"></property>
</bean>
</property>
</bean>
<!-- <bean id="writeAction" class="com.exam.aop01.WriteAction" scope="prototype"/> -->
<bean id="writeAction" class="com.exam.aop01.WriteAction" scope="prototype">
<property name="writer" value="홍길동" />
</bean>
<!-- AOP 적용 -->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="writeAction"></property>
<property name="interceptorNames">
<list>
<value>pointcutAdvisor1</value>
<value>pointcutAdvisor2</value>
</list>
</property>
</bean>
</beans>
package com.exam.aop01;
import org.springframework.context.support.GenericXmlApplicationContext;
public class App {
public static void main(String[] args) {
GenericXmlApplicationContext ctx
= new GenericXmlApplicationContext("classpath:com/exam/aop01/context.xml");
// 인터셉터 하지 않으면 전처리 및 후처리가 들어가지 않는다.
//BoardAction action = (BoardAction)ctx.getBean("writeAction");
// 인터셉터 해주면 전처리 및 후처리가 들어간다.
//BoardAction action = (BoardAction)ctx.getBean("proxy");
//WriteAction action = (WriteAction)ctx.getBean("writeAction");
WriteAction action = (WriteAction)ctx.getBean("proxy");
// Java Source로 처리
System.out.println("전처리 구간");
action.execute1();
action.execute2();
System.out.println("후처리 구간");
ctx.close();
}
}
execute1() 과 execute2() 메서드를 추가한 후 App.java에서 ProxyFactoryBean으로 코드를 실행시키면 Exception 이 발생했다.
물론 전처리 및 후처리를 안하는 단순 writeAction으로 실행시키면 Exception 발생이 안되고 정상적으로 실행되긴 한다.
형변환 문제라고 한다.
인터페이스 구현받았을 경우에는 인터페이스로 객체 생성을 해주어야 한다.
메서드 실행 속도를 측정
메서드 System / Date / Calendar
1 ~ 100,000,000의 반복을 수행하는 메서드
package com.exam.aop01;
public class App {
public static void main(String[] args) {
long beforeTime = System.currentTimeMillis();
System.out.println(beforeTime);
for(int i=1; i<=100000000; i++) {
flag
}
long afterTime = System.currentTimeMillis();
System.out.println((afterTime - beforeTime) + " ms");
}
}
org.springframework.util.StopWatch
Spring에서 지원해주는 속도 측정 기능이 있다.
package com.exam.aop01.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.util.StopWatch;
public class BasicAdvice1 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// TODO Auto-generated method stub
// 전처리 구간
String methodName = invocation.getMethod().getName();
System.out.println(methodName + " 호출 시작");
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object rtnObj = invocation.proceed();
// 후처리 구간
stopWatch.stop();
System.out.println("처리시간 : " + stopWatch.getTotalTimeSeconds() + " 초");
return null;
}
}
package com.exam.aop01;
public class ForAction{
public ForAction() {
// TODO Auto-generated constructor stub
System.out.println("ForAction() 실행");
}
public void execute() {
// TODO Auto-generated method stub
System.out.println("execute() 실행");
}
}
<?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.3.xsd">
<bean id="basicAdvice" class="com.exam.aop01.advice.BasicAdvice1" />
<bean id="pointcutAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="basicAdvice" />
<property name="pointcut">
<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*execute.*"></property>
</bean>
</property>
</bean>
<bean id="forAction" class="com.exam.aop01.ForAction" scope="prototype" />
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="forAction"></property>
<property name="interceptorNames">
<list>
<value>pointcutAdvisor</value>
</list>
</property>
</bean>
</beans>
package com.exam.aop01;
import org.springframework.context.support.GenericXmlApplicationContext;
public class App {
public static void main(String[] args) {
GenericXmlApplicationContext ctx
= new GenericXmlApplicationContext("classpath:com/exam/aop01/context.xml");
ForAction action = (ForAction)ctx.getBean("proxy");
action.execute();
ctx.close();
}
}
10:47:42.058 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 4 bean definitions from class path resource [com/exam/aop01/context.xml]
10:47:42.060 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@1d7acb34
10:47:42.096 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice'
10:47:42.107 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'pointcutAdvisor'
10:47:42.153 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'proxy'
ForAction() 실행
10:47:42.187 [main] DEBUG o.s.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
execute 호출 시작
execute() 실행
처리시간 : 0.0163092 초
10:47:42.283 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@1d7acb34, started on Tue Jul 05 10:47:42 KST 2022
AspectJ를 사용해보자.
선언이 더 간단해진다
시작 전에 라이브러리 추가를 해주자.
나는 Maven으로 프로젝트를 생성했기 때문에
pom.xml
내부, dependencies
에 아래 내용을 추가했다.
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
Maven Dependencies
에 라이브러리가 추가된 것을 꼭 확인하자.
context.xml에 aop를 사용하기 위한 설정을 해주어야 한다.
<?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.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
</beans>
package com.exam.aop01.advice;
import org.aspectj.lang.ProceedingJoinPoint;
public class BasicAdvice1 {
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("전처리 : 공통 관심 1");
Object rtnObj = joinPoint.proceed();
System.out.println("후처리 : 공통 관심 1");
return rtnObj;
}
}
BasicAdvice2는 1과 크게 다를 바가 없기 때문에 패스한다.
package com.exam.aop01;
public class WriteAction{
public WriteAction() {
// TODO Auto-generated constructor stub
System.out.println("WriteAction() 실행");
}
public void execute1() {
// TODO Auto-generated method stub
System.out.println("WriteAction execute1() 실행");
}
public void execute2() {
// TODO Auto-generated method stub
System.out.println("WriteAction execute2() 실행");
}
}
ListAction 또한 WriteAction 과 크게 다를 바가 없어서 패스한다.
<?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.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<bean id="writeAction" class="com.exam.aop01.WriteAction" scope="prototype"/>
<bean id="ListAction" class="com.exam.aop01.ListAction" scope="prototype"/>
<bean id="basicAdvice1" class="com.exam.aop01.advice.BasicAdvice1" scope="prototype"/>
<bean id="basicAdvice2" class="com.exam.aop01.advice.BasicAdvice2" scope="prototype"/>
<!-- AOP 연결 설정 -->
<aop:config>
<aop:aspect ref="basicAdvice1">
<aop:around method="logAround" pointcut="execution(* execute1())"/>
</aop:aspect>
<aop:aspect ref="basicAdvice2">
<aop:around method="logAround" pointcut="execution(* execute2())"/>
</aop:aspect>
</aop:config>
</beans>
package com.exam.aop01;
import org.springframework.context.support.GenericXmlApplicationContext;
public class App {
public static void main(String[] args) {
GenericXmlApplicationContext ctx
= new GenericXmlApplicationContext("classpath:com/exam/aop01/context.xml");
WriteAction writeAction = (WriteAction)ctx.getBean("writeAction");
ListAction listAction = (ListAction)ctx.getBean("ListAction");
writeAction.execute1();
writeAction.execute2();
listAction.execute1();
listAction.execute2();
ctx.close();
}
}
11:41:33.909 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 7 bean definitions from class path resource [com/exam/aop01/context.xml]
11:41:33.912 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@4b9e255
11:41:33.936 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.config.internalAutoProxyCreator'
11:41:34.006 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#0'
11:41:34.135 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#1'
WriteAction() 실행
ListAction() 실행
전처리 : 공통 관심 1
WriteAction execute1() 실행
후처리 : 공통 관심 1
전처리 : 공통 관심 2
WriteAction execute2() 실행
후처리 : 공통 관심 2
전처리 : 공통 관심 1
ListAction execute1() 실행
후처리 : 공통 관심 1
전처리 : 공통 관심 2
ListAction execute2() 실행
후처리 : 공통 관심 2
11:41:34.263 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@4b9e255, started on Tue Jul 05 11:41:33 KST 2022
package com.exam.aop01;
public class WriteAction {
public WriteAction() {
// TODO Auto-generated constructor stub
System.out.println("WriteAction() 실행");
}
public void execute() {
// TODO Auto-generated method stub
System.out.println("WriteAction execute() 실행");
}
}
package com.exam.aop01.advice;
public class BasicAdvice {
public void before() throws Throwable {
System.out.println("before() 실행");
}
public void after() throws Throwable {
System.out.println("after() 실행");
}
}
<?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.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<bean id="writeAction" class="com.exam.aop01.WriteAction" scope="prototype"/>
<bean id="basicAdvice" class="com.exam.aop01.advice.BasicAdvice" scope="prototype"/>
<!-- AOP 연결 설정 -->
<aop:config>
<aop:aspect ref="basicAdvice">
<aop:before method="before" pointcut="execution(* execute())"/>
<aop:after method="after" pointcut="execution(* execute())"/>
</aop:aspect>
</aop:config>
</beans>
package com.exam.aop01;
import org.springframework.context.support.GenericXmlApplicationContext;
public class App {
public static void main(String[] args) {
GenericXmlApplicationContext ctx
= new GenericXmlApplicationContext("classpath:com/exam/aop01/context.xml");
WriteAction writeAction = (WriteAction)ctx.getBean("writeAction");
writeAction.execute();
ctx.close();
}
}
14:55:30.482 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 5 bean definitions from class path resource [com/exam/aop01/context.xml]
14:55:30.484 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@4b9e255
14:55:30.508 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.config.internalAutoProxyCreator'
14:55:30.578 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#0'
14:55:30.695 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#1'
WriteAction() 실행
before() 실행
WriteAction execute() 실행
after() 실행
14:55:30.806 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@4b9e255, started on Tue Jul 05 14:55:30 KST 2022
어노테이션을 사용하려면 POJO 시스템이 있어야한다.
POJO 자바가 뭐나면 advice에 기술을 한다.
그냥 AspectJ 대비 변동사항이 있는데, 라이브러리 수정을 해줘야 한다.
pom.xml
에서 수정을 해주자.
아래 내용을 삭제해주면 된다.
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.2</version>
</dependency>
package com.exam.aop01;
public class WriteAction {
public WriteAction() {
// TODO Auto-generated constructor stub
System.out.println("WriteAction() 실행");
}
public void execute() {
// TODO Auto-generated method stub
System.out.println("WriteAction execute() 실행");
}
}
package com.exam.aop01.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class BasicAdvice1 {
@Around("execution(* execute())")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("전처리 : 공통 관심 1");
Object rtnObj = joinPoint.proceed();
System.out.println("후처리 : 공통 관심 1");
return rtnObj;
}
}
<?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.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<bean id="writeAction" class="com.exam.aop01.WriteAction" scope="prototype"/>
<bean id="basicAdvice" class="com.exam.aop01.advice.BasicAdvice1" scope="prototype"/>
<!-- AOP 연결 설정 -->
<aop:aspectj-autoproxy />
</beans>
package com.exam.aop01;
import org.springframework.context.support.GenericXmlApplicationContext;
public class App {
public static void main(String[] args) {
GenericXmlApplicationContext ctx
= new GenericXmlApplicationContext("classpath:com/exam/aop01/context.xml");
WriteAction writeAction = (WriteAction)ctx.getBean("writeAction");
writeAction.execute();
ctx.close();
}
}
14:23:01.561 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 3 bean definitions from class path resource [com/exam/aop01/context.xml]
14:23:01.563 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@1e4a7dd4
14:23:01.584 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.config.internalAutoProxyCreator'
14:23:01.717 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object com.exam.aop01.advice.BasicAdvice1.logAround(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
WriteAction() 실행
14:23:01.721 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object com.exam.aop01.advice.BasicAdvice1.logAround(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
14:23:01.722 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object com.exam.aop01.advice.BasicAdvice1.logAround(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
전처리 : 공통 관심 1
WriteAction execute() 실행
후처리 : 공통 관심 1
14:23:01.960 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@1e4a7dd4, started on Tue Jul 05 14:23:01 KST 2022
BasicAdvice1로 가자.
아래와 같이 수정해주면 된다.
package com.exam.aop01.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class BasicAdvice1 {
@Pointcut("execution(* execute())")
private void myTarget() {}
@Around("myTarget()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("전처리 : 공통 관심 1");
Object rtnObj = joinPoint.proceed();
System.out.println("후처리 : 공통 관심 1");
return rtnObj;
}
}
BasicAdvice2 생성 후 context.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.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<bean id="writeAction" class="com.exam.aop01.WriteAction" scope="prototype"/>
<bean id="basicAdvice1" class="com.exam.aop01.advice.BasicAdvice1" scope="prototype"/>
<bean id="basicAdvice2" class="com.exam.aop01.advice.BasicAdvice2" scope="prototype"/>
<!-- AOP 연결 설정 -->
<aop:aspectj-autoproxy />
</beans>
BasicAdvice2 를 아래와 같이 수정하자
package com.exam.aop01.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class BasicAdvice2 {
@Pointcut("execution(* execute())")
private void myTarget() {}
@Around("myTarget()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("전처리 : 공통 관심 2");
Object rtnObj = joinPoint.proceed();
System.out.println("후처리 : 공통 관심 2");
return rtnObj;
}
@Before("execution(* execute())")
public void before() {
System.out.println("전처리");
}
@After("execution(* execute())")
public void after() {
System.out.println("후처리");
}
}
실행 결과는 다음과 같다.
14:34:37.295 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 4 bean definitions from class path resource [com/exam/aop01/context.xml]
14:34:37.299 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@1e4a7dd4
14:34:37.324 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.config.internalAutoProxyCreator'
14:34:37.482 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object com.exam.aop01.advice.BasicAdvice1.logAround(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
14:34:37.490 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object com.exam.aop01.advice.BasicAdvice2.logAround(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
14:34:37.491 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public void com.exam.aop01.advice.BasicAdvice2.before()
14:34:37.491 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public void com.exam.aop01.advice.BasicAdvice2.after()
WriteAction() 실행
14:34:37.493 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object com.exam.aop01.advice.BasicAdvice1.logAround(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
14:34:37.497 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object com.exam.aop01.advice.BasicAdvice2.logAround(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
14:34:37.497 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public void com.exam.aop01.advice.BasicAdvice2.before()
14:34:37.497 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public void com.exam.aop01.advice.BasicAdvice2.after()
14:34:37.498 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object com.exam.aop01.advice.BasicAdvice1.logAround(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
14:34:37.498 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object com.exam.aop01.advice.BasicAdvice2.logAround(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
14:34:37.499 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public void com.exam.aop01.advice.BasicAdvice2.before()
14:34:37.499 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public void com.exam.aop01.advice.BasicAdvice2.after()
전처리 : 공통 관심 1
전처리 : 공통 관심 2
전처리
WriteAction execute() 실행
후처리
후처리 : 공통 관심 2
후처리 : 공통 관심 1
14:34:37.700 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@1e4a7dd4, started on Tue Jul 05 14:34:37 KST 2022
BasicAdvice1 ➡ ListAction(execute)
BasicAdvice2 ➡ WriteAction(execute)
package com.exam.aop01;
public class ListAction {
public ListAction() {
// TODO Auto-generated constructor stub
System.out.println("ListAction() 실행");
}
public void execute() {
// TODO Auto-generated method stub
System.out.println("ListAction execute() 실행");
}
}
WriteAction 은 ListAction 과 구성이 비슷해서 패스.
package com.exam.aop01.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class BasicAdvice1 {
@Pointcut("execution(* com.exam.aop01.ListAction.execute())")
private void myTarget() {}
@Around("myTarget()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("전처리 : 공통 관심 1");
Object rtnObj = joinPoint.proceed();
System.out.println("후처리 : 공통 관심 1");
return rtnObj;
}
}
BasicAdvice2는 Pointcut 부분만 아래와 같이 수정해주면 된다.
@Pointcut("execution(* com.exam.aop01.WriteAction.execute())")
<?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.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<bean id="writeAction" class="com.exam.aop01.WriteAction" scope="prototype"/>
<bean id="listAction" class="com.exam.aop01.ListAction" scope="prototype"/>
<bean id="basicAdvice1" class="com.exam.aop01.advice.BasicAdvice1" scope="prototype"/>
<bean id="basicAdvice2" class="com.exam.aop01.advice.BasicAdvice2" scope="prototype"/>
<!-- AOP 연결 설정 -->
<aop:aspectj-autoproxy />
</beans>
package com.exam.aop01;
import org.springframework.context.support.GenericXmlApplicationContext;
public class App {
public static void main(String[] args) {
GenericXmlApplicationContext ctx
= new GenericXmlApplicationContext("classpath:com/exam/aop01/context.xml");
ListAction listAction = (ListAction)ctx.getBean("listAction");
WriteAction writeAction = (WriteAction)ctx.getBean("writeAction");
listAction.execute();
writeAction.execute();
ctx.close();
}
}
xml
사용안해보기?package com.exam.aop01.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Scope;
import com.exam.aop01.WriteAction;
import com.exam.aop01.advice.BasicAdvice1;
import com.exam.aop01.advice.BasicAdvice2;
@Configuration
@Scope("prototype")
@EnableAspectJAutoProxy
public class BeanConfig {
@Bean
public WriteAction writeAction() {
return new WriteAction();
}
@Bean
public BasicAdvice2 basicAdvice2() {
return new BasicAdvice2();
}
}
package com.exam.aop01;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import com.exam.aop01.config.BeanConfig;
public class App02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx
= new AnnotationConfigApplicationContext(BeanConfig.class);
WriteAction writeAction = (WriteAction)ctx.getBean("writeAction");
writeAction.execute();
ctx.close();
}
}
15:25:52.440 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5e57643e
15:25:52.455 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
15:25:52.575 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
15:25:52.577 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
15:25:52.579 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
15:25:52.580 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.config.internalAutoProxyCreator'
15:25:52.655 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'writeAction'
15:25:52.678 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object com.exam.aop01.advice.BasicAdvice2.logAround(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
WriteAction() 실행
15:25:52.792 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice2'
전처리 : 공통 관심 2
WriteAction execute() 실행
후처리 : 공통 관심 2
15:25:52.821 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@5e57643e, started on Tue Jul 05 15:25:52 KST 2022
각자 사용하는 의도가 있다.
뭐는 절대적으로 쓰이고 어떤 것은 안쓰이고 이러는 것이 아니다.
※ 특이사항 ※
1번은 기존꺼를 스프링 방식으로 변경할 때,
2번과 3번은 처음부터 스프링 방식으로 사용할 때,
사용한다.
list.do ➡ listview1.jsp
<?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.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<bean name="/list1.do" class="org.springframework.web.servlet.mvc.ParameterizableViewController">
<property name="viewName" value="listview1.jsp"></property>
</bean>
</beans>
list1.do ➡ ListAction1 ➡ listview1.jsp
list2.do ➡ ListAction2 ➡ listview2.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
listview1.jsp
<br> <br>
<form action="list2.do" method="get">
데이터 <input type="text" name="data">
<input type="submit" value="전송">
</form>
<form action="list2.do" method="post">
데이터 <input type="text" name="data">
<input type="submit" value="전송">
</form>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
listview2.jsp
<br>
data : <%=request.getAttribute("data") %>
data : ${ data }
</body>
</html>
<?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.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<bean name="/list1.do" class="model2.ListAction1"></bean>
<bean name="/list2.do" class="model2.ListAction2"></bean>
<!-- 통합해서 사용하기 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
id="WebApp_ID" version="4.0">
<display-name>SpringEx01</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.jsp</welcome-file>
<welcome-file>default.htm</welcome-file>
</welcome-file-list>
<!-- 각 model2 Page 마다 문자열 인코딩 형식 지정해주는 것보다는 xml에서 한번에 필터처리하는 것이 편하다. -->
<!-- 다국어 필터 적용! -->
<!-- 일반적인 MVC 모델에서는 필터를 만들어줘야 한다. -->
<!-- 그런데 Spring에서는 필터가 이미 만들어져 있다. 따라서 적용만 해주면 된다. -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<!-- DispatcherServlet(Front Controller) -->
<servlet>
<servlet-name>servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>servlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
package model2;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class ListAction1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// TODO Auto-generated method stub
System.out.println("ListAction1 호출");
//return new ModelAndView("listview1");
// 인스턴스화 해서 넘겨줄 수도 있다.
//ModelAndView modelAndView = new ModelAndView("listview1");
//return modelAndView;
// 이렇게 사용해도 된다.
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("listview1");
return modelAndView;
}
}
package model2;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class ListAction2 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// TODO Auto-generated method stub
arg0.setCharacterEncoding("UTF-8");
System.out.println("ListAction2 호출");
System.out.println(arg0.getParameter("data"));
//return new ModelAndView("listview2");
ModelAndView modelAndView = new ModelAndView();
// 전통적으로 데이터를 옮기는 방식
//arg0.setAttribute("data", arg0.getParameter("data"));
// ModelAndView로 데이터를 옮기는 방식
modelAndView.addObject("data", arg0.getParameter("data"));
modelAndView.setViewName("listview2");
return modelAndView;
}
}
실행결과
write.do ➡ WriteAction ➡ /WEB-INF/views/write.jsp
write_ok.do ➡ WriteOkAction ➡ /WEB-INF/views/write_ok.jsp
data
라는 이름으로 데이터를 보내고 화면을 이동시킨다.data
라는 이름의 데이터를 받아 표시한다.package model2;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class WriteAction implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// TODO Auto-generated method stub
System.out.println("WriteAction 호출");
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("write");
return modelAndView;
}
}
package model2;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class WriteOkAction implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// TODO Auto-generated method stub
arg0.setCharacterEncoding("UTF-8");
System.out.println("WriteOkAction 호출");
System.out.println(arg0.getParameter("data"));
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("data", arg0.getParameter("data"));
modelAndView.setViewName("write_ok");
return modelAndView;
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
write.jsp
<br> <br>
<form action="write_ok.do" method="get">
데이터 <input type="text" name="data">
<input type="submit" value="전송">
</form>
<form action="write_ok.do" method="post">
데이터 <input type="text" name="data">
<input type="submit" value="전송">
</form>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
write_ok.jsp
<br>
<hr>
data : <%=request.getAttribute("data") %>
<br>
data : ${ data }
<br>
<a href="javascript:history.back()">index.jsp</a>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
Hello Spring MVC
<br><br>
<ul>
<li><a href="write.do">write.do</a></li>
</ul>
</body>
</html>
<?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.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<bean name="/write.do" class="model2.WriteAction"></bean>
<bean name="/write_ok.do" class="model2.WriteOkAction"></bean>
<!-- 통합해서 사용하기 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
id="WebApp_ID" version="4.0">
<display-name>SpringEx01</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.jsp</welcome-file>
<welcome-file>default.htm</welcome-file>
</welcome-file-list>
<!-- 다국어 필터 적용! -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<!-- DispatcherServlet(Front Controller) -->
<servlet>
<servlet-name>servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>servlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
결과는 다음과 같다.