지구정복

[Spring] 01/26 | Spring(LifeCycle, Bean의 전후처리, AOP의 개념 및 사용(DI형식사용, AspectJ 사용)) 본문

데이터 엔지니어링 정복/HTML-CSS-JavaScript-Spring-Node.js

[Spring] 01/26 | Spring(LifeCycle, Bean의 전후처리, AOP의 개념 및 사용(DI형식사용, AspectJ 사용))

eeaarrtthh 2021. 1. 26. 18:04
728x90
반응형

1. Spring

더보기
java framework 최신설정
- xml
	- 기존
- annotation
	- POJO (Plain Old Java Object) : 오래된 방식의 간단한 자바 오브젝트
	즉, 진정한 POJO란 객체지향적인 원리에 충실하면서 환경과 기술에 종속되지 않고
	필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 의미

DI - 객체 생성(초기화)방법
	생성자
	setter
	- 생성자주입

	=> 소멸
	=> 라이프사이클=> spring framework
			- thread
			

1. Spring Lifecycle

출처: https://javaslave.tistory.com/48

 

라이프사이클을 확인해보자.

새로운 메이븐 프로젝트를 만들고 pom.xml을 작성한다.

-pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	
	<groupId>com.exam</groupId>
	<artifactId>lifecycle1</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	
	<name>lifecycle1</name>
	<url>http://maven.apache.org</url>
	
	<properties>
	
		<!-- Generic properties -->
		<java.version>1.8</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
	
		<!-- Spring -->
		<spring-framework.version>5.2.8.RELEASE</spring-framework.version>
	
		<!-- Hibernate / JPA -->
		<hibernate.version>4.2.1.Final</hibernate.version>
	
		<!-- Logging -->
		<logback.version>1.0.13</logback.version>
		<slf4j.version>1.7.5</slf4j.version>
	
		<!-- Test -->
		<junit.version>4.11</junit.version>
	</properties>

	<dependencies>
		<!-- Spring and Transactions -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<!-- Logging with SLF4J & LogBack -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${slf4j.version}</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
			<scope>runtime</scope>
		</dependency>

		<!-- Hibernate -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.version}</version>
		</dependency>

		<!-- Test Artifacts -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring-framework.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>${junit.version}</version>
			<scope>test</scope>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
		<dependency>
			<groupId>org.mariadb.jdbc</groupId>
			<artifactId>mariadb-java-client</artifactId>
			<version>2.7.1</version>
		</dependency>
		

	</dependencies>
</project>

 com.exam.lifecycle1.model 이란 패키지를 만든다.

-BoardAction.java (인터페이스)

package com.exam.lifecycle1.model;

public interface BoardAction {
	public abstract void execute();
}

-WriterAction.java

package com.exam.lifecycle1.model;

public class WriteAction implements BoardAction {
	private String writer;
	
	public WriteAction() {
		System.out.println( "1. Bean의 생성자 호출" );
	}
	
	
	public void setWriter(String writer) {
		System.out.println( "2. setWriter(String writer) 호출" );
		this.writer = writer;
	}
	public void execute() {
		System.out.println( "*. execute() 호출" );
		System.out.println( "writer : " + writer );
	}

}

com.exam.lifecycle1 패키지에

-context.xml (bean configuration file)

<?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 https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

	<bean id="action" class="com.exam.lifecycle1.model.WriteAction" scope="prototype">
		<property name="writer">
			<value>Hello Writer</value>
		</property>
	</bean>

</beans>

 -App.java (실행클래스)

package com.exam.lifecycle1;

import org.springframework.context.support.GenericXmlApplicationContext;

import com.exam.lifecycle1.model.BoardAction;
import com.exam.lifecycle1.model.WriteAction;

public class App {
    public static void main( String[] args ) {
        GenericXmlApplicationContext ctx = 
        		new GenericXmlApplicationContext("classpath:com/exam/lifecycle1/context.xml");
        
        WriteAction action = (WriteAction)ctx.getBean( "action" );
        action.execute();
        
        ctx.close();
    }
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
09:32:15.215 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 1 bean definitions from class path resource [com/exam/lifecycle1/context.xml]
09:32:15.218 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@eb21112
1. Bean의 생성자 호출
2. setWriter(String writer) 호출
*. execute() 호출
writer : Hello Writer
09:32:15.339 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@eb21112, started on Tue Jan 26 09:32:15 KST 2021

 

 이때 라이프사이클의 흐름을 알기 위해서 다음과 같이 WriteAction.java 코드를 수정한다.

package com.exam.lifecycle1.model;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class WriteAction implements BoardAction, ApplicationContextAware, 
				BeanFactoryAware, BeanNameAware, DisposableBean, InitializingBean {
	
	private String writer;
	private String beanName;
	private BeanFactory beanFactory;
	
	public WriteAction() {
		System.out.println( "1. Bean의 생성자 호출" );
	}
	
	public void setWriter(String writer) {
		System.out.println( "2. setWriter(String writer) 호출" );
		this.writer = writer;
	}
	
	public void execute() {
		System.out.println( "*. execute() 호출" );
		
		System.out.println( "beanName : " + beanName );
		System.out.println( "beanFactory : " + beanFactory );
		
		System.out.println( "writer : " + writer );
	}

	public void afterPropertiesSet() throws Exception {
		System.out.println( "7. afterPropertiesSet() 호출" );
	}

	public void destroy() throws Exception {
		System.out.println( "10. destroy() 호출" );
	}

	public void setBeanName(String name) {
		System.out.println( "3. setBeanName(String name) 호출" );
		this.beanName = name;
	}

	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		System.out.println( "4. setBeanFactory(BeanFactory beanFactory) 호출" );
		this.beanFactory = beanFactory;
	}

	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		System.out.println( "5. setApplicationContext(ApplicationContext applicationContext) 호출" );
	}
}

다시 App.java를 실행시키면 아래와 같은 결과가 나온다.

09:43:52.120 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 1 bean definitions from class path resource [com/exam/lifecycle1/context.xml]
09:43:52.123 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@eb21112
1. Bean의 생성자 호출
2. setWriter(String writer) 호출
3. setBeanName(String name) 호출
4. setBeanFactory(BeanFactory beanFactory) 호출
5. setApplicationContext(ApplicationContext applicationContext) 호출
7. afterPropertiesSet() 호출
*. execute() 호출
beanName : action
beanFactory : org.springframework.beans.factory.support.DefaultListableBeanFactory@31f9b85e: defining beans [action]; root of factory hierarchy
writer : Hello Writer
09:43:52.260 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@eb21112, started on Tue Jan 26 09:43:52 KST 2021

 

 이번에는 빈의 전처리 후처리를 확인해보자.

 -WriteAction.java

