절차지향프로그램의 함수는 저장해서 사용이 불가능하여 생산성 감소.
이를 보완하기 위해 클래스를 자료형으로 만들어 객체의 메소드로 구현하는 객체 지향 프로그램이 생성됨
but OOP는 모듈화가 너무 강력하여 핵심관심코드와 횡단관심코드를 분리하여 프로그램 작성이 어려움
코드가 지저분해짐, 재활용성이 저하됨
횡단관심코드 : 프로그램 실행에 보조적인 기능을 제공하는 명령
핵심관심코드 : 프로그램 실행에 핵심적인 기능을 제공하는 명령
Joinpoint : 횡단 관심 모듈의 기능이 삽입되어 동작할 수 있는 실행 가능한 특정위치 (핵심관심코드의 앞 or 뒤)
Pointcut : 어떤 클래스의 어느 Joinpoint(메소드)를 사용할 것인지를 결정하는 선택 기능
Advice : 각 Joinpoint에 삽입되어 동작할 수 있는 코드 (횡단관심코드)
Weaving : Pointcut에 의해서 결정된 조인포인트에 지정된 Advice를 삽입하는 과정
Introduction : 기존의 클래스와 인터페이스에 필요한 메소드나 필드를 추가해서 사용할 수 있게 해주는 방법
Aspect : Pointcut(어디에서)과 Advice(무엇을 할 것인지)를 합쳐놓은 것 (Proxy 클래스)
Proxy 클래스 : 핵심관심모듈의 메소드에 횡단관심모듈의 메소드를 삽입하여 동작하는 기능을 제공하는 클래스 (Aspect)
인터페이스의 추상메소드(PointCut)를 오버라이드 선언하여 핵심관심코드와 횡단관심코드를 결합하여 실행되도록 작성 (Weaving)
핵심관심모듈의 메소드 호출 전 또는 후에 횡단관심모듈의 모듈의 메소드를 호출하여 실행되도록 설정 (JoinPoint)
package xyz.itwill06.oop;
public class AopProxy implements Aop {
//핵심관심모듈로 선언된 클래스의 객체를 저장하기 위한 필드
// => 필드의 자료형을 인터페이스로 선언하여 모든 자식 클래스의 객체 저장 가능
private Aop target;
//횡단관심모듈로 선언된 클래스의 객체를 저장하기 위한 필드
private AopLogger logger;
//필드에 객체를 생성하여 저장하거나 매개변수로 객체를 전달받아 저장 - 의존성 주입(DI)
public AopProxy(Aop target) {
super();
this.target = target;
logger=new AopLogger();
}
//인터페이스의 추상메소드(PointCut)를 오버라이드 선언하여 핵심관심코드와 횡단관심코드를
//결합하여 실행되도록 작성 - Weaving
// => 핵심관심모듈의 메소드 호출 전 또는 후에 횡단관심모듈의 모듈의 메소드를 호출하여
//실행되도록 설정 - JoinPoint
@Override
public void display1() {
logger.beforeLog();
target.display1();
}
@Override
public void display2() {
logger.beforeLog();
target.display1();
}
@Override
public void display3() {
logger.beforeLog();
target.display1();
}
}
package xyz.itwill06.oop;
public class AopApp {
public static void main(String[] args) {
AopProxy one= new AopProxy(new AopOne());
AopProxy two= new AopProxy(new AopTwo());
one.display1();
one.display2();
one.display3();
two.display1();
two.display2();
two.display3();
}
}
package xyz.itwill07.aop;
import lombok.Data;
@Data
public class Student {
private int num;
private String name;
}
package xyz.itwill07.aop;
import java.util.List;
public interface StudentDAO {
int insertStudent(Student student);
Student selectStudent(int num);
List<Student> selectStudentList();
}
핵심관심모듈(Core Concern Module) : 핵심관심코드만 이용하여 메소드를 작성한 클래스
package xyz.itwill07.aop;
import java.util.List;
public class StudentDAOImpl implements StudentDAO {
@Override
public int insertStudent(Student student) {
System.out.println("*** StudentDAOImpl 클래스의 insertStudent(Student student) 메소드 호출 ***");
return 0;
}
@Override
public Student selectStudent(int num) {
System.out.println("*** StudentDAOImpl 클래스의 selectStudent(int num) 메소드 호출 ***");
return null;
}
@Override
public List<Student> selectStudentList() {
System.out.println("*** StudentDAOImpl 클래스의 selectStudentList() 메소드 호출 ***");
return null;
}
}
package xyz.itwill07.aop;
import java.util.List;
public interface StudentService {
void addStudent(Student student);
Student getStudent(int num);
List<Student> getStudentList();
}
package xyz.itwill07.aop;
import java.util.List;
import lombok.Setter;
@Setter
public class StudentServiceImpl implements StudentService {
private StudentDAO studentDAO;
@Override
public void addStudent(Student student) {
System.out.println("*** StudentServiceImpl 클래스의 addStudent(Student student) 메소드 호출 ***");
studentDAO.insertStudent(student);
}
@Override
public Student getStudent(int num) {
System.out.println("*** StudentServiceImpl 클래스의 getStudent(int num) 메소드 호출 ***");
return studentDAO.selectStudent(num);
}
@Override
public List<Student> getStudentList() {
System.out.println("*** StudentServiceImpl 클래스의 getStudentList() 메소드 호출 ***");
return studentDAO.selectStudentList();
}
}
횡단관심모듈(CrossCutting Concern Module) : 횡단관심코드만 이용하여 메소드를 작성한 클래스 (Advice 클래스)
JoinPoint : 핵심관심코드를 기준으로 횡단관심코드가 삽입되어 동작될 위치를 표현
타겟메소드(Target Method) : 핵심관심모듈의 메소드 중 PointCut 표현식으로 지정된 메소드
횡단관심코드가 삽입될 위치(JoinPoint)에 대한 하위 엘리먼트
public void aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {}
package xyz.itwill07.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class StudentAdvice {
private static final Logger logger=LoggerFactory.getLogger(StudentAdvice.class);
//타겟메소드의 명령 실행전에 삽입되어 실행될 명령을 작성한 메소드 - Before Advice 메소드
// => JoinPoint : 핵심관심코드를 기준으로 횡단관심코드가 삽입되어 동작될 위치를 표현
//타겟메소드(Target Method) : 핵심관심모듈의 메소드 중 PointCut 표현식으로 지정된 메소드
// => PointCut 표현식 : 핵심관심모듈의 메소드 중 원하는 메소드만 지정하기 위한 언어
public void beforeLog() {
logger.info("[before]핵심관심코드 동작 전 삽입되어 실행될 횡단관심코드");
}
//타겟메소드의 명령 실행후에 예외와 상관없이 무조건 삽입되어 실행될 명령을 작성한 메소드
// => After Advice 메소드
public void afterLog() {
logger.info("[after]핵심관심코드 동작 후 무조건 삽입되어 실행될 횡단관심코드");
}
//타겟메소드의 명령이 정상적으로 실행된 후에 삽입되어 실행될 명령을 작성한 메소드
// => After Returning Advice 메소드
public void afterReturningLog() {
logger.info("[after-returning]핵심관심코드가 정삭적으로 동작된 후 삽입되어 실행될 횡단관심코드");
}
//타겟메소드의 명령 실행시 예외가 발생된 경우에만 삽입되어 실행될 명령을 작성한 메소드
// => After Throwing Advice 메소드
public void afterThrowingLog() {
logger.info("[after-throwing]핵심관심코드 동작시 예외가 발생된 경우 삽입되어 실행될 횡단관심코드");
}
//타겟메소드의 명령이 실행 전과 후에 삽입되어 실행될 명령을 작성한 메소드
// => Around Advice 메소드
public void aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
logger.info("[around]핵심관심코드 동작 전 삽입되어 실행될 횡단관심코드");
joinPoint.proceed();//타겟메소드 호출 - 핵심관심코드 실행
logger.info("[around]핵심관심코드 동작 후 삽입되어 실행될 횡단관심코드");
}
}
핵심관심모듈로 작성된 클래스와 횡단관심모듈로 작성된 클래스를 Spring Bean으로 등록
Spring AOP(Aspect Oriented Programming - 관점 지향 프로그래밍) : AspectJ 컴파일러에 의해 프로그램 실행시 핵심관심코드와 횡단관심코드가 결합(Weaving)되어 동작하기 위한 기능 제공
Spring Bean Configuration File에서 Spring AOP 기능을 구현하고자 할 경우 AOP 네임스페이스를 추가하여 spring-aop.xsd 파일의 엘리먼트로 설정
<aop:config>
- aspect : 핵심관심코드에 횡단관심코드를 원하는 위치(JoinPoint)에 삽입되어 실행되도록 설정하기 위한 엘리먼트
- 횡단관심코드가 삽입될 위치(JoinPoint)를 하위 엘리먼트로 설정
- 하위 엘리먼트 : before, after, after-returning, after-throwing, around
- ref 속성 : 횡단관심모듈로 작성된 Advice 클래스에 대한 Spring Bean의 식별자(beanName)를 속성값으로 설정
<aop:aspect ref="studentAdvice">
- aspect의 하위 엘리먼트에 대한 속성
- method 속성 : Advice 클래스의 메소드명(횡단관심코드가 작성된 메소드)을 속성값으로 설정
- pointcut 속성 : 핵심관심모듈의 메소드 중 횡단관심모듈의 메소드가 삽입된 타겟메소드를 지정하기 위한 PointCut 표현식을 속성값으로 설정
- aspect의 하위 엘리먼트 종류
- before : 핵심관심코드 실행 전에 횡단관심코드를 삽입하여 실행되도록 설정하기 위한 엘리먼트
- pointcut-ref 속성 : pointcut 엘리먼트의 식별자를 속성값으로 설정
<aop:before method="beforeLog" pointcut-ref="studentDAOPointCut"/>
- after : 핵심관심코드 실행 후 예외 발생과 상관없이 무조건 횡단관심코드를 삽입하여 실행되도록 설정하기 위한 엘리먼트
- after-returning : 핵심관심코드가 정상적으로 실행된 후 횡단관심코드를 삽입하여 실행되도록 설정하기 위한 엘리먼트
- after-throwing : 핵심관심코드 실행시 예외가 발생된 후 횡단관심코드를 삽입하여 실행되도록 설정하기 위한 엘리먼트
- around : 핵심관심코드 실행 전과 후에 횡단관심코드를 삽입하여 실행되도록 설정하기 위한 엘리먼트
execution 함수 또는 within 함수에 검색패턴문자와 연산자를 사용하여 타겟메소드 지정
- execution 함수를 이용하여 타겟메소드를 지정하는 방법
- execution 함수에 메소드의 머릿부를 표현하여 타겟메소드 지정
- 형식) execution([접근지정자] 반환형 [패키지.클래스.]메소드명(자료형,자료형,...)
- 클래스 대신 인터페이스 사용 가능 (인터페이스를 상속받은 모든 자식클래스 표현)
- 반환형 또는 매개변수의 자료형이 클래스(인터페이스)인 경우 패키지를 포함하여 표현
<aop:before method="beforeLog" pointcut="execution(void addStudent(xyz.itwill07.aop.Student))"/>
<aop:before method="beforeLog" pointcut="execution(* *(..))"/>
<aop:before method="beforeLog" pointcut="execution(* xyz.itwill07.aop..*(..))"/>
<aop:before method="beforeLog" pointcut="execution(* xyz.itwill07.aop.StudentDAO.*(..))"/>
<aop:before method="beforeLog" pointcut="execution(xyz.itwill07.aop.Student *(..))"/>
<aop:before method="beforeLog" pointcut="execution(* get*(..))"/>
<aop:before method="beforeLog" pointcut="execution(* *(int)) or execution(int *(..))"/>
- within 함수를 이용하여 타겟메소드를 지정하는 방법
- Spring Bean으로 등록된 클래스(핵심관심모듈)의 모든 메소드를 타겟메소드로 지정
- 형식) within(패키지.클래스명)
- 클래스 대신 인터페이스 사용 불가능
<aop:before method="beforeLog" pointcut="within(xyz.itwill07.aop.StudentDAOImpl)"/>
<aop:pointcut expression="execution(* xyz.itwill07..StudentDAO.*(..))" id="studentDAOPointCut"/>
<?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.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 핵심관심모듈로 작성된 클래스를 Spring Bean으로 등록 -->
<bean class="xyz.itwill07.aop.StudentDAOImpl" id="studentDAO"/>
<bean class="xyz.itwill07.aop.StudentServiceImpl" id="studentService">
<property name="studentDAO" ref="studentDAO"/>
</bean>
<!-- 횡단관심모듈로 작성된 클래스를 Spring Bean으로 등록 -->
<bean class="xyz.itwill07.aop.StudentAdvice" id="studentAdvice"/>
<!-- config : Spring AOP 관련 설정을 제공하기 위한 엘리먼트 -->
<aop:config>
<!-- aspect : 핵심관심코드에 횡단관심코드를 원하는 위치(JoinPoint)에 삽입되어 실행
되도록 설정하기 위한 엘리먼트 -->
<!-- => 횡단관심코드가 삽입될 위치(JoinPoint)를 하위 엘리먼트로 설정 -->
<!-- => 하위 엘리먼트 : before, after, after-returning, after-throwing, around -->
<!-- ref 속성 : 횡단관심모듈로 작성된 Advice 클래스에 대한 Spring Bean의 식별자
(beanName)를 속성값으로 설정 -->
<aop:aspect ref="studentAdvice">
<!-- before : 핵심관심코드 실행 전에 횡단관심코드를 삽입하여 실행되도록 설정하기 위한 엘리먼트 -->
<!-- method 속성 : Advice 클래스의 메소드명(횡단관심코드가 작성된 메소드)을 속성값으로 설정 -->
<!-- pointcut 속성 : 핵심관심모듈의 메소드 중 횡단관심모듈의 메소드가 삽입된
타겟메소드를 지정하기 위한 PointCut 표현식을 속성값으로 설정 -->
<!-- => execution 함수 또는 within 함수에 검색패턴문자와 연산자를 사용하여 타겟메소드 지정 -->
<!-- => PointCut 표현식으로 사용 가능한 검색패턴문자 : ..(0개 이상), *(1개 이상), ?(0개 또는 1개) -->
<!-- => PointCut 표현식으로 사용 가능한 연산자 : !(Not), &&(And), ||(Or) -->
<!-- execution 함수를 이용하여 타겟메소드를 지정하는 방법 -->
<!-- => execution 함수에 메소드의 머릿부를 표현하여 타겟메소드 지정 -->
<!-- 형식)execution([접근지정자] 반환형 [패키지.클래스.]메소드명(자료형,자료형,...) -->
<!-- => 클래스 대신 인터페이스 사용 가능 - 인터페이스를 상속받은 모든 자식클래스 표현 -->
<!-- => 반환형 또는 매개변수의 자료형이 클래스(인터페이스)인 경우 패키지를 포함하여 표현 -->
<!-- <aop:before method="beforeLog" pointcut="execution(* *(..))"/> -->
<!-- within 함수를 이용하여 타겟메소드를 지정하는 방법 -->
<!-- => Spring Bean으로 등록된 클래스(핵심관심모듈)의 모든 메소드를 타겟메소드로 지정 -->
<!-- 형식)within(패키지.클래스명) -->
<!-- => 클래스 대신 인터페이스 사용 불가능 -->
<!-- <aop:before method="beforeLog" pointcut="within(xyz.itwill07.aop.StudentDAOImpl)"/> -->
<!-- <aop:before method="beforeLog" pointcut="within(xyz.itwill07.aop.StudentServiceImpl)"/> -->
<!-- pointcut : 핵심관심모듈의 메소드 중 횡단관심모듈의 메소드가 삽입된 타겟
메소드를 지정하기 위한 엘리먼트 -->
<!-- => 자주 사용되는 PointCut 표현식을 저장하여 타겟메소드에 대한 정보 제공 -->
<!-- => aspect 엘리먼트 선언 전에 작성하거나 aspect 엘리먼트의 하위 엘리먼트로 작성 -->
<!-- expression 속성 : 타겟메소드를 지정하기 위한 PointCut 표현식을 속성값으로 설정 -->
<!-- id 속성 : PointCut 표현식을 구분하기 위한 식별자를 속성값으로 설정 -->
<aop:pointcut expression="execution(* xyz.itwill07..StudentDAO.*(..))" id="studentDAOPointCut"/>
<aop:pointcut expression="execution(* xyz.itwill07..StudentService.*(..))" id="studentServicePointCut"/>
<!-- pointcut-ref 속성 : pointcut 엘리먼트의 식별자를 속성값으로 설정 -->
<!-- <aop:before method="beforeLog" pointcut-ref="studentDAOPointCut"/> -->
<aop:before method="beforeLog" pointcut-ref="studentServicePointCut"/>
<!-- after : 핵심관심코드 실행 후 예외 발생과 상관없이 무조건 횡단관심코드를 삽입
하여 실행되도록 설정하기 위한 엘리먼트 -->
<aop:after method="afterLog" pointcut-ref="studentServicePointCut"/>
<!-- after-returning : 핵심관심코드가 정상적으로 실행된 후 횡단관심코드를 삽입하여
실행되도록 설정하기 위한 엘리먼트 -->
<aop:after-returning method="afterReturningLog" pointcut-ref="studentServicePointCut"/>
<!-- after-throwing : 핵심관심코드 실행시 예외가 발생된 후 횡단관심코드를 삽입하여
실행되도록 설정하기 위한 엘리먼트 -->
<aop:after-throwing method="afterThrowingLog" pointcut-ref="studentServicePointCut"/>
<!-- around : 핵심관심코드 실행 전과 후에 횡단관심코드를 삽입하여 실행되도록 설정하기
위한 엘리먼트 -->
<aop:around method="aroundLog" pointcut-ref="studentServicePointCut"/>
</aop:aspect>
</aop:config>
</beans>
Spring AOP 기능을 사용하기 위해서는 aspectjrt 라이브러리와 aspectjweaver 라이브러리가 프로젝트에 빌드 처리
<!-- AspectJ -->
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<!-- => AspectJ로 생성된 결과물(핵심관심코드+횡단관심코드)를 실행하기 위한 라이브러리 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<!-- => AspectJ를 사용하여 핵심관심코드와 횡단관심코드가 결합된 결과물을 생성하기 위한 라이브러리 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${org.aspectj-version}</version>
<scope>runtime</scope>
</dependency>
package xyz.itwill07.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class StudentAopApp {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("07-1_aop.xml");
StudentService service=context.getBean("studentService", StudentService.class);
System.out.println("================================================================");
service.addStudent(null);
System.out.println("================================================================");
service.getStudent(0);
System.out.println("================================================================");
service.getStudentList();
System.out.println("================================================================");
((ClassPathXmlApplicationContext)context).close();
}
}
root 엘리먼트의 속성값을 warn으로 설정하였으므로 warn 로그 이벤트보다 상위의 이벤트가 발생된 경우에만 로그 구현체로 기록된다.
원하는 패키지에서 발생되는 info 레벨의 로그 이벤트를 기록하기 위해서는 logger 엘리먼트를 추가해야한다.
<!-- logger : 특정 패키지의 클래스에서 발생되는 로그 이벤트를 기록하기 위한 엘리먼트 -->
<!-- name 속성 : 패키지 경로를 속성값으로 설정 -->
<logger name="xyz.itwill07.aop">
<level value="info" />
</logger>
<!-- root : 모든 클래스에서 발생되는 로그 이벤트를 기록하기 위한 엘리먼트 -->
<!-- => 모든 logger 엘리먼트는 root 엘리먼트의 정보를 상속받아 사용 -->
<root>
<!-- priority : 모든 클래스에서 발생되는 기본 로그 이벤트를 설정하기 위한 엘리먼트 -->
<!-- value 속성 : 로그 이벤트를 속성값으로 설정 -->
<!-- => 속성값으로 설정된 로그 이벤트보다 상위의 이벤트가 발생된 경우 로그 구현체로 기록 -->
<priority value="warn" />
<!-- appender-ref : 로그 구현체를 참조하기 위한 엘리먼트 -->
<!-- ref 속성 : appender 엘리먼트의 식별자를 속성값으로 설정 -->
<appender-ref ref="console" />
</root>
package xyz.itwill07.aop;
public class JoinPointBean {
public void add() {
System.out.println("### JoinPointBean 클래스의 add() 메소드 호출 ###");
}
public void modify(int num, String name) {
System.out.println("### JoinPointBean 클래스의 modify(int num, String name) 메소드 호출 ###");
}
public void remove(int num) {
System.out.println("### JoinPointBean 클래스의 remove(int num) 메소드 호출 ###");
}
public String getName() {
System.out.println("### JoinPointBean 클래스의 getName() 메소드 호출 ###");
return "홍길동";
}
public void calc(int num1, int num2) {
System.out.println("### JoinPointBean 클래스의 calc(int num1, int num2) 메소드 호출 ###");
System.out.println("몫 = "+(num1/num2));
}
}
Around Advice 메소드를 제외한 나머지 Advice 메소드는 일반적으로 반환형을 void로 작성하고 매개변수를 작성하지 않거나 JoinPoint 인터페이스로 선언된 매개변수 작성 가능
JoinPoint 객체 : 타겟메소드 관련 정보가 저장된 객체
- 타겟메소드의 클래스 이름
- JoinPoint.getTarget() : 타겟메소드를 호출한 객체(Spring Bean)를 반환하는 메소드
- Object.getClass() : 객체를 생성한 클래스의 Class 객체(Clazz)를 반환하는 메소드
- Class.getName() : Class 객체에 저장된 클래스의 이름(패키지 포함)을 문자열로 반환하는 메소드
System.out.println(joinPoint.getTarget().getClass().getName());
- Class.getSimpleName() : Class 객체에 저장된 클래스의 이름(패키지 미포함)을 문자열로 반환하는 메소드
- 타겟메소드 이름
- JoinPoint.getSignature() : 타겟메소드의 정보가 저장된 Signature 객체를 반환하는 메소드
- Signature.getName() : 타겟메소드의 이름을 문자열로 반환하는 메소드
System.out.println(joinPoint.getSignature().getName());
- 타겟메소드의 매개변수에 저장된 값
- JoinPoint.getArgs() : 타겟메소드의 매개변수에 저장된 모든 값(객체)을 제공받아 Object객체 배열로 반환하는 메소드
System.out.println(joinPoint.getArgs());
( [L : 객체 배열 )
After Returning Advice 메소드에는 JoinPoint 인터페이스의 매개변수 외에 Object 클래스의 매개변수 선언 가능
After Throwing Advice 메소드에는 JoinPoint 인터페이스의 매개변수 외에 Exception 클래스의 매개변수 선언 가능
Around Advice 메소드는 반환형을 Object 클래스로 작성하고 매개변수의 자료형은 ProceedingJoinPoint 인터페이스로 작성
package xyz.itwill07.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
//횡단관심모듈 - Advice 클래스
public class JoinPointAdvice {
//Around Advice 메소드를 제외한 나머지 Advice 메소드는 일반적으로 반환형을 void로 작성하고
//매개변수를 작성하지 않거나 JoinPoint 인터페이스로 선언된 매개변수 작성 가능
// => Advice 메소드를 작성 규칙에 맞지 않게 작성할 경우 IllegalArgumentException 발생
//JoinPoint 객체 : 타겟메소드 관련 정보가 저장된 객체
// => 스프링 컨테이너가 Advice 클래스의 메소드를 호출할 때 타겟메소드 관련 정보를 JoinPoint
//객체에 저장하여 매개변수에 전달
// => Advice 클래스의 메소드에서 타겟메소드 관련 정보가 필요한 경우 매개변수를 선언하여 사용
//Before Advice 메소드
public void beforeDisplay(JoinPoint joinPoint) {
//System.out.println("[before]핵심관심코드 실행 전에 삽입되어 실행될 횡단관심코드");
//JoinPoint.getTarget() : 타겟메소드를 호출한 객체(Spring Bean)를 반환하는 메소드
//Object.getClass() : 객체를 생성한 클래스의 Class 객체(Clazz)를 반환하는 메소드
//Class.getName() : Class 객체에 저장된 클래스의 이름(패키지 포함)을 문자열로 반환하는 메소드
//System.out.println(joinPoint.getTarget().getClass().getName());
//Class.getSimpleName() : Class 객체에 저장된 클래스의 이름(패키지 미포함)을 문자열로 반환하는 메소드
//System.out.println(joinPoint.getTarget().getClass().getSimpleName());
//JoinPoint.getSignature() : 타겟메소드의 정보가 저장된 Signature 객체를 반환하는 메소드
//Signature.getName() : 타겟메소드의 이름을 문자열로 반환하는 메소드
//System.out.println(joinPoint.getSignature().getName());
//JoinPoint.getArgs() : 타겟메소드의 매개변수에 저장된 모든 값(객체)을 제공받아 Object
//객체 배열로 반환하는 메소드
//System.out.println(joinPoint.getArgs());
String className=joinPoint.getTarget().getClass().getSimpleName();
String methodName=joinPoint.getSignature().getName();
Object[] params=joinPoint.getArgs();
System.out.print("[before]"+className+" 클래스의 "+methodName+"(");
for(int i=0;i<params.length;i++) {
System.out.print(params[i]);
if(i<params.length-1) {
System.out.print(", ");
}
}
System.out.println(") 메소드 호출");
}
//After Advice 메소드
public void displayMessage(JoinPoint joinPoint) {
//System.out.println("[after]핵심관심코드 실행 후에 무조건 삽입되어 실행될 횡단관심코드");
Object[] params=joinPoint.getArgs();
System.out.println("[after]학번이 "+params[0]+"인 학생정보를 삭제 하였습니다.");
}
//After Returning Advice 메소드에는 JoinPoint 인터페이스의 매개변수 외에 Object 클래스의
//매개변수 선언 가능
// => 스프링 컨테이너는 Object 클래스의 매개변수에 타겟메소드의 반환값이 저장되도록 전달
// => 타겟메소드에서 반환되는 값(객체)의 자료형이 고정되어 있는 경우 Object 클래스 대신
//반환되는 값(객체)의 자료형으로 매개변수 작성 가능
// => Spring Bean Configuration File의 AOP 설정에서 after-returning 엘리먼트에 반드시
//returning 속성을 사용하여 반환값을 저장할 매개변수의 이름을 속성값으로 지정
// => after-returning 엘리먼트에 returning 속성이 없거나 속성값이 잘못 설정된 경우 IllegalArgumentException 발생
//After Returning Advice 메소드
public void displayName(Object object) {
//System.out.println("[after-returning]핵심관심코드가 정상적으로 실행된 후에 삽입되어 실행될 횡단관심코드");
//instanceof 연산자를 사용하여 매개변수에 저장된 객체의 자료형을 구분하여 처리
if(object instanceof String) {
String name=(String)object;//명시적 객체 형변환
System.out.println("[after-returning]"+name+"님, 안녕하세요.");
}
}
//After Throwing Advice 메소드에는 JoinPoint 인터페이스의 매개변수 외에 Exception 클래스의
//매개변수 선언 가능
// => 스프링 컨테이너는 Exception 클래스의 매개변수에 타겟메소드의 명령 실행시 발생된
//예외(Exception 객체)가 저장되도록 전달
// => 타겟메소드에서 발생되는 예외가 고정되어 있는 경우 Exception 클래스 대신 자식클래스로
//선언된 매개변수 작성 가능
// => Spring Bean Configuration File의 AOP 설정에서 after-throwing 엘리먼트에 반드시
//throwing 속성을 사용하여 Exception 객체를 저장할 매개변수의 이름을 속성값으로 지정
// => after-throwing 엘리먼트에 throwing 속성이 없거나 속성값이 잘못 설정된 경우 IllegalArgumentException 발생
//After Throwing Advice 메소드
public void exceptionHandle(JoinPoint joinPoint, Exception exception) {
//System.out.println("[after-throwing]핵심관심코드 실행시 예외가 발생된 경우 삽입되어 실행될 횡단관심코드");
String className=joinPoint.getTarget().getClass().getSimpleName();
String methodName=joinPoint.getSignature().getName();
System.out.println("[after-throwing]"+className+" 클래스의 "+methodName
+" 메소드에서 발생된 예외 = "+exception.getMessage());
}
//Around Advice 메소드는 반환형을 Object 클래스로 작성하고 매개변수의 자료형은
//ProceedingJoinPoint 인터페이스로 작성
// => 타겟메소드의 반환값을 제공받아 반환하기 위해 Object 클래스를 반환형으로 작성
// => 타겟메소드 관련 정보를 ProceedingJoinPoint 인터페이스의 매개변수로 제공받아
//Around Advice 메소드에서 사용
//ProceedingJoinPoint : 타겟메소드 관련 정보를 저장하기 위한 객체
// => JoinPoint 객체와 다른점은 타겟메소드를 직접 호출하기 위한 메소드 제공
//Around Advice 메소드
public Object disply(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("[around]핵심관심코드 실행 전 삽입되어 실행될 횡단관심코드");
//ProceedingJoinPoint.proceed() : 타겟메소드를 호출하는 메소드 - 핵심관심코드 실행
// => 타겟메소드를 호출하여 반환되는 결과값을 제공받아 저장
// => Throwable(Error 클래스와 Exception 클래스의 부모클래스) 객체(예외)가 발생되므로
//예외를 처리하거나 예외를 전달
Object object=joinPoint.proceed();
System.out.println("[around]핵심관심코드 실행 후 삽입되어 실행될 횡단관심코드");
return object;//타겟메소드를 호출하여 반환된 결과값을 메소드를 호출한 명령으로 반환
}
}
After Returning Advice 메소드에서 Object 클래스의 매개변수 선언 시 Spring Bean Configuration File의 AOP 설정에서 after-returning 엘리먼트에 반드시 returning 속성을 사용하여 반환값을 저장할 매개변수의 이름을 속성값으로 지정
<aop:after-returning method="displayName" pointcut="execution(java.lang.String getName())" returning="object"/>
After Throwing Advice 메소드에서 Exception 클래스의 매개변수 선언 시 Spring Bean Configuration File의 AOP 설정에서 after-throwing 엘리먼트에 반드시 throwing 속성을 사용하여 Exception 객체를 저장할 매개변수의 이름을 속성값으로 지정
<?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.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 핵심관심모듈로 선언된 클래스를 Spring Bean으로 등록 -->
<bean class="xyz.itwill07.aop.JoinPointBean" id="joinPointBean"/>
<!-- 횡단관심모듈로 선언된 클래스를 Spring Bean으로 등록 -->
<bean class="xyz.itwill07.aop.JoinPointAdvice" id="joinPointAdvice"/>
<aop:config>
<aop:aspect ref="joinPointAdvice">
<aop:before method="beforeDisplay" pointcut="execution(* *(..))"/>
<aop:after method="displayMessage" pointcut="execution(void remove(int))"/>
<!-- returning 속성 : 타겟메소드의 반환값을 전달받아 저장할 매개변수의 이름을 속성값으로 설정 -->
<aop:after-returning method="displayName"
pointcut="execution(java.lang.String getName())" returning="object"/>
<aop:after-throwing method="exceptionHandle"
pointcut="execution(void calc(int, int))" throwing="exception"/>
<aop:around method="disply" pointcut="execution(* modify(..))"/>
</aop:aspect>
</aop:config>
</beans>
package xyz.itwill07.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class JoinPointApp {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("07-2_param.xml");
JoinPointBean bean=context.getBean("joinPointBean", JoinPointBean.class);
System.out.println("================================================================");
bean.add();
System.out.println("================================================================");
bean.modify(1000, "홍길동");
System.out.println("================================================================");
bean.remove(2000);
System.out.println("================================================================");
bean.getName();
System.out.println("================================================================");
//bean.calc(20, 10);
bean.calc(20, 0);//ArithmeticException 발생
System.out.println("================================================================");
((ClassPathXmlApplicationContext)context).close();
}
}
System.currentTimeMillis() : 시스템의 현재 날짜와 시간에 대한 타입스템프를 반환하는 메소드
메소드가 실행되는데 걸리는 시간을 출력하는 클래스
package xyz.itwill07.aop;
public class ExecutionTimeBean {
public void one() {
//long startTime=System.currentTimeMillis();
long count=0;
for(long i=1;i<=10000000000L;i++) {
count++;
}
System.out.println("count = "+count);
//long endTime=System.currentTimeMillis();
//System.out.println("ExecutionTimeBean 클래스의 one 메소드 실행 시간 = "+(endTime-startTime)+"ms");
}
public void two() {
//long startTime=System.currentTimeMillis();
long count=0;
for(long i=1;i<=20000000000L;i++) {
count++;
}
System.out.println("count = "+count);
//long endTime=System.currentTimeMillis();
//System.out.println("ExecutionTimeBean 클래스의 two 메소드 실행 시간 = "+(endTime-startTime)+"ms");
}
}
타겟메소드의 명령이 실행되는 처리시간을 계산하여 출력하기 위한 메소드 (Around Advice Method)
StopWatch 객체 : 시간을 측정하기 위한 기능을 제공하기 위한 객체
package xyz.itwill07.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;
public class ExecutionTimeAdvice {
public Object timeWatchLog(ProceedingJoinPoint joinPoint) throws Throwable {
//타겟메소드의 명령 실행전에 동작될 명령 작성
//long startTime=System.currentTimeMillis();
//StopWatch 객체 : 시간을 측정하기 위한 기능을 제공하기 위한 객체
StopWatch stopWatch=new StopWatch();
//stopWatch.start() : 시간 측정을 시작하는 메소드
stopWatch.start();
//타겟메소드 호출하여 명령 실행
Object object=joinPoint.proceed();
//타겟메소드의 명령 실행후에 동작될 명령 작성
//long endTime=System.currentTimeMillis();
//StopWatch.stop() : 시간 측정을 종료하는 메소드
stopWatch.stop();
String className=joinPoint.getTarget().getClass().getSimpleName();
String methodName=joinPoint.getSignature().getName();
//System.out.println(className+" 클래스의 "+methodName+" 메소드 실행 시간 = "+(endTime-startTime)+"ms");
//StopWatch.getTotalTimeMillis() : 측정된 시간을 ms 단위로 반환하는 메소드
System.out.println(className+" 클래스의 "+methodName+" 메소드 실행 시간 = "
+stopWatch.getTotalTimeMillis()+"ms");
return object;
}
}
<?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.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean class="xyz.itwill07.aop.ExecutionTimeBean" id="executionTimeBean"/>
<bean class="xyz.itwill07.aop.ExecutionTimeAdvice" id="executionTimeAdvice"/>
<aop:config>
<aop:aspect ref="executionTimeAdvice">
<aop:around method="timeWatchLog" pointcut="within(xyz.itwill07.aop.ExecutionTimeBean)"/>
</aop:aspect>
</aop:config>
</beans>
package xyz.itwill07.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ExecutionTimeApp {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("07-3_timer.xml");
ExecutionTimeBean bean=context.getBean("executionTimeBean", ExecutionTimeBean.class);
System.out.println("================================================================");
bean.one();
System.out.println("================================================================");
bean.two();
System.out.println("================================================================");
((ClassPathXmlApplicationContext)context).close();
}
}
Java Mail 기능을 구현하기 위해서는 spring-context-support 라이브러리와 javax.mail 라이브러리가 프로젝트에 빌드되도록 처리
spring-context-support : Spring Context 기능을 지원하는 라이브러리 (springframework와 동일한 version 사용)
javax.mail : Java Mail 기능을 제공하는 라이브러리
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context-support -->
<!-- => Spring Context 기능을 지원하는 라이브러리 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.sun.mail/javax.mail -->
<!-- => Java Mail 기능을 제공하는 라이브러리 -->
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
메일 전송 기능을 제공하는 클래스 (메일 서버의 SMTP 서비스를 사용하여 메일 전송)
private JavaMailSender javaMailSender;
메일을 전송하는 메소드
public String sendEmail(String email, String subject, String content) {}
- JavaMailSender.createMimeMessage() : MimeMessage 객체를 생성하여 반환하는 메소드
- MimeMessage 객체 : 메일 전송 관련 정보를 저장하기 위한 객체
- MimeMessage.setSubject(subject) : 전송할 메일의 제목을 변경하는 메소드
- MimeMessage.setText(content) : 전송할 메일의 내용(텍스트 메소드)을 변경하는 메소드
- MimeMessage.setContent(Object o, String type) : 전송할 메일의 내용을 변경하는 메소드 (type 매개변수에 문서의 형식(MimeType)을 전달하여 저장 )
message.setContent(content, "text/html; charset=utf-8");
- MimeMessage.setRecipient(RecipientType type, Address address) : 받는 사람의 이메일 주소 관련 정보를 변경하는 메소드
- RecipientType : 메일 수신 사용자를 구분하기 위한 상수값 전달
- Address : 이메일 주소가 저장된 Address 객체를 전달
- InternetAddress : 이메일 주소를 저장하기 위한 클래스 (Address 추상 클래스를 상속받은 자식클래스)
- InternetAddress.parse(String email) : 문자열로 전달된 이메일 주소를 Address 객체 배열로 반환
message.setRecipient(MimeMessage.RecipientType.TO, InternetAddress.parse(email)[0]);
JavaMailSender.send(MimeMessage message) : 메일을 보내는 서비스를 이용하여 메일을 전송하는 메소드
메일을 보내는 사람의 정보도 설정 가능
package xyz.itwill07.aop;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.springframework.mail.javamail.JavaMailSender;
import lombok.Setter;
//Java Mail 기능을 구현하기 위해서는 spring-context-support 라이브러리와 javax.mail 라이브러리가
//프로젝트에 빌드되도록 처리 - 메이븐사용 : pom.xml
//메일 전송 기능을 제공하는 클래스 - 메일 서버의 SMTP 서비스를 사용하여 메일 전송
// => 메일 서버(Mail Server) : 메일을 송수신하는 서비스를 제공하는 컴퓨터
// => SMTP(Simple Message Transfer Protocol) 서비스로 메일을 보내고 POP3(Post Office Protocol 3)
//서비스나 IMAP(Internet Message Access Protocol) 서비스로 메일을 받아 사용자에게 전달
@Setter
public class EmailSendBean {
//메일을 전송하는 서비스(SMTP)를 제공하는 서버의 정보가 저장된 JavaMailSender 객체를 저장하기 위한 필드 선언
private JavaMailSender javaMailSender;
//메일을 전송하는 메소드
// => 메일을 받는 사람의 이메일 주소, 제목, 내용을 매개변수로 전달받아 저장
// => 메일을 받는 사람의 이메일 주소를 반환
public String sendEmail(String email, String subject, String content) throws Exception {
//JavaMailSender.createMimeMessage() : MimeMessage 객체를 생성하여 반환하는 메소드
//MimeMessage 객체 : 메일 전송 관련 정보를 저장하기 위한 객체
MimeMessage message=javaMailSender.createMimeMessage();
//MimeMessage.setSubject(subject) : 전송할 메일의 제목을 변경하는 메소드
message.setSubject(subject);
//MimeMessage.setText(content) : 전송할 메일의 내용(텍스트 메세지)을 변경하는 메소드
//message.setText(content);
//MimeMessage.setContent(Object o, String type) : 전송할 메일의 내용을 변경하는 메소드
// => type 매개변수에 문서의 형식(MimeType)을 전달하여 저장
message.setContent(content, "text/html; charset=utf-8");
//MimeMessage.setRecipient(RecipientType type, Address address) : 받는 사람의
//이메일 주소 관련 정보를 변경하는 메소드
// => RecipientType : 메일 수신 사용자를 구분하기 위한 상수값 전달
// => Address : 이메일 주소가 저장된 Address 객체를 전달
//InternetAddress : 이메일 주소를 저장하기 위한 클래스 - Address 클래스를 상속받은 자식클래스
message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(email));
//MimeMessage.setRecipients(RecipientType type, Address[] addresses) : 받는 사람의
//이메일 주소 관련 정보를 변경하는 메소드 - 다수의 메일을 전송하는 메소드
//InternetAddress.parse(String emailList) : 문자열로 전달된 이메일 주소 목록을 Address 객체 배열로 반환
//message.setRecipients(MimeMessage.RecipientType.TO, InternetAddress.parse(email));
//JavaMailSender.send(MimeMessage message) : 메일을 보내는 서비스를 이용하여 메일을 전송하는 메소드
javaMailSender.send(message);
System.out.println("[메세지]메일을 성공적으로 전송 하였습니다.");
return email;
}
}
메일을 전송하기 전에 삽입되어 실행될 명령이 작성된 메소드 (Before Advice 메소드)
메일 전송이 성공한 경우 삽입되어 실행될 명령이 작성된 메소드 (After Returning Advice 메소드)
예외가 발생되어 메일 전송이 실패한 경우 삽입되어 실행될 명령이 작성된 메소드 (After Throwing 메소드)
package xyz.itwill07.aop;
import org.aspectj.lang.JoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EmailSendAdvice {
private static final Logger logger=LoggerFactory.getLogger(EmailSendAdvice.class);
//메일을 전송하기 전에 삽입되어 실행될 명령이 작성된 메소드 - Before Advice 메소드
// => 받는 사람의 이메일 주소와 제목을 얻어와 저장하기 위해 JointPoint 매개변수 필요
public void accessLog(JoinPoint joinPoint) {
//타겟메소드(sendEmail)의 매개변수에 저장된 값(객체)를 반환받아 저장
String email=(String)joinPoint.getArgs()[0];//받는 사람의 이메일 주소
String subject=(String)joinPoint.getArgs()[1];//메일 제목
logger.info(email+"님에게 <"+subject+"> 제목을 이메일을 전송합니다.");
}
//메일 전송이 성공한 경우 삽입되어 실행될 명령이 작성된 메소드 - After Returning Advice 메소드
// => 타겟메소드(sendEmail)의 반환값(받는 사람의 이메일 주소)을 얻어와 저장하기 위해 매개변수 필요
public void successLog(String email) {
logger.info(email+"님에게 이메일을 성공적으로 전송 하였습니다.");
}
//예외가 발생되어 메일 전송이 실패한 경우 삽입되어 실행될 명령이 작성된 메소드 - After Throwing 메소드
// => 타겟메소드(sendEmail)의 명령 실행시 발생된 예외(Exception 객체)를 얻어와 저장하기 위한 매개변수 필요
public void errorLog(Exception exception) {
logger.info("이메일 전송 실패 = "+exception.getMessage());
}
}
SMPT 서비스를 제공하는 메일 서버의 정보를 JavaMailSenderImpl 객체 필드에 저장되도록 값 주입
<bean class="org.springframework.mail.javamail.JavaMailSenderImpl" id="javaMailSender">
<?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.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- JavaMailSender 인터페이스를 상속받은 JavaMailSenderImpl 클래스를 Spring Bean으로 등록 -->
<!-- => SMPT 서비스를 제공하는 메일 서버의 정보를 JavaMailSenderImpl 객체 필드에 저장되도록 값 주입 -->
<bean class="org.springframework.mail.javamail.JavaMailSenderImpl" id="javaMailSender">
<!-- host 필드 : SMTP 서비스를 제공하는 메일 서버의 이름을 전달하여 저장 -->
<property name="host" value="smtp.gmail.com"/>
<!-- port 필드 : SMTP 서비스를 제공하는 메일 서버의 POST 번호를 전달하여 저장 -->
<property name="port" value="587"/>
<!-- username 필드 : SMTP 서비스를 제공하는 메일 서버의 접속 사용자 이름(아이디)을 전달하여 저장 -->
<property name="username" value="userid"/>
<!-- password 필드 : SMTP 서비스를 제공하는 메일 서버의 접속 사용자 비밀번호를 전달하여 저장 -->
<!-- => 사용자 비밀번호 대신 앱 비밀번호를 필드에 저장 -->
<!-- 구글의 SMTP 서비스를 제공받기 위해서는 계정의 2단계 보안 인증 후 앱 비밀번호를 발급받아 사용 -->
<!-- => 구글 계정 관리에서 보안 메뉴에 앱 비밀번호를 생성하여 사용 -->
<property name="password" value="bzrpsglztzzylvrh"/>
<!-- javaMailProperties 필드 : SMTP 서비스를 제공하는 메일 서버의 메일 전송 관련 부가적인
정보를 Properties 객체로 전달하여 저장 -->
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.ssl.trust">smtp.gmail.com</prop><!-- -->
<prop key="mail.smtp.starttls.enable">true</prop>
<prop key="mail.smtp.auth">true</prop>
<!--인증서(ssl)를 통해 발급받아 실뢰할 수 있도록 암호화(starttls) 처리 -->
</props>
</property>
</bean>
<!-- 핵심관심모듈로 작성된 클래스를 Spring Bean으로 등록 -->
<!-- => javaMailSender 필드에 JavaMailSender 객체가 저장되도록 의존성 주입 -->
<bean class="xyz.itwill07.aop.EmailSendBean" id="emailSendBean">
<property name="javaMailSender" ref="javaMailSender"/>
</bean>
<!-- 횡단관심모듈로 작성된 클래스를 Spring Bean으로 등록 -->
<bean class="xyz.itwill07.aop.EmailSendAdvice" id="emailSendAdvice"/>
<aop:config>
<aop:aspect ref="emailSendAdvice">
<aop:before method="accessLog" pointcut="execution(* sendEmail(..))"/>
<aop:after-returning method="successLog" pointcut="execution(* sendEmail(..))" returning="email"/>
<aop:after-throwing method="errorLog" pointcut="execution(* sendEmail(..))" throwing="exception"/>
</aop:aspect>
</aop:config>
</beans>
package xyz.itwill07.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class EmailSendApp {
public static void main(String[] args) throws Exception {
ApplicationContext context=new ClassPathXmlApplicationContext("07-4_email.xml");
EmailSendBean bean=context.getBean("emailSendBean", EmailSendBean.class);
System.out.println("================================================================");
bean.sendEmail("kjk2875@google.com", "메일 전송 테스트", "javaMail 기능을 사용하여 전달된 이메일입니다.");
System.out.println("================================================================");
((ClassPathXmlApplicationContext)context).close();
}
}
package xyz.itwill07.aop;
import org.springframework.stereotype.Component;
@Component
public class AopAnnotationBean {
public void display1() {
System.out.println("### AopAnnotationBean 클래스의 display1() 메소드 호출 ###");
}
public void display2() {
System.out.println("### AopAnnotationBean 클래스의 display2() 메소드 호출 ###");
}
public void display3() {
System.out.println("### AopAnnotationBean 클래스의 display3() 메소드 호출 ###");
throw new RuntimeException();//인위적 예외 발생
}
}
- @Aspect: 핵심관심코드에 횡단관심코드를 삽입하여 실행하기 위한 기능을 제공하는 어노테이션
- Spring Bean Configuration File의 aspect 엘리먼트와 유사한 기능 제공
- @Pointcut : 타겟메소드를 지정하기 위한 어노테이션
- 메소드 호출을 이용하여 타겟메소드를 지정한 PointCut 표현식을 제공하기 위해 사용
- Spring Bean Configuration File의 pointcut 엘리먼트와 유사한 기능 제공
- value 속성 : 타겟메소드를 지정하기 위한 PointCut 표현식을 속성값으로 설정 (다른 속성이 없는 경우 속성값만 설정 가능)
- @Before : 핵심관심코드 실행 전에 횡단관심코드를 실행하는 기능을 제공하는 어노테이션
- Spring Bean Configuration File의 before 엘리먼트와 유사한 기능 제공 (다른 속성이 없는 경우 속성값만 설정 가능)
@Before(value = "within(xyz.itwill07.aop.AopAnnotationBean)")
- value 속성값으로 @Pointcut 어노테이션을 사용한 메소드를 호출하면 등록된 PointCut 표현식을 제공받아 사용 가능
@Before("aopPointCut()")
- @After : 핵심관심코드 실행 후 무조건 횡단관심코드를 실행하는 기능을 제공하는 어노테이션
- @AfterReturning : 핵심관심코드가 정상적으로 실행된 후 횡단관심코드를 실행하는 기능을 제공하는 어노테이션
- returning 속성 : 타겟메소드의 반환값을 저장하기 위한 매개변수의 이름을 속성값으로 설정
@AfterReturning(value="aopPointCut()",returning = "object")
- @AfterThrowing : 핵심관심코드 실행시 예외가 발생된 경우 횡단관심코드를 실행하는 기능을 제공하는 어노테이션
- throwing 속성 : 타겟메소드에서 발생된 예외(Exception 객체)를 저장하기 위한 매개변수의 이름을 속성값으로 설정
throwing 속성 : 타겟메소드에서 발생된 예외(Exception 객체)를 저장하기 위한 매개변수의 이름을 속성값으로 설정
- @Around : 핵심관심코드 실행 전과 후에 횡단관심코드를 실행하는 기능을 제공하는 어노테이션
package xyz.itwill07.aop;
@Component
//@Aspect: 핵심관심코드에 횡단관심코드를 삽입하여 실행하기 위한 기능을 제공하는 어노테이션
// => Spring Bean Configuration File의 aspect 엘리먼트와 유사한 기능 제공
@Aspect
public class AopAnnotationAdvice {
private static final Logger logger=LoggerFactory.getLogger(AopAnnotationAdvice.class);
//@Pointcut : 타겟메소드를 지정하기 위한 어노테이션
// => 메소드 호출을 이용하여 타겟메소드를 지정한 PointCut 표현식을 제공하기 위해 사용
// => Spring Bean Configuration File의 pointcut 엘리먼트와 유사한 기능 제공
//value 속성 : 타겟메소드를 지정하기 위한 PointCut 표현식을 속성값으로 설정
// => 다른 속성이 없는 경우 속성값만 설정 가능
@Pointcut("within(xyz.itwill07.aop.AopAnnotationBean)")
public void aopPointCut() {}
//Before Advice 메소드
//@Before : 핵심관심코드 실행 전에 횡단관심코드를 실행하는 기능을 제공하는 어노테이션
// => Spring Bean Configuration File의 before 엘리먼트와 유사한 기능 제공
// => 다른 속성이 없는 경우 속성값만 설정 가능
//@Before(value = "within(xyz.itwill07.aop.AopAnnotationBean)")
//value 속성값으로 @Pointcut 어노테이션을 사용한 메소드를 호출하면 등록된 PointCut 표현식을 제공받아 사용 가능
@Before("aopPointCut()")
public void beforeLog() {
logger.info("[before]핵심관심코드 실행 전 삽입되어 실행될 횡단관심코드");
}
//After Advice 메소드
//@After : 핵심관심코드 실행 후 무조건 횡단관심코드를 실행하는 기능을 제공하는 어노테이션
// => Spring Bean Configuration File의 after 엘리먼트와 유사한 기능 제공
//@After("within(xyz.itwill07.aop.AopAnnotationBean)")
@After("aopPointCut()")
public void afterLog() {
logger.info("[after]핵심관심코드 실행 후 무조건 삽입되어 실행될 횡단관심코드");
}
//After Returning Advice 메소드
//@AfterReturning : 핵심관심코드가 정상적으로 실행된 후 횡단관심코드를 실행하는 기능을 제공하는 어노테이션
// => Spring Bean Configuration File의 after-returning 엘리먼트와 유사한 기능 제공
//returning 속성 : 타겟메소드의 반환값을 저장하기 위한 매개변수의 이름을 속성값으로 설정
@AfterReturning(value="aopPointCut()",returning = "object")
public void afterReturningLog(Object object) {
logger.info("[after-returning]핵심관심코드가 정상적으로 실행된 후 삽입되어 실행될 횡단관심코드");
}
//After Throwing Advice 메소드
//@AfterThrowing : 핵심관심코드 실행시 예외가 발생된 경우 횡단관심코드를 실행하는 기능을 제공하는 어노테이션
// => Spring Bean Configuration File의 after-throwing 엘리먼트와 유사한 기능 제공
//throwing 속성 : 타겟메소드에서 발생된 예외(Exception 객체)를 저장하기 위한 매개변수의 이름을 속성값으로 설정
@AfterThrowing(value="aopPointCut()", throwing = "exception")
public void afterThrowingLog(Exception exception) {
logger.info("[after-throwing]핵심관심코드 실행시 예외가 발생되면 삽입되어 실행될 횡단관심코드");
}
//Around Advice 메소드
//@Around : 핵심관심코드 실행 전과 후에 횡단관심코드를 실행하는 기능을 제공하는 어노테이션
// => Spring Bean Configuration File의 around 엘리먼트와 유사한 기능 제공
@Around("aopPointCut()")
public Object aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
logger.info("[around]핵심관심코드 실행 전에 삽입되어 실행될 횡단관심코드");
Object object=joinPoint.proceed();
logger.info("[around]핵심관심코드 실행 후에 삽입되어 실행될 횡단관심코드");
return object;
}
}
aspectj-autoproxy : Spring Annotation를 이용하여 AOP 기능을 제공하기 위한 엘리먼트
<aop:aspectj-autoproxy/>
<?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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="xyz.itwill07.aop"/>
<!-- aspectj-autoproxy : Spring Annotation를 이용하여 AOP 기능을 제공하기 위한 엘리먼트 -->
<!-- => AOP 관련 Annotation을 사용하여 핵심관심코드에 횡단관심코드를 삽입하여 실행되도록 설정 -->
<aop:aspectj-autoproxy/>
</beans>
package xyz.itwill07.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AopAnnotationApp {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("07-5_aopAnnotaion.xml");
AopAnnotationBean bean=context.getBean("aopAnnotationBean", AopAnnotationBean.class);
System.out.println("================================================================");
bean.display1();
System.out.println("================================================================");
bean.display2();
System.out.println("================================================================");
bean.display3();
System.out.println("================================================================");
((ClassPathXmlApplicationContext)context).close();
}
}