Ioc, Di, Aop, Object Mapping

duckbill413·2023년 1월 21일
0

Spring boot

목록 보기
2/8
post-thumbnail

POJO

Ioc (Inversion of Control)

스프링에서는 일반적인 Java 객체를 new로 생성하여 개발자가 관리하는 것이 아닌 Spring Container에 모두 맡긴다.
즉, 개발자 → 프레임워크로 제어의 객체 관리 권한이 넘어 갔음으로 ‘제어의 역전’ 이라고 한다.

DI (Dependency Injection)

DI 장점

  • 의존성으로 부터 격리시켜 코드 테스트에 용이하다.
  • DI를 통해서, 불가능한 상황을 Mock와 같은 기술을 통하여, 안정적으로 테스트가 가능
  • 코드를 확장하거나 변경할 때 영향을 최소화한다.(추상화)
  • 순환 참조를 막을 수 있다.

Spring ioc 실습 예제(Github)

Ioc, Di Spring 예제

Url을 Encode하는 작업을 Ioc(제어의 역전)과 Di(의존성 주입)을 통하여 관리하는 예제

  • ApplicationContextAware
    Spring Bean 객체를 참조하기 위한 인터페이스 이다.
     @Component
     public class ApplicationContextProvider implements ApplicationContextAware {
         private static ApplicationContextcontext;
         @Override
         public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
     context= applicationContext;
         }
     
         public static ApplicationContext getContext(){ returncontext;}
     }
  • Encoder
    public interface IEncoder {
        String encode(String message);
    }
    
    //@Component
    public class Encoder {
        private IEncoder iEncoder;
    
    //    public Encoder(@Qualifier("base64Encoder") IEncoder iEncoder) { this.iEncoder = iEncoder;}
        public Encoder(IEncoder iEncoder) { this.iEncoder = iEncoder;}
        public void setIEncoder(IEncoder iEncoder) { this.iEncoder = iEncoder;}
        public String encode(String message){ return this.iEncoder.encode(message);}
    }
    
    @Component("base64Encoder")
    public class Base64Encoder implements IEncoder{
        @Override
        public String encode(String message) {
            return Base64.getEncoder().encodeToString(message.getBytes(StandardCharsets.UTF_8));
        }
    }
    
    @Component
    public class UrlEncoder implements IEncoder{
        @Override
        public String encode(String message) {
            try{
                return URLEncoder.encode(message, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }
    }
  • Encode Bean 생성
    // 여러가지 Bean 생성 => Configuration
    @Configuration
    class AppConfig{
      @Bean("base64Encode")
      public Encoder encoder(Base64Encoder base64Encoder){
          return new Encoder(base64Encoder);
      }
      @Bean("urlEncode")
      public Encoder encoder(UrlEncoder urlEncoder){
          return new Encoder(urlEncoder);
    }}
    
  • SpringApplication
    public class SpringIocApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringIocApplication.class, args);
    
            ApplicationContext context = ApplicationContextProvider.getContext();
    
    //        Encoder 주입 방식
    //        Base64Encoder base64Encoder = context.getBean(Base64Encoder.class);
    //        UrlEncoder urlEncoder = context.getBean(UrlEncoder.class);
    //        Encoder encoder = new Encoder(base64Encoder);
    //        encoder.encode(msg);
    
            Encoder encoder = context.getBean("base64Encode", Encoder.class);
            String url = "www.naver.com/books/it?page=1&size=123&name=duckbillLvr";
            String result = encoder.encode(url);
            System.out.println(result);
        }
    }

AOP (Aspect Oriented Programming)

관점 지향 프로그래밍

스프링 어플리케이션은 대부분 특별한 경우를 제외 하고는 MVC 웹 어플리케이션에서는 Web Layer, Business Layer, Data Layer로 정의

  • Web Layer : REST API를 제공하며, Client 중심의 로직 적용
  • Business Layer : 내부 정책에 따른 logic을 개발하며, 주로 해당 부분을 개발
  • Data Layer : 데이터 베이스 및 외부와의 연동을 처리

주요 Annotation

Annotation의미
@Aspect자바에서 널리 사용하는 AOP 프레임워크에 포함되며, AOP를 정의하는 Class에 할당
@Pointcut기능을 어디에 적용시킬지(메소드, 어노테이션 등) AOP를 적용시킬 시점을 설정
@Before메소드를 실행하기 이전
@After메소드가 성공적으로 실행 후, 예외가 발생하더라도 실행
@AfterReturning메소드 호출 성공 실행 시 (Not Throws)
@AfterThrowing메소드 호출 실패 예외 발생 (Throws)
@AroundBefore / After 모두 제어

Spring AOP 실습 예제(Github)

AOP 실습

