2.3.3 다른 생성자에서 특정 생성자 호출

  • 또 다른 생성자에서 어느 한 생성자를 호출할 수 있는데, 호출하는 쪽 생성자 바디의 첫 번째 문장으로만 허용한다.
  • 그리고 호출할 생성자 이름이 아니라 this 키워드를 사용한다.
public Employee(double salary){
	
    this("", salary); //Employee(String, double) 호출
    
    //이후에 다른 문장이 올 수 있다.
}

 

2.3.4 기본 초기화

  • 생성자 안에서 인스턴스 변수를 명시적으로 설정하지 않으면 자동으로 변수를 기본 값으로 설정한다. 숫자는 0, bool 값은 false, 객체 참조는 null이 기본 값

 

2.3.5 인스턴스 변수 초기화

  • 객체를 할당하고 나서 생성자가 실행되기 전에 일어난다.
  • 인스턴스 변수를 선언할 때 초기화하는 방법 외에 클래스 안에 임의의 초기화 블록을 넣는 방법도 있다.
  • 인스턴스 변수 초기화와 초기화 블록 클래스 선언에 나타난 순서로 실행하며, 그 다음에 생성자를 실행한다.
public class Employee(){
	
    private String name = " ";
    private int id;
    private double salary;
    
    { //초기화 블록
    	Random generator = new Random();
        id = 1 + generator.nextInt(1_000_000);
    }
    
    ...
}

 

2.4 정적 변수와 정적 메소드

 

2.4.3 정적 초기화 블록

  • 정적 변수에 초기화 작업이 추가로 필요할 때 사용한다.
  • 정적 초기화는 클래스를 처음 로드할 때 일어난다. 인스턴스 변수와 마찬가지로 정적 변수를 명시적으로 다른 값으로 설정하지 않으면 0이나 false, null이 된다.
  • 모든 정젹 변수는 초기화와 정적 초기화 블록은 클래스 선언 안에 나타난 순서로 실행된다.
public class CreditCardForm{
	
    private static final ArrayList<Integer> expiration = new ArrayList<>();
    static{
    	int year = LocalDate.now().getYear();

		for (int i = year; i <= year + 20; i++){
        	expirationYear.add(i);	
        }
    }
    
    ...
}

2.4.4 팩토리 메소드

  • 정적 메소드는 흔히 팩토리 메소드를 만드는데 사용한다.
  • 팩토리 메소드는 클래스의 새 인스턴스를 반환하는 정적 메소드를 의미한다.
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();

double x = 0.1;

System.out.println(currencyFormatter.format(x)); 

-> 왜 생성자 대신 팩토리 메소드를 사용할까? 

  • 생성자를 구별하는 유일한 방법은 생성자의 매개변수. 타입이다. 따라서 매개변수가 없는 생성자를 두개씩 둘 수 없다.
  • 게다가 new NumberFormat(…) 생성자는 NumberFormat을 돌려준다. 하지만 팩토리 메소드는 서브클래스의 객체를 반환할 수 있다.
  • 그리고 팩토리 메소드를 사용하면 불필요하게 새 객체를 생성하는 대신 공유 객체를 반환할 수 있다. 예를 들어 Collections.emptyList()를 호출하면 변경할 수 없는 빈 리스트(공유객체)를 반환한다.

 

2.6 중첩 클래스

  • 클래스를 다른 클래스 내부에 두는 방법이 있다. 이런 클래스를 중첩 클래스(nested class)라고 한다. 중첩 클래스는 가시성을 제한하거나 Element, Node, Item 처럼 일반적인 이름을 사용하면서도 정돈된 상태를 유지할 때 유용하다.
  • 자바에는 작동 방식이 약간 다른 중첩 클래스가 두 종류 있다.

 

2.6.1 정적 중첩 클래스

public class Invoice{

	private static class Item { //Invoice 내부에 Item 중첩했다.
		String description;
		int quantity;
		double unitPrice;

		double price() {return quantity * unitPrice; }
	}

	private ArrayList<Item> items = new ArrayList<>();
    
