728x90
반응형
728x90
반응형
반응형

환경: java 8+

java optional을 사용할 경우

null값을 대비해 기본 값을 return 할 수 있는 아래 두 함수가 있다.

orElse vs orElseGet

 

두 함수 모두 기본 값을 반환 할 수 있는데

orElse는 T객체 그 자체를, orElseGet은 람다식을 넘길 수 있다는 차이가 있다.

//orElse
UserStatistics one = statistics.stream().filter(UserStatistics::isHoldem).findFirst().orElse(new UserStatistics());
//orElseGet
UserStatistics one = statistics.stream().filter(UserStatistics::isHoldem).findFirst().orElseGet(() -> new UserStatistics());

 

또한 메모리/성능 상에서 차이도 있다.

orElse 는 값이 존재하여 else에 설정한 값을 반환하지 않게 될 때에도 이미 만들어 놓는다.

orElseGet은 값이 없어 진짜 필요할 때 실행된다.

 

따라서, 

constant, static variable, or caching result 을 반환할 경우라면 orElse를 사용해도 되지만, 대량 작업을 하거나 새로운 객체를 생성해야할 경우 orElseGet를 사용하는게 좋겠다.


참고

https://medium.com/@ductin.tran/use-java-optional-orelse-and-orelseget-wisely-4eebb50aa12e

 

Use Java Optional OrElse and OrElseGet wisely

1. Introduction

medium.com

 

728x90
반응형

'개발 > java' 카테고리의 다른 글

[pom] http로 repository 연결  (0) 2023.10.23
[pom] element annotationprocessorpaths is not allowed here  (0) 2023.10.23
디자인 패턴  (0) 2023.07.11
[이슈해결] NPE at HSSFSheet.autoSizeColumn  (1) 2023.06.28
[java] lambda stream and final  (0) 2023.02.06
반응형

요즘.. 디자인 패턴의 중요도 낮아짐

  • 개발환경 성숙: 프래임워크, 라이브러리 사용
    • 직접 구현할 필요 없음
  • 아키텍쳐의 변화: 모노리틱 -> MSA
    • 복잡성이 떨어지게 됨(구조같은데 좀 더 심플해짐)
  •  개발 언어의 변화(자바 1.8~)
    • 디자인패턴을 쓰지 않아도 해결할 수 있음(ex. 람다 등)

 

디자인 패턴?

소프트웨어 디자인 과정에서 자주 혹은 공통적으로 발생하는 문제들에 대해 재사용 가능한 해결책 혹은 형식화 된 가장 좋은 관행

 

알고리즘과의 차이점?

  • 실질적인 문제를 해결하기 위한 절차나 방법을 의미
    • 속도와 효율성(리소스)을 중요시(목표가 여기에 있음)
  • 패턴은 해결책에 대한 더 상위 수준의 설명 / 설계 구조 등 / 유지보수에 초점

 

바이블?ㅋ

왜 패턴?

  • 개발자 간의 원활한 협업이 가능
  • 소프트웨어의 구조를 파악하기 용이함
  • 재사용을 통해 개발 시간 단축
  • 설계 변경이 있을 경우 비교적 원활하게 조치가 가능

 

패턴에 대한 비판

  • 비 효율적인 해결책일 수 있음
    • Strategy패턴은 간단한 익명(람다) 함수로 구현할 수 있음
    • 많은 사람들이 이렇게 통합된 패턴들을 도그마처럼 신봉하여 패턴을 프로젝트의 맥락에 따라 적용하지 않고 문자 그대로 구현
    • 더 간단한 코드로도 문제 해결이 되는 상황에도 모든 곳에 패턴을 적용하려고 하는 패턴병에 걸리지 않도록 조심해야
    • 디자인 패턴은 모든 상황의 해결책이 아닌 코딩 방법론일 뿐이며 디자인 패턴에 얽매이지 말자

유지보수 쉽고 가독성 좋고 짧고 중복없고 객체지향 원칙에 맞는지 가 더 중요

 

객체지향 5대 원칙(SOLID)

> 높은 응집도와 낮은 결합도

  • SRP 단일 책임 원칙
    • proxy, aop
  • OCP 개방 폐쇄 원칙
    • 옵저버 패턴, if문 제거, 변경 발생 시 클래스 추가로 수정에는 닫혀있고 확장에는 열려있게
  • LSP 리스코프 치환
    • 다형성에 관한 원칙 제공; 상위 타입의 코드로 이뤄진 메서드에서 하위 타입으로 대신해도 메서드에는 아무 문제가 없어야 함
    • List list = new ArrayList(); <<- 받을 때 인터페이스로 하는게 맞다
  • ISP 인터페이스 분리 원칙
    • 거대 인터페이스를 쪼개서 기능별 인터페이스 분리
  • DIP 의존 역전 원칙
    • 인터페이스 호출; 구현체 변경으로 기능 변경 가능하도록 추상화에 의존해야함(구체화 의존 노노)

 

디자인 패턴 종류(23개)

https://m.hanbit.co.kr/channel/category/category_view.html?cms_code=CMS8616098823 

 

[Design pattern] 많이 쓰는 14가지 핵심 GoF 디자인 패턴의 종류

디자인 패턴을 활용하면 단지 코드만 ‘재사용’하는 것이 아니라, 더 큰 그림을 그리기 위한 디자인도 재사용할 수 있습니다. 우리가 일상적으로 접하는 문제 중 상당수는 다른 많은 이들이 접

m.hanbit.co.kr

생성 패턴

특정 객체가 생성되거나 변경되어도 프로그램 구조에 영향을 최소화 할 수 있도록 유연성 제공(ocp와 비슷)

 

추상 팩토리 패턴

  • (interface를 통해) 연관된 객체를 묶는데 초점

1. if else로 분기 겁나 많고 거기 안에서 로직 마니마니

2. switch..로 하고 거기 안에서 클래스호출(빈 등록이 계속되는 문제)

3. 중간 매소드인 팩토리 클래스를 만듦, 타입을 넘기고 거기 안에서 return service

맵으로 서비스를 가질 수 있음(Map<String, VehicleService> <- 빈 이름과 interface를 구현한 해당 빈)

나머지들이 그 interface를 구현

 

