본문 바로가기
  • 실행력이 모든걸 결정한다
Spring Series/Spring Framework

[Spring] profile 속성 / AOP(1) - xml 이용

by 김코더 김주역 2020. 12. 28.
반응형

1. profile 속성

- 개발 환경에 따라 bean 설정을 달리 적용하고 싶을 때 사용하는 속성이다.

 

1) Profile 지정하기

(1) XML 방식

- <beans>의 profile 속성을 이용하여 bean을 나눌 수 있다.

<beans ....
    <beans profile="dev, test">   
        <bean ...>
            <property .../>
        </bean>
    </beans>
    <beans profile="production">   
        <bean ...>
            <property .../>
        </bean>
    </beans>
</beans>

 

(2) Annotation 방식

- 설정 클래스에 @Profile 어노테이션을 추가하여 profile을 지정할 수 있다.

- 아래 예시에서 profile 이름은 test로 설정했으며, profile이 지정되어 있지 않은 bean 설정은 default profile로 취급한다.

@Configuration
@Profile("test")
public class TestApplicationContext {
    ...
}

- 여러 설정 클래스들을 대표 설정 클래스의 내부 static 클래스로 만들어 한 파일에 모으는 방법도 있다. 내부 static 클래스마다 똑같이 @Configuration과 @Profile을 적용해주면 된다.

 

 

2) Profile 활성화 하기

- 활성 프로파일 목록에 profile 이름을 등록하지 않으면 profile을 설정한 클래스는 무시된다. 특정 profile을 설정한 Bean 설정을 가져와서 사용하고 싶다면 해당 profile을 활성 profile로 만들어야 한다.

- 하나의 profile의 이름이 2개 이상으로 설정되었다면 그 중 하나의 profile만 활성화 시켜도 인식된다.

 

(1) Xml 방식

- <context-param> 또는 <init-param>의 spring.profiles.active 프로퍼티를 이용한다. <context-param>의 경우에는 루트 컨텍스트와 서블릿 컨텍스트 모두에 지정된다.

<context-param>
    <param-name>spring.profiles.active</param-name>
    <param-value>dev, test</param-value>
</context-param>

 

(2) Annotation 방식

- 다음과 같이 @ActiveProfiles 어노테이션을 사용한다.

- @ContextConfiguration에는 설정 클래스를 지정하거나 설정 클래스들이 모여있는 패키지를 지정해도 된다.

@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("test")
@ContextConfiguration(classes=TestApplicationContext.class)
public class ATest {
	...
}

 

(3) JVM의 커맨드라인 파라미터 방식

-Dspring.profiles.active=dev

 

 

3) 테스트 코드

- 지정한 profile의 bean 설정만 잘 적용되었는지 테스트하기 위해 DefaultListableBeanFactory 클래스를 이용할 수 있다.

@Autowired
DefaultListableBeanFactory bf;

@Test
public void beansTest(){
    for(String s : bf.getBeanDefinitionNames()) {
        System.out.println(n+" "+bf.getBean(n).getClass().getName()); // Bean 이름과 구현 클래스를 출력
    }
}

 

 

 

2. AOP(관점 지향 프로그래밍)

Aspect Oriented Programming

애플리케이션에 흩어져 있는 부가적인 공통 기능들을 독립적으로 모듈화하는 프로그래밍 모델이다.

Advice라고 하는 모듈화된 공통 기능과 Pointcut이라고 하는 적용 대상의 조합을 통해 공통 기능을 편리하게 관리할 수 있다.

 

1) 관련 용어

  • Aspect : 관점, 공통 기능
  • Advice : 공통 부가 기능을 담은 모듈
  • Joinpoint : 공통 기능을 적용해야 되는 부분
  • Pointcut : 적용 대상
  • Weaving : 공통 기능을 핵심 기능에 적용 하는 것

 

 

2) Advice 종류

  • <aop:before> : 메소드 실행 전에 advice 실행
  • <aop:after-returning> : 정상적으로 메소드에서 반환값이 return되고 advice 실행
  • <aop:after-throwing> : 메소드 실행 중에 예외 발생 시 advice 실행
  • <aop:after> : 메소드 실행 후 및 예외 발생 시 advice 실행
  • <aop:around> : 메소드가 호출되는 전 과정 동안 advice 실행

 

 

3) Pointcut 표현식

- 포인트컷 표현식을 통해 AOP를 적용할 메소드를 선정할 수 있다.

- 포인트컷 표현식을 아래 포스팅에 정리해놓았으니 참고하길 바란다.

https://kimcoder.tistory.com/543

 

[Spring] AOP 포인트컷 표현식