	public void addItem(String description, int quantity, double unitPrice) {
		
		Item newItem = new Item();
		
		newItem.description = description;
		newItem.quantity = quantity;
		newItem.unitPrice = unitPrice;
		
		items.add(newItem);
	}
}

Invoice.Item Item = new Invoice.Item();
myInvoice.add(newItem);

 

Invoice.Item 클래스와 다른 클래스 외부에 선언한 InvoiceItem 클래스는 근본적으로 차이가 없다. 클래스 중첩은 그저 Item 클래스가 청구서에 들어 있는 물품을 표현한다는 사실을 분명하게 할 뿐이다.

 

2.6.2 내부 클래스

  • static 을 붙이지 않은 클래스를 내부 클래스(이너 클래스 inner class)라고 한다.

각 회원이 다른 회원과 관계를 맺는 소셜 네트워크를 생각해 보자.

 

public class Network {

	public class Member{  //static 제어자를 빼면 근본적인 차이가 하나 생긴다. 예를 들어 Member 객체는 자신이 어느 네트워크에 속하는지 알게 된다.
		private String name;
		private ArrayList<Member> friends;

		public Member(String name) {
			this.name = name;
			friends = new ArrayList();
		}

		public void deactivate() {
			members.remove(this);
		}

		public boolean belongsTo(Network n){
			return Network.this == n;
		}
	}

	private ArrayList<Member> members = new ArrayList()<>;

	public Member enroll(String name) {
		Member newMember = new Member(name);
		members.add(newMember);
		return newMember;
	}
}

 

다음과 같이 멤버를 추가하고 참조를 얻을 수 있다.

Network myFace = new Network();
Network.Member fred = myFace.enroll("Fred");

이제 Fred는 멤버십을 해지하려고 한다.

fred.deactivate();

 

메소드 구현에서 볼 수 있듯이, 내부 클래스의 메소드는 외부 클래스의 인스턴스 변수에 접근할 수 있다.

이 코드에서는 내부 클래스를 생성한 외부 클래스 객체(myFace 네트워크)의 인스턴스 변수 members이다.

 

바로 이 점이 내부 클래스를 정적 중첩 클래스와 구별시키는 요인이다.

내부 클래스의 각 객체는 외부 클래스의 객체에 대한 참조를 포함한다.

 

members.remove(this); // 이 호출은 실제로 다음을 의미한다.
outer.members.remove(this);

 

여기서는 외부 클래스의 숨은 참조를 outer로 나타냈다.

정적 중첩 클래스에는 이런 참조가 없다.(static 메소드에 this 참조가 없는 것과 마찬가지)

 

중첩 클래스의 인스턴스가 자신을 감싸고 있는 클래스의 어느 인스턴스에 속하는지 알 필요가 없을 때 정적 중첩 클래스를 사용하자.

내부 클래스는 이 정보가 중요할 때만 사용하자.

 

2.6.3 내부 클래스용 특수 문법 규칙

 

앞에서는 내부 클래스 객체의 외부 클래스 참조를 outer로 지칭해서 설명했다. 외부 클래스 참조를 나타내는 실제 문법은 조금 더 복잡하다. 다음 표현식은 외부 클래스 참조를 나타낸다.

 

OuterClass.this

 

예를 들어 내부 클래스 Member의 deactivate 메소드는 다음과 같이 작성할 수 있다.

public void deactivate(){ 
	Network.this.members.remove(this)
}

여기서는 Network.this 문법은 필수가 아니다. 그냥 members로만 참조해도 암묵적으로 외부 클래스 참조를 사용한다.

하지만 외부 클래스 참조가 명시적으로 필요할 때도 있다.

public boolean belongsTo(Network n){
	return Network.this == n;
}

 

* 내부 클래스에는 시간 상수 외에 정적 멤버를 선언할 수 없다.

 

 

'Java' 카테고리의 다른 글

인터페이스와 람다 표현식 (1)  (0) 2020.09.09
Mac에 Java 설치하기  (1) 2017.03.05

Ribbon이란?

  • 클라이언트 사이드 load balancer
  • 로드밸런싱 룰을 커스터마이징 가능

@LoadBalanced