팩토리 매서드 패턴

  • 추상 팩토리 패턴과의 차이점
    • 팩토리 메서드 패턴은 어떤 객체를 생성할지에 집중
    • 추상 팩토리 패턴은 연관된 객체들을 모아둔다는 것에 집중(Factory)
  • 객체 생성을 서브클래스로 분리하여 위임(캡슐화)

 

빌더 패턴(@Builder)

  • 문제
    • 자바 빈즈 패턴(facet): data/getter/setter 사용
    • 점층적 생성자 패턴(생성자 파라미터 갯수별로 생성자 만드는거)
      • : 인자가 많아지면 생성자도 많아지지고
        • 3개 이상은 생성자 사용하지 말고 빌더를..
      • : 순서도 헷갈림
  • 빌더 패턴의 장점
    • 필요한 데이터만 설정
    • 유연성을 확보
    • 가독성을 확보
    • 불변성을 확보
      • 그 후에 set하면 안됨?? setter가 있으면 되고 없으면 안되고(setter를 선언하면 안되겠지)

 

프로토타입 패턴

  • JPA -> entity를 내리면 영속성이 유지될 수 있음
  • 별도의 dto 리턴(model mapper, mapstruct)하므로써 영속성을 끊는게 맞고
  • 변환하는 걸 프로토타입 패턴이라고 함
    • OSIV default true 

https://bangpurin.tistory.com/36

 

[jpa] OSIV란; spring.jpa.open-in-view

OSIV에 대한 이해를 하려면 영속성 컨텍스트가 뭔지 알아야한다. 이전 글을 참고하자. 2022.01.27 - [개발/spring] - [jpa] 영속성 컨텍스트 in spring [jpa] 영속성 컨텍스트 in spring 영속성 컨텍스트? 엔티티

bangpurin.tistory.com

Member member = memberService.saveMember(snsType, token);
member.setSnsType("kakao"); /////<<<<<<<-- 여기서 실수하면 반영됨 상하로 트랜잭션이 있어서
memberService.emptyLogic();

=> dto 리턴 시 해결!

 

싱글톤 패턴

  • 한 클래스마다 인스턴스를 하나만 생성하여 어디서든 참조
  • 병렬로 여러 스레드에서 돌릴 경우 조심
  • 직접 구현할 일 적음(스프링 빈)
  • 장점
    • 새로운 인스턴스를 계속해서 생성하지 않으므로 메모리 낭비가 적음
  • 단점
    • 코드량 많고, 테스트 어렵고, 자식 클래스 만들기 어렵, 클라이언트가 구체 클래스에 의존(dip 위반)
    • 스프링이 모두 해결해 줌!
public class SingletonService {
    private static final SingletonService instance = new SingletonService();

    public static SingletonService getInstance() {
        return instance;
    }

    // 생성자를 private으로 적용시켜 외부에서 인스턴스 생성을 막는다
    private SingletonService() {

    }
}

구조 패턴

클래스나 객체를 조합하여 더 큰 구조를 만들 수 있게 해줌

 

어댑터 패턴

  • 타사의 라이브러리를 호출부의 변경 없이 사용하고 싶을 경우
  • Array.AsList(), Collections.enumeration(), Collections.list() 등도 어댑터 패턴을 활용한 예시
@Service
@Primary //얘랑 동일한 빈이 있어도 이게 우선이다라고 해줘야(override)
public class MailAdapter implements MailSenderA {
    private final MailSolutionB mailSolutionB;

    @Override
    public void send(MailSolutionA.MailParam mailParam) {
        MailSolutionB.MailParam param = MailSolutionB.MailParam.builder().mailTitle(mailParam.getTitle())
                .mailBody(mailParam.getBody()).receiverEmail(mailParam.getEmail()).build();
        mailSolutionB.sendApi(param);
    }
}
  • 위 처럼 A -> B로 바꿀 때, 기존 호출부를 그대로 두고
  • A를 쏘는 것 처럼 보이지만 사실 B로 쏘는 것
  • Interface를 써야 구현체를 교체할 수 있음 그래야 primary로 오버라이딩이 가능
  • 모든 부분을 다 고치지 않고 한 곳만 고칠 때 좋음

 

브릿지 패턴

  • 기능의 구현 클래스를 런타임 때 자유롭게 지정이 가능
    • 주입을 set을 이용하여 동적으로 받음
  • 기능과 구현을 분리하여 구현이 변경되더라도 기능 클래스 부분에 대한 변경은 필요 없음
if (StringUtils.isNotEmpty(member.getAccountNumber())) {
    paymentService.setPaymentMethod(new CardMethodService()); // <<-- 서비스 주입을 런타임에
} else if (StringUtils.isNotEmpty(member.getPhoneNumber())) {
    paymentService.setPaymentMethod(new PhoneMethodService());
}
paymentService.pay1(order.getAmount(), member);

 

컴포지트 패턴

  • 일반적으로 직접 구현하지 않음
  • 클라이언트 전체와 부분을 구별하지 않고 동일한 인터페이스로 사용
  • 트리구조와 상당히 유사한 성격을 가지고 있다
  • 하지만 기능이 너무 다른 클래스들에는 공통 인터페이스를 제공하기 어려움

 

데코레이터 패턴

  • 일반적으로 직접 구현하지 않음
  • 기존 코드를 수정하지 않고도 행동을 확장 가능
  • 객체를 여러 데코레이터로 래핑하여 여러 행동들을 합성 가능
  • 데코레이터를 너무 많이사용하면 코드가 필요 이상으로 복잡해짐
  • file, buffered reader 등등 New로 만들어서 래핑해서 합성하는 것

 

퍼사드 패턴

  • 복잡한 로직들을 숨기고 간단한 인터페이스 함수만을 호출(추상화)
  • 우리는 이미 MVC패턴을 통해서 퍼사드 패턴을 알게 모르게 사용하고 있다.

 

플라이웨이트 패턴

  • 일반적으로 직접 구현하지 않음
  • 인스턴스가 필요할 때마나 매번 생성하는게 아니라 가능한 공유해서 메모리를 절약
    • 캐시, 레디스.. 디비 접근 최소화
  • String 객체를 생성하는 리터럴 방식의 String Constant Pool이 대표적인 플라이웨이트 패턴을 적용한 예시
  • https://www.baeldung.com/java-string-constant-pool-heap-stack
