Servlet 애플리케이션 개발

웹 MVC 시작하기

처음 프로젝트 시작을 maven-archetype-webapp 으로 시작

Pom.xml에 servlet-api 디펜던시 추가

<dependency>
    <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>4.0.1</version>
  <scope>provided</scope>
</dependency>

provided 는 개발할때만 사용되고, 빌드 될때는 빠진다. 이유는 tomcat 에서 구현이 될것이다.

Servlet 생성

web.xml 파일

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>me.morris.HelloServlet</servlet-class>
  </servlet>


  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>

</web-app>

me.morris 패키지 HelloServlet.java

package me.morris;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class HelloServlet extends HttpServlet {

    @Override
    public void init() throws ServletException {
        System.out.println("init");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doGet");
        resp.getWriter().println("" +
                "<html>\n" +
                "<body>\n" +
                "<h2>Hello Servlet!</h2>\n" +
                "</body>\n" +
                "</html>\n");

    }

    @Override
    public void destroy() {
        System.out.println("Destroy");
    }
}

톰캣을 사용하여 실행 하면 처리가 됨

Init 메소드는 처음에 요청시에만 출력

Destroy는 tomcat servlet콘테이너가 종료 될때 실행

Servlet Listener와 Filter

Servlet Listener :

  • 웹 어플리케이션에서 발생하는 주요 이벤트를 감지하고, 각 이벤트에 특별한 작업이 필요한 경우에 사용 할 수 있다.
    • 서블릿 컨택스트 수준의 이벤트
      • 컨텍스트 라이프사이클 이벤트
      • 컨텍스트 애트리뷰트 변경 이벤트
    • 세션 수준의 이벤트
      • 세션 라이프 사이클 이벤트
      • 세션 애트리뷰트 변경 이벤트

Servlet Container가 시작될때, DatabaseConnection을 미리 만들어놓고, 다양한 servlet에서 생성할 수 있다.

Servlet attribute에 DatabaseConnection을 미리 만들어 놓을 수 있다.

구현

아래는 Servlet Context의 라이프사이클 변경에 대한 이벤트 를 감지 한것

MyListener.java 추가 하기

public class MyListener implements ServletContextListener {
    //Servlet Container의 라이프 사이클의 이벤트를 감지
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("contextInitialized ");
        sce.getServletContext().setAttribute("name","morriskim");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("contextDestroyed");
    }
}

web.xml 에 listener 태그 추가

<listener>
  <listener-class>me.morris.MyListener</listener-class>
</listener>

결과 :

[2020-03-01 06:39:52,389] Artifact SpringWebMVC:war exploded: Artifact is being deployed, please wait...
contextInitialized <-- 여기 추가된것을 알수 있다.
[2020-03-01 06:39:52,781] Artifact SpringWebMVC:war exploded: Artifact is deployed successfully
[2020-03-01 06:39:52,782] Artifact SpringWebMVC:war exploded: Deploy took 393 milliseconds
01-Mar-2020 18:40:02.244 정보 [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/Users/daeyunkim/Documents/03.util/apache-tomcat-8.5.42/webapps/manager]
01-Mar-2020 18:40:02.281 정보 [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/Users/daeyunkim/Documents/03.util/apache-tomcat-8.5.42/webapps/manager] has finished in [37] ms
init
doGet

Servlet Filter :

  • 들어온 요청을 서블릿으로 보내고, 또 서블릿이 작성한 응답을 클라이언트로 보내기 전에 특별한 처리가 필요한 경우에 사용 할 수 있다.
  • 체인 형태의 구조

어떠한 요청이 들어왔을때, 요청, 응답에 있어서 전에 처리를 하고 싶은 경우, 여러개의 서블릿에 추가적으로 할 수 있다.

구현

MyFilter.java 추가

  • 주의 해야할점 : javax.servlet의 Filter를 implements 해야됨
    doFilter의 파라미터 들의 값을 보면 ServletRequest request, ServletResponse response, FilterChain chain 을 들어간것을 볼수 있다.
    여기에서는 Filterchain이 request,response를 연결 해줘야한다.

    package me.morris;

    import javax.servlet.*;
    import java.io.IOException;

    public class MyFilter implements Filter {

      @Override
      public void init(FilterConfig filterConfig) throws ServletException {
          System.out.println("Filter init");
      }
    
      @Override
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
          System.out.println("Filter ");
          chain.doFilter(request,response);
      }
    
      @Override
      public void destroy() {
          System.out.println("Filter destroy");
      }

    }

