플레이데이터 - 31일차 Spring Framework 학습(3)

Kim Hyen Su·2023년 8월 11일

🌟Spring Framework

스프링 제공 기능

  • 객체 관리(IOC)
  • MVC(DI) - 느슨한 결합력과 인터페이스
  • 👉트랜잭션 처리(AOP)
  • 인증과 권한(Servlet Filter)

🐽AOP(Aspect Oriented Programming)

  • 주 업무 로직(사용자 관점에서 요구한 코드, core)
  • 보조 업무 로직(개발자/운영자 관점에서 로직 구현에 필요한 기능을 담은 코드, cross-cutting)
  • 사용자 관점과 개발자/운영자 관점에 따라서 프로그램을 구성하는 코드가 다르다.
  • 사용자 관점에서 주기능만 사용한다면 개발자나 운영자 관점에서 사용자와 무관하게 보안문제나 트랜잭션 처리 등의 기능들이 추가적으로 필요하기 때문에 관점에 따라 다른 로직들을 어떻게 결합하고 구현할 것인가를 고려하여 프로그래밍 하는 것을 관점 지향 프로그래밍이라고 한다.

✅Concern

  • Core(Primary) Concern : 주기능 로직.
  • Cross-cutting Concern : 로그처리, 보안처리, 트랜잭션 처리 등의 로직.

✅AOP 구현 방식

  • 프록시를 생성하여 Cross-cutting Cocern(보조기능)을 사용하도록 해주고, 실제 로직에는 Core Concern(주기능)만 구현해주는 방식으로 구현된다.

✅AOP 구현(with Java)

  • 프록시 클래스에 구현되는 Cross-cutting Concern
Exam exam = new NewLecExam(1, 1, 1, 1);

Exam proxyExam = (Exam)Proxy.newProxyInstance(NewLecExam.class.getClassLoader(),
	new Class[] { Exam.class },
	new InvocationHandler() {
					
		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						
		long start = System.currentTimeMillis();
						
		Object result = method.invoke(exam, args);
						
		long end = System.currentTimeMillis();
		String msg = (end-start) + "ms 시간";
		System.out.println(msg);
		return result;
		}
	});

	System.out.printf("total is %d \n", proxyExam.total());
	System.out.printf("avg is %f \n",proxyExam.avg())

✅AOP 구현(with Spring)

  • setting.xml 설정 추가
// exam 빈 등록
<bean id="exam" class="spring.aop.entity.NewLecExam" p:kor="1" p:eng="1" p:math="1" p:com="1"/>

// proxy 빈 등록
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target" ref="exam"/><!-- DI -->
	<property name="interceptorNames">
		<list>
			<value>logAroundAdvice</value>
			<value>logBeforeAdvice</value>
			<value>logAfterReturningAdvice</value>
			<value>logAfterThrowingAdvice</value>
		</list>
	</property>
</bean>
  • IoC 컨테이너 xml 설정 추가 및 프록시 빈생성 후 실행
ApplicationContext context =  
new ClassPathXmlApplicationContext("spring/aop/setting.xml");

Exam exam = (Exam) context.getBean("proxy");
		
System.out.printf("total is %d \n", exam.total());
System.out.printf("avg is %f \n",exam.avg());

✅Advice 종류

BeforeAdvice

  • core concern 실행 전 advice
  • 구현
package spring.aop.advice;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class LogBeforeAdvice implements MethodBeforeAdvice{

	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		System.out.println("앞에서 실행될 로직");
	}
}

○setting.xml
<bean id="exam" class="spring.aop.entity.NewLecExam" p:kor="1" p:eng="1" p:math="1" p:com="1"/>
<bean id="logBeforeAdvice" class="spring.aop.advice.LogBeforeAdvice"/>
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target" ref="exam"/><!-- DI -->
	<property name="interceptorNames">
		<list>
			<value>logBeforeAdvice</value>
		</list>
	</property>
</bean>

AfterReturningAdvice

  • core concern 실행 후 반환 결과관련 advice
  • 구현
package spring.aop.advice;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

// core concern 실행 후 반환값으로 처리할 것이 있는 경우 사용하는 프록시
public class LogAfterReturningAdvice implements AfterReturningAdvice{