@Test
void 스트링_플라이웨이트패턴_테스트() {
    String str1 = "Hello";
    String str2 = "Hello";
    String str3 = new String("Hello");

    // 서로 같다!
    Assertions.assertSame(str1, str2);

    // 서로 다르다!
    Assertions.assertNotSame(str1, str3);

    // 서로 같다!
    Assertions.assertSame(str1, str3.intern());

    /*
        클래스가 JVM에 로드되면 모든 리터럴은 constant pool에 위치하게 된다.
        그리고 리터럴을 통해 같은 문자를 생성한다면 풀 안의 같은 상수를 참조하게 되는데 이를 String interning이라고 한다.
        String을 리터럴로 생성될 때 intern()이라는 메서드가 호출되고 이 intern() 메서드는
        constant pool에 같은 문자가 존재하는지 확인 후 존재한다면 그 참조 값을 가지게 된다.
     */
}
@Test
void Integer_플라이웨이트패턴_테스트() {
    Integer integer1 = Integer.valueOf("123");
    Integer integer2 = Integer.valueOf("123");

    // 서로 같다!
    Assertions.assertSame(integer1, integer2);


    Integer integer3 = Integer.valueOf("128");
    Integer integer4 = Integer.valueOf("128");

    // 서로 다르다!
    Assertions.assertNotSame(integer3, integer4);

    //직접 Integer.valueOf 함수 까보기!
    //-128 ~ 127 까지는 캐싱이라 같고 그 이외 값은 캐싱안하고 새롭게 인스턴스 생성이라 다름
}

 

프록시 패턴

  • SRP와 연관
  • 공통되는 로직 빼서 모듈화
  • AOP(aspect oriented programming; cglib etc..)
    • OOP로 독립적으로 분리하기 어려운 부가 기능을 모듈화 하는 방식
    • oop를 더 oop스럽게 해주는 방법
    • 객체지향을 더 객체지향스럽게 만들어 주는 프로그래밍
  • transactional, cache, test에 사용하는..
  • spring 큰 특징 : DI, AOP(aspect), PSA(portable service abstraction)
import com.example.pattern.order.service.OrderService;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.objenesis.SpringObjenesis;

@Configuration
public class ProxyConfig {
    private final SpringObjenesis objenesis = new SpringObjenesis();

    @Bean
    @Primary //order service말고 프락시를 빈으로 등록하도록 해야 aop 적용
    public OrderService orderServiceProxy() {
        return (OrderService) createCGLibProxy(OrderService.class, new MethodCallLogInterceptor());
    }

    private Object createCGLibProxy(Class<? extends Object> targetClass, MethodInterceptor interceptor) {
        // Create the proxy
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(interceptor);
        return enhancer.create();
    }
}

행동 패턴

반복적으로 사용되는 객체들의 커뮤니케이션을 패턴화, 결합도를 최소화 하는 것이 목적

 

책임 연쇄 패턴

스프링 필터 사용(필터 체인)

  • 검증 절차는 자유롭게 추가될 수 있어야 하고, 순서도 자유롭게 변경할 수 있어야 할 때 
  • 기존 클라이언트 코드를 수정하지 않고 앱에 새 핸들러를 도입 가능
@Configuration
public class FilterConfig {
    //    @Bean
//    @Order(0)
    public FilterRegistrationBean userAgentCheckFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(new UserAgentCheckFilter());
        registrationBean.addUrlPatterns("/orders/*");
        return registrationBean;
    }

    //    @Bean
//    @Order(1)
    public FilterRegistrationBean memberCheckFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(new MemberCheckFilter());
        registrationBean.addUrlPatterns("/orders/*");
        return registrationBean;
    }
}

 

커맨드 패턴

  • 잘 사용하지 않음
  • 기존 클라이언트 코드를 수정하지 않고 새로운 커맨드들을 도입 가능
  • 요청부와 동작부를 분리시켜 주기 때문에 결합도를 낮출 수 있음
  • 보통 비동기
@Test
void 혜택_발송_스레드_테스트() throws InterruptedException {
    int numberOfThreads = 5;
    CountDownLatch latch = new CountDownLatch(numberOfThreads);
    //ExecutorService 객체가 Invoker를 의미
    ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads);
    for (int i = 0; i < numberOfThreads; i++) {
        //Runnable 구현 객체가 Command를 의미;
        // runnable interface 특징? 구현할 함수가 1개, callable과 디르게 반환타입이 없음
        //CouponApiService 객체가 Receiver를 의미
        Runnable doThread = new CouponService(new CouponApiService());
        //위 대신 lambda로 대체가능
        executorService.execute(() -> {
            doThread.run();
            latch.countDown();
        });
    }
    latch.await();
}

Runnable -> void

Callable -> return 있음

 

반복자 패턴

  • 잘 사용하지 않음
  • 혜택이 아이템을 어떻게 구현하였는지 호출하는 쪽에서는 알 필요가 없음
    • 즉 캡슐화가 잘 되어 있음
  • 내부 구현을 외부로 노출시키지 않으면서도 모든 항목에 접근 가능
  • 적용 시 덜 효율적이거나 과도하지는 않은지 확인

 

옵저버 패턴

  • 한 객체의 상태가 변경되어 다른 객체들을 변경해야 할 필요성이 생겼을 때 사용(Pub/Sub 패턴이라고도 함)
  • message push, pull 하는 방식을 통해 결합도를 낮출 수/없앨 수 있음 
    • rabbitMQ, kafka 등
  • 느슨한 결합으로 객체간의 의존성 제거
  • 너무 많이 사용하게 되면 상태 관리가 힘듦
private final ApplicationEventPublisher eventPublisher;

//함수 내부에서 호출 publish
   eventPublisher.publishEvent(new OrderEvent(order.getId(), OrderType.CREATE));
   
//

import com.example.pattern.order.model.OrderEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
@Slf4j
@Async
public class OrderListener {
    @EventListener
    public void onApplicationEvent(OrderEvent event) {
        log.info("주문이벤트 Published [orderId : {}, orderType : {}]", event.getOrderId(), event.getOrderType().toString());
        log.info("이후 리뷰 알림 발송이 시작됩니다.");
        log.info("이후 정산 API 호출이 시작됩니다.");
    }
}

 