1. 지시자의 종류 - 메소드가 아닌 오브젝트를 선정하는 경우에는 해당 오브젝트 안에 있는 메소드들이 AOP 적용 대상이 된다. 1) execution() - 표현식 언어를 사용해서 포인트컷을 작성하는 방법이

kimcoder.tistory.com

 

 

4) xml 이용 예제 - while문과 for문 시간 차이 구하기

여기서 핵심 기능은 각각 while문, for문으로 연산하는 것이고

공통 기능은 시간를 구하는 것이다.

 

<pom.xml>

aop라이브러리를 추가 하기 위해 dependency를 다음과 같이 추가해줘야 한다.

<dependencies>
	...
	<dependency>
		<groupId>org.aspectj</groupId>
		<artifactId>aspectjweaver</artifactId>
		<version>1.7.4</version>
	</dependency>
</dependencies>

 

<WhileOperation.java>

while문으로 1부터 num까지의 누적합을 구하는 클래스이다.

package com.example.demo;

public class WhileOperation {
	private int num;
	private int sum = 0;
	public void setNum(int num) {
		this.num = num;
	}
	
	public void addTonum() {
		int i=1;
		while(i<=num) {
			sum+=i;
			i++;
		}
	}
}

 

<ForOperation.java>

for문으로 1부터 num까지의 누적합을 구하는 클래스이다.

package com.example.demo;

public class ForOperation {
	private int num;
	private int sum = 0;
	
	public void setNum(int num) {
		this.num = num;
	}
	
	public void addTonum() {
		for(int i=1;i<=num;i++) sum+=i;
	}
}

 

<LogAop.java>

공통 기능 클래스이다.

joinpoint.getSignature().toShortString() 메소드는 현재 적용하고 있는 핵심 기능 메소드를 "클래스.메소드" 형태로 반환해준다.

ProceedingJoinPoint.proceed() 메소드는 클라이언트가 보낸 파라미터를 그대로 사용해서 AOP 적용 대상 메소드를 실행해준다.

package com.example.demo;

import org.aspectj.lang.ProceedingJoinPoint;

public class LogAop {
	public Object loggerAop(ProceedingJoinPoint joinpoint) throws Throwable {
		String str = joinpoint.getSignature().toShortString();
		System.out.println("start - "+str);
		long start = System.currentTimeMillis();
		try {
			Object obj = joinpoint.proceed();
			return obj;
		} finally {
			long end = System.currentTimeMillis();
			System.out.println("end - "+str);
			System.out.println("Time : "+(end-start));
		}
	}
}

 

<application.xml>

aop 스키마 경로를 beans에 추가해주고, <aop:config> 태그에 공통 기능을 적용할 핵심 기능을 지정해줘야 한다.

<aop:aspect>의 id는 공통 기능 이름이고, ref는 공통 기능 객체이다.

<aop:pointcut>의 expression에는 포인트컷 표현식을 지정하면 된다. 예시에서는 com.example.demo 패키지의 모든 메소드에 적용하였고, pointcut의 이름은 publicTimer로 지정하였다.

<aop:around>에서는 "publicTimer" 포인트컷에 해당하는 객체들에 대하여, 공통 기능 클래스의 내부 메소드인 loggerAop메소드를 수행하도록 지정하면 된다.

만약 공통 기능 메소드를 추가하고 싶다면 추가할 때마다 <aop:aspect> 태그를 더 만들어주면 된다.

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

    <bean id="logAop" class="com.example.demo.LogAop"/>
    
    <aop:config>
    	<aop:aspect id="logger" ref="logAop">
    		<aop:pointcut expression="within(com.example.demo.*)" id="publicTimer"/>
    		<aop:around pointcut-ref="publicTimer" method="loggerAop"/>
    	</aop:aspect>
    </aop:config>
    
    <bean id="whileoperation" class="com.example.demo.WhileOperation">
    	<property name="num" value="100"/>
    </bean>
    
    <bean id="foroperation" class="com.example.demo.ForOperation">
    	<property name="num" value="100"/>
    </bean>
    
</beans>

 

<DemoApplication.java>

실행 클래스이다.

xml 설정 파일을 GenericXmlApplicationContext로 받고 해당 id의 Bean들을 생성해주고,

두 Bean의 addTonum() 메소드를 실행해주는 것 뿐이다.

package com.example.demo;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		
		AbstractApplicationContext ctx = new GenericXmlApplicationContext("classpath:application.xml");
		
		WhileOperation wfo = ctx.getBean("whileoperation",WhileOperation.class);
		wfo.addTonum();
		ForOperation foro = ctx.getBean("foroperation",ForOperation.class);
		foro.addTonum();
		ctx.close();
	}
}

 

 

실행 결과

 

 

@(Annotation)을 이용하는 기법은 다음 포스팅에서 살펴볼 것이다.

반응형

댓글