RestTemplate, WebClient가 Bean으로 선언된 것에 한해서 @LoadBalanced 어노테이션을 붙이면 Intercept를 통해서 LoadBalancer를 끼어넣게된다. 따라서 알아서 로드 밸런싱을 하게된다.

이 때, RestTemplate, WebClient에 주는 주소는 IP, Port를 지우고 호출할 서버 군의 이름을 적으면 군에 해당하는 목록을 알아서 LoadBalancer에서 호출한다.

 

서버쪽 설정

서버에 들어올 경우 어디로 들어왔는지 콘솔에 로그를 남기도록 간단하게 구현했다.

pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
package com.example.circuitbreakerbookstore;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.List;
import java.util.Random;

@RestController
@SpringBootApplication
public class CircuitBreakerBookstoreApplication {
    private static Logger logger = LoggerFactory.getLogger(CircuitBreakerBookstoreApplication.class);

    @RequestMapping(value = "/recommended")
    public String readingList(){
        logger.info("Access /recommended");
        Random random = new Random();
        List<String> bookList = Arrays.asList("Spring in ACtion (Manning)", "Cloud Native Java", "Learning Spring Boot");
        int randomNum = random.nextInt(bookList.size());
        return bookList.get(randomNum);
    }

    @RequestMapping(value = "/")
    public String home(){
        logger.info("Access home");
        return "Welcome home";
    }
    public static void main(String[] args){
        SpringApplication.run(CircuitBreakerBookstoreApplication.class, args);
    }
}

 

클라이언트 쪽 설정

pom.xml

 <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
@EnableCircuitBreaker
@RestController
@SpringBootApplication
//다른 자바 설정파일 처럼 @Configuration을 사용하면 안된다. @ComponentScan으로 해당 설정이 로딩될 경우 에러갈 발생한다.
@RibbonClient(name="say-book", configuration = SayBookConfiguration.class) //RibbonClient Annotaion은 Controller에 넣어야한다.
public class CircuitBreakerReadingApplication {

    @Autowired
    private BookService bookService;

    @Bean
    @LoadBalanced
    public WebClient.Builder WebClient(){ //공홈에서는 RestTemplate를 썻지만 그냥 Webflux를 써봤다.
        return WebClient.builder();
    }

    @RequestMapping("/to-read")
    public String toRead(@RequestParam(value="name", defaultValue = "Aroon") String name)
    {

        return String.format("%s, %s",name,bookService.readingList());
    }

    public static void main(String[] args) {
        SpringApplication.run(CircuitBreakerReadingApplication.class, args);
    }
}
public class SayBookConfiguration {
    @Autowired
    IClientConfig ribbonClientConfig;

    @Bean // 첫번째 Parameter가 True일 경우 https false일 경우 http. AppendString은 Ping 보낼 URL
    public IPing ribbonPing(IClientConfig config){
        return new PingUrl(false, "/");
    }

    @Bean //LoadBalancing Rule을 설정하는 것
    public IRule ribbonRule(IClientConfig config){
        return new AvailabilityFilteringRule();
    }
}
@Service
public class BookService {
    @Autowired
    WebClient.Builder webClientBuilder;
    /*
        Hystrix Command 설정
        commandKey 별로 Circuit Breaker 생성
        fallbackMethod : 실패시 해당 메소드 실행
     */
    @HystrixCommand(commandKey = "EX", fallbackMethod = "reliable")
    public String readingList(){
        return webClientBuilder.baseUrl("http://say-book").build().get() //get 방식
                .uri("recommended") //endpoint
                .retrieve() // body를 가지고 온다는 뜻
                .bodyToMono(String.class)
                .block()
                ;
    }

    public String reliable(){
        return "Success Fallback";
    }
}

Eureka와 연동하지 않을 것이므로 false를 하고 Server 군의 목록을 지정해준다.

ServerListRefreshInterval마다 IPing이 실행되며 서버 상태를 확인한다.

server.port= 8888
say-book.ribbon.eureka.enabled=false #유레카를 사용하지 않겠다. 
say-book.ribbon.listOfServers=localhost:8090,localhost:9092 #서버군 목록
say-book.ribbon.ServerListRefreshInterval= 15000 #서버리스트 refresh 간격 ms

 

실행방법 :

 