중재자 패턴

  • 옵저버 패턴
    • 1개의 publish에 대해 N개의 subcriber가 존재하고, observer가 pulling 이나 push방식을 통해 관리
  • 중재자 패턴
    • M개의 publisher와 N개의 subcriber 사이에서 1개의 mediator를 통해 통신

 

메멘토 패턴

  • 상태정보 저장 관련 메모리에 들고 있을 일 없음; 잘 사용하지 않음
  • 캡슐화를 위반하지 않고 객체의 state 스냅샷을 생성할 수 있음
  • 메멘토를 너무 자주 생성하면 많은 Ram을 소모할 수 있음

 

상태 패턴

  • 잘 사용하지 않음

 

전략 패턴

  • 하나의 메시지와 책임을 정의하고 이를 수행할 수 있는 다양한 전략을 만든 후, 다형성을 통해 전략을 선택해 구현을 실행
  • 유사한 패턴들
    • 팩토리, 추상 팩토리, 브릿지, 커맨드 패턴 등 이미 사용 중
  • 특징
    • OCP 준수, 하지만 과도하게 복잡해질 수 있음

 

템플릿 메서드 패턴

  • 잘 사용하지 않음, 요즘은 implement
    • interface는 내부 instance를 둘 수 없음 -> 필요 시 abstract class 를 사용
  • 상속을 통해 슈퍼 클래스의 기능을 확장할 때 사용하는 대표적인 방법
  • 변하지 않는 기능은 슈퍼 클래스에 만들어 두고, 확장할 기능은 서브 클래스에서 만듦
  • 단점
    • 알고리즘 변경 시 거의 모든 클래스에 수정이 가해질 수 있음
    • 상속의 단점.. 결합이 커서 
public abstract class Review { //abstract 사용예시
    public void review() {
        //부모 클래스에서 알고리즘의 골격을 정의
        login();
        selectBooks();
        putContent();
        selectEvaluation();
    }

    public void login() {
        log.info("로그인 성공!");
    }

    public void selectBooks() {
        log.info("리뷰 대상 상품 선택");
    }

    public void selectEvaluation() {
        log.info("별점 선택");
    }

    public abstract void putContent(); //

}

 

방문자 패턴

  • 잘 사용하지 않음
  • 맴버는 맴버 클래스로, 리워드는 리워드 클래스로.. 둘을 섞지 않는다
  • OCP : 다른 클래스를 변경하지 않으면서 새로운 행동 도입 가능
  • SRP : 같은 행동의 여러 버전을 같은 클래스로 이동할 수 있음
public class PointBenefit implements Benefit {
    @Override
    public void getBenefit(GoldMember member) {
        log.info("골드 멤버를 위한 포인트 제공 혜택");
    }

    @Override
    public void getBenefit(VIPMember member) {
        log.info("VIP 멤버를 위한 포인트 제공 혜택");
    }

    @Override
    public void getBenefit(SilverMember member) {
        log.info("실버 멤버를 위한 포인트 제공 혜택");
    }
}
public class GoldMember implements Member {
//    public void point() {
//        log.info("골드 멤버를 위한 포인트 제공 혜택");
//    }
//    public void discount() {
//        log.info("골드 멤버를 위한 할인 혜택");
//    }

    @Override
    public void getBenefit(Benefit benefit) {
        benefit.getBenefit(this);
    }
}
728x90
반응형
반응형

환경: java8

엑셀 다운로드 기능을 서버에 배포하면 NPE가 나면서 안되는 현상, 특이사항으로는 배포 시 되는 서버가 있고 안되는 서버가 있었음

org.apache.poi.hssf.usermodel.HSSFSheet.autoSizeColumn(HSSFSheet.java:1937)

 

에러 발생 코드

workbook.getSheetAt(0).autoSizeColumn(colIndex);

 

소스 상 딱히 null이 날 곳이 없었다.

 

되는 곳이 있고 안되는 곳이 있어, 환경의 문제에서 나온게 아닌가 싶어 구글링 하던 중 아래 글들을 보게된다.

설마 싶어 되는 서버와 안되는 서버의 자바 내 폰트 설정파일을 확인해보니

진짜 폰트 설정 파일(fontconfig.properties)이 있는 곳에서는 되고 없는 곳에서는 안되었다..

 

서버 담당자한테 말했더니 아래를 설치해주셨고, 설정 파일이 생기면서 이슈 해결

sudo yum install fontconfig dejavu-sans-fonts dejavu-serif-fonts
vi /usr/local/jdk8/jre/lib/fontconfig.properties

-----------

version=1
sequence.allfonts=default

참고

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=amabile29&logNo=222292712456 

 

OpenJDK8 version upgrade - Font Issue

OpenJDK에는 폰트 파일이 있지 않아서 업그레이드 후 Excel 파일 생성 등 폰트가 필요한 부분에서 에...

blog.naver.com

 

https://github.com/adoptium/adoptium-support/issues/70

 

There was no fonts in Linux version · Issue #70 · adoptium/adoptium-support

From @JamesMung on October 23, 2018 3:54 JDK Version ; jdk8u181-b13 When I use jasper print PDF in Windows is fine, but for Linux found the error as follow: Caused by: net.sf.jasperreports.engine.J...

github.com

 

728x90
반응형

'개발 > java' 카테고리의 다른 글

[java] orElse vs orElseGet  (0) 2023.10.11
디자인 패턴  (0) 2023.07.11
[java] lambda stream and final  (0) 2023.02.06
[google admob] ssv 콜백 적용  (0) 2023.01.06
[Date] java8 이하에서 날짜 timezone 변환  (0) 2022.11.04
반응형

환경: java 8 ++

 

stream을 사용하여 리스트의 홀수번째(index 기준 0, 2, 4)에 있는 원소를 콘솔에 찍어본다고 가정하자.

아래와 같이 짜야지라고 쉽게 생각할 수 있다.

variable used in lambda expression should be final or effectively final

근데 위와 같은 에러가 난다.

그 이유는 아래와 같다. 자세한 내용을 알려면 람다 캡쳐링과 그 원리에 대해 이해해야한다.

The restriction to effectively final variables prohibits access to dynamically-changing local variables, whose capture would likely introduce concurrency problems.

람다식 내부에서는 람다식 안에서 정의된 변수가 아닌 외부 변수에 접근할 수 있는데 이를 람다 캡쳐링이라고 한다.

