[SPRING] AOP(Aspect Oriented Programming)
DAO database 연결 GUI(4)에 있음
DI
객체 조립기
Spring Bean Configuration - xml
Spring Boot
pojo + annotation 표기법
lifecycle
전처리와 후처리
* 의존성 자동주입
- AOP 구현법
DI
AspectJ + DI
Annoitation
약결합 구조
어드바이스가 어디에서 동작할지 지정
import확인 필수
< aop:around => 전처리, 후처리 모두 다 적용
AOP(Aspect Oriented Programming)
- 여러 객체 공통으로 적용할 수 있는 기능을 분리해서 재사용성을 높여주는 프로그래밍 기법
- 핵심기능과 공통기능의 구현을 분리함으로써 핵심기능을 구현한 코드의 수정 없이 공통기능을 적용할 수 있게 만들어줌
- 핵심기능에 공통 기능을 삽입한다. 즉 핵심 기능의 코드를 수정하지 않으면서 공통 기능의 구현을 추가하는 것
< 핵심 기능에 공통 기능을 삽입하는 방법 >
1. 컴파일 시점에 코드에 공통 기능을 삽입하는 방법
2. 클래스 로딩 시점에 바이트 코드에 공통 기능을 삽입하는 방법
3. 런타임에 프록시 객체를 생성해서 공통 기능을 삽입하는 방법
* 1, 2번은 Spring AOP에서는 지원하지 않으며 AspectJ와 같이 AOP 전용 도구를 사용해서 적용할 수 있다.
<!--AspectJ Maven pom.xml에 dependency 추가-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.20.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.20.1</version>
</dependency>
스프링 AOP 구현
- Aspect로 사용할 클래스에 @Aspect 어노테이션을 붙인다
- @Pointcut 어노테이션으로 공통기능을 적용할 Pointcut을 정의한다.
- 공통 기능을 구현한 메서드에 @Around 어노테이션을 적용한다.
AOP 어노테이션 알아보기
@Aspect
- @Aspect 어노테이션을 붙여 공통 기능을 제공하는 클래스를 만들면, 프록시는 스프링 프레임워크가 알아서 만든다.
- @Aspect 어노테이션을 적용한 클래스는 Advice와 Pointcut을 함께 제공한다.
@Pointcut
- 공통 기능을 적용할 대상을 설정
@Before("${pattern}")
- 지정한 패턴에 해당하는 메소드가 실행되기 전에 동작
- 해당 어노테이션이 붙은 메소드의 반환 값은 void여야 한다.
@After("${pattern}")
- 지정한 패턴에 해당하는 메소드가 실행된 후에 동작
- 해당 어노테이션이 붙은 메소드의 반환 값은 void여야 한다.
@Around("${pattern}")
- 지정된 패턴에 해당하는 메소드의 실행되기 전, 실행된 후 모두에서 동작
- 해당 어노테이션이 붙은 메소드의 반환 값은 Object여야 한다.
execution 명시자 표현식
- Aspect를 적용할 위치를 지정할 때 사용 (예제에서는 Around어노테이션에 사용하였다)
execution 명시자의 기본 형식
execution(수식어패턴? 리턴타입패턴 클래스이름패턴?메서드이름패턴(파라미터패턴))
- 각 패턴은 * 을 이용하여 모든 값을 표현할 수 있다. 또한 '..'을 이용하여 0개 이상이라는 의미를 표현할 수 있다.
EX | 설명 |
execution(public void set*(..)) | - 리턴 타입 void - 메서드 이름 set으로 시작 - 파라미터가 0개 이상인 메서드 호출 - 파라미터 부분에 '..'을 사용 => 파라미터가 0개 이상 |
execution(* chap07.*.*()) | - chap07 패키지의 타입에 속한 파라미터가 없는 모든 메서드 호출 |
execution(* chap07..*.*(..)) | - chap07 패기지 및 하위 패키지에 있음 (패키지 이름뒤에 ..를 사용해 해당 패키지 혹은 하위 패키지를 표현할 수 있다) - 파라미터가 0개 이상 |
execution(Long chap07.Calculator.factorial(..)) | - return 타입이 Long인 Calculator 타입의 factorial() 메서드 호출 |
execution(* get*(*,*)) | - 이름이 get으로 시작하고 파라미터가 2개인 메서드 호출 (* 한 개 붙이면 파라미터가 1개인 메서드 호출) |
execution(* read*(Integer, ..)) | - 메서드 이름이 read로 시작하고, 첫 번째 파라미터 타입이 정수형(Integer)이며, 한 개 이상(..로 표현)의 파라미터를 갖는 메서드 호출 |
Main 코드
//MainEx.java
package com.exam.aop03;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.core.io.FileSystemResource;
import com.exam.aop03.config.BeanConfig;
import com.exam.aop03.model.Action;
public class MainEx {
public static void main(String[] args) {
//annotation으로 선언
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BeanConfig.class);
//제네릭으로 사용
// GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(new FileSystemResource("./src/com/exam/aop03/context.xml"));
Action action = ctx.getBean("writeAction", Action.class);
action.execute();
ctx.close();
}
}
Advice 코드
//BasicAdvice1.java
package com.exam.aop03.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class BasicAdvice1 {
//이름이 execute이며 파라미터가 없는 메서드 호출
//execution(* exe*()) => 이름이 exe로 시작하고 파라미터가 없는 메서드 호출
@Around("execution(* execute())")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("전처리 구간1");
Object rtnObj = joinPoint.proceed();
System.out.println("후처리 구간1");
return rtnObj;
}
}
//BasicAdvice2.java
package com.exam.aop03.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class BasicAdvice2 {
@Around("execution(* execute())")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("전처리 구간2");
Object rtnObj = joinPoint.proceed();
System.out.println("후처리 구간2");
return rtnObj;
}
}
//BasicAdvice3.java
package com.exam.aop03.advice;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BasicAdvice3 {
//어노테이션을 통한 전처리/후처리 삽입(실행)
@Before("execution(* execute())")
public void before() throws Throwable{
System.out.println("전처리 실행");
}
@After("execution(* execute())")
public void after() throws Throwable{
System.out.println("후처리 실행");
}
}
Config코드
//BeanConfig.java
package com.exam.aop03.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import com.exam.aop03.advice.BasicAdvice1;
import com.exam.aop03.advice.BasicAdvice2;
import com.exam.aop03.advice.BasicAdvice3;
import com.exam.aop03.model.WriteAction;
@EnableAspectJAutoProxy
public class BeanConfig {
@Bean
public WriteAction writeAction() {
WriteAction writeAction = new WriteAction();
writeAction.setWriter("손수빈");
return writeAction;
}
// @Bean
// public BasicAdvice1 basicAdvice1() {
// return new BasicAdvice1();
// }
//
//
// @Bean
// public BasicAdvice2 basicAdvice2() {
// return new BasicAdvice2();
// }
@Bean
public BasicAdvice3 basicAdvice3() {
return new BasicAdvice3();
}
}
Model코드
//Action.java
package com.exam.aop03.model;
public interface Action {
void execute();
}
//WriteAction.java
package com.exam.aop03.model;
public class WriteAction implements Action {
private String writer;
public WriteAction() {
// TODO Auto-generated constructor stub
System.out.println("WriteAction() 생성자 호출");
}
public void setWriter(String writer) {
this.writer = writer;
}
// 핵심 기능
@Override
public void execute() {
// TODO Auto-generated method stub
System.out.println("execute() 시작");
System.out.println("Hello" + 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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
">
<!-- AOP 객체 생성 -->
<!-- 어노테이션을 사용하지 않고 bean을 context.xml에서 정의하여 사용할 때 작성함-->
<bean id="basicAdvice1" class="com.exam.aop03.advice.BasicAdvice1" />
<bean id="basicAdvice2" class="com.exam.aop03.advice.BasicAdvice2" />
<bean name="writeAction" class="com.exam.aop03.model.WriteAction" >
<property name="writer">
<value>홍길동</value>
</property>
</bean>
<!-- AOP 환경설정 -->
<!-- autoproxy로 어노테이션이 붙은 곳을 자동으로 처리 할 수 있도록 함-->
<aop:aspectj-autoproxy />
</beans>