package com.exam.lifecycle1.model;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class WriteAction implements BoardAction, ApplicationContextAware, 
				BeanFactoryAware, BeanNameAware, DisposableBean, InitializingBean {
	
	private String writer;
	private String beanName;
	private BeanFactory beanFactory;
	
	public WriteAction() {
		System.out.println( "1. Bean의 생성자 호출" );
	}
	
	public void setWriter(String writer) {
		System.out.println( "2. setWriter(String writer) 호출" );
		this.writer = writer;
	}
	
	public void execute() {
		System.out.println( "*. execute() 호출" );
		
		System.out.println( "beanName : " + beanName );
		System.out.println( "beanFactory : " + beanFactory );
		
		System.out.println( "writer : " + writer );
	}

	public void afterPropertiesSet() throws Exception {
		System.out.println( "7. afterPropertiesSet() 호출" );
	}

	public void destroy() throws Exception {
		System.out.println( "10. destroy() 호출" );
	}

	public void setBeanName(String name) {
		System.out.println( "3. setBeanName(String name) 호출" );
		this.beanName = name;
	}

	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		System.out.println( "4. setBeanFactory(BeanFactory beanFactory) 호출" );
		this.beanFactory = beanFactory;
	}

	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		System.out.println( "5. setApplicationContext(ApplicationContext applicationContext) 호출" );
	}
	
	public void init_method() {
		System.out.println( "8. init_method() 호출" );
	}
	
	public void destory_method() {
		System.out.println( "11. destory_method() 호출" );
	}
}

 -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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

	<bean id="action" class="com.exam.lifecycle1.model.WriteAction" scope="singleton" init-method="init_method" destroy-method="destory_method">
		<property name="writer">
			<value>Hello Writer</value>
		</property>
	</bean>

</beans>

-실행결과

09:52:23.406 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 1 bean definitions from class path resource [com/exam/lifecycle1/context.xml]
09:52:23.409 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@eb21112
1. Bean의 생성자 호출
2. setWriter(String writer) 호출
3. setBeanName(String name) 호출
4. setBeanFactory(BeanFactory beanFactory) 호출
5. setApplicationContext(ApplicationContext applicationContext) 호출
7. afterPropertiesSet() 호출
8. init_method() 호출
*. execute() 호출
beanName : action
beanFactory : org.springframework.beans.factory.support.DefaultListableBeanFactory@31f9b85e: defining beans [action]; root of factory hierarchy
writer : Hello Writer
09:52:23.536 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@eb21112, started on Tue Jan 26 09:52:23 KST 2021

 하지만 destory_method가 안보이므로 App.java를 아래와 같이 수정한다.

package com.exam.lifecycle1;

import org.springframework.context.support.GenericXmlApplicationContext;

import com.exam.lifecycle1.model.BoardAction;
import com.exam.lifecycle1.model.WriteAction;

public class App {
    public static void main( String[] args ) {
        GenericXmlApplicationContext ctx = 
        		new GenericXmlApplicationContext("classpath:com/exam/lifecycle1/context.xml");
        
        WriteAction action = (WriteAction)ctx.getBean( "action" );
        action.execute();
        
        //bean을 메모리 제거하기
        ctx.removeBeanDefinition("action");
        
        //컨테이너 제거
        ctx.close();
    }
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
09:54:17.600 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 1 bean definitions from class path resource [com/exam/lifecycle1/context.xml]
09:54:17.603 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@eb21112
09:54:17.634 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
1. Bean의 생성자 호출
2. setWriter(String writer) 호출
3. setBeanName(String name) 호출
4. setBeanFactory(BeanFactory beanFactory) 호출
5. setApplicationContext(ApplicationContext applicationContext) 호출
7. afterPropertiesSet() 호출
8. init_method() 호출
*. execute() 호출
beanName : action
beanFactory : org.springframework.beans.factory.support.DefaultListableBeanFactory@31f9b85e: defining beans [action]; root of factory hierarchy
writer : Hello Writer
10. destroy() 호출
11. destory_method() 호출
09:54:17.733 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@eb21112, started on Tue Jan 26 09:54:17 KST 2021

 위 결과를 보면 destory_method가 실행됨을 알 수 있다.

 

이번에는 Bean의 전처리, 제거메서드만 사용해보자.

-ListAction.java

package com.exam.lifecycle1.model;

public class ListAction {
	private String writer;

	public ListAction() {
		System.out.println( "ListAction 생성자 호출" );
	}
	
	public void setWriter(String writer) {
		System.out.println( "setWriter(String writer) 호출" );
		this.writer = writer;
	}
	
	public void execute() {
		System.out.println( "writer : " + writer );
	}
	
	public void init_method() {
		System.out.println( "Bean의 사용자 초기화" );
	}
	
	public void destory_method() {
		System.out.println( "Bean의 사용자 제거" );
	}
}

 -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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

	
	<bean id="action" class="com.exam.lifecycle1.model.ListAction" scope="singleton" init-method="init_method" destroy-method="destory_method">
		<property name="writer">
			<value>Hello Writer</value>
		</property>
	</bean>
	
	<!-- 
	<bean id="action" class="com.exam.lifecycle1.model.WriteAction" scope="singleton" init-method="init_method" destroy-method="destory_method">
		<property name="writer">
			<value>Hello Writer</value>
		</property>
	</bean>
 	-->
</beans>

 -App.java

package com.exam.lifecycle1;

import org.springframework.context.support.GenericXmlApplicationContext;

import com.exam.lifecycle1.model.BoardAction;
import com.exam.lifecycle1.model.ListAction;
import com.exam.lifecycle1.model.WriteAction;

public class App {
    public static void main( String[] args ) {
        GenericXmlApplicationContext ctx = 
        		new GenericXmlApplicationContext("classpath:com/exam/lifecycle1/context.xml");
        
        ListAction action = (ListAction)ctx.getBean( "action" );
        action.execute();
        
        //bean을 메모리 제거하기
        ctx.removeBeanDefinition("action");
        
        //컨테이너 제거
        ctx.close();
    }
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
10:19:31.760 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 1 bean definitions from class path resource [com/exam/lifecycle1/context.xml]
10:19:31.763 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@eb21112
10:19:31.794 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
ListAction 생성자 호출
setWriter(String writer) 호출
Bean의 사용자 초기화
writer : Hello Writer
Bean의 사용자 제거
10:19:31.882 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@eb21112, started on Tue Jan 26 10:19:31 KST 2021

 

처리해야하는 메서드를 외부에서 정의해놓고 필요할 때 사용해보자.

아래와 같이 클래스를 만든다.

 

-CustomBeanPostProcess.java

package com.exam.lifecycle1;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class CustomBeanPostProcessor implements BeanPostProcessor {
	
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println( "6. 초기화 전 빈에 대한 처리 실행");
		return bean;
	}
	
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println( "9. 초기화 후 빈에 대한 처리 실행");
		return bean;
	}
}

