Java 2023. 1. 14. 19:39

JIT 컴파일

정의

  • Just-In-Time Compilation (= Dynamic Translation, 동적 번역)
  • 프로그램을 실행하는 시점(런타임)에 기계어로 번역하는 컴파일 기법이다.
    • 실행 시점에 인터프리트 방식으로 코드를 읽어가면서 기계어 코드를 생성한다.
    • 생성된 기계어 코드를 캐싱하여, 재사용 시에는 캐싱된 기계어 코드를 사용한다.

Java의 경우

  • Java 컴파일러 : 자바 프로그램 코드 → 바이트코드
  • JIT 컴파일러 : 바이트코드 → 기계어 코드

장점 (사용 이유)

  • Only 인터프리터 방식보다는 성능이 좋고, Only 정적 컴파일 방식보다는 이식성이 좋다.
  • 즉, 정적 컴파일러의 성능과 인터프리터 언어의 편의성을 모두 추구한다.

의외의 장점

정적 컴파일러로 번역한 결과보다 성능이 더 좋을 수 있다.

  • 실행 환경에 맞추어 최적화된 코드를 생성할 수 있다.
    • CPU 세대마다 지원하는 명령어가 다를 수 있는데, 더 효율적인 명령어로 수행 가능한 작업은 해당 명령어를 통해 수행하도록 할 수 있다. (ex. 인텔 x86 CPU의 MMX 명령어)
    • 실행 환경의 캐시, 메모리 크기 등 런타임에만 알 수 있는 H/W, S/W 정보를 활용할 수 있기 때문에 정적 컴파일보다 좋은 성능의 코드를 생성할 가능성이 있다.
  • 가상 함수 테이블을 통해 가상 메서드가 간접 호출될 때, 해당 메서드를 오버라이드하는 서브(자식) 클래스가 존재하지 않을 경우에 간접 호출을 정적 바인딩(Static Binding)으로 호출하거나 인라인으로 전개할 수 있다고 한다.

참고

Java 2022. 12. 10. 11:12

OpenJDK 아카이브 링크

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

 

 

Java 2022. 11. 11. 01:26