스트림은 여러 thread의 병렬처리를 염두하고 만들어졌다. 즉 별도의 스레드에서 실행할 수 있다. 그렇다면 어떻게 기존 스레드의 값을 참조하여 쓸 수 있을까? 기존 스레드의 작업이 종료되었을 수도 있는데 말이다.

람다 캡쳐링이 일어날 때 데이터의 참조값(call by reference)이 아닌 데이터 값 그 자체(call by value)를 복사하여 자신의 스택에 두고 작업을 한다. 그렇기 때문에 값이 변경될 여지가 있는 변수는 사용할 수 없고 final에 준하는(effectively final) 변수만 람다 안에서 사용할 수 있다.

반면 heap에 저장된 값은 thread끼리 공유하고 있기 때문에 언제든지 구할 수 있으므로 변경하더라도 에러가 나지 않는다.

Java의 스트림 API에서 외부 변수를 사용할 때 해당 변수가 effectively final해야 하는 이유는 다음과 같습니다:

1. 스레드 안전성

  • 스트림의 연산은 종종 병렬로 실행되며, 외부 변수가 여러 스레드에서 동시에 접근될 수 있습니다. 이를 방지하기 위해 외부 변수가 변경되지 않도록 보장해야 합니다.

2. 불변성

  • 외부 변수가 effectively final이면, 그 값이 변경되지 않는 것을 보장합니다. 이로 인해, 람다 표현식이나 메서드 참조가 이 변수를 안전하게 사용할 수 있습니다. 불변성을 유지함으로써 예측 가능한 동작을 보장합니다.