-context.xml customBeanPostProcess를 context에 추가해준다.

<?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 https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

	<!-- 
	<bean id="action" class="com.exam.lifecycle1.model.ListAction" scope="singleton" init-method="init_method" destroy-method="destory_method">
		<property name="writer">
			<value>Hello Writer</value>
		</property>
	</bean>
	 -->
	<bean class="com.exam.lifecycle1.CustomBeanPostProcessor" />
	
	<bean id="action" class="com.exam.lifecycle1.model.WriteAction" scope="singleton" init-method="init_method" destroy-method="destory_method">
		<property name="writer">
			<value>Hello Writer</value>
		</property>
	</bean>
</beans>

 -App.java (실행클래스)

package com.exam.lifecycle1;

import org.springframework.context.support.GenericXmlApplicationContext;

import com.exam.lifecycle1.model.BoardAction;
import com.exam.lifecycle1.model.ListAction;
import com.exam.lifecycle1.model.WriteAction;

public class App {
    public static void main( String[] args ) {
        GenericXmlApplicationContext ctx = 
        		new GenericXmlApplicationContext("classpath:com/exam/lifecycle1/context.xml");
        
        //ListAction action = (ListAction)ctx.getBean( "action" );
        WriteAction action = (WriteAction)ctx.getBean( "action" );
        action.execute();
        
        //bean을 메모리 제거하기
        ctx.removeBeanDefinition("action");
        
        //컨테이너 제거
        ctx.close();
    }
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
10:32:27.979 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [com/exam/lifecycle1/context.xml]
10:32:27.981 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@eb21112
10:32:28.012 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'com.exam.lifecycle1.CustomBeanPostProcessor#0'
10:32:28.031 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
1. Bean의 생성자 호출
2. setWriter(String writer) 호출
3. setBeanName(String name) 호출
4. setBeanFactory(BeanFactory beanFactory) 호출
5. setApplicationContext(ApplicationContext applicationContext) 호출
6. 초기화 전 빈에 대한 처리 실행
7. afterPropertiesSet() 호출
8. init_method() 호출
9. 초기화 후 빈에 대한 처리 실행
*. execute() 호출
beanName : action
beanFactory : org.springframework.beans.factory.support.DefaultListableBeanFactory@117e949d: defining beans [com.exam.lifecycle1.CustomBeanPostProcessor#0,action]; root of factory hierarchy
writer : Hello Writer
10. destroy() 호출
11. destory_method() 호출
10:32:28.116 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@eb21112, started on Tue Jan 26 10:32:27 KST 2021

 따라서 빈의 호출 전에 해야될 기능이나 호출 후에 해야될 기능들을 위와 같이 추가할 수 있다.

 

실습) 위의 호출 전후 메서드를 어노테이션으로 사용하기

-BeanConfig.java

package com.exam.lifecycle1.model;

import org.springframework.context.annotation.Bean;

public class BeanConfig {
	
	@Bean(initMethod = "init_method", destroyMethod = "destory_method")
	public ListAction action() {
		ListAction listAction = new ListAction();
		listAction.setWriter( "Hello Writer" );
		return listAction;
	}
}

-App2.java (실행클래스)

package com.exam.lifecycle1;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

import com.exam.lifecycle1.model.BeanConfig;
import com.exam.lifecycle1.model.BoardAction;
import com.exam.lifecycle1.model.ListAction;
import com.exam.lifecycle1.model.WriteAction;

public class App2 {
    public static void main( String[] args ) {
    	AnnotationConfigApplicationContext ctx = 
    			new AnnotationConfigApplicationContext( BeanConfig.class );
    	
    	ListAction action = (ListAction)ctx.getBean( "action" );
    	action.init_method();
    	action.destory_method();
    	
    	ctx.close();
    }
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
10:52:04.888 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@706a04ae
10:52:04.905 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
10:52:04.983 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
10:52:04.986 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
10:52:04.987 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
10:52:04.993 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanConfig'
10:52:04.995 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
ListAction 생성자 호출
setWriter(String writer) 호출
Bean의 사용자 초기화
Bean의 사용자 초기화
Bean의 사용자 제거
10:52:05.024 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@706a04ae, started on Tue Jan 26 10:52:04 KST 2021
Bean의 사용자 제거

 

 

2. AOP

AOP (Aspect Oriented Programming)
	:관점 중심 프로그래밍
	관점이란? 공통적인 적용되어야할 사항
	즉, 공통적으로 적용되어야 할 전처리나 후처리
		예시: 보안작업, 조건검사

	- Servlet Fiilter와 같다.
	=> 프로그램 전체에 대해서 전처리나 후처리와 같은 역할을 담당하는 것을 AOP

advice - 적용시점을 의미
joinpoint - advice를 적용할 지점을 의미
	어느 메서드
pointcut - 문법으로 표현한 것을 의미

이러한 관점사항들 적용하는 것을 weaving이라고 한다.

관점 프로그래밍은 간단히 말해서 methodA와 methodB, methodC가 있는데 이 3개의 메소드 안에 중복되는 코드가 있다고 해보자. 이럴 경우 메모리낭비도 되며 만일 중복되는 코드를 변경할 경우 3개의 메소드 모두 바꿔줘야하는 비효율적인 상황이 발생한다. 따라서 AOP는 중복되는 코드를 따로 빼내서 사용하고 각각의 메소드는 각각의 기능만 가지고 있도록 하는 것이다.

 

이를 구현하는 방법은 Proxy 패턴이다. 

 

 

2.1 DI 형식으로 표현하기

새로운 메이븐 프로젝트를 만들고 pom.xml을 설정한다.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	
	<groupId>com.exam</groupId>
	<artifactId>aop1</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	
	<name>aop1</name>
	<url>http://maven.apache.org</url>
	
	<properties>
	
		<!-- Generic properties -->
		<java.version>1.8</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
	
		<!-- Spring -->
		<spring-framework.version>5.2.8.RELEASE</spring-framework.version>
	
		<!-- Hibernate / JPA -->
		<hibernate.version>4.2.1.Final</hibernate.version>
	
		<!-- Logging -->
		<logback.version>1.0.13</logback.version>
		<slf4j.version>1.7.5</slf4j.version>
	
		<!-- Test -->
		<junit.version>4.11</junit.version>
	</properties>

	<dependencies>
		<!-- Spring and Transactions -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<!-- Logging with SLF4J & LogBack -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${slf4j.version}</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
			<scope>runtime</scope>
		</dependency>

		<!-- Hibernate -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.version}</version>
		</dependency>

		<!-- Test Artifacts -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring-framework.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>${junit.version}</version>
			<scope>test</scope>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
		<dependency>
			<groupId>org.mariadb.jdbc</groupId>
			<artifactId>mariadb-java-client</artifactId>
			<version>2.7.1</version>
		</dependency>
		