1. 서버를 아래 mvn 명령어도 두개를 실행한다.

SERVER_PORT=9092 mvn spring-boot:run

SERVER_PORT=8090 mvn spring-boot:run

 

*만약 Maven이 설치되어 있지 않은 경우 homebrew insall maven 명령어로 설치해준다.

 

2. Client를 실행 후 접속해서 확인해본다.

 

 

 

참고 

https://gunju-ko.github.io/spring-cloud/netflixoss/2018/12/14/Ribbon.html

 

Ribbon

Ribbon Netflix가 만든 Software Load Balancer를 내장한 REST Library이다. Ribbon은 Client 사이드 LoadBalancer로 Ribbon을 사용하면 L4등과 같이 하드웨어에서 이루어지던 Load Balance를 애플리케이션 영역에서 할 수 있다. Ribbon을 사용하면 애플리케이션 영역에서 서버목록을 가지고 번갈아가면서 호출하게 된다. Spring Cloud에서 Ribbon 클라이언트를 직접 사용하는 경우는

gunju-ko.github.io

https://spring.io/guides/gs/client-side-load-balancing/

 

Spring

Level up your Java code and explore what Spring can do for you.

spring.io

https://piotrminkowski.com/2018/05/04/reactive-microservices-with-spring-webflux-and-spring-cloud/

 

Reactive Microservices with Spring WebFlux and Spring Cloud

I have already described Spring reactive support about one year ago in the article Reactive microservices with Spring 5. At that time project Spring WebFlux has been under active development, and n…

piotrminkowski.com

 

'Spring' 카테고리의 다른 글

SpringBoot Hystrix 적용하기  (0) 2020.03.01
Hystrix 란  (0) 2020.03.01

간단한 Hystrix Circuit Breaker를 실습하기 위해선 두개의 프로젝트를 생성해야한다.

서버 설정

1. pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

2. application.properties에서 server port 설정 (서버, 클라이언트를 한 컴퓨터에서 실행하기 때문에 port를 설정해줘야함. )

server.port=8090

3. Controller 생성

package com.example.circuitbreakerbookstore;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class CircuitBreakerBookstoreApplication {
    @RequestMapping(value = "/recommended")
    public String readingList(){
        return "Spring in ACtion (Manning), Cloud Native Java, Learning Spring Boot";
    }
    public static void main(String[] args){
        SpringApplication.run(CircuitBreakerBookstoreApplication.class, args);
    }
}

 

클라이언트 설정

1. pom.xml

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.M3</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

2. application.properties

server.port=8080

3.Controller

package com.example.circuitbreakerreading;


import com.example.circuitbreakerreading.service.BookService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.web.client.RestTemplate;


@EnableCircuitBreaker
@RestController
@SpringBootApplication
public class CircuitBreakerReadingApplication {

    @Autowired
    private BookService bookService;

    @Bean
    public RestTemplate rest(RestTemplateBuilder builder) {
        return builder.build();
    }

    @RequestMapping("/to-read")
    public String toRead() {
        return bookService.readingList();
    }

    public static void main(String[] args) {
        SpringApplication.run(CircuitBreakerReadingApplication.class, args);
    }
}

4. Service

package com.example.circuitbreakerreading.service;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;

@Service
public class BookService {
    private final WebClient webClient;

    public BookService(){
        this.webClient = WebClient.builder().baseUrl("http://localhost:8090").build();
    }

    /*
        Hystrix Command 설정
        commandKey 별로 Circuit Breaker 생성
        fallbackMethod : 실패시 해당 메소드 실행
     */
    @HystrixCommand(commandKey = "EX", fallbackMethod = "reliable")
    public String readingList(){
        return webClient.get() //get 방식
                .uri("recommended") //endpoint
                .retrieve() // body를 가지고 온다는 뜻
                .bodyToMono(String.class)
                .block()
                ;
    }

    public String reliable(){
        return "Success Fallback";
    }
}

 

참고 :

https://spring.io/guides/gs/circuit-breaker/

'Spring' 카테고리의 다른 글

Springboot ribbon 적용하기  (1) 2020.03.03
Hystrix 란  (0) 2020.03.01

+ Recent posts