	@Override
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		System.out.println("returnValue : " + returnValue + ", method : " + method.getName());
		
	}

}


○setting.xml
<bean id="exam" class="spring.aop.entity.NewLecExam" p:kor="1" p:eng="1" p:math="1" p:com="1"/>
<bean id="logAfterReturningAdvice" class="spring.aop.advice..LogAfterReturningAdvice"/>
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target" ref="exam"/><!-- DI -->
	<property name="interceptorNames">
		<list>
			<value>logAfterReturningAdvice</value>
		</list>
	</property>
</bean>

AfterThrowingAdvice

  • core concern 실행 시 예외 처리관련 advice
  • 구현
package spring.aop.advice;

import org.springframework.aop.ThrowsAdvice;

// core concern 실행 시 예외 발생할 경우 실행.
public class LogAfterThrowingAdvice implements ThrowsAdvice{
	public void afterThrowing(IllegalArgumentException e) throws Throwable{
		System.out.println("예외가 발생하였습니다." + e.getMessage());
	}
}


○setting.xml
<bean id="exam" class="spring.aop.entity.NewLecExam" p:kor="1" p:eng="1" p:math="1" p:com="1"/>
<bean id="logAfterThrowingAdvice" class="spring.aop.advice.LogAfterThrowingAdvice"/>
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target" ref="exam"/><!-- DI -->
	<property name="interceptorNames">
		<list>
			<value>logAfterThrowingAdvice</value>
		</list>
	</property>
</bean>

AroundAdvice

  • core concern 전후 advice
  • 구현
package spring.aop.advice;

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

public class LogAroundAdvice implements MethodInterceptor{

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		
		long start = System.currentTimeMillis();
		
		Object result =invocation.proceed();
		
		long end = System.currentTimeMillis();
		String msg = (end-start) + "ms 시간";
		System.out.println(msg);
		return result;
	}
	
}

○setting.xml
<bean id="exam" class="spring.aop.entity.NewLecExam" p:kor="1" p:eng="1" p:math="1" p:com="1"/>
<bean id="logAroundAdvice" class="spring.aop.advice.LogAroundAdvice"/>
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target" ref="exam"/><!-- DI -->
	<property name="interceptorNames">
		<list>
			<value>logAroundAdvice</value>
		</list>
	</property>
</bean>

✅Pointcut, JoinPoint, Weaving

  • AOP 형태로 구현한 코드를 weaving(뜨게질) 형태라고 한다.
  • 프록시의 대상이 되는 주로직(target) 메서드를 JoinPoint라고 한다.
  • 특정 메서드만 프록시의 target으로 지정하고 싶은 경우 지정하기 위한 것이 Pointcuts라고 한다.

Pointcut 기본 설정

  • xml 파일에 advisor를 추가하여 pointcut을 제한.
  • advice 하나당 pointcut 하나씩 만들어줘야 사용이 가능하다.
<!-- pointcut 생성-->
<bean id="classicPointcut" class="org.springframework.aop.support.NameMatchMethodPointcut">
	<property name="mappedName" value="total"/>
</bean>
<!-- advisor 생성-->	
<bean id="classicBeforeAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
	<property name="advice" ref="logBeforeAdvice"></property>
	<property name="pointcut" ref="classicPointcut"></property>
</bean>
...
<value>classicBeforeAdvisor</value> 

Pointcut과 Advisor 간소화하여 사용하는 방법

  • 하나의 빈 객체로 pointcut과 advisor를 동시에 사용.
<bean id="classicBeforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
	<property name="advice" ref="logBeforeAdvice"/>
	<property name="mappedNames">
		<list>
			<value>total</value>
			<value>avg</value>
		</list>
	</property>
</bean>
  • 메서드명을 적어주어 일치하는 메서드를 target으로 지정하여 cross-cutting concern을 적용.
<bean id="classicBeforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
	<property name="advice" ref="logBeforeAdvice"/>
	<property name="patterns">
		<list>
			<value>.*to.*</value>
		</list>
	</property>
</bean> 
  • 정규표현식(Regex)를 사용하여 패턴에 해당하는 메서드를 target으로 지정할 수 있도록 해주는 방식.
profile
백엔드 서버 엔지니어

0개의 댓글