	</dependencies>
</project>

-BoardAction.java (인터페이스)

package com.exam.aop1.model;

public interface BoardAction {
	public abstract void execute();
}

 -WriteAction.java

package com.exam.aop1.model;

public class WriteAction implements BoardAction {
	private String writer;
	
	public void setWriter(String writer) {
		this.writer = writer;
	}
	
	public void execute() {
		System.out.println( "execute() 시작" );
		System.out.println( "writer : " + writer );
		System.out.println( "execute() 끝" );
	}
}

 -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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

	<bean id="action" class="com.exam.aop1.model.WriteAction">
		<property name="writer">
			<value>홍길동</value>
		</property>
	</bean>

</beans>

-App.java (실행클래스)

package com.exam.aop1;

import org.springframework.context.support.GenericXmlApplicationContext;

import com.exam.aop1.model.BoardAction;

public class App {
    public static void main( String[] args ) {
        GenericXmlApplicationContext ctx = 
        		new GenericXmlApplicationContext("classpath:com/exam/aop1/context.xml");
        
        BoardAction action = (BoardAction)ctx.getBean("action");
        action.execute();
        
        ctx.close();
    }
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
11:25:34.575 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 1 bean definitions from class path resource [com/exam/aop1/context.xml]
11:25:34.577 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@eb21112
11:25:34.610 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
execute() 시작
writer : 홍길동
execute() 끝
11:25:34.700 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@eb21112, started on Tue Jan 26 11:25:34 KST 2021

 

Around Advice방식을 적용해보자. ( 전처리와 후처리 동시에 하는 것)

 com.exam.aop1.advice란 패키지를 만든다.

-BasicAdvice.java

package com.exam.aop1.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class BasicAdvice implements MethodInterceptor {

	public Object invoke(MethodInvocation invocation) throws Throwable {
		
		// Around Advice 방식이라고 한다.(전처리와 후처리 동시에 하는 것)
		
		//전처리 구간
		System.out.println( "전처리 구간" );
		Object rtnObj = invocation.proceed();
		
		//후처리기간
		return rtnObj;
	}

}

-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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

	<!-- AOP설정에 대한 기술  -->
	<bean id="basicAdvice" class="com.exam.aop1.advice.BasicAdvice" />
	<bean id="pointcutAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<property name="advice">
			<ref bean="basicAdvice" />
		</property>
		<property name="pointcut">
			<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
				<property name="pattern">
					<value>.*execute.*</value>
				</property>
			</bean>
		</property>
	</bean>

	<bean id="action" class="com.exam.aop1.model.WriteAction">
		<property name="writer">
			<value>홍길동</value>
		</property>
	</bean>
	
	<!-- aop => 객체화  -->
	<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="target" ref="action" />
		<property name="interceptorNames">
			<list>
				<value>pointcutAdvisor</value>
			</list>
		</property>
	</bean>

</beans>

-App.java (실행클래스)

package com.exam.aop1;

import org.springframework.context.support.GenericXmlApplicationContext;

import com.exam.aop1.model.BoardAction;

public class App {
    public static void main( String[] args ) {
        GenericXmlApplicationContext ctx = 
        		new GenericXmlApplicationContext("classpath:com/exam/aop1/context.xml");
        
        //BoardAction action = (BoardAction)ctx.getBean("action");
        BoardAction action = (BoardAction)ctx.getBean("proxy");
        action.execute();
        
        ctx.close();
    }
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
11:42:59.381 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 4 bean definitions from class path resource [com/exam/aop1/context.xml]
11:42:59.383 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@eb21112
11:42:59.430 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice'
11:42:59.445 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'pointcutAdvisor'
11:42:59.528 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
11:42:59.530 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'proxy'
11:42:59.586 [main] DEBUG o.s.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
11:42:59.586 [main] DEBUG o.s.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
전처리 구간
execute() 시작
writer : 홍길동
execute() 끝
후처리 구간
11:42:59.600 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@eb21112, started on Tue Jan 26 11:42:59 KST 2021

결과를 보면 전처리, 후처리 결과가 나오는 것을 알 수 있다.

 

이번에는 BasicAdvice를 하나 더 만든다.

 

-BasicAdvice2.java

package com.exam.aop1.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class BasicAdvice2 implements MethodInterceptor {

	public Object invoke(MethodInvocation invocation) throws Throwable {
		
		// Around Advice 방식이라고 한다.(전처리와 후처리 동시에 하는 것)
		
		//전처리 구간
		System.out.println( "전처리 구간 2" );
		Object rtnObj = invocation.proceed();
		
		//후처리기간
		System.out.println( "후처리 구간 2" );
		return rtnObj;
	}

}

-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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

	<!-- AOP설정에 대한 기술  -->
	<bean id="basicAdvice" class="com.exam.aop1.advice.BasicAdvice" />
	<bean id="basicAdvice2" class="com.exam.aop1.advice.BasicAdvice2" />
	
	<bean id="pointcutAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<property name="advice">
			<ref bean="basicAdvice" />
		</property>
		<property name="pointcut">
			<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
				<property name="pattern">
					<value>.*execute.*</value>
				</property>
			</bean>
		</property>
	</bean>

	<bean id="pointcutAdvisor2" class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<property name="advice">
			<ref bean="basicAdvice2" />
		</property>
		<property name="pointcut">
			<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
				<property name="pattern">
					<value>.*execute.*</value>
				</property>
			</bean>
		</property>
	</bean>

	<bean id="action" class="com.exam.aop1.model.WriteAction">
		<property name="writer">
			<value>홍길동</value>
		</property>
	</bean>
	
	<!-- aop => 객체화  -->
	<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="target" ref="action" />
		<property name="interceptorNames">
			<list>
				<value>pointcutAdvisor</value>
				<value>pointcutAdvisor2</value>
			</list>
		</property>
	</bean>

</beans>

-App.java (실행클래스)

package com.exam.aop1;

import org.springframework.context.support.GenericXmlApplicationContext;

import com.exam.aop1.model.BoardAction;

public class App {
    public static void main( String[] args ) {
        GenericXmlApplicationContext ctx = 
        		new GenericXmlApplicationContext("classpath:com/exam/aop1/context.xml");
        
        //BoardAction action = (BoardAction)ctx.getBean("action");
        BoardAction action = (BoardAction)ctx.getBean("proxy");
        action.execute();
        
        ctx.close();
    }
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
11:50:51.908 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 6 bean definitions from class path resource [com/exam/aop1/context.xml]
11:50:51.911 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@eb21112
11:50:51.956 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice'
11:50:51.971 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice2'
11:50:51.971 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'pointcutAdvisor'
11:50:52.037 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'pointcutAdvisor2'
11:50:52.038 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
11:50:52.041 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'proxy'
11:50:52.084 [main] DEBUG o.s.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
11:50:52.084 [main] DEBUG o.s.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
11:50:52.084 [main] DEBUG o.s.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
전처리 구간
전처리 구간 2
execute() 시작
writer : 홍길동
execute() 끝
후처리 구간 2
후처리 구간
11:50:52.101 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@eb21112, started on Tue Jan 26 11:50:51 KST 2021

마치 filter와 같은 기능을 하는 것을 알 수 있다.

전처리1 -> 전처리2 -> execute() -> 후처리2 -> 후처리1

 

이러한 advice를 이용해서 여러가지 기능을 추가할 수 있다.

 

 

간단하게 수행시간을 측정해보자.

-TimeAdvice.java

package com.exam.aop1.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.util.StopWatch;

public class TimeAdvice implements MethodInterceptor {

