AOP를 스프링을 이용해 구현해보자.
IntelliJ와 Spring5를 사용한다.
일단 환경설정을 위해서
pom.xml에
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.4</version>
</dependency>
이 dependency를 추가해준다. 버전은 알아서.
기본 개념을 바탕으로 어노테이션을 사용하지 않는 코드부터 구현해보자.
advice의 종류 중에서 around를 사용해보자.
어노테이션을 사용하지 않으므로
resources 폴더에 xml 파일을 만들어 관리한다.
먼저 파일을 만들어둔다.
그리고 핵심 기능을 할 클래스와 메서드들을 각각 만들어두고
공통적인 기능을 할 메서드들을 모아둘 클래스를 만들어준다.
나는 Student 클래스에 이름과 나이를 필드값에 두고
getStudentInfo 라는 필드값을 가져오는 메서드를 두었다.
그리고 LogAop라는 클래스를 만들어
메서드로 logAop()를 만들어 그 안을
public Object logAop(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
String signatureStr = proceedingJoinPoint.getSignature().toShortString();
System.out.println(signatureStr+" is start");
long st = System.currentTimeMillis();
try{
Object obj = proceedingJoinPoint.proceed();
return obj;
}finally{
long et = System.currentTimeMillis();
System.out.println(signatureStr+" is end, " + (et-st) + " is cost");
}
}
이렇게 만들었다.
around 방식이므로 pointcut으로 지정된 메서드들이 실행되기 전이랑 후에 위 메서드를 실행시킨다.
이 코드에서 proceedingJoinPoint.proceed() 가 핵심 기능을 실행시키게 된다.
만들어둔 xml 파일을 완성시켜보자.
먼저 LogAop 클래스와 Student 클래스를 빈으로 만들어준다.
<aop:config>
<aop:aspect id="logAop" ref="logAop" >
<aop:pointcut id="publicM" expression="within(com.example.springdemo.aop.annotation.nouse.ex.Student)"/>
<aop:around pointcut-ref="publicM" method="loggeerAop" />
</aop:aspect>
</aop:config>
위 코드를 작성해주는데, aop 설정을 해주는 구문이다.
자세히 보면 ref에 LogAop의 빈 id를 넣어줬다.
그리고 pointcut을 지정해주어 Joinpoint가 어디인지 정밀하게 지정해주었고,
around 방식으로 pointcut을 지정해서 어떤 공통적인 기능의 메서드를 사용할 것인지 작성했다.
이렇게 작성이 끝나고 xml 파일을 사용해 빈객체를 컨테이너에 만들어 저장하고
Student 클래스의 빈을 가져와서 getStudentInfo 메서드를 실행시키면
이 핵심기능이 실행되기 전과 후에 공통적인 기능이 수행되는 것을 확인할 수 있다.
advice의 종류들 모두 이러한 방식으로 가능하다.
여기서 pointcut을 지정해줄 때 within을 사용했는데
여기에 within, execution, bean을 사용할 수 있다.
execution은 메서드의 경로를 지정해주어 그 메서드를 실행시킬 때,
within은 파일의 경로를 지정해주어 그 파일 내에 모든 메서드를 실행시킬 때,
bean은 빈 객체명을 지정해주어 그 빈 객체의 메서드가 실행될 때로 지정해준다.
또한 경로로 ..을 입력하면 하위패키지까지의 모든 메서드를 뜻한다.
그리고 execution을 사용할 때 public void get*() 처럼 메서드를 지정해주면
public void 이면서 get 으로 시작하는 모든 메서드를 지정해준다.
이제 어노테이션을 써보자.
xml 파일에는 빈 객체의 설정만 해주고
맨 위에
<aop:aspectj-autoproxy/>
이 구문만 작성해준다.
코드를 보며 살펴보자.
@Aspect
public class LogAop {
@Pointcut("bean(*ker)")
private void pointcutMethod(){
}
@Around("pointcutMethod()")
public Object logAop(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
String signatureStr = proceedingJoinPoint.getSignature().toShortString();
System.out.println(signatureStr+" is start");
long st = System.currentTimeMillis();
try{
Object obj = proceedingJoinPoint.proceed();
return obj;
}finally{
long et = System.currentTimeMillis();
System.out.println(signatureStr+" is end, " + (et-st) + " is cost");
}
}
}
위 코드를 보면 일단 클래스를 Aspect로 지정해주었다.
그리고 Pointcut을 bean 객체 중 ker로 끝나는 것으로 지정해주었고,
Around 방식으로 위에서 지정해준 pointcut을 가져와서 적용했다.
위 xml에 작성하는 방식을 이해했다면 이 코드를 보자마자 이해가 되었을거라 생각이 든다.
한 개 이상의 advice를 사용할 경우 순서를 정의해줄 수 있다.
-- 참고 --
https://snoopy81.tistory.com/298
추가로 Advice의 적용 위치를 지정할 때 사용자 정의 어노테이션을 입력할 수도 있다.
@Documented
@Target(ElementType.METHOD)
public @interface PerfLogging{
}
이렇게 어노테이션을 만들고
@Aspect
public class LogAop{
@Around("@annotation(PerfLogging)")
public Object logging(ProceedingJoinPoint joinpoint)throws Throwable{
String methodName = joinPoint.getSignature().toShortString();
try{
System.out.println("Start");
Object obj = joinPoint.proceed();
return obj;
}finally{
System.out.println("Finish");
}
}
}
위처럼 사용해준다.
MainClass.class
public class MainClass {
public static void main(String[] args) {
AbstractApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationCTX_AOP.xml");
// ctx.refresh();
Student student = ctx.getBean("studentkerm ", Student.class);
student.getStudentInfo();
ctx.close();
}
}
LogAop.class
public class LogAop {
public Object loggeerAop(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
String signatureStr = proceedingJoinPoint.getSignature().toShortString();
System.out.println(signatureStr+" is start");
long st = System.currentTimeMillis();
try{
Object obj = proceedingJoinPoint.proceed();
return obj;
}finally{
long et = System.currentTimeMillis();
System.out.println(signatureStr+" is end, " + (et-st) + " is cost");
}
}
}
Student.class
public class Student {
private String name;
private int age;
private int gradeNum;
private int classNum;
public Student(){}
public Student(String name, int age, int gradeNum, int classNum) {
this.name = name;
this.age = age;
this.gradeNum = gradeNum;
this.classNum = classNum;
}
public void getStudentInfo(){
System.out.println("student name is : " + getName());
System.out.println("student age is : " + getAge());
System.out.println("student gradeNum is : " + getGradeNum());
System.out.println("student classNum is : " + getClassNum());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getGradeNum() {
return gradeNum;
}
public void setGradeNum(int gradeNum) {
this.gradeNum = gradeNum;
}
public int getClassNum() {
return classNum;
}
public void setClassNum(int classNum) {
this.classNum = classNum;
}
}
applicationCTX_AOP.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.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- use annotation -->
<aop:aspectj-autoproxy/>
<bean id="logAop" class="com.example.springdemo.aop.annotation.use.ex.LogAop"/>
<bean id="studentker" class="com.example.springdemo.aop.annotation.use.ex.Student">
<constructor-arg value="student" />
<constructor-arg value="25" />
<constructor-arg value="2" />
<constructor-arg value="5" />
</bean>
<bean id="worker" class="com.example.springdemo.aop.annotation.use.ex.Worker">
<constructor-arg value="worker" />
<constructor-arg value="30"/>
<constructor-arg value="developer" />
</bean>
<!-- no use annotation -->
<!-- <bean id="logAop" class="com.example.springdemo.aop.annotation.nouse.ex.LogAop" />-->
<!-- <aop:config>-->
<!-- <aop:aspect id="logAop" ref="logAop" >-->
<!-- <aop:pointcut id="publicM" expression="within(com.example.springdemo.aop.annotation.nouse.ex.Student)"/>-->
<!-- <aop:around pointcut-ref="publicM" method="loggeerAop" />-->
<!-- <aop:after method="afterAdvice" pointcut-ref="publicM"/>-->
<!-- <aop:after-throwing method="afterThrowingAdvice" pointcut-ref="publicM"/>-->
<!-- <aop:after-returning method="afterReturningAdvice" pointcut-ref="publicM"/>-->
<!-- <aop:before method="beforeAdvice" pointcut-ref="publicM" />-->
<!-- </aop:aspect>-->
<!-- <aop:aspect id="logAop" ref="logAop">-->
<!-- <aop:pointcut id="publicM" expression="within(com.example.springdemo.aop.annotation.nouse.ex.Student)"/>-->
<!-- <aop:before method="beforeAdvice" pointcut-ref="publicM" />-->
<!-- </aop:aspect>-->
<!-- <aop:aspect id="logAop" ref="logAop">-->
<!-- <aop:pointcut id="publicM" expression="within(com.example.springdemo.aop.annotation.nouse.ex.Student)"/>-->
<!-- <aop:before method="afterAdvice" pointcut-ref="publicM" />-->
<!-- </aop:aspect>-->
<!-- <aop:aspect id="logAop" ref="logAop">-->
<!-- <aop:pointcut id="publicM" expression="within(com.example.springdemo.aop.annotation.nouse.ex.Student)"/>-->
<!-- <aop:before method="afterReturningAdvice" pointcut-ref="publicM" />-->
<!-- </aop:aspect>-->
<!-- <aop:aspect id="logAop" ref="logAop">-->
<!-- <aop:pointcut id="publicM" expression="within(com.example.springdemo.aop.annotation.nouse.ex.Student)"/>-->
<!-- <aop:before method="afterThrowingAdvice" pointcut-ref="publicM" />-->
<!-- </aop:aspect>-->
<!-- </aop:config>-->
<!-- <bean id="student" class="com.example.springdemo.aop.annotation.nouse.ex.Student">-->
<!-- <constructor-arg value="student" />-->
<!-- <constructor-arg value="25" />-->
<!-- <constructor-arg value="2" />-->
<!-- <constructor-arg value="5" />-->
<!-- </bean>-->
<!-- <bean id="worker" class="com.example.springdemo.aop.annotation.nouse.ex.Worker">-->
<!-- <constructor-arg value="worker" />-->
<!-- <constructor-arg value="30"/>-->
<!-- <constructor-arg value="developer" />-->
<!-- </bean>-->
</beans>
'WEB > Spring' 카테고리의 다른 글
[Spring Boot] Spring Boot에서 TDD 방법론 (0) | 2023.06.23 |
---|---|
[Spring] 패키지 구조 (0) | 2023.03.29 |
[Spring Boot] Spring boot EC2에 배포하기 (0) | 2022.12.19 |
[Spring] RestTemplate Response Entity Type (0) | 2022.12.15 |
[Spring] AOP의 기본 개념 (0) | 2022.10.27 |