Spring Framework에서는 AOP 사용을 위하여 XML방식도 지원을 한다.

        

AOP라는 네임스페이스 태그를 사용하면 된다.

 

XML 방식 사용 시, XML선언에 하단 내용을 추가해주여야 한다.

(기존 내용에서 xmlns:aop, xsi:schemaLocation의 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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
		https://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>

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

	<aop:config>
		<aop:aspect id="aspect" ref="schemaBasedCls">
		
			<aop:pointcut id="executionTest" expression="execution(* com.example.aop.PrintCls.*(..))"/>
			
			<aop:before 
				pointcut-ref="executionTest" 
				method="beforeTest"/>
			<aop:after-returning
        		pointcut-ref="executionTest"
       			returning="retVal"
        		method="executionAndReturnValTest"/>
		  	<aop:after-throwing
		        pointcut-ref="executionTest"
		        throwing="ex"
		        method="executionAndAfterThrowingValTest"/>
		  	<aop:after
        		pointcut-ref="executionTest"
        		method="executionAndAfterTest"/>
			<aop:around
        		pointcut-ref="executionTest"
        		method="executionAndAroundTest"/>
		</aop:aspect>
	</aop:config>
	
	<bean id="schemaBasedCls" class="com.example.aop.SchemaBasedCls"></bean>
	<bean id="printCls" class="com.example.aop.PrintCls"></bean>
</beans>
package com.example.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ImportResource;

import com.example.aop.PrintCls;

@SpringBootApplication
@ImportResource("classpath:aop.xml")
public class DemoSchemabasedApplication {
	private static final Logger logger = LoggerFactory.getLogger(DemoSchemabasedApplication.class);	
	
	public static void main(String[] args) {
		ConfigurableApplicationContext run = SpringApplication.run(DemoSchemabasedApplication.class, args);
		
		PrintCls bean = run.getBean(PrintCls.class);

		logger.info("--------------    bean method start	--------------");
		bean.printExample();
		logger.info("--------------    bean method end	--------------");
		
		logger.info("--------------    bean return method start    --------------");
		bean.returnStr();
		logger.info("--------------    bean return method end    --------------");
		
		logger.info("--------------    bean throw method start    --------------");
		try {
			bean.returnThrow();
		} catch (Exception e) {
		}
		logger.info("--------------    bean throw method end    --------------");
	}
}
package com.example.aop;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PrintCls {

	private static final Logger logger = LoggerFactory.getLogger(PrintCls.class);
	
	public void printExample() {
		logger.info("*** printExample");
	}
	
	public String returnStr() {
		String value = "testReturning";
		logger.info("*** print returning Str" + value );
		return value;
	}
	
	public void returnThrow() throws Exception {
		String msg = "execption test";
		logger.info("*** print throw " + msg);
		throw new Exception(msg);	
	}
}
package com.example.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SchemaBasedCls {
	private static final Logger logger = LoggerFactory.getLogger(SchemaBasedCls.class);
	
	//--before--
	public void beforeTest() {
		logger.info("before start");
		logger.info("before end");
	}
	//--before--
	
	//--AfterReturning--
	public void executionAndReturnValTest(String retVal) {
		logger.info("execution and afterReturning Value start");
		logger.info("Value :" + retVal);
		logger.info("execution and afterReturning Value end");
	}
	//--AfterReturning--
	
	//--AfterThrowing--
	public void executionAndAfterThrowingValTest(Exception ex) {
		logger.info("execution and AfterThrowing Value start");
		logger.error(ex.getMessage());
		logger.info("execution and AfterThorwing Value end");
	}
	//--AfterThrowing--
	
	//--After--
	public void executionAndAfterTest() {
		logger.info("execution and After start");
		logger.info("execution and AFter end");
	}
	//--After--
	
	//--Around--
	public void executionAndAroundTest(ProceedingJoinPoint pjp) {
		logger.info("execution and around start");
		logger.info(pjp.getSignature().getName() + " Before Method Execution");
		try {
			pjp.proceed();
		} catch (Throwable e) {
			logger.info(pjp.getSignature().getName() + "Throw Method Execution");
		} finally {
			// Do Something useful, If you have
		}
		logger.info(pjp.getSignature().getName() + " After Method Execution");
		logger.info("execution and around end");
	}
	//--Around
}

PointCut

- execution : 지정한 메서드가 일치하는 곳에서 Aspect 적용.

- within : 특정 타입에 대해서 Aspect 적용.

- bean : 스프링 빈 이름을 사용하여 빈의 메서드에 적용.

- 연산자를 통한 PointCut ( 예시 : @PointCut("executionTest() && withinTest()") )

 

Advice

-Before : 대상 메서드가 호출되기 전에 Advice 수행

-AfterReturning : 대상 메서드가 성공적으로 완료된 후에 Adivce 수행

-AfterThrowing : 대상 메서드가 예외를 던진 후에 Advice 수행

-After : 대상 메서드의 결과에 상관없이 Advice 수행 ( finally와 같이 최종적으로 수행 )

-Around : 대상 메서드를 감싸서 대상 메서드 호출 전과 후에 Advice 수행

 

Introductions

-DeclarParents : 인터페이스로 구현된 클래스를 대상 메서드의 부모로 추가할 수 있는 기능.

 

 

<!--pom.xml-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
	<version>2.4.2</version>
</dependency>
<!--service.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.xsd">
     <bean id="printCls" class="com.example.aop.PrintCls"></bean>
     <bean id="aspectJTest" class="com.example.aop.AspectJTest"></bean>
</beans>
//DemoApplication.java

@SpringBootApplication
@EnableAspectJAutoProxy
@ImportResource("classpath:service.xml")
public class DemoApplication {
	private static final Logger logger = LoggerFactory.getLogger(DemoApplication.class);

	public static void main(String[] args) {
		ApplicationContext run = SpringApplication.run(DemoApplication.class, args);
		
		PrintCls bean = run.getBean(PrintCls.class);

		logger.info("--------------    bean method start	--------------");
		bean.printExample();
		logger.info("--------------    bean method end	--------------");
		
		logger.info("--------------    bean return method start    --------------");
		bean.returnStr();
		logger.info("--------------    bean return method end    --------------");
		
		logger.info("--------------    bean throw method start    --------------");
		try {
			bean.returnThrow();
		} catch (Exception e) {
		}
		logger.info("--------------    bean throw method end    --------------");
	}
}
//PrintCls.java

package com.example.aop;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PrintCls{
	private static final Logger logger = LoggerFactory.getLogger(PrintCls.class);
	
	public void printExample() {
		logger.info("*** printExample");
	}
	
	public String returnStr() {
		String value = "testReturning";
		logger.info("*** print returning Str" + value );
		return value;
	}
	
	public void returnThrow() throws Exception {
		String msg = "execption test";
		logger.info("*** print throw " + msg);
		throw new Exception(msg);	
	}

}
//DelcarInterface.java

package com.example.aop;

public interface DelcarInterface {
	public void declarInterfaceTest();
}
//DeclarInterfaceImpl.java

package com.example.aop;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DeclarInterfaceImpl implements DelcarInterface {
	private static final Logger logger = LoggerFactory.getLogger(DeclarInterfaceImpl.class);
	
	@Override
	public void declarInterfaceTest() {
		logger.info("/////////////// declarInterfaceTest ///////////////////");
	}
}
//AspectJTest.java

package com.example.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.DeclareParents;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Aspect
public class AspectJTest {

	private static final Logger logger = LoggerFactory.getLogger(AspectJTest.class);

	//--PointCut--
	@Pointcut("execution(* com.example.aop.PrintCls.*(..))")
	public void executionTest() {
		logger.info("executeTest start");
		logger.info("executeTest end");
	}
	
	@Pointcut("within(com.example.aop.PrintCls)")
	public void withinTest() {
		logger.info("withinTest start");
		logger.info("withinTest end");
	}
	//--PointCut--
	
	//--Before--
	@Before("execution(* com.example.aop.PrintCls.*(..))")
	public void beforeTest() {
		logger.info("before start");
		logger.info("before end");
	}
	
	@Before("executionTest()")
	public void executionAndBefore() {
		logger.info("execution and Before start");
		logger.info("execution and Before end");
	}
	
	@Before("withinTest()")
	public void withinAndBefore() {
		logger.info("within and Before start");
		logger.info("within and Before end");
	}
	//--Before--
	
	
	//--AfterReturning--
	@AfterReturning("execution(* com.example.aop.PrintCls.*(..))")
	public void afterReturningTest() {
		logger.info("afterReturningTest start");
		logger.info("afterReturningTest end");
	}
	
	@AfterReturning("executionTest()")
	public void executionAndAfterReturning() {
		logger.info("execution and afterReturning start");
		logger.info("execution and afterReturning end");
	}
	
	@AfterReturning("withinTest()")
	public void withinAndAfterReturningTest() {
		logger.info("within and afterReturning start");
		logger.info("within and afterReturning end");
	}

	@AfterReturning(
			pointcut="executionTest()",
			returning="retVal")
	public void executionAndReturnValTest(String retVal) {
		logger.info("execution and afterReturning Value start");
		logger.info("Value :" + retVal);
		logger.info("execution and afterReturning Value end");
	}
	
	@AfterReturning(
			pointcut="withinTest()",
			returning="retVal")
	public void withinAndReturnValTest(String retVal) {
		logger.info("within and afterReturning Value start");
		logger.info("Value :" + retVal);
		logger.info("within and afterReturning Value end");
	}
	//--AfterReturning--
	
	//--AfterThrowing--
	 @AfterThrowing("executionTest()")
	 public void executionAndAfterThrowingTest() {
		 logger.info("execution and AfterThrowing start");
		 logger.info("execution and AfterThrowing end");
	 }
	 
	 @AfterThrowing("withinTest()")
	 public void withinAndAfterThrowingTest() {
		 logger.info("within and AfterThrowing start");
		 logger.info("within and AfterThrowing end");
	 }
	 
	 @AfterThrowing(
		        pointcut="executionTest()",
		        throwing="ex")
	 public void executionAndAfterThrowingValTest(Exception ex) {
		 logger.info("execution and AfterThrowing Value start");
		 logger.error(ex.getMessage());
		 logger.info("execution and AfterThorwing Value end");
	 }
	 
	 
	 @AfterThrowing(
		        pointcut="executionTest()",
		        throwing="ex")
	 public void withinAndAfterThrowingValTest(Exception ex) {
		 logger.info("execution and AfterThrowing Value start");
		 logger.error(ex.getMessage());
		 logger.info("execution and AfterThorwing Value end");
	 }
	 //--AfterThrowing--
	 
	 
	 //--After--
	 @After("executionTest()")
	 public void executionAndAfterTest() {
		 logger.info("execution and After start");
		 logger.info("execution and AFter end");
	 }
	 
	 @After("withinTest()")
	 public void withinAndAfterTest() {
		 logger.info("within and after start");
		 logger.info("within and after end");
	 }
	 //--After
	 
	 //--Around
	 @Around("executionTest()")
	 public void executionAndAroundTest(ProceedingJoinPoint  pjp){
		 logger.info("execution and around start");
		 logger.info(pjp.getSignature().getName() + " Before Method Execution");
		 try {
			 pjp.proceed();
		 } catch (Throwable e) {
			 logger.info(pjp.getSignature().getName() + "Throw Method Execution");
		 } finally {
			 // Do Something useful, If you have
		 }
		 logger.info(pjp.getSignature().getName() + " After Method Execution");
		 logger.info("execution and around end");
	 }
	 
	 @Around("withinTest()")
	 public void withinAndAroundTest(ProceedingJoinPoint  pjp) {
		 logger.info("within and around start");
		 logger.info(pjp.getSignature().getName() + " Before Method Execution");
		 try {
			 pjp.proceed();
		 } catch(Throwable e) {
			 logger.info(pjp.getSignature().getName() + "Throw Method Execution");
		 } finally {
			 // Do Something useful, If you have
		 }
		logger.info(pjp.getSignature().getName() + " After Method Execution");

		 logger.info("within and around end");
	 }
	 //--Around--
	 
	 
	 
	 //--Introductions ( DeclarParents )--
	 @DeclareParents(value="com.example.aop.PrintCls", defaultImpl=DeclarInterfaceImpl.class)
	 public static DelcarInterface mixin;

	 @Before("executionTest() && this(dInterface)")
	 public void declarTest(DelcarInterface dInterface) {
		 dInterface.declarInterfaceTest();
	 }
	 //--Introductions ( DeclarParents )--
}

AOP

- 기본적인 개념은 공통 관심 사항을 구현한 코드를 핵심로직을 구현한 코드 안에 삽입한다는 것.

   

AOP 용어

- Aspect : 공통 코드 ( ex : 트랙잭션, 로깅 )

- Advice : 공통코드를 적용하는 시점. ( 특정 메소드 실행전, 특정 메소드 실행후 등등 )

- Joinpoint : Advice가 적용 가능한 지점. 

- Pointcut : JointPoint중에서 Aspect가 적용되는 위치.

- Introduction : 함수나 멤버변수를 추가적으로 선언하는 기능

- Weaving : 핵심로직에서 Aspect가 동작이 되는 것.

    Weaving 방식

    1. 컴파일 시

    2. 클래스 로딩시

    3. 런타임 시

 

Spring에서 제공되는 AOP.

- @AspectJ ( 어노테이션 방식 )

- Schema-based AOP ( XML 방식 )



시스템의 요구사항이나 설계에 따라 상황에 맞는 AOP를 사용하면 되지만, 

Schema-based AOP가 가진 단점을 이해해야 한다.

 

1. XML방식이기 때문에 완전한 캡슐화 되지는 않는다.

- 설정파일에 XML과 빈(Bean)에 대한 선언이 분할되어 사용되기 때문이다.

2. XML에 선언 된 포인트 컷을 결합 할 수 없습니다. 

@Pointcut("execution(* get*())")
public void propertyAccess() {}

@Pointcut("execution(org.xyz.Account+ *(..))")
public void operationReturningAnAccount() {}

@Pointcut("propertyAccess() && operationReturningAnAccount()")
public void accountPropertyAccess() {}
<aop:pointcut id="propertyAccess"
        expression="execution(* get*())"/>

<aop:pointcut id="operationReturningAnAccount"
        expression="execution(org.xyz.Account+ *(..))"/>