	public Object invoke(MethodInvocation invocation) throws Throwable {
		String methodName = invocation.getMethod().getName();
		System.out.println( methodName + " : 호출 시작" );
		
		//시간측정 시작
		StopWatch stopWatch = new StopWatch();
		stopWatch.start( methodName );
		
		Object rtnObj = invocation.proceed();
		
		//시간측정 끝 및 출력
		stopWatch.stop();
		System.out.println( "처리시간 : " + stopWatch.getTotalTimeSeconds() + " 초" );
		
		System.out.println( methodName + " : 호출 끝" );
		return rtnObj;
	}

}

-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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

	<!-- AOP설정에 대한 기술  -->
	<bean id="basicAdvice" class="com.exam.aop1.advice.BasicAdvice" />
	<bean id="basicAdvice2" class="com.exam.aop1.advice.BasicAdvice2" />
	<bean id="TimeAdvice" class="com.exam.aop1.advice.TimeAdvice" />
	
	<!-- 
	<bean id="pointcutAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<property name="advice">
			<ref bean="basicAdvice" />
		</property>
		<property name="pointcut">
			<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
				<property name="pattern">
					<value>.*execute.*</value>
				</property>
			</bean>
		</property>
	</bean>

	<bean id="pointcutAdvisor2" class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<property name="advice">
			<ref bean="basicAdvice2" />
		</property>
		<property name="pointcut">
			<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
				<property name="pattern">
					<value>.*execute.*</value>
				</property>
			</bean>
		</property>
	</bean>
	 -->
	
	<bean id="pointcutAdvisor3" class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<property name="advice">
			<ref bean="TimeAdvice" />
		</property>
		<property name="pointcut">
			<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
				<property name="pattern">
					<value>.*execute.*</value>
				</property>
			</bean>
		</property>
	</bean>

	<bean id="action" class="com.exam.aop1.model.WriteAction">
		<property name="writer">
			<value>홍길동</value>
		</property>
	</bean>
	
	<!-- aop => 객체화  -->
	<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="target" ref="action" />
		<property name="interceptorNames">
			<list>
				<!-- 
				<value>pointcutAdvisor</value>
				<value>pointcutAdvisor2</value>
				 -->
				<value>pointcutAdvisor3</value>
			</list>
		</property>
	</bean>

</beans>

-App.java (실행클래스)

package com.exam.aop1;

import org.springframework.context.support.GenericXmlApplicationContext;

import com.exam.aop1.model.BoardAction;

public class App {
    public static void main( String[] args ) {
        GenericXmlApplicationContext ctx = 
        		new GenericXmlApplicationContext("classpath:com/exam/aop1/context.xml");
        
        //BoardAction action = (BoardAction)ctx.getBean("action");
        BoardAction action = (BoardAction)ctx.getBean("proxy");
        action.execute();
        
        ctx.close();
    }
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
12:24:27.026 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 6 bean definitions from class path resource [com/exam/aop1/context.xml]
12:24:27.029 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@eb21112
12:24:27.073 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice'
12:24:27.090 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice2'
12:24:27.090 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'TimeAdvice'
12:24:27.091 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'pointcutAdvisor3'
12:24:27.169 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
12:24:27.172 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'proxy'
12:24:27.233 [main] DEBUG o.s.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
12:24:27.233 [main] DEBUG o.s.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
execute : 호출 시작
execute() 시작
writer : 홍길동
execute() 끝
처리시간 : 2.013E-4 초
execute : 호출 끝
12:24:27.253 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@eb21112, started on Tue Jan 26 12:24:27 KST 2021

 

2.2 AspectJ 이용하기

새로운 메이븐 프로젝트를 만들고 pom.xml을 작성한다. 이때 맨 마지막 두개의 dependency가 aspectJ의 라이브러리이다.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	
	<groupId>com.exam</groupId>
	<artifactId>aop2</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	
	<name>aop2</name>
	<url>http://maven.apache.org</url>
	
	<properties>
	
		<!-- Generic properties -->
		<java.version>1.8</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
	
		<!-- Spring -->
		<spring-framework.version>5.2.8.RELEASE</spring-framework.version>
	
		<!-- Hibernate / JPA -->
		<hibernate.version>4.2.1.Final</hibernate.version>
	
		<!-- Logging -->
		<logback.version>1.0.13</logback.version>
		<slf4j.version>1.7.5</slf4j.version>
	
		<!-- Test -->
		<junit.version>4.11</junit.version>
	</properties>

	<dependencies>
		<!-- Spring and Transactions -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<!-- Logging with SLF4J & LogBack -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${slf4j.version}</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
			<scope>runtime</scope>
		</dependency>

		<!-- Hibernate -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.version}</version>
		</dependency>

		<!-- Test Artifacts -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring-framework.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>${junit.version}</version>
			<scope>test</scope>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
		<dependency>
			<groupId>org.mariadb.jdbc</groupId>
			<artifactId>mariadb-java-client</artifactId>
			<version>2.7.1</version>
		</dependency>
		
		<!-- 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>		

	</dependencies>
</project>

com.exam.aop2.model 패키지를 만들고 아래 두 개를 코딩한다.

-BoardAction.java (인터페이스)

package com.exam.aop2.model;

public interface BoardAction {
	public abstract void execute();
}

-WriteAction.java

package com.exam.aop2.model;

public class WriteAction implements BoardAction {
	private String writer;
	
	public void setWriter(String writer) {
		this.writer = writer;
	}
	
	public void execute() {
		System.out.println( "execute() 시작" );
		System.out.println( "writer : " + writer );
		System.out.println( "execute() 끝" );
	}
}

-context.xml

XSD를 선택할 때 beans의 4.3 그리고 aop의 4.3을 선택해서 만든다.

<?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 https://www.springframework.org/schema/beans/spring-beans-4.3.xsd
		http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<bean id="action" class="com.exam.aop2.model.WriteAction">
		<property name="writer" value="홍길동" />
	</bean>