Java의 정석 13장 리뷰

  • 쓰레드를 구현할 때는 run() 메서드를, 실행할 때는 start() 메서드를 호출한다. (p.726)
    • 쓰레드의 호출 스택을 별도로 생성하기 위해서이다.
  • 쓰레드 그룹(thread group) (p.741)
    • 보안 상 이유로 도입된, 쓰레드를 그룹으로 관리하는 클래스이다.
      • 자신이 속한 쓰레드 그룹이나 하위 쓰레드 그룹은 변경할 수 있지만, 다른 쓰레드 그룹의 쓰레드를 변경할 수는 없다.
    • 모든 쓰레드는 반드시 쓰레드 그룹에 포함되어 있어야 하며, 기본적으로는 부모 쓰레드와 같은 쓰레드 그룹에 속한다.
    • JVM은 기본적으로 system과 main 쓰레드 그룹을 생성한다.
    • 다음과 같이 참조 변수 없이 쓰레드를 생성하여 바로 실행시켜도, 이 쓰레드의 참조가 ThreadGroup에 저장되어 있기 때문에 GC 대상이 되지 않는다.
      public static void main(String[] args) throws Exception {
      		ThreadGroup grp1 = new ThreadGroup("Group1");
      		
      		new Thread(grp1, () -> {
              try {
                  Thread.sleep(1000);
              } catch (InterruptedException e) {}
          }, "th1").start();
      }
  • 관련 메서드
    • getAllStackTraces()
      • 작업이 완료되지 않은 모든 쓰레드의 호출스택을 출력할 수 있다.
    • Thread.sleep()
      • sleep() 메서드는 static으로 선언되어 항상 현재 실행 중인 쓰레드에 대해 작동하기 때문에, 다른 쓰레드 인스턴스 메서드를 호출하는 코드(th1.sleep(1000) 등)를 작성해도 해당 쓰레드가 아닌 현재 쓰레드가 WAITING 상태로 들어간다.
    • isInterrupted()
      • interrupted() 메서드와 달리, isInterrupted() 메서드는 쓰레드의 interrupted 상태를 false로 초기화하지 않는다.

       

       

    • suspend(), resume(), stop() (p.755)
      • suspend() 메서드는 sleep() 메서드처럼 쓰레드를 멈추게 하고, resume() 메서드를 통해 실행대기 상태로 돌릴 수 있다.
      • stop() 메서드는 호출 즉시 쓰레드를 종료시킨다.
      • 위의 메서드들은 데드락을 일으키기 쉽게 작성되어 있어서, deprecated 되었다.
    • yield() (p.760)
      • 쓰레드의 남은 할당 시간을 포기하고 다시 실행대기 상태로 돌아가는 메서드이다.
      • while 무한루프 + Thread.sleep() + Thread.yield() 조합으로 애플리케이션의 응답 속도를 높일 수 있다. (busy-waiting 방지)
        • (참고) suspend()stop() 메서드 호출 시에 interrupt() 메서드를 사용하면 Waiting 상태에서 바로 깨어나서 정지시키는 기법으로도 응답 속도를 높일 수 있다.
  • InterruptedException (p.754)
    • Thread.sleep() 메서드 실행 중(Waiting 상태) interrupt 신호를 받으면, 해당 메서드에서 InterruptedException이 발생하고 interrupted 상태는 false로 자동 초기화된다. 따라서 해당 코드 바깥의 try ~ catch 문까지 interrupt 신호가 전달되지 않는다.
  • Concurrency
    • synchronized 블럭
      • 블럭이나 메서드에 synchronized 키워드를 붙이면, 내부 코드에 접근 시 자동으로 임계 영역이 설정된다.
        synchronized(객체의 참조변수) {
            // Critical Section
        }
    • wait(), notify()
      • synchronized 블럭은 특정 쓰레드가 객체의 락을 가진 상태로 오랜 시간 있을 수 있기 때문에, 이를 보완하고자 나온 메서드가 위의 두 가지이다.
      • 두 메서드는 Object 클래스에 정의되어 있다.
      • notifyAll() : 기다리는 모든 쓰레드에게 통보한다. 단, lock을 얻을 수 있는 건 하나의 쓰레드 뿐이다.
    • Lock
      • java.util.concurrent.locks 패키지(JDK 1.5 이상)의 lock 클래스들을 이용할 수도 있다.
      • ReentrantLock
        • 가장 일반적인 Lock이다.
        • 생성자에 ReentrantLock(boolean fair) 와 같이 fait 변수를 줄 수 있고, 이 값이 true면 가장 오래 기다린 쓰레드가 Lock을 획득하도록 한다. 하지만 이 과정에서 성능이 떨어지며, 대부분의 경우 공정함보다는 성능을 선택한다.
        • tryLock() 메서드는, 다른 쓰레드에 의해 Lock이 걸려있으면 기다리지 않거나 인자로 받은 시간만큼만 기다리고 Lock을 얻지 못하면 false를 반환한다. 당연히 얻으면 true를 반환한다.
      • ReentrantReadWriteLock
        • 읽기 Lock이 걸려있을 경우, 다른 쓰레드가 읽기 Lock을 중복해서 걸고 데이터를 읽을 수 있다. 하지만 읽기 Lock과 쓰기 Lock을 동시에 걸 수는 없다.
      • StampedLock
        • 읽기, 쓰기 Lock 외에 낙관적 읽기 Lock(optimistic reading lock)을 추가한 것이다. 읽기 Lock이 풀리는 것을, 쓰기 Lock이 기다리는 걸 방지하려고 낙관적 읽기 Lock을 만든 것이다.
        • 낙관적 읽기 Lock은 쓰기 Lock에 의해 바로 풀린다.
        • 즉, 쓰기와 읽기 충돌 시 쓰기가 끝난 후에 읽기 Lock을 건다.

       

    • Condition
      • 쓰레드가 원하는 Lock의 종류를 구분하기 위한 waiting pool이다.
      • Object 클래스의 wait(), notify() 메서드 대신, Condition의 await(), signal() 메서드를 사용하면 된다.
  • volatile (p.786)
    • 코어에서 변수의 값을 읽을 때, 캐시가 아닌 메모리에서 읽어오도록 강제하는 키워드로 사용한다.
      • C, C++의 volatile 키워드와 문맥이 다른 것 같다. (C 계열에서는 최적화 방지 키워드로 사용)
  • fork & join (p.788)
    • JDK 1.7부터 추가된, 하나의 작업을 작은 단위로 나눠서 여러 쓰레드가 동시에 처리하도록 하는 기법이다.
    • RecursiveAction : 반환 값이 없는 작업 구현 시 사용
    • RecursiveTask : 반환 값이 있는 작업 구현 시 사용