Aop를 이용하여 Decode와 Timer 설계

  • Decode, Timer Annotation
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Decode {
    }
    
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Timer {
    }
  • Decode Aop
    @Aspect
    @Component
    public class DecodeAop {
        // MEMO: Controller 하위 메소드를 Pointcut 시점으로 지정
        @Pointcut("execution(* com.example.springaop.controller..*.*(..))")
        private void cut(){}
        // MEMO: Decode Annotation을 Pointcut 시점으로 지정
        @Pointcut("@annotation(com.example.springaop.anotation.Decode)")
        private void enableDecode(){}
    
        @Before("cut() && enableDecode()")
        public void before(JoinPoint joinPoint) throws UnsupportedEncodingException {
            Object [] args = joinPoint.getArgs();
    
            for (Object arg : args){
                if (arg instanceof User){
                    User user = User.class.cast(arg);
                    String base64Email = user.getEmail();
                    String email = new String(Base64.getDecoder().decode(base64Email), "UTF-8");
                    user.setEmail(email);
                }
            }
        }
    
        @AfterReturning(value = "cut() && enableDecode()", returning = "returnObj")
        public void afterReturn(JoinPoint joinPoint, Object returnObj){
            if(returnObj instanceof User){
                User user = User.class.cast(returnObj);
                String email = user.getEmail();
                String base64Email = Base64.getEncoder().encodeToString(email.getBytes(StandardCharsets.UTF_8));
                user.setEmail(base64Email);
                System.out.println("after returning :" + user);
            }
        }
    }
    Aop를 이용하여 Base64 request를 Decode하고 실행 완료후 afterReturn을 이용하여 다시 Encode하여 저장하는 AOP이다.
  • Timer Aop
    @Aspect
    @Component
    public class TimerAop {
        @Pointcut("execution(* com.example.springaop.controller..*.*(..))")
        private void cut(){}
    
        @Pointcut("@annotation(com.example.springaop.anotation.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.getTotalTimeMillis());
        }
    }
    Around Aop를 사용하여 작업 시간을 측정하는 Aop
  • AopController
    @RestController
    @RequestMapping("/api")
    public class RestApiController{
        @Timer
        @DeleteMapping("/delete")
        public void delete() throws InterruptedException {
            Thread.sleep(1000 * 2);
        }
    
        @Decode
        @PutMapping("/put")
        public User put(@RequestBody User user){
            System.out.println("put method" + user);
            return user;
        }
    }
    Aop를 사용할 Method에 Annotation을 적용하여 Aop를 사용한다는 것을 Spring에 알려준다.

Object Mapper

Object Mapper을 활용하면 Object → json → Object의 변환에 유용하다.

  • object Mapper을 활용하려면 해당 클래스에 get 메서드가 필요하다.
  • 매개변수를 입력받지 않을때 변수를 초기화 해주는 빈 생성자가 필요하다.
  • get이 들어간 다른 Method를 만들면 안된다.
  • User Class
    @Getter
    @Setter
    @ToString
    public class User {
        private String name;
        private int age;
        @JsonProperty("phone_number")
        private String phoneNumber;
        public User(){
            this.name = null;
            this.age = 0;
            this.phoneNumber = null;
        }
        public User defaultUser(){
            return new User("default", 0, "000-0000-0000");
        }
        public User(String name, int age, String phoneNumber) {
            this.name = name;
            this.age = age;
            this.phoneNumber = phoneNumber;
        }
    }
  • Object Mapping Test Method
    @SpringBootTest
    class SpringObjectMapperApplicationTests {
    
        @Test
        void contextLoads() throws JsonProcessingException {
    
            /*INFO: Json -> Object
            *  Object -> Json
    
            controller req json(text) -> object
            response object -> json(text)
             */
    
            var objectMapper = new ObjectMapper(); // ObjectMapper 생성
    
            //MEMO: object -> text objectMapper가 get method활용
    //TIP: objectMapper가 get method를 참고하므로 다른 method이름 앞에 get을 붙이면 안됨
    var user = new User("duckbill", 27, "010-1234-1234");
            var text = objectMapper.writeValueAsString(user);
            System.out.println(text);
    
            //MEMO: text -> object기본생성자로 초기화 필요
    var objectUser = objectMapper.readValue(text, User.class);
            System.out.println(objectUser);
        }
    }
  • Result
    System.in: User(name=duckbill, age=27, phoneNumber=010-1234-1234)
    Json Text: {"name":"duckbill","age":27,"phone_number":"010-1234-1234"}
    System.out: User(name=duckbill, age=27, phoneNumber=010-1234-1234)
    User → Json User → User 로 변환되는 과정의 Object Mapping
profile
같이 공부합시다~

0개의 댓글