	<bean id="basicAdvice1" class="com.exam.aop2.advice.BasicAdvice1" />
	
	<!-- AOP 설정  -->
	<aop:config>
		<aop:aspect ref="basicAdvice1">
			<aop:pointcut  id="pointCut" expression="execution(* execute())"/>
			<aop:around method="logAround" pointcut-ref="pointCut"/>
		</aop:aspect>
	</aop:config>
	<!-- basicAdvice 빈을 이용해서 execute란 메서드를 찾고 그 메서드의 전 후(around)처리를 적용한다는 뜻  -->
	
</beans>

 -App.java (실행클래스)

package com.exam.aop2;

import org.springframework.context.support.GenericXmlApplicationContext;

import com.exam.aop2.model.BoardAction;

public class App {
    public static void main( String[] args ) {
        GenericXmlApplicationContext ctx =
        		new GenericXmlApplicationContext("classpath:com/exam/aop2/context.xml");
        
        BoardAction action = (BoardAction)ctx.getBean("action");
        action.execute();
        
        ctx.close();
    }
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
14:26:59.516 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 5 bean definitions from class path resource [com/exam/aop2/context.xml]
14:26:59.519 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@6eceb130
14:26:59.546 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.config.internalAutoProxyCreator'
14:26:59.636 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
14:26:59.636 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#0'
14:26:59.803 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice1'
전처리 구간
execute() 시작
writer : 홍길동
execute() 끝
후처리 구간
14:26:59.824 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@6eceb130, started on Tue Jan 26 14:26:59 KST 2021

 

실습) BasicAdvice2.java를 만들어서 전후처리 구간 하나 더 추가하기

-실행결과

14:40:16.347 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 7 bean definitions from class path resource [com/exam/aop2/context.xml]
14:40:16.349 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@6eceb130
14:40:16.382 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.config.internalAutoProxyCreator'
14:40:16.477 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
14:40:16.477 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#0'
14:40:16.618 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#1'
14:40:16.647 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice1'
14:40:16.648 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice2'
전처리 구간 1
전처리 구간 2
execute() 시작
writer : 홍길동
execute() 끝
후처리 구간 2
후처리 구간 1
14:40:16.669 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@6eceb130, started on Tue Jan 26 14:40:16 KST 2021

-BasicAdvice2.java

package com.exam.aop2.advice;

import org.aspectj.lang.ProceedingJoinPoint;

public class BasicAdvice2 {
	public Object logAround( ProceedingJoinPoint joinPoint ) throws Throwable {
		//전처리구간
		System.out.println( "전처리 구간 2" );
		
		
		Object rtnObj = joinPoint.proceed();
		
		//후처리구간
		System.out.println( "후처리 구간 2" );
		
		return rtnObj;
	}
}

-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 https://www.springframework.org/schema/beans/spring-beans-4.3.xsd
		http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<bean id="action" class="com.exam.aop2.model.WriteAction">
		<property name="writer" value="홍길동" />
	</bean>

	<bean id="basicAdvice1" class="com.exam.aop2.advice.BasicAdvice1" />
	<bean id="basicAdvice2" class="com.exam.aop2.advice.BasicAdvice2" />
	
	<!-- AOP 설정  -->
	<aop:config>
		<aop:aspect ref="basicAdvice1">
			<aop:pointcut  id="pointCut" expression="execution(* execute())"/>
			<aop:around method="logAround" pointcut-ref="pointCut"/>
		</aop:aspect>
		<aop:aspect ref="basicAdvice2">
			<aop:pointcut  id="pointCut" expression="execution(* execute())"/>
			<aop:around method="logAround" pointcut-ref="pointCut"/>
		</aop:aspect>
	</aop:config>
	<!-- basicAdvice 빈을 이용해서 execute란 메서드를 찾고 그 메서드의 전 후(around)처리를 적용한다는 뜻  -->
	
</beans>

App을 실행하면 위와 같은 결과가 나온다.

 

만약 아래와 같이 BoardAction의 execute메서드를 수정하면 aop가 실행되지 않는다.

-BoardAction.java

package com.exam.aop2.model;

public interface BoardAction {
	public abstract void execute1();
	public abstract void execute2();
}

-WriteAction.java

package com.exam.aop2.model;

public class WriteAction implements BoardAction {
	private String writer;
	
	public void setWriter(String writer) {
		this.writer = writer;
	}
	
	public void execute1() {
		System.out.println( "execute1() 실행" );
	}
	public void execute2() {
		System.out.println( "execute2() 실행" );
	}
}

-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 https://www.springframework.org/schema/beans/spring-beans-4.3.xsd
		http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<bean id="action" class="com.exam.aop2.model.WriteAction">
		<property name="writer" value="홍길동" />
	</bean>

	<bean id="basicAdvice1" class="com.exam.aop2.advice.BasicAdvice1" />
	<bean id="basicAdvice2" class="com.exam.aop2.advice.BasicAdvice2" />
	
	<!-- AOP 설정  -->
	<!-- basicAdvice 빈을 이용해서 execute란 메서드를 찾고 그 메서드의 전 후(around)처리를 적용한다는 뜻  -->
	<aop:config>
		<aop:aspect ref="basicAdvice1">
			<aop:pointcut  id="pointCut" expression="execution(* execute())"/>
			<aop:around method="logAround" pointcut-ref="pointCut"/>
		</aop:aspect>
		<!-- 
		<aop:aspect ref="basicAdvice2">
			<aop:pointcut  id="pointCut" expression="execution(* execute())"/>
			<aop:around method="logAround" pointcut-ref="pointCut"/>
		</aop:aspect>
		 -->
	</aop:config>
	
</beans>

 -App.java

package com.exam.aop2;

import org.springframework.context.support.GenericXmlApplicationContext;

import com.exam.aop2.model.BoardAction;

public class App {
    public static void main( String[] args ) {
        GenericXmlApplicationContext ctx =
        		new GenericXmlApplicationContext("classpath:com/exam/aop2/context.xml");
        
        BoardAction action = (BoardAction)ctx.getBean("action");
        action.execute1();
        action.execute2();
        
        ctx.close();
    }
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
15:11:37.536 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 6 bean definitions from class path resource [com/exam/aop2/context.xml]
15:11:37.539 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@6eceb130
15:11:37.573 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.config.internalAutoProxyCreator'
15:11:37.660 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
15:11:37.661 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#0'
15:11:37.824 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice1'
15:11:37.824 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice2'
execute1() 실행
execute2() 실행
15:11:37.844 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@6eceb130, started on Tue Jan 26 15:11:37 KST 2021

위와 같이 전후처리 작업이 실행되지 않는다. 이럴 때는 context.xml에 wild card 문자 *를 삽입한다.

-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 https://www.springframework.org/schema/beans/spring-beans-4.3.xsd
		http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<bean id="action" class="com.exam.aop2.model.WriteAction">
		<property name="writer" value="홍길동" />
	</bean>