web.xml 에 Filter 설정 추가

,

의 안의 속성에는 servlet-name과 url-pattern이 있는데,

하나의 주소 이외에 다양한 곳에 필터를 사용할때 url-pattern을 사용한다.

  <filter>
    <filter-name>myfilter</filter-name>
    <filter-class>me.morris.MyFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>myfilter</filter-name>
    <servlet-name>hello</servlet-name>
  </filter-mapping>

Filter를 추가한 실행 동작 순서

[2020-03-01 06:49:03,499] Artifact SpringWebMVC:war exploded: Artifact is being deployed, please wait...
contextInitialized 
Filter init
[2020-03-01 06:49:03,887] Artifact SpringWebMVC:war exploded: Artifact is deployed successfully
[2020-03-01 06:49:03,887] Artifact SpringWebMVC:war exploded: Deploy took 388 milliseconds
init
Filter 
doGet
01-Mar-2020 18:
  1. contextInitialized : 서블릿 리스너에서 contextInitinalize가 실행
  2. Filter init : Filter 를 구현한 클래스에서 init 메서드 실행
  3. FilterMapping된 Servlet 클래스 호출
  4. init : Servlet클래스 init 메서드 실행
  5. Filter : Filter 클래스에서의 doFilter 메서드 실행
  6. doGet : Servlet 메서드의 doGet 메서드 실행

으로 적용된다.

Servlet Container ->(request)-> Filter -> Servlet ->(response)-> Servlet Container

해결하고 싶은 것

아래와 같은 내용을 Spring @RequestBody 에 값을 전달하고싶다.

{
    "zooName":"MorrisZoo",
    "animals":[
        {"type":"cat", "name":"yammi", "likesCream":true,"lives":0},
        {"type":"dog", "name":"Fancy","barkVolume":4}
    ]
}

animals는 Animal 타입을 상속 받은 dog, cat 의 객체로 받아와야한다.

구현하기
Jackson Polymorphic Type Handling Annotations 을 참고
Jackson 라이브러리는 다음에 한번에 정리를 하겠습니다.

AnimalController.java

@RestController
public class AnimalController {

    @PostMapping(value="/zooinfo")
    public void jsonTypeCheck(@RequestBody Zoo zoo){
        String zooName = zoo.zooName;
        System.out.println("동물원 이름 : "+zooName);
        zoo.animals.forEach((value)->value.println());
    }
}

Zoo.java : @RequestBody에 받아올 zoo 객체

package com.jackson.jacksontutorial;

import java.util.List;

public class Zoo {
    public String zooName;
    public List<Animal> animals;
}