3. 람다 캡처

  • Java의 람다 표현식은 외부 변수를 캡처할 수 있지만, 캡처된 변수는 내부적으로 복사되어 사용됩니다. 만약 이 변수가 변경 가능하다면, 예상치 못한 결과를 초래할 수 있습니다. 이를 방지하기 위해 effectively final 조건이 필요합니다.`

 

위 함수의 에러는 여러 방법으로 수정할 수 있다.

1. AtomicInteger를 사용하여 수정

2. list/array를 이용해 수정

 

2번 방식으로 수정하다 보니 신기했던 것은 단순 값이 변경된다는 것에 초점을 둘게 아니라 final이면 된다는 점에 초점을 뒀어야 한다는 것이다. 일반적으로 final이라고 하면 불변, 즉 string이나 int인 경우 값이 바뀌면 안 된다고 인식하기 때문에 '값의 변경'에 나도 모르게 초점이 갔는데, collection 같은 경우에는 final이어도 값이 변동(추가 혹은 수정)될 수 있다!

전체 변경; 재할당
일부 변경

즉 참조값이 바뀌면 안 되고 같은 참조값 안에서의 변경은 된다(final but mutable)

final --> You cannot change the reference to the collection (Object). You can modify the collection / Object the reference points to. You can still add elements to the collection
immutable --> You cannot modify the contents of the Collection / Object the reference points to. You cannot add elements to the collection.

실제로 list를 final로 선언하고 값을 수정할 때 별문제 없이 작동한다.


위에서 말한 것과 같이 heap에 있는 변수는 수정이 가능하다.

stack
heap

 


 

Arrays.asList는 값 추가 안됨; size가 정해진 list라서: https://www.baeldung.com/java-list-unsupported-operation-exception

collection의 final이란? https://stackoverflow.com/questions/26500423/what-does-it-mean-for-a-collection-to-be-final-in-java

https://www.geeksforgeeks.org/final-arrays-in-java/

람다 캡쳐링: https://perfectacle.github.io/2019/06/30/java-8-lambda-capturing/

728x90
반응형
반응형
개요: 앱에서 동영상 시청 시 리워드를 주는 플랫폼으로 google admob 사용 중. 보상 지급 수치에 대한 유효성 적립이 필요하여 서버 콜백 부분 추가 개발 진행.
 
서버 측 인증(SSV) 콜백 확인이란:
  • 서버 측 확인은 앱에서 보상형 광고 조회수를 확인하는 추가 단계로, 표준 클라이언트 측 콜백 외에 추가로 실시됨
  • 서버 측 확인을 사용하여 완료된 보상형 동영상 광고 조회수를 확인하여 앱에서 실제로 동영상 광고 시청을 완료한 사용자에게만 보상을 제공할 수 있음
  • 사용자가 보상형 동영상 광고 조회를 완료할 때마다 Google에 설정한 콜백 URL을 사용하여 조회를 확인
 
적용으로 얻는 효과:
  1. 사용자가 동영상 광고를 다 볼 때 에드몹에서 설정한 콜백 url 호출
  2. 응답으로 성공(200 ok)이 떨어지면 시청을 완료했다고 간주
  3. 보상 수치의 유효성 확인 가능

특이사항: '광고형식' 이 총 6종류가 있는데, 그 중 rewarded(리워드) 에 해당하는 것에 한함
 
  • admob 관리 페이지에 들어가서 해당 앱을 찾는다

  • 콜백을 설정하고자 하는 광고 단위를 클릭

  • 고급 설정 > 서버 측 확인 이 부분이 있는지 확인 

  • 서버 측 확인 수정 클릭, 콜백 URL 입력 후 URL 확인 버튼 클릭

  • 4번처럼 확인이 되었으면 확인된 URL 사용 버튼을 눌러 저장


 
 
개발 내용
1. 관련 dependency 추가

환경: java8, springboot1.5.9, maven

 <!-- 애드몹 인증 -->
 <dependency>
  <groupId>com.google.crypto.tink</groupId>
  <artifactId>apps-rewardedads</artifactId>
  <version>1.7.0</version>
 </dependency>
 <dependency>
  <groupId>joda-time</groupId>
  <artifactId>joda-time</artifactId>
  <version>2.10.10</version>
 </dependency>
 <dependency>
  <groupId>com.google.code.gson</groupId>
  <artifactId>gson</artifactId>
  <version>2.8.9</version>
 </dependency>

 

 
2. 개발 가이드 문서
이 중 Tink의 RewardedAdsVerifier 사용 방식으로 구현
 
3. 특이사항
  1. https 통신 필수
    1. 로컬에서 테스트를 하려면 로컬 인증서를 설치, 세팅해야 함
    2. 최종적으로 사용할 컴포넌트에 https 설정이 되어 있어야 함
  2. 구글과의 테스트 통신을 위해서 외부에서 접속되는 url이어야 함
  3. 실패 시 상태코드 200이 아닌 다른 코드로 반환해야 함(exception handler 유의)
    1. 서버에 연결할 수 없거나 서버에서 기대하는 응답을 제공하지 않으면 Google은 1초 간격으로 최대 5회 SSV 콜백을 전송하려고 재시도함
 
728x90
반응형
반응형

환경: Java6, springframework

api에서 아래와 같이 내려오는데

"startDateTime": "2022-10-05 00:00:00",
"endDateTime": "2022-10-31 23:59:59",

 

java.util.Date로 받는 경우

private Date startDateTime;
private Date endDateTime;

아래와 같이 그대로 안 받아지고 timezone이 변환되어 당황스러웠던 적이 있다.

startDateTime=Wed Nov 02 00:00:00 KST 2022, endDateTime=Wed Nov 30 23:59:59 KST 2022,

 

아니, 나는 온 그대로 받아서 그대로 화면에 뿌리고 싶은 건데, 대신 날짜 비교가 필요해서 string 말고 Date로 받아야 하는 건데.. 날짜가 지맘대로 바뀌어서 매우 곤란했다.

timeZone 이슈인걸 알아도 보통 java8+에 있는 LocalDateTime이나 ZonedDateTime 등을 사용했던지라 레거시 코드(java6)에서는 되지 않아 당황했는데...

방법은 생각보다 단순하였다. 물론 이를 알기 위한 삽질을 한 시간 동안 하긴 했다 ㅠㅠ

내가 있는 타임존을 아래처럼 알려주니, 그대로 받아졌다..?ㅋㅋㅋ

<정답>

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+9")
private Date startDateTime;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+9")
private Date endDateTime;

 

<오답>

@JsonFormat timezone 부분에 내가 변환하고자 하는 것, 즉 여기서는 kst(+9)로 오니까 utc/gmt로 받고 싶어서 아래와 같이 시도하였으나 동일했다.

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "UTC")
private Date startDateTime;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "UTC")
private Date endDateTime;
//////////
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT")
private Date startDateTime;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT")
private Date endDateTime;

 

결론.. 쉬운 길도 돌아가지 않게 해주는 ㅋㅋ jsonformat 짱...


비슷한 풀이

https://stackoverflow.com/questions/31822877/jackson-jsonformat-set-date-with-one-day-less/45456037#45456037

728x90
반응형

'개발 > java' 카테고리의 다른 글

[java] lambda stream and final  (0) 2023.02.06
[google admob] ssv 콜백 적용  (0) 2023.01.06
[mac] oracle jdk -> open jdk java교체하기  (0) 2022.08.22
[java] for loops and performance  (0) 2022.08.04
[jmh] benchmark test  (0) 2022.08.04
반응형

jdk가 무료 버전이 있고 유료버전이 있는데, 사용하던 jdk 11, 17이 oracle jdk인 듯하여 openJdk로 교체해본다.

https://auramin.tistory.com/25

 

오라클 Java SE(Standard Edition) 유,무료 버전

오라클 정책 변경으로 유료버전의 오라클 Java SE(JDK:Java Development Kit/  JRE:Java Runtime Environment)를 다운로드 또는 업데이트 할 경우 라이센스 계약 및 비용을 지불해야한다 합니다. 오라클 정책을..

auramin.tistory.com

 

현재 나의 jdk현황은 아래와 같다.

 

1.7.0_80은 무료 버전이라 그냥 두기로 하였고

1.7.80.15 버전은 지워야 할 것 같아서 아래와 같은 명령어를 통해 삭제 후, 1.7.0_80 버전을 심볼릭으로 걸어주었다.

sudo rm -fr /Library/Internet\ Plug-Ins/JavaAppletPlugin.plugin
sudo rm -fr /Library/PreferencesPanes/JavaControlPanel.prefPane
sudo rm -fr ~/Library/Application\ Support/Oracle/Java

-----------------
sudo rm -rf “/Library/Internet Plug-Ins/JavaAppletPlugin.plugin”
sudo ln -s /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk /Library/Internet Plug-Ins/JavaAppletPlugin.plugin

 

openJdk11, 17 버전은 mac을 사용하면 brew 명령어를 통해 설치가 가능하나, 시도해본 결과 의도한 대로 잘 되지 않아서 uninstall 하고 수동으로 설치하였다.

설치 파일은 아래 사이트에서 다운로드하였고 zip을 풀어준다.

https://jdk.java.net/archive/

 

Archived OpenJDK GA Releases

Archived OpenJDK General-Availability Releases This page is an archive of previously released builds of the JDK licensed under the GNU General Public License, version 2, with Classpath Exception. WARNING: These older versions of the JDK are provided to he

jdk.java.net

https://adoptium.net/marketplace

 

아래처럼 기존 자바를 지우고

cd /Library/Java/JavaVirtualMachines
sudo rm -rf jdk-17.0.3.1.jdk

zip을 풀어준 폴더를 아래 경로로 옮긴다.

sudo mv jdk-17.0.2.jdk/ /Library/Java/JavaVirtualMachines/

 

잘 되었는지 확인

/usr/libexec/java_home -V

 

참고로 잘못된 파일을 받은 경우(나의 경우는 Mac/AArch64를 받았었다) 위 명령어로 잡히지 않는다.

success?

어쨌건 위 명령어로 잘 깔린 것을 확인하고 intellij의 프로젝트를 빌드해보는데.. 아래와 같은 에러가 난다.

mac 시스템 환경설정 > 보안 및 개인 정보보호 메뉴로 가서 팝업창 하단 아래쪽에 뜬 아래 문구를 허용을 눌렀다.

확인된 개발자가 등록한 응용 프로그램이 아니기 때문에 ~~ 사용을 차단했습니다. 

하지만 그래도 gradle clean 조차 failed 하길래 아래와 같이 우선 owner설정을 바꾸고(이게 무슨 의미가 있는지는 확실하게 모르겠다. 단지 기존 jdk가 저렇게 설정이 되어 있어서 똑같이 맞춰봤다)

sudo chown -R root:wheel ./jdk-17.0.2.jdk

 

intellij에서 project close -> reopen -> invalid cache를 하니 정상적으로 실행되었다..

 


참고

https://engineering.linecorp.com/ko/blog/line-open-jdk/

 

728x90
반응형

'개발 > java' 카테고리의 다른 글

[google admob] ssv 콜백 적용  (0) 2023.01.06
[Date] java8 이하에서 날짜 timezone 변환  (0) 2022.11.04
[java] for loops and performance  (0) 2022.08.04
[jmh] benchmark test  (0) 2022.08.04
[powermock] mock static method  (0) 2022.07.21
반응형

java를 사용하여 배열 안의 원소 loop을 도는 많은 방법들에 대해 정리한다.

 

old school for loop

List<Student> studentList = new ArrayList<>();
studentList.add(a);
studentList.add(b);
studentList.add(c);

for (int i = 0; i < studentList.size(); i++) {
  System.out.println("Roll number: " + studentList.get(i).getRollNumber());
}

advanced for loop

for (Student st : studentList) {
   System.out.println("Roll number: " +  st.getRollNumber());
}

while, hasNext

Iterator<Object> it = list.iterator();
while (it.hasNext()) {
  Object o = it.next();
  // do stuff
}
  old for advanced for
since jdk1 jdk5
index increasing custom가능(2씩 증가 등); 역순 가능 무조건 1씩 증가만 가능; 역순 불가
index approach index 접근 가능 index 접근 불가
usage 어떠한 셀 수 있는 container object에 사용 가능 iterable interface를 구현한 구현체만 사용가능

위 세 방법 모두 성능상에 큰 차이는 없고, 굳이 따지자면 old for loop이 index의 객체를 탐사해야 하니(Object.get(i)) 조금 더 느릴 수 있다는 글이 있다. advance for loop으로 짜면 컴파일러가 while hasNext 문으로 변환할 거라 두 방법은 사실 거의 같은 거라고 볼 수 있다.

단순 1씩 증가한 loop이라면 advanced for loop을 사용하는 게 보기에도 더 좋을 듯하다.

라는 의견을 보았으나.. 믿기 힘든 상황이 되었다.(아래 테스트 참고)

 

Collection.foreach

List<Integer> list = Arrays.asList(1,2,3,4);
list.forEach(System.out::println);
// Output
1
2
3
4

Collection.stream.foreach

List<Integer> list = Arrays.asList(1,2,3,4);
list.stream().forEach(System.out::println);
// Output
1
2
3
4
  foreach stream.foreach
iterator collection iterator사용 collection을 stream으로 바꾸고 stream의 iterator 사용
order 순서 보장 순서 보장 않음
exception 구조 변화가 있으면 바로 exception 반환 나중에 exception반환
lock synchronized collection에 대한 작업이 중첩되면 우선 lock을 걸고 작업이 끝날 때 까지 기다림  lock없이 바로 접근

이 두 foreach의 성능을 비교하자면 stream.foreach가 스트림으로 변환 수 iterator를 사용하므로 더 느릴 것이라는 의견이 많았다.


benchmark test: jdk18, gradle7.4, jmh1.29, mac intel i7

그냥 글로 보기에는 와닿지 않아서 간단하게나마 벤치마크 테스트를 돌려본다.

아래 소스 외, 나머지는 기본 세팅으로 진행했다.

private static List<Integer> list = new ArrayList<>();
static {
    for(int i=0; i < 1_000_000; i++) {
        list.add(i);
    }
}

@Benchmark
@Fork(value = 1, warmups = 1)
@BenchmarkMode(Mode.Throughput)
public void usingStream(Blackhole blackhole) {
    list.stream().forEach(i -> blackhole.consume(i));
}

@Benchmark
@Fork(value = 1, warmups = 1)
@BenchmarkMode(Mode.Throughput)
public void usingIterator(Blackhole blackhole) {
    //list.listIterator().forEachRemaining(i -> blackhole.consume(i));
    list.forEach(i -> blackhole.consume(i));
}

@Benchmark
@Fork(value = 1, warmups = 1)
@BenchmarkMode(Mode.Throughput)
public void usingAdvancedForLoop(Blackhole blackhole) {
    for(Integer i : list) {
        blackhole.consume(i);
    }
}

@Benchmark
@Fork(value = 1, warmups = 1)
@BenchmarkMode(Mode.Throughput)
public void usingSimpleForLoop(Blackhole blackhole) {
    for(int i = 0; i < list.size() ; i++) {
        blackhole.consume(i);
    }
}

@Benchmark
@Fork(value = 1, warmups = 1)
@BenchmarkMode(Mode.Throughput)
public void usingWhileHasNext(Blackhole blackhole) {
    Iterator<Integer> itr = list.iterator();
    while(itr.hasNext()){
        blackhole.consume(itr.next());
    }
}

benchmark result

throughput mode(1초당 진행횟수)에서는 점수가 클수록 성능이 좋은 거다.

구관이 명관인가. (의외로) old for loop이 제일 성능이 좋았다.. 다른 것보다 거의 두배 정도?(정확히 두배라고 말할 순 없지만..) 그다음이 while문이라니.. 나머지는 비슷비슷한 것 같은데 어쨌건 꼴찌는 Collection.foreach였다..ㅋㅋㅋ 어느 글을 믿어야 하나,, 신기방기 하다.

 

jmh modes: http://hg.openjdk.java.net/code-tools/jmh/file/6cc1450c6a0f/jmh-core/src/main/java/org/openjdk/jmh/annotations/Mode.java

 

code-tools/jmh: 6cc1450c6a0f jmh-core/src/main/java/org/openjdk/jmh/annotations/Mode.java

view jmh-core/src/main/java/org/openjdk/jmh/annotations/Mode.java @ 929:6cc1450c6a0f profilers: perfasm, warn about low event count. author shade date Thu, 24 Jul 2014 00:21:17 +0400 parents 8d5845e7d89a children 0ca62574e95f line source /* * Copyright (c)

hg.openjdk.java.net

 

728x90
반응형

'개발 > java' 카테고리의 다른 글

[Date] java8 이하에서 날짜 timezone 변환  (0) 2022.11.04
[mac] oracle jdk -> open jdk java교체하기  (0) 2022.08.22
[jmh] benchmark test  (0) 2022.08.04
[powermock] mock static method  (0) 2022.07.21
[java] jvm, java, gc  (0) 2022.02.24
반응형

단순한 자바 성능 테스트를 하기 위한 툴로 benchmark가 있다. 간단하게 세팅하고 실행해본다.

환경: java 11 / gradle 7.4

 

1. intellij에서 gradle로 프로젝트 생성

 

2. 파일 구조 아래와 같이 만들기

main/test 폴더 삭제

 

3. build.gradle에 plugin 추가

plugins {
    id 'java'
    id "me.champeau.jmh" version "0.6.6"
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}

test {
    useJUnitPlatform()
}

dependency에 추가하는 게 아니었다..

 

4. benchmark code 작성

private static List<Integer> list = new ArrayList<>();
static {
    for(int i=0; i < 1_000_000; i++) {
        list.add(i);
    }
}

@Benchmark
@Fork(value = 1, warmups = 1)
@BenchmarkMode(Mode.Throughput)
public void usingStream(Blackhole blackhole) {
    list.stream().forEach(i -> blackhole.consume(i));
}

@Benchmark
@Fork(value = 1, warmups = 1)
@BenchmarkMode(Mode.Throughput)
public void usingIterator(Blackhole blackhole) {
    list.listIterator().forEachRemaining(i -> blackhole.consume(i));
}

 

5. 테스트 실행

터미널에 gradle jmh로 실행(intellij run 버튼이나 다른 걸로 하면 에러남)

이렇게 그냥 실행하면 jmh 기본 설정 값으로 테스트를 진행한다(warmup을 5번 하고 등등..).

기본적인 테스트임에도 시간이 상당히 걸리는데, 아래 readme를 참고하면 옵션들을 바꿀 수 있어 다양한 값으로 테스트가 가능하다.

공통 룰에 대한 옵션은 build.gradle의 jmh{}(블록) 안에다 작성하면 된다. 그 외 각 테스트 별로 어노테이션을 통한 개별 옵션을 줄 수도 있다.


jmh README: https://github.com/melix/jmh-gradle-plugin#configuration-options

 

GitHub - melix/jmh-gradle-plugin: Integrates the JMH benchmarking framework with Gradle

Integrates the JMH benchmarking framework with Gradle - GitHub - melix/jmh-gradle-plugin: Integrates the JMH benchmarking framework with Gradle

github.com

 

https://mkyong.com/java/java-jmh-benchmark-tutorial/

 

Java JMH Benchmark Tutorial - Mkyong.com

- Java JMH Benchmark Tutorial

mkyong.com

 

728x90
반응형

'개발 > java' 카테고리의 다른 글

[mac] oracle jdk -> open jdk java교체하기  (0) 2022.08.22
[java] for loops and performance  (0) 2022.08.04
[powermock] mock static method  (0) 2022.07.21
[java] jvm, java, gc  (0) 2022.02.24
[keyword] transient in serialization  (0) 2022.02.16
반응형

powermock이란? junit4로 테스트코드를 작성 시 static method를 mock할 수 있게 하는 방법

Mockito Mock Static Method using PowerMock
PowerMock provides different modules to extend Mockito framework and run JUnit and TestNG test cases.

Note that PowerMock doesn’t support JUnit 5 yet, so we will create JUnit 4 test cases. We will also learn how to integrate TestNG with Mockito and PowerMock.

Junit5는 지원하지 않나보다.

 

개발 환경: ivy(ant), java6

아래와 같이 관련 lib를 선언해준다.

<dependency org="org.powermock" name="powermock-core" rev="1.6.3"/>
<dependency org="org.powermock" name="powermock-module-junit4" rev="1.6.3"/>
<dependency org="org.powermock" name="powermock-api-mockito" rev="1.6.3"/>
<dependency org="org.powermock" name="powermock-api-easymock" rev="1.6.3"/>
<dependency org="org.javassist" name="javassist" rev="3.19.0-GA" conf="runtime" force="true" transitive="false"/>
<dependency org="org.mockito" name="mockito-core" rev="1.10.19"/>

 

목표: 아래의 static method를 테스트 코드 내에서 mock한다.

public static DataHandler getDataHandler() throws IllegalStateException {
   ...
}
We need to do the following to integrate PowerMock with Mockito and JUnit 4.

1. Annotate test class with @RunWith(PowerMockRunner.class) annotation.
2. Annotate test class with @PrepareForTest and provide classed to be mocked using PowerMock.
3. Use PowerMockito.mockStatic() for mocking class with static methods.
4. Use PowerMockito.verifyStatic() for verifying mocked methods using Mockito.

 

powermockito가 기존의 junit4와 혼용이 가능한지 궁금했는데, junit4코드를 아래와 같이 수정하니 잘 되는 것을 확인할 수 있었다.

@RunWith(PowerMockRunner.class)  //1.
@PrepareForTest(DataHandlerFactory.class) //2.
//@RunWith(MockitoJUnitRunner.class)
public class HuntBOTest {

//기존 junit4 스타일로 선언
    @InjectMocks
    private HuntBO huntBO;
    @Mock
    private HuntDAO huntDAO;

    public String ALPHA_FILE = "data-alpha.xml";

    public void includeForAlpha(){
    //mockStatic 이후에 하면 null이 나오기 때문에 mock하기 전에 stub을 만들어야 한다.
        DataHandler sample = DataHandlerFactory.create(ALPHA_FILE);

        PowerMockito.mockStatic(DataHandlerFactory.class); //3.
        when(DataHandlerFactory.getDataHandler()).thenReturn(sample);
    }

    @Test
    public void getTop1000__subList__alpha(){
        includeForAlpha();
        when(huntDAO.selectHuntTop1000AfterAug()).thenReturn(getList());
        List<HuntRank> hrList = huntBO.getTop1000();
        List<HuntRank> hrListSub = hrList.subList(0, 1);
        assertThat(hrListSub.get(0).getRank(), is(1));
    }
    
}
728x90
반응형

'개발 > java' 카테고리의 다른 글

[java] for loops and performance  (0) 2022.08.04
[jmh] benchmark test  (0) 2022.08.04
[java] jvm, java, gc  (0) 2022.02.24
[keyword] transient in serialization  (0) 2022.02.16
[junit5] no test found / checked exception is invalid  (0) 2022.02.07

+ Recent posts