	<bean id="basicAdvice1" class="com.exam.aop2.advice.BasicAdvice1" />
	<bean id="basicAdvice2" class="com.exam.aop2.advice.BasicAdvice2" />
	
	<!-- AOP 설정  -->
	<!-- basicAdvice 빈을 이용해서 execute란 메서드를 찾고 그 메서드의 전 후(around)처리를 적용한다는 뜻  -->
	<aop:config>
		<aop:aspect ref="basicAdvice1">
			<aop:pointcut  id="pointCut" expression="execution(* execute*())"/>
			<aop:around method="logAround" pointcut-ref="pointCut"/>
		</aop:aspect>
		<!-- 
		<aop:aspect ref="basicAdvice2">
			<aop:pointcut  id="pointCut" expression="execution(* execute())"/>
			<aop:around method="logAround" pointcut-ref="pointCut"/>
		</aop:aspect>
		 -->
	</aop:config>
	
</beans>

다시 실행해보면 아래와 같이 실행됨을 알 수 있다.

15:13:16.648 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 6 bean definitions from class path resource [com/exam/aop2/context.xml]
15:13:16.651 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@6eceb130
15:13:16.685 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.config.internalAutoProxyCreator'
15:13:16.777 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
15:13:16.777 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#0'
15:13:16.949 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice1'
15:13:16.950 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice2'
전처리 구간 1
execute1() 실행
후처리 구간 1
전처리 구간 1
execute2() 실행
후처리 구간 1
15:13:16.975 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@6eceb130, started on Tue Jan 26 15:13:16 KST 2021

 

이번에는 Around로 전후처리를 동시에 하지말고 각각 해보자.

기준은 Object rtnObj = joinPoint.proceed(); 이다.

 

  • around - 전후처리
  • before - 전처리
  • after - 후처리
  • after-throwing - 후처리 에러

먼저 전처리만 사용해보자.

-BasicAdvice1.java

package com.exam.aop2.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;
	}
	
	//전처리 구문
	public void before() throws Throwable {
		System.out.println( "before() 실행" );
	}
}

 -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 https://www.springframework.org/schema/beans/spring-beans-4.3.xsd
		http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<bean id="action" class="com.exam.aop2.model.WriteAction">
		<property name="writer" value="홍길동" />
	</bean>

	<bean id="basicAdvice1" class="com.exam.aop2.advice.BasicAdvice1" />
	<bean id="basicAdvice2" class="com.exam.aop2.advice.BasicAdvice2" />
	
	<!-- AOP 설정  -->
	<!-- basicAdvice 빈을 이용해서 execute란 메서드를 찾고 그 메서드의 전 후(around)처리를 적용한다는 뜻  -->
	<aop:config>
		<aop:aspect ref="basicAdvice1">
			<aop:pointcut  id="pointCut" expression="execution(* execute*())"/>
			<!-- <aop:around method="logAround" pointcut-ref="pointCut"/> -->
			<aop:before method="before" pointcut-ref="pointCut"/>
		</aop:aspect>
	</aop:config>
	
</beans>

-App.java

package com.exam.aop2;

import org.springframework.context.support.GenericXmlApplicationContext;

import com.exam.aop2.model.BoardAction;

public class App {
    public static void main( String[] args ) {
        GenericXmlApplicationContext ctx =
        		new GenericXmlApplicationContext("classpath:com/exam/aop2/context.xml");
        
        BoardAction action = (BoardAction)ctx.getBean("action");
        action.execute1();
        action.execute2();
        
        ctx.close();
    }
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
15:18:39.001 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 6 bean definitions from class path resource [com/exam/aop2/context.xml]
15:18:39.004 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@6eceb130
15:18:39.034 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.config.internalAutoProxyCreator'
15:18:39.130 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
15:18:39.131 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#0'
15:18:39.317 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice1'
15:18:39.317 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice2'
before() 실행
execute1() 실행
before() 실행
execute2() 실행
15:18:39.341 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@6eceb130, started on Tue Jan 26 15:18:39 KST 2021

 

어노테이션 사용하기

새로운 메이븐 프로젝트 만들고 pom.xml을 작성한다. aspectweaver만 있으면 된다.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	
	<groupId>com.exam</groupId>
	<artifactId>aop3</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	
	<name>aop3</name>
	<url>http://maven.apache.org</url>
	
	<properties>
	
		<!-- Generic properties -->
		<java.version>1.8</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
	
		<!-- Spring -->
		<spring-framework.version>5.2.8.RELEASE</spring-framework.version>
	
		<!-- Hibernate / JPA -->
		<hibernate.version>4.2.1.Final</hibernate.version>
	
		<!-- Logging -->
		<logback.version>1.0.13</logback.version>
		<slf4j.version>1.7.5</slf4j.version>
	
		<!-- Test -->
		<junit.version>4.11</junit.version>
	</properties>

	<dependencies>
		<!-- Spring and Transactions -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<!-- Logging with SLF4J & LogBack -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${slf4j.version}</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
			<scope>runtime</scope>
		</dependency>

		<!-- Hibernate -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.version}</version>
		</dependency>

		<!-- Test Artifacts -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring-framework.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>${junit.version}</version>
			<scope>test</scope>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
		<dependency>
			<groupId>org.mariadb.jdbc</groupId>
			<artifactId>mariadb-java-client</artifactId>
			<version>2.7.1</version>
		</dependency>
		
		<!-- aspectj 라이브러리  --> 
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.9.2</version>
		</dependency>		

	</dependencies>
</project>

-BasicAdvice1.java

package com.exam.aop3.advice;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class BasicAdvice1 {
	
	@Around( "execution(* execute1())" )
	public Object logAround( ProceedingJoinPoint joinPoint ) throws Throwable {
		System.out.println( "전처리 1" );
		
		Object rtnObj = joinPoint.proceed();
		
		System.out.println( "후처리 1" );
		return rtnObj;
	}
}

 -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 https://www.springframework.org/schema/beans/spring-beans-4.3.xsd
		http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<bean id="action" class="com.exam.aop3.model.WriteAction">
		<property name="writer" value="홍길동" />
	</bean>

	<bean id="basicAdvice1" class="com.exam.aop3.advice.BasicAdvice1" />
	
	<!-- 클래스를 묶어주는 기능  -->
	<aop:aspectj-autoproxy />
</beans>

