반응형
관점지향 프로그램
스프링 어플리케이션은 대부분 특별한 경우를 제외 하고는 MVC 웹 어플리케이션에서는 Web Layer, Business Layer, Data Layer로 정의
- Web Layer: REST API를 제공하며, Client 중심의 로직 적용
- Business Layer: 내부 정책에 따른 logic를 개발하며, 주로 해당 부분을 개발
- Data Layer: 데이터 베이스 및 외부와의 연동을 처리
주요 Annotation
Annotation | 의미 |
@Aspect | 자바에서 널리 사용하는 AOP 프레임워크에 포함되며, AOP를 정의하는 Class에 할당 |
@Pointcut | 기능을 어디에 적용시킬지, 메소드? Annotation? 등 AOP를 적용 시킬 지점을 설정 |
@Before | 메소드 실행하기 이전 |
@After | 메소드가 성공적으로 실행 후, 예외가 발생 되더라도 실행 |
@AfterReturing | 메소드 호출 성공 실행 시 (Not Throws) |
@AfterThrowing | 메소드 호출 실패 예외 발생(Throws) |
@Around | Before / After 모두 제어 |
예제
AOP 패키지 추가
dependencies {
// aop 추가
implementation 'org.springframework.boot:spring-boot-starter-aop'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
User 클래스 생성
user.java
package com.example.aop.dto;
public class User {
private String id;
private String pw;
private String email;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPw() {
return pw;
}
public void setPw(String pw) {
this.pw = pw;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", pw='" + pw + '\'' +
", email='" + email + '\'' +
'}';
}
}
사용자 지정 어노테이션 클래스 생성
Decode.java -> 디코드 실행하는 메소드에 적용
package com.example.aop.annotation;
public @interface Decode {
}
Timer.java -> 메소드 실행 시간을 잴 때 적용
package com.example.aop.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
// @interface: 어노테이션 생성
public @interface Timer {
}
AOP 클래스 생성
ParameterAop.java -> controller 실행시 파라미터값을 확인하고, 리턴값을 확인하는 클래스
package com.example.aop.aop;
import com.example.aop.annotation.Timer;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import java.lang.reflect.Method;
// @Aspect: AOP 사용 선언
@Aspect
// @Component: 스프링에서 해당 객체 관리
@Component
public class ParameterAop {
// @Pointcut: 어디에 AOP를 적용할 지 설정
// com.example.aop.controller 하위 모든 메소드에 적용
@Pointcut("execution(* com.example.aop.controller..*.*(..))")
private void cut(){
}
// 해당 pointcut이 실행되기전에 실행
@Before("cut()")
public void before(JoinPoint joinPoint){
System.out.println("====================================");
// 어떤 메소드를 실행했는지 확인
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
System.out.println("어떤한 메소드가 실행 되었는가? " + method.getName());
// AOP가 동작하기 전에 어떠한 데이터가 전달되었는지 확인
Object[] args = joinPoint.getArgs();
for (Object obj: args){
System.out.println("type: " + obj.getClass().getSimpleName());
System.out.println("value: " + obj);
}
}
// 해당 pointcut이 실행되고 반환값을 확인하기 위해, returning 이름은 메소드의 파라미터와 일치 시켜줘야 한다.
@AfterReturning(value = "cut()", returning = "returnobj")
public void afterReturn(JoinPoint joinPoint, Object returnobj){
// AOP실행 성공 후 어떤값이 반환되어졌는지 확인
System.out.println("return obj: " + returnobj);
// System.out.println(returnobj);
System.out.println("===================================");
}
}
TimerAop.java -> 메소드 실행 시간을 재는 클래스
package com.example.aop.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
// @Aspect: AOP 사용 선언
@Aspect
// @Component: 스프링에서 해당 객체 관리
@Component
public class TimerAop {
// @Pointcut: 어디에 AOP를 적용할 지 설정
// com.example.aop.controller 하위 모든 메소드에 적용
@Pointcut("execution(* com.example.aop.controller..*.*(..))")
private void cut(){
}
// Timer 어노테이션이 동작할때만 적용
@Pointcut("@annotation(com.example.aop.annotation.Timer)")
private void enableTimer(){
}
// 실행시간이 얼마나 걸리는지 확인
@Around("cut() && enableTimer()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
// 시간 재는거 시작
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 메소드 실행
Object result = joinPoint.proceed();
// 시간 재는거 멈춤
stopWatch.stop();
System.out.println("total time: " + stopWatch.getTotalTimeSeconds());
}
}
DecodeAop -> 인코딩된 데이터를 전달받은 후 인코딩하고 다시 디코딩할때 사용
예시: YWlpaUBuYXZlci5jb20= -> aiii@naver.com
package com.example.aop.aop;
import com.example.aop.dto.User;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
// @Aspect: AOP 사용 선언
@Aspect
// @Component: 스프링에서 해당 객체 관리
@Component
public class DecodeAop {
// @Pointcut: 어디에 AOP를 적용할 지 설정
// com.example.aop.controller 하위 모든 메소드에 적용
@Pointcut("execution(* com.example.aop.controller..*.*(..))")
private void cut(){
}
// Decode 어노테이션이 동작할때만 적용
@Pointcut("@annotation(com.example.aop.annotation.Decode)")
private void enableDecode(){
}
@Before("cut() && enableDecode()")
public void before(JoinPoint joinPoint) throws UnsupportedEncodingException {
Object[] args = joinPoint.getArgs();
for (Object arg : args){
// User 클래스와 매칭될때
if (arg instanceof User) {
// User 클래스로 형변환
User user = User.class.cast(arg);
// user클래스에 email값을 가져온다.
String base64Email = user.getEmail();
// email값을 Base64로 디코딩 한다.
String email = new String(Base64.getDecoder().decode(base64Email), "UTF-8");
// 변환된 값을 user 클래스에 email에 저장
user.setEmail(email);
System.out.println("Decode Before: " + email);
}
}
}
@AfterReturning(value = "cut() && enableDecode()", returning = "returnObj")
public void afterReturn(JoinPoint joinPoint, Object returnObj){
if (returnObj instanceof User) {
// User 클래스로 형변환
User user = User.class.cast(returnObj);
// user클래스에 email값을 가져온다.
String email = user.getEmail();
// email값을 Base64로 디코딩 한다.
String base64Email = Base64.getEncoder().encodeToString(email.getBytes(StandardCharsets.UTF_8));
// 변환된 값을 user 클래스에 email에 저장
user.setEmail(email);
System.out.println("Decode after: " + base64Email);
}
}
}
RestApiController.java -> URL에 따라 실행되는 클래스
package com.example.aop.controller;
import com.example.aop.annotation.Decode;
import com.example.aop.annotation.Timer;
import com.example.aop.dto.User;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class RestApiController {
@GetMapping("/get/{id}")
public String get(@PathVariable Long id, @RequestParam String name){
// System.out.println("get method");
// System.out.println("get method: " + id);
// System.out.println("get method: " + name);
return id +" " + name + " 보내줄게";
}
@PostMapping("/post")
// Json형태를 받기위해 RequestBody 사용
public User post(@RequestBody User user){
// System.out.println("post method: " + user);
return user;
}
// 직접 만든 어노테이션 적용
@Timer
@DeleteMapping("/delete")
public void delete() throws InterruptedException {
// StopWatch 관련 처리를 AOP로 사용
// StopWatch stopWatch = new StopWatch();
// stopWatch.start();
// db logic
// 2초간 대기
Thread.sleep(1000 * 2);
// stopWatch.stop();
// System.out.println("total time: " + stopWatch.getTotalTimeSeconds());
}
@Decode
@PutMapping("/put")
// Json형태를 받기위해 RequestBody 사용
public User put(@RequestBody User user){
System.out.println("put method: " + user);
return user;
}
}
실행 화면
Get 테스트 -> http://localhost:8080/api/get/100?name=hidd
파라미터 전달
POST 테스트 -> http://localhost:8080/api/post
json 형식으로 전달
Delete 테스트 -> http://localhost:8080/api/delete
2초간 정지후 동작
Put 테스트 -> http://localhost:8080/api/put
Java 코드로 인코딩 하는 방법
@SpringBootApplication
public class AopApplication {
public static void main(String[] args) {
SpringApplication.run(AopApplication.class, args);
System.out.println(Base64.getEncoder().encodeToString("aiii@naver.com".getBytes(StandardCharsets.UTF_8)));
}
}
email 부분에 인코딩한 값을 전달
반응형
'Spring' 카테고리의 다른 글
Json으로 출력된 데이터 확인 (0) | 2021.06.13 |
---|---|
주요 어노테이션 (0) | 2021.06.13 |
IoC / DI (0) | 2021.06.12 |
Spring 핵심 (0) | 2021.06.12 |
Object Mapper (0) | 2021.06.12 |