Java 2022. 11. 7. 02:08

백기선 스프링 프레임워크 핵심 기술 완강 후기

후기
  • Spring Framework의 Core 부분만 간략하게 학습한 것인데도 상당히 방대하다고 느꼈다.
  • Spring Boot를 사용하면 상당히 편리해 보이는 부분이 많아서, 요즘 추세에 따라 반드시 알아야 할 것 같다.
  • 토비의 스프링 3에 많은 정보가 있는 듯 하다. 백기선 스프링 커리큘럼 완강 이후 심화 학습용으로 보면 좋을 것 같다.
  • 리액트나 안드로이드의 라이프사이클을 파악해야 쉽게 프로그래밍할 수 있는 것처럼, 스프링 Bean의 라이프사이클을 아는 것이 중요한 것 같다.

 

스프링
  • 등장 시기 : 2003년
  • 스프링 5부터는 서블릿 기반이 아닌 서버 애플리케이션도 개발할 수 있게 되었다. (WebFlux 지원)
디자인 철학
  • 특정 기술을 강요하지 않는다.
  • 다양한 관점을 지향한다.
  • 하위 호환성을 지킨다.
  • API를 신중하게 설계한다.
  • 높은 수준의 코드를 지향한다.

 

스프링 프레임워크 핵심 기술
  • Spring IoC Container
    • IoC Container
      • 객체가 사용하는 의존 객체를 직접 만들어 사용하는 게 아니라, 주입 받아 사용하는 방법을 뜻한다.
      • DI(Dependency Injection, 의존성 주입)이라고도 한다.
    • Spring Bean
      • 스프링 IoC 컨테이너가 관리하는 객체이다.
      • 사용 시 장점
        • 의존성 관리가 수월하다.
        • 스코프를 쉽게 지정할 수 있다.
          • 싱글톤, 프로토타입, 쓰레드 등
        • 라이프사이클 인터페이스가 존재하여, 필요 시 해당 메서드를 Override하여 사용할 수 있다.
      • 참고) Java Bean
        • 스프링 빈이 아닌 자바 빈 규약도 존재한다.
        • 자바 빈 특징
          • 기본 패키지 외 특정 패키지에 속한다.
          • 매개변수가 없는 기본 생성자가 존재한다.
          • 멤버변수의 접근제어자는 private이다.
          • 접근제어자가 public인 getter, setter가 존재한다.
          • (Optional) 직렬화가 가능하다.
    • 스프링 빈 설정 방법
      • 스프링 빈 직접 설정 (스프링 빈에 대한 정의)
        • 이름, 클래스, 스코프, Constructor, Setter 등
      • Component Scan
        • 특정 패키지 이하의 모든 클래스를 순회하여 @Component 애노테이션이 붙은 클래스를 스프링 빈으로 등록한다.
        • 설정 방법
          • context:component-scan : XML
          • @ComponentScan : Java
    • @Autowired
      • 의존 객체의 타입을 통해 해당 스프링 빈을 찾아서 주입하는 애노테이션이다.
      • 옵션
        • required (기본 값 = true) : 스프링 빈에서 해당 타입을 못 찾으면 애플리케이션 구동에 실패한다.
      • 사용할 수 있는 위치
        • 생성자 주입 (스프링 4.3 부터 생략 가능)
        • 수정자(Setter) 주입
        • 필드 주입
      • 같은 타입의 빈이 여러 개 인 경우 (상속, 인터페이스 구현 등)
        • 기본적으로는 변수명을 PascalCase로 변환한 타입의 스프링 빈을 주입한다.
        • 우선적으로 주입하고 싶은 스프링 빈에 @Primary 애노테이션을 붙이면 우선적용된다. (권장)
        • @Qualifier(빈이름) 애노테이션을 통해서 개별적으로 적용할 수도 있다.
    • @Component, @ComponentScan
      • @Component : 스캔 대상이 될 빈을 지정한다.
        • @Component 애노테이션을 적용한 애노테이션들
          • @Repository
          • @Service
          • @Controller
          • @Configuration

           

      • @ComponentScan : 스캔할 패키지 위치와 필터링 할 애노테이션에 대한 정보를 제공한다.
      • 스프링 빈을 직접 등록하는 방법
        public static void main(String[] args) {
            new SpringApplicationBuilder()
                .sources(Demospring51Application.class)
                .initializers((ApplicationContextInitializer<GenericApplicationContext>) applicationContext -> {
                    applicationContext.registerBean(MyBean.class);
                })
                .run(args);
        }
    • 빈 스코프
      • 스코프 종류
        • 싱글톤
        • 프로토타입
          • Request
          • Session
          • WebSocket
          • 기타 등등
      • 싱글톤 빈이 프로토타입 빈을 참조할 경우
        • 프로토타입 빈은 사용할 때마다 다른 인스턴스를 사용해야 하는데, 싱글톤 빈은 반드시 하나만 존재하므로 꺼내 사용할 때 프로토타입 빈이 수정되지 않는 문제가 발생한다.
        • 이를 해결하기 위해 Proxy 패턴을 사용한다.
          • ScopedProxy : 프로토타입 빈 클래스 @Scope 애노테이션에 proxyMode=ScopedProxyMode.TARGET_CLASS 속성을 주면 CGLib 다이나믹 프록시가 적용되어 매번 인스턴스가 달라진다.
            • TARGET_CLASS 로 주는 이유 : JDK 다이나믹 프록시는 Interface로만 만들 수 있는데, Class를 대상으로 하여 서드파티 다이나믹 프록시 라이브러리인 CGLib를 사용하도록 지정함
          • ObjectProvider : 프로토타입 빈을 참조하는 코드를 ObjectProvider로 감싸서 프록시 패턴을 적용할 수도 있다. (비권장 - 소스코드에 스프링코드가 들어가기 때문)
    • Environment
      • ApplicationContext extends EnvironmentCapable : EnvironmentCapable 인터페이스 기반으로 환경 설정을 제공한다.
      • 프로파일
        • 개발 환경과 배포 환경에서 서로 다른 빈을 사용하고 싶을 때 적용하는 프로파일을 의미한다.ex. 배포 시에만 사용할 모니터링 빈은 테스트 환경에서 필요 없으므로 등록하지 않고싶은 경우
        • 프로파일 정의 방법 : @Profile("프로파일명") 애노테이션을 클래스나 메소드에 붙인다.
          • 프로파일 표현식 : !, &, | 등 논리 연산자를 조합하여 다양하게 활동할 수 있다.
        • 프로파일 설정 방법 : Java VM 옵션으로 다음 값을 준다. -Dspring.profiles.active="프로파일명"
          • 혹은 IntelliJ 설정에서 Active Profile 속성으로 값을 주면 되는데 아마 Ultimate 버전만 뜨는 듯 하다.

         

      • 프로퍼티
        • VM 옵션이나 프로퍼티 파일에서 속성을 불러와 사용할 수 있다.
        • @PropertySource : 프로퍼티 파일에서 속성을 불러올 수 있다.
        • 우선 순위가 높은 프로퍼티를 적용하여 제공한다.
        • 스프링 부트 기본 프로퍼티 소스 : application.properties
    • MessageSource
      • ApplicationContext extends MessageSource : 국제화 기능(다국어 지원, i18n)을 제공하는 인터페이스이다.
      • 스프링 부트 사용 시 기본적으로 message.properties 등 파일을 사용할 수 있다.
        • 다국어 메시지 프로퍼티 파일 네이밍 규칙은 해당 언어를 언더바로 이으면 된다.
          • messages_ko_kr.properties
      • ReloadableResourceBundleMessageSource 객체를 통해 프로퍼티 파일이 실시간으로 반영되도록 할 수 있다. (Build 시 적용된다.)
    • ApplicationEventPublisher
      • ApplicationContext extends ApplicationEventPublisher : 이벤트 프로그래밍에 필요한 인터페이스를 제공한다. (옵저버 패턴)
      • Event
        • ApplicationEvent 를 상속받는 클래스를 사용한다. (스프링 4.2부터는 상속 안 해도 사용 가능)
      • 이벤트 발생시키기 : ApplicationEventPublisher.publishEvent();
      • 이벤트 핸들링 : ApplicationListener<이벤트> 를 구현하여 빈으로 등록한다.
        • 스프링 4.2부터는 @EventListener 애노테이션을 스프링 빈 메소드에 붙여서 사용할 수 있다.
        • @Order : 동일 이벤트에 대한 핸들링의 순서를 정할 수 있다.
        • @Async : 비동기로 실행한다. (기본 핸들링은 동기적으로 처리된다.)
      • 스프링 제공 기본 이벤트
        • ContextRefreshedEvent : ApplicationContext 초기화 or 리프레시 시 발생한다.
        • ContextStartedEvent : ApplicationContext start() 를 통해 라이프사이클 빈이 시작 신호를 받은 시점에 발생한다.
        • ContextStoppedEvent : ApplicationContext stop() 을 통해 라이프사이클 빈이 정지 신호를 받은 시점에 발생한다.
        • ContextClosedEvent : ApplicationContext close()를 통해 싱글톤 빈이 소멸되는 시점에 발생한다.
        • RequestHandledEvent : HTTP 요청을 처리했을 때 발생한다.

       

    • ResourceLoader
      • ApplicationContext extends ResourceLoader : 리소스를 읽어오는 기능을 제공한다.
      • 리소스 읽어오는 메서드 : getResource(String location)
      • 리소스를 어떤 방식으로 읽어올 수 있는가? (location 종류)
        • 파일 시스템
        • classpath
        • URL
        • 상대경로, 절대경로
  • 스프링 추상화
    • Resource 추상화
      • org.springframework.core.io.Resource : 스프링 내부에서 많이 사용하는, java.net.URL 을 추상화 한 인터페이스이다.
      • 추상화 이유
        • classpath 기준 리소스 읽어오는 기능 부재
        • ServletContext 기준 상대 경로로 읽어오는 기능 부재
      • 주요 메서드
        • getInputStream()
        • exists()
        • isOpen()
        • getDescription() : 전체 경로 포함한 파일 이름 또는 실제 URL
      • 주요 구현체
        • UrlResource : http, https, ftp, file, jar 프로토콜 기본 지원
        • ClassPathResource : classpath: 접두어 지원
        • FileSystemResource
        • ServletContextResource : 웹 애플리케이션 루트에서 상대 경로로 리소스를 찾는다.
      • 리소스 읽어오기
        • 리소스의 타입은 getResource(String location)location 문자열과 ApplicationContext 타입에 따라 결정된다.
          • ClassPathXmlApplicationContextClassPathResource
          • FileSystemXmlApplicationContextFileSystemResource
          • WebApplicationContextServletContextResource
        • ApplicationContext 타입에 상관없이 리소스 타입을 강제하기 위해서 java.net.URL 접두어 중 하나를 사용할 수 있다. (권장)
          • classpath:ClassPathResource
          • file://FileSystemResource
            • 루트 경로부터 시작하려면 다소 이상하게 보이지만 file:///something/path 이런 식으로 작성해야 한다.
    • Validation 추상화
      • 모든 계층(웹, 서비스, 데이터)에서 사용해도 된다. (어떠한 계층과도 관계가 없다.)
      • 데이터 바인딩 시 같이 사용되기도 한다.
      • 메서드 오버라이딩
        • supports(Class clazz) : 특정 타입의 객체를 검증할 때 사용할 것인지 결정한다.
        • validate(Object obj, Errors e) : 실제 검증 로직을 이 안에서 구현한다.
          • 구현 시 ValidationUtils 를 사용한다.
      • 스프링 부트 2.0.5 이상 버전 사용 시
        • LocalValidatorFactoryBean 을 스프링 빈으로 자동 등록해준다.
        • Default message도 위의 클래스에서 제공한다.

       

    • 데이터 바인딩 추상화
      • 사용자 입력 값(문자열)을 애플리케이션 도메인 모델(객체)에 동적으로 변환하여 입력하는 기능이다.
      • PropertyEditor : 스프링 3.0 이전까지 DataBinder 가 바인딩 작업에 사용하던 인터페이스이다.
        • 상태 정보를 저장하기 때문에 thread-safe하지 않다.
        • Object ~ String 간 변환만 지원하여, 사용 범위가 다소 제한적이다.
        • PropertyEditorSupportgetAsText(), setAsText 메서드를 오버라이딩하여 사용한다.
      • ConversionService : DataBinder 대신에 타입 변환 작업에 사용하는 인터페이스이며, thread-safe 하다.
        • addFormatters() 메서드를 통해 ConverterFormatter를 등록할 수 있다.
          • DefaultFormattingConversionServiceFormatterRegistryConverterRegistry 상속 구조
        • 스프링 MVC, SpEL 등에서 사용한다.
        • Converter : 일반적인 S(source) → T(target) 타입 변환기
          • 상태 정보를 저장하지 않아 thread-safe하다.
          • ConverterRegistry 에 등록해서 사용한다.
        • Formatter : PropertyEditor 를 대체한다.
          • Object ~ String 변환에 사용한다.
          • 문자열을 Locale에 따라 다국화 할 수 있다.
          • FormatterRegistry 에 등록해서 사용한다.
        • 스프링 부트에서 웹 애플리케이션인 경우, public classWebConversionService extendsDefaultFormattingConversionService 를 스프링 빈으로 등록하여, FormatterConverter 스프링 빈을 찾아 자동으로 등록해준다.

       

  • SpEL (Spring Expression Language)
    • Unified EL 과 비슷하나, 메소드 호출을 지원하며, 문자열 템플릿 기능도 제공한다.
    • 스프링 3.0 부터 지원하는 표현식이다.
    • 문법
      • #{”표현식”}
      • ${"프로퍼티"}
      • 표현식 안에 프로퍼티 사용 가능 (#{${my.data} + 1})
    • 주로 사용하는 곳
      • @Value, @ConditionalOnExpression 애노테이션
      • 스프링 시큐리티
      • 스프링 데이터 : @Query 애노테이션
      • Thymeleaf
  • 스프링 AOP (Aspect-Oriented Programming)
    • AOP : 흩어진 Aspect를 모듈화 하는 프로그래밍 기법이다.
      • Aspect : 관심사를 모듈화 한 것. 즉 공통적으로 사용되는 코드 모듈을 의미한다.
      • Advice : Aspect에서 수행할 실질적 작업
      • Target : Aspect의 적용 대상
      • Join Point : Advice가 적용될 수 있는 지점
      • Pointcut : Advice가 적용될 Join Point 지점을 의미한다. (Join Point의 한 부분)
    • Java AOP 구현체
      • AspectJ
      • Spring AOP
    • 스프링 AOP 특징
      • 프록시 기반의 AOP 구현체이다.
        • 프록시 패턴 사용 이유 : 기존 코드의 변경 없이 접근 제어 또는 부가 기능을 추가하기 용이하다.
      • 스프링 빈에만 AOP 적용 가능하다.
      • 동적 프록시
        • Java 제공 프록시 : 인터페이스 기반의 프록시를 생성할 수 있다.
        • CGlib : 클래스 기반의 프록시 생성도 지원한다.
      • 스프링 IoC : AbstractAutoProxyCreator implements BeanPostProcessor 를 통해 기존 스프링 빈을 대체하는 동적 프록시 빈을 생성하여 등록시킨다.

         

      • 프록시 클래스를 작성하여 구현할 수 있으나, 애노테이션 기반의 스프링 AOP를 통해 매번 프록시 클래스를 작성해야 하는 번거로움을 줄일 수 있다.
    • @AOP
      • 스프링 부트에서 제공하는 AOP 애노테이션이다.
      • Aspect 정의 : @Aspect
        • 빈으로 등록해야 하므로 @Component 도 추가해야 한다.
      • Advice 정의
        • 다음 애노테이션을 통해 Advice가 적용될 범위를 설정할 수 있다.
          • @Around : ProceedingJoinPoint 매개변수를 받는 함수를 통해 Advice를 구현한다. 원래의 메서드가 실행되기 전후로 Advice를 설정할 수 있는 장점이 있다.
          • @Before
          • @AfterReturning
          • @AfterThrowing
      • Pointcut 정의 : @Pointcut
        • Pointcut Designator (표현식)
          • execution
          • @annotation
          • bean
        • 포인트컷 조합식
          • &&, ||, !

     

  • Null-safety
    • 스프링 프레임워크 5에 추가된, 컴파일 시점에 최대한 NullPointerException을 방지하기 위한 Null 관련 애노테이션이다.
    • 종류
      • @NonNull
      • @Nullable
      • @NonNullApi : 패키지 레벨
      • @NonNullFields : 패키지 레벨
TODO
  • 자바 클래스 로딩 복습

 

 

'Java' 카테고리의 다른 글

JIT 컴파일  (0) 2023.01.14
OpenJDK 아카이브 링크  (0) 2022.12.10
Java의 정석 13장 리뷰  (1) 2022.11.11
Java 이클립스 The project description file (.project) for ~ is missing 에러  (0) 2021.12.13
Java 2021. 12. 13. 19:14

Java 이클립스 The project description file (.project) for ~ is missing 에러

workspace를 구성하고나서 리포지토리를 옮겼더니 이클립스를 켜서 Navigator를 볼 때 위의 에러가 발생했다.

.metadata\.plugins\org.eclipse.core.resources\.projects\프로젝트명\.location 의 경로와 실제 경로가 맞지 않으면 발생하는 것 같다.

.metadata\.plugins\org.eclipse.core.resources\.projects 폴더를 지우고 다시 이클립스를 켰더니 해결됐다.

참고

'Java' 카테고리의 다른 글

JIT 컴파일  (0) 2023.01.14
OpenJDK 아카이브 링크  (0) 2022.12.10
Java의 정석 13장 리뷰  (1) 2022.11.11
백기선 스프링 프레임워크 핵심 기술 완강 후기  (0) 2022.11.07