 -App.java

package com.exam.aop3;

import org.springframework.context.support.GenericXmlApplicationContext;

import com.exam.aop3.model.BoardAction;

public class App {
    public static void main( String[] args ) {
    	 GenericXmlApplicationContext ctx =
         		new GenericXmlApplicationContext("classpath:com/exam/aop3/context.xml");
         
         BoardAction action = (BoardAction)ctx.getBean("action");
         action.execute1();
         action.execute2();
         
         ctx.removeBeanDefinition( "action" );
         
         ctx.close();
    }
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
15:43:25.920 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 3 bean definitions from class path resource [com/exam/aop3/context.xml]
15:43:25.923 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@3578436e
15:43:25.947 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.config.internalAutoProxyCreator'
15:43:26.053 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
15:43:26.097 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object com.exam.aop3.advice.BasicAdvice1.logAround(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
15:43:26.227 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice1'
전처리 1
execute1() 실행
후처리 1
execute2() 실행
15:43:26.241 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@3578436e, started on Tue Jan 26 15:43:25 KST 2021

 

실습) 현재 context.xml을 모두 어노테이션 기법으로 변경하기

-BeanConfig.java

package com.exam.aop3;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import com.exam.aop3.advice.BasicAdvice1;
import com.exam.aop3.advice.BasicAdvice2;
import com.exam.aop3.model.WriteAction;

@Configuration
@EnableAspectJAutoProxy
public class BeanConfig {
	
	@Bean
	public WriteAction action() {
		WriteAction writeAction = new WriteAction();
		writeAction.setWriter( "홍길동" );
		return writeAction;
	}
	@Bean
	public BasicAdvice1 basicAdvice1() {
		return new BasicAdvice1();
	}
	@Bean
	public BasicAdvice2 basicAdvice2() {
		return new BasicAdvice2();
	}
}

-BasicAdvice1.java

package com.exam.aop3.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;

@Aspect
public class BasicAdvice1 {
	
	@Around( "execution(* execute1())" )
	public Object logAround( ProceedingJoinPoint joinPoint ) throws Throwable {
		System.out.println( "전처리 1" );
		
		Object rtnObj = joinPoint.proceed();
		
		System.out.println( "후처리 1" );
		return rtnObj;
	}
	
	@Before( "execution(* execute2())" )
	public void before() {
		System.out.println( "before() 호출" );
	}
	
	@After( "execution(* execute2())" )
	public void after() {
		System.out.println( "after() 호출" );
	}
}

-BasicAdvice2.java

package com.exam.aop3.advice;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class BasicAdvice2 {
	
	@Around( "execution(* execute2())" )
	public Object logAround( ProceedingJoinPoint joinPoint ) throws Throwable {
		System.out.println( "전처리 2" );
		
		Object rtnObj = joinPoint.proceed();
		
		System.out.println( "후처리 2" );
		return rtnObj;
	}
}

-App2.java

package com.exam.aop3;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

import com.exam.aop3.model.BoardAction;

public class App2 {
    public static void main( String[] args ) {
    	 AnnotationConfigApplicationContext ctx = 
    			 new AnnotationConfigApplicationContext( BeanConfig.class );
    	 BoardAction action = (BoardAction)ctx.getBean( "action" );
    	 action.execute1();
    	 action.execute2();
         
         ctx.close();
    }
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
16:30:02.186 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@10a035a0
16:30:02.208 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
16:30:02.354 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
16:30:02.356 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
16:30:02.357 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
16:30:02.359 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.config.internalAutoProxyCreator'
16:30:02.465 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanConfig'
16:30:02.495 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object com.exam.aop3.advice.BasicAdvice1.logAround(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
16:30:02.496 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public void com.exam.aop3.advice.BasicAdvice1.before()
16:30:02.497 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public void com.exam.aop3.advice.BasicAdvice1.after()
16:30:02.497 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object com.exam.aop3.advice.BasicAdvice2.logAround(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
16:30:02.601 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
16:30:02.630 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice1'
16:30:02.631 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice2'
전처리 1
execute1() 실행
후처리 1
before() 호출
전처리 2
execute2() 실행
후처리 2
after() 호출
16:30:02.653 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@10a035a0, started on Tue Jan 26 16:30:02 KST 2021

 

실습) 1~100까지 구구단 처리시간 구하기(어노테이션 기법사용)

-실행결과

19:02:02.480 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@10a035a0
19:02:02.505 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
19:02:02.642 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
19:02:02.643 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
19:02:02.645 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
19:02:02.646 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.config.internalAutoProxyCreator'
19:02:02.744 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanConfig2'
19:02:02.764 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object com.exam.aop3.advice.GugudanAdvice.gugudanAround(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
19:02:02.861 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
19:02:02.885 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'gugudanAdvice'
gugudan 실행 시작
gugudan 실행 완료
처리시간 : 2.855E-4 초
19:02:02.908 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@10a035a0, started on Tue Jan 26 19:02:02 KST 2021

-GugudanAction.java (인터페이스)

package com.exam.aop3.model;

public interface GugudanAction {
	public abstract void gugudan();
}

-CalculateAction.java

package com.exam.aop3.model;

public class CalculateAction implements GugudanAction {

	public void gugudan() {
		System.out.println( "gugudan 실행 시작" );
		for( int i=1; i<=100; i++ ) {
			for( int j=1; j<=9; j++ ) {
				int result = i * j;
			}
		}
		System.out.println( "gugudan 실행 완료" );
	}
}

-GugudanAdvice.java

package com.exam.aop3.advice;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.util.StopWatch;

@Aspect
public class GugudanAdvice {
	
	@Around( "execution(* gugudan())" )
	public Object gugudanAround( ProceedingJoinPoint joinPoint ) throws Throwable {
		
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		
		Object rtnObj = joinPoint.proceed();
		
		stopWatch.stop();
		System.out.println( "처리시간 : " + stopWatch.getTotalTimeSeconds() + " 초" );
		
		return rtnObj;
	}
}

-BeanConfig2.java

package com.exam.aop3.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import com.exam.aop3.advice.GugudanAdvice;
import com.exam.aop3.model.CalculateAction;

@Configuration
@EnableAspectJAutoProxy
public class BeanConfig2 {
	@Bean
	public CalculateAction action() {
		return new CalculateAction();
	}
	@Bean
	public GugudanAdvice gugudanAdvice() {
		return new GugudanAdvice();
	}
}

-App3.java (실행클래스)

package com.exam.aop3;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.exam.aop3.config.BeanConfig2;
import com.exam.aop3.model.GugudanAction;

public class App3 {
    public static void main( String[] args ) {
    	
    	 AnnotationConfigApplicationContext ctx = 
    			 new AnnotationConfigApplicationContext( BeanConfig2.class );
    	 
    	 GugudanAction action = (GugudanAction)ctx.getBean( "action" );
    	 action.gugudan();
         
         ctx.close();
    }
}

 

2.3 Spring 자체의 API 이용하기

 

728x90
반응형
Comments