Animal.java` : Dog, Cat의 상위 클래스

package com.jackson.jacksontutorial;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = Dog.class, name = "dog"),
        @JsonSubTypes.Type(value = Cat.class, name = "cat")
})
public abstract class Animal {
    public String name;

    public void println( ){
        System.out.println("Animal Type : "+name);
    };

}

Cat.java

package com.jackson.jacksontutorial;

import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.annotation.JsonTypeName;


//@JsonTypeName("cat")
@JsonRootName("cat")
public class Cat extends Animal {
    boolean likesCream;
    public int lives;

    @Override
    public String toString() {
        return "Cat{" +
                "likesCream=" + likesCream +
                ", lives=" + lives +
                '}';
    }

    @Override
    public void println() {
        String AnimalType = super.name;
        System.out.println("name : "+name+","+this.toString());
    }
}

Dog.java

package com.jackson.jacksontutorial;

import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.annotation.JsonTypeName;

//@JsonTypeName("dog")
@JsonRootName("dog")
public class Dog extends Animal {
    public double barkVolume;

    @Override
    public String toString() {
        return "Dog{" +
                "barkVolume=" + barkVolume +
                '}';
    }
    @Override
    public void println() {
        String name = super.name;
        System.out.println("name : "+name+","+this.toString());
    }
}

결과

요청

post- @RequestBody

{
    "zooName":"MorrisZoo",
    "animals":[
        {"type":"cat", "name":"yammi", "likesCream":true,"lives":0},
        {"type":"dog", "name":"Fancy","barkVolume":4}
    ]
}

SpringApplication

2020-02-28 16:51:14.376  INFO 17268 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 5 ms
동물원 이름 : MorrisZoo
name : yammi,Cat{likesCream=false, lives=0}
name : Fancy,Dog{barkVolume=4.0}

ClassPath Scanning

특정 classpath 이하에 있는 관리할 컴포넌트(@Component)들을 등록을 하기 위해 스캔함

@Component : Annotation Type으로 런타임에 동작함. classpath scanning을 통해 자동적으로 검색됨.

@Repository, @Service, @Controller

@ComponentScan(basePackages = "kr.co.fastcampus.cli") 을 해주면 xml에 별도로 설정을 안해줘도 알아서 추가가 됨.

예시

@Configuration
@ComponentScan(basePackages = "kr.co.fastcampus.cli")
public class AppConfig {
    @Bean
    public A a1(){
        return new A();
    }
    @Bean
      @Scope("singleton")
    public B b1(){
        return new B();
    }
}

@Component
class A {}

@Component
class B{}

아래와 같이 AnnotationConfigApplicationContext()를 사용해서 사용이 가능하다.

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

AnnotationConfigApplicationContext API 를 살펴보면 아래와 같이 파라미터를 넣어주면 사용이 가능하다.

Constructor and Description
AnnotationConfigApplicationContext()Create a new AnnotationConfigApplicationContext that needs to be populated through register(java.lang.Class...) calls and then manually refreshed.
AnnotationConfigApplicationContext(Class... componentClasses)Create a new AnnotationConfigApplicationContext, deriving bean definitions from the given component classes and automatically refreshing the context.
AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory)Create a new AnnotationConfigApplicationContext with the given DefaultListableBeanFactory.
AnnotationConfigApplicationContext(String... basePackages)Create a new AnnotationConfigApplicationContext, scanning for components in the given packages, registering bean definitions for those components, and automatically refreshing the context.

Filter

ComponentScan에서 특정 클래스를 빼고 싶을 때, (pattern, class, 등 의 다양함 )

아래처럼 filter를 사용해서 execludeFilters를 사용하여 가능하다.

@Slf4j
@ComponentScan(basePackageClasses = Main.class , excludeFilters = @ComponentScan.Filter (type= FilterType.REGEX,pattern="kr.co.fastcampus.cli.B"))
public class Main {
    static Logger logger = LoggerFactory.getLogger(Main.class);

    public static void main(String []args) {

        //Java Anotation을 사용한 주입
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
        B b = context.getBean(B.class);
        log.info("b : "+b);
        context.close();
    }
}

ComponentScan 성능 향상

ComponentScan은 Java Generic을 사용하여 JVM에서 어노테이션이 붙어있는지 아닌지 결정하여 판단을 하게 되는데, 만약 어노테이션이 너무 많이 사용되게 되면 ComponentScan이 오래 걸릴 수 있다.

그래서 indexer를 만들어 놓음

pom.xml에 추가

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.2.3.RELEASE</version>
        <optional>true</optional>
    </dependency>
</dependencies>

빌드를 하게 되면 META-INF/spring.components 가 만들어짐.

java.io.InvalidClassException: org.apache.spark.rdd.RDD

java.io.InvalidClassException: org.apache.spark.rdd.RDD; local class incompatible: stream classdesc serialVersionUID 에러

19:58:43.380 [dag-scheduler-event-loop] INFO  o.a.spark.scheduler.DAGScheduler - ShuffleMapStage 0 (count at TaskProcessService.java:152) failed in 0.880 s due to Job aborted due to stage failure: Task 0 in stage 0.0 failed 4 times, most recent failure: Lost task 0.3 in stage 0.0 (TID 3, 192.168.44.80, executor 1): java.io.InvalidClassException: org.apache.spark.rdd.RDD; local class incompatible: stream classdesc serialVersionUID = -3328732449542231715, local class serialVersionUID = 4416556597546473068
        at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
        at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1843)
        at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
        at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1843)
        at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2000)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
        at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2245)
        at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2169)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2027)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)

spark 를 사용하는 어딘가에서 spark version이 여러개라서 오류가 나는것

저 같은 경우에는 springboot에서 spark의 버전을 2.4.0 으로 설정이 되어있고, 서버의 spark 모듈은 2.4.5라서 발생했었음

'BackEnd > Spark' 카테고리의 다른 글

[Spark] RDD Persistence 와 Caching  (0) 2020.03.05
스파크 SQL 사용하여 파일로 보내기  (0) 2020.03.02
Spark History Server 실행 오류 시  (0) 2020.02.10
SparkSession  (0) 2020.02.06
RDD 영속화(캐싱)  (0) 2019.09.02

MYSQL 8.0 이후 접속시

8.0 이상의 Dbeaver로 접속을 하려고 하는데,

이전에는 주소값, 데이터베이스, 드라이버이름, 유저아이디, 패스워드 등만 가지고 있으면 접속이 가능했었다. 하지만 8.0 이후의 버전으로 접속을 시도 했을때

아래와 같은 에러가 발생했다.

public key retrieval is not allowed

8.0 이후 부터는

allowPublicKeyRetrieval=true 의 설정을 추가 해줘야한다.

jdbc 주소 는 아래와 같이

jdbc:mysql://localhost:3306/database?allowPublicKeyRetrieval=true

Dbeaver 에서는 드라이버 설정

> driver properties

'BackEnd > SQL' 카테고리의 다른 글

분산 마리아 디비 , galera cluster  (0) 2017.12.18

Bean Scope

빈의 범위

객체를 만들때 컨테이너가 객체를 한번만 호출하는지, 객체를 호출받을때마다 생성하는지를 정의

singleton, prototype을 주로 많이 쓰임

Singleton : 객체를 한번만 생성 (동일성을 보장)

<bean id="A" class="kr.co.fastcampus.cli.A" scope="singleton"></bean>

prototype : 객체를 계속 새로 만듬

<bean id="A" class="kr.co.fastcampus.cli.A" scope="prototype"></bean>

Scope Description
singleton (Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
prototype Scopes a single bean definition to any number of object instances.
request Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
session Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
application Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
websocket Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.

동일성과 동등성

동일성 : 객체 주소가 같다.

동등성 : 객체의 값이 같다.

동일성

@Test
public void testIdentity(){
  //동일성 (identity) : 객체 주소 가 같다. (object1==object2==object3) 는 동일한 주소 값을 가질 수 있다.
  //동등성 (eqauls) : 객체의 값이 같다. (object1.equals(object2) ==true )

  A a1 = new A();
  A a2 = new A();
  Assert.assertFalse(a1==a2); //a1과 a2의 주소값이 같은지 ? 다르다

  A a3 = a1;
  Assert.assertTrue(a3==a1);

}
class A{

}

동등성

Object 의 equals 는 아래와 같이 동일성 체크를 한다.

public boolean equals(Object obj) {
            return (this == obj);
}



@Test
public void testEquals(){

  A a1 = new A(10,"Hello world");
  A a2 = new A(10,"Hello world");
  A a3 = new A(5,"Hello world");


  Assert.assertTrue(a1.equals(a2));
  Assert.assertFalse(a1.equals(a3));

}

코드

class A{
    private int a1 ;
    private String a2;

    public A(int a1, String a2) {
        this.a1 = a1;
        this.a2 = a2;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof A)) return false;
        A a = (A) o;
        return a1 == a.a1 &&
                Objects.equals(a2, a.a2);
    }

    @Override
    public int hashCode() {
        return Objects.hash(a1,a2);
    }
}

lombok을 응용하여 사용하면 아래와 같이 쉽게 만들 수 있다.

@EqualsAndHashCode
@AllArgsConstructor
class A{
    private int a1 ;
    private String a2;
}

TestCode

https://junit.org/junit5/docs/current/user-guide/#running-tests

https://junit.org/junit5/docs/current/user-guide/#running-tests-build-maven

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-scopes

HistoryServer 가 실행이 되지 않고, 아래와 같은 로그가 출력이 된다면

Exception in thread "main" java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at org.apache.spark.deploy.history.HistoryServer$.main(HistoryServer.scala:296)
        at org.apache.spark.deploy.history.HistoryServer.main(HistoryServer.scala)
Caused by: java.io.FileNotFoundException: Log directory specified does not exist: file:/tmp/spark-events Did you configure the correct one through spark.history.fs.logDirectory?
        at org.apache.spark.deploy.history.FsHistoryProvider.org$apache$spark$deploy$history$FsHistoryProvider$$startPolling(FsHistoryProvider.scala:267)
        at org.apache.spark.deploy.history.FsHistoryProvider.initialize(FsHistoryProvider.scala:211)
        at org.apache.spark.deploy.history.FsHistoryProvider.<init>(FsHistoryProvider.scala:207)
        at org.apache.spark.deploy.history.FsHistoryProvider.<init>(FsHistoryProvider.scala:86)
        ... 6 more
Caused by: java.io.FileNotFoundException: File file:/tmp/spark-events does not exist
        at org.apache.hadoop.fs.RawLocalFileSystem.deprecatedGetFileStatus(RawLocalFileSystem.java:611)
        at org.apache.hadoop.fs.RawLocalFileSystem.getFileLinkStatusInternal(RawLocalFileSystem.java:824)
        at org.apache.hadoop.fs.RawLocalFileSystem.getFileStatus(RawLocalFileSystem.java:601)
        at org.apache.hadoop.fs.FilterFileSystem.getFileStatus(FilterFileSystem.java:421)
        at org.apache.spark.deploy.history.FsHistoryProvider.org$apache$spark$deploy$history$FsHistoryProvider$$startPolling(FsHistoryProvider.scala:257)
        ... 9 more

해결 spark-default.conf 에 아래와 같이spark.history.fs.logDirectory 설정을 추가 해준다

spark.eventLog.enabled          true
spark.eventLog.dir              file:/opt/spark-events
spark.history.fs.logDirectory   file:/opt/spark-events

https://stackoverflow.com/questions/44835026/how-to-enable-spark-history-server-for-standalone-cluster-non-hdfs-mode?rq=1

'BackEnd > Spark' 카테고리의 다른 글

스파크 SQL 사용하여 파일로 보내기  (0) 2020.03.02
java.io.InvalidClassException: org.apache.spark.rdd.RDD  (0) 2020.02.18
SparkSession  (0) 2020.02.06
RDD 영속화(캐싱)  (0) 2019.09.02
Spark BroadCast  (0) 2019.08.28

도커 컨테이너 종료 

$ docker stop 컨테이너id

 

도커 컨테이너 실행 

$docker exec 컨테이너id

 

실행중인 컨테이너 확인
$docker ps

 

전체 컨테이너 확인
$docker ps -a

 

컨테이너 삭제
$docker rm 컨테이너id

 

도커 이미지 확인
$docker images

 

도커 이미지 지우기
$docker rmi 이미지id

 

컨테이너 삭제, 이미지 삭제 동시
$docker rmi -f 이미지id

 

참고: https://brunch.co.kr/@hopeless/10

fastcampus Java SpringCore 강좌를들으면서 정리를 하였습니다.

ClassPath Scanning

특정 classpath 이하에 있는 관리할 컴포넌트(@Component)들을 등록을 하기 위해 스캔함

@Component : Annotation Type으로 런타임에 동작함. classpath scanning을 통해 자동적으로 검색됨.

@Repository, @Service, @Controller

@ComponentScan(basePackages = "kr.co.fastcampus.cli") 을 해주면 xml에 별도로 설정을 안해줘도 알아서 추가가 됨.

예시

@Configuration
@ComponentScan(basePackages = "kr.co.fastcampus.cli")
public class AppConfig {
    @Bean
    public A a1(){
        return new A();
    }
    @Bean
      @Scope("singleton")
    public B b1(){
        return new B();
    }
}

@Component
class A {}

@Component
class B{}

아래와 같이 AnnotationConfigApplicationContext()를 사용해서 사용이 가능하다.

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

AnnotationConfigApplicationContext API 를 살펴보면 아래와 같이 파라미터를 넣어주면 사용이 가능하다.

Constructor and Description
AnnotationConfigApplicationContext()Create a new AnnotationConfigApplicationContext that needs to be populated through register(java.lang.Class...) calls and then manually refreshed.
AnnotationConfigApplicationContext(Class... componentClasses)Create a new AnnotationConfigApplicationContext, deriving bean definitions from the given component classes and automatically refreshing the context.
AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory)Create a new AnnotationConfigApplicationContext with the given DefaultListableBeanFactory.
AnnotationConfigApplicationContext(String... basePackages)Create a new AnnotationConfigApplicationContext, scanning for components in the given packages, registering bean definitions for those components, and automatically refreshing the context.

Filter

ComponentScan에서 특정 클래스를 빼고 싶을 때, (pattern, class, 등 의 다양함 )

아래처럼 filter를 사용해서 execludeFilters를 사용하여 가능하다.

@Slf4j
@ComponentScan(basePackageClasses = Main.class , excludeFilters = @ComponentScan.Filter (type= FilterType.REGEX,pattern="kr.co.fastcampus.cli.B"))
public class Main {
    static Logger logger = LoggerFactory.getLogger(Main.class);

    public static void main(String []args) {

        //Java Anotation을 사용한 주입
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
        B b = context.getBean(B.class);
        log.info("b : "+b);
        context.close();
    }
}

ComponentScan 성능 향상

ComponentScan은 Java Generic을 사용하여 JVM에서 어노테이션이 붙어있는지 아닌지 결정하여 판단을 하게 되는데, 만약 어노테이션이 너무 많이 사용되게 되면 ComponentScan이 오래 걸릴 수 있다.

그래서 indexer를 만들어 놓음

pom.xml에 추가

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.2.3.RELEASE</version>
        <optional>true</optional>
    </dependency>
</dependencies>

빌드를 하게 되면 META-INF/spring.components 가 만들어짐.

'BackEnd > Spring' 카테고리의 다른 글

ClassPath Scanning and Managed Components  (0) 2020.02.22
Bean Scope  (0) 2020.02.11
Customizing the Nature of a Bean  (0) 2020.02.01
DI (Dependency Injection)  (0) 2020.02.01
Spring-Core  (0) 2020.01.19

SparkSession

모든 스파크 애플리케이션은 가장 먼저 SparkSession을 생성

기존 코드 : new SparkContext 패턴을 사용 (과거 SparkContext, SQLContext 를 직접 생성)

최신 코드 : SparkSession의 빌더 메서드를 사용해 생성할것을 추천

SparkSession 클래스는 스파크2.x 버전 에서 사용이 가능하다

new SparkContext 에서 보다 안전하게 생성할 수 있다. 다수의 라이브러리가 세션을 생성하려는 상황에서 컨텍스트 충돌을 방지 할 수 있다.

예제

val spark = SparkSession.builder
.appName("My Spark Application")  // optional and will be autogenerated if not specified
.master("local[*]")      // only for demo and testing purposes, use spark-submit instead
.enableHiveSupport()              // self-explanatory, isn't it?
.config("spark.sql.warehouse.dir", "target/spark-warehouse")
.config("spark.som.config.option","some-value")
.getOrCreate()



SparkSession, SQLContext, HiveContext

과거 버전의 스파크에서는 SQLContext와 HiveContext를 사용해 DataFrame과 스파크 SQL을 다룰수 있었음.
과거 SparkContext는 스파크의 핵심 추상화 개념을 다루는데 중점을 두고 , SQLContext는 스파크의 핵심 추상화 개념을 다루는데 중점을 둠 
Spark2.x 이후 부터는 두 콘텍스트를 SparkSession으로 단일화함
SparkContext, SQLContext는 존재하지만 SparkSession을 통하여 접근이 가능하다.

멀티 세션

newSession

SparkContext를 공유하고 session 만 달라지는 방법, 개별의 session 마다 동일한 이름의 테이블 생성 가능.

동일한 jvm에 여러개의 스파크 컨텍스트를 갖는것은 권장되지 않으며 더 불안하고 하나의 스파크 컨텍스트가 충돌하면 다른 스파크에 영향을 줄수 있다.

newSession으로 만들어진 세션중 하나라도 stop/close 를 하게 되면 동일한 SparkContext를 사용하기 때문에 종료가 된다.

sparkSession.newSession();

 

 

참고

Spark 완벽 가이드(도서)

Spark -newSession (https://medium.com/@achilleus/spark-session-10d0d66d1d24)

SparkSession - The Entry Point to Spark SQL (https://jaceklaskowski.gitbooks.io/mastering-spark-sql/spark-sql-SparkSession.html)

+ Recent posts