2024.09.22 시작
'Java' 카테고리의 다른 글
log4j2 jndi 취약점 (2.15.0 -> 2.16.0 -> 2.17.0 -> 2.17.1) (0) | 2021.12.12 |
---|
2024.09.22 시작
log4j2 jndi 취약점 (2.15.0 -> 2.16.0 -> 2.17.0 -> 2.17.1) (0) | 2021.12.12 |
---|
Camel에서 제공하는 Netty Http 컴포넌트 클래스는 org.apache.camel.component.netty.http.NettyHttpComponent이다.
해당 컴포넌트를 이용하여 서버 모드를 기동할 때 org.apache.camel.component.netty.http.NettyHttpEndpoint의 createConsumer()가 호출된다.
@Override
public Consumer createConsumer(Processor processor) throws Exception {
NettyHttpConsumer answer = new NettyHttpConsumer(this, processor, getConfiguration());
configureConsumer(answer);
if (nettySharedHttpServer != null) {
answer.setNettyServerBootstrapFactory(nettySharedHttpServer.getServerBootstrapFactory());
LOG.info("NettyHttpConsumer: {} is using NettySharedHttpServer on port: {}", answer,
nettySharedHttpServer.getPort());
} else {
// reuse pipeline factory for the same address
HttpServerBootstrapFactory factory = getComponent().getOrCreateHttpNettyServerBootstrapFactory(answer);
// force using our server bootstrap factory
answer.setNettyServerBootstrapFactory(factory);
LOG.debug("Created NettyHttpConsumer: {} using HttpServerBootstrapFactory: {}", answer, factory);
}
return answer;
}
getComponent().getOrCreateHttpNettyServerBootstrapFactory(answer)를 호출하게 되는데, getComponent()가 NettyHttpComponent 이고 해당 코드는 아래와 같다.
protected synchronized HttpServerBootstrapFactory getOrCreateHttpNettyServerBootstrapFactory(NettyHttpConsumer consumer) {
String key = consumer.getConfiguration().getAddress();
HttpServerBootstrapFactory answer = bootstrapFactories.get(key);
if (answer == null) {
HttpServerConsumerChannelFactory channelFactory = getMultiplexChannelHandler(consumer.getConfiguration().getPort());
answer = new HttpServerBootstrapFactory(channelFactory);
answer.init(getCamelContext(), consumer.getConfiguration(), new HttpServerInitializerFactory(consumer));
bootstrapFactories.put(key, answer);
}
return answer;
}
Address를 key로하여 Map 타입의 bootstrapFactories에 존재여부를 판단하여, 존재하지 않을 경우, 새로운 HttpServerBootstrapFactory 객체를 생성하고, 있을 경우, 그 설정을 그대로 사용.
따라서 초기에 설정하는 HttpServerBootstrapFactory의 NettyServerBootstrapConfiguration이 항상 로드된 채로 사용하게 됨.
만약 로드된 상태에서, 새롭게 엔드포인트를 활성화할 경우, 기존 캐시에 저장되어 있던 HttpServerBootstrapFactory의 NettyServerBootstrapConfiguration과 새롭게 엔드포인트를 전달한 설정값을 확인하여, 다른 경우 에러 발생함. 코드는 아래와 같음
HttpServerBootstrapFactory.addConsumer
@Override
public void addConsumer(NettyConsumer consumer) {
if (compatibleCheck) {
// when adding additional consumers on the same port (eg to reuse port for multiple routes etc) then the Netty server bootstrap
// configuration must match, as its the 1st consumer that calls the init method, which configuration is used for the Netty server bootstrap
// we do this to avoid mis configuration, so people configure SSL and plain configuration on the same port etc.
// first it may be the same instance, so only check for compatibility of different instance
if (bootstrapConfiguration != consumer.getConfiguration()
&& !bootstrapConfiguration.compatible(consumer.getConfiguration())) {
throw new IllegalArgumentException(
"Bootstrap configuration must be identical when adding additional consumer: " + consumer.getEndpoint()
+ " on same port: " + port
+ ".\n Existing " + bootstrapConfiguration.toStringBootstrapConfiguration()
+ "\n New "
+ consumer.getConfiguration().toStringBootstrapConfiguration());
}
}
if (LOG.isDebugEnabled()) {
NettyHttpConsumer httpConsumer = (NettyHttpConsumer) consumer;
LOG.debug("BootstrapFactory on port {} is adding consumer with context-path {}", port,
httpConsumer.getConfiguration().getPath());
}
channelFactory.addConsumer((NettyHttpConsumer) consumer);
}
만약 새롭게 엔드포인트를 올릴때마다, 옵션이 바뀌는 경우 NettyHttpComponent를 stop하고, 새로운 NettyHttpComponent 객체를 만들던지, bootstrapFactories이 캐싱하는 로직을 제거.
필자는 bootstrapFactories에 캐싱하는 로직을 제거하였음. 다만, 제거하여 현재 어떠한 문제가 발생할지 의문.
요리를 할 때 재료를 선택하는 것이 중요하듯, 프로그래밍에서도 자료 구조를 선택하는 것은 매우 중요합니다. 오늘 우리는 두 가지 주요 재료, 즉 '배열'과 '리스트'를 비교하며, 왜 리스트를 사용하는 것이 종종 더 나은 선택인지에 대해 탐색해볼 것입니다. 마치 요리사가 재료의 특성을 이해하고 최적의 요리를 만들어내듯, 우리도 각 자료 구조의 특성을 이해하여 더 나은 프로그램을 만들 수 있습니다.
배열과 리스트 각각의 특성을 이해하는 것은 프로그래머에게 매우 중요합니다. 배열은 그 간단함과 성능으로 특정 상황에서 유용할 수 있지만, 타입 안전성, 크기의 유연성, 그리고 편의성 측면에서 리스트가 더 나은 선택일 수 있습니다. 프로그램의 안정성을 높이고, 유지보수를 용이하게 하며, 더 나은 코드 작성을 위해서는 '리스트 사용하기'가 훌륭한 조언이 될 수 있습니다. 마치 요리사가 각 재료의 특성을 이해하고 최적의 재료를 선택하는 것처럼, 우리도 각 상황에 맞는 최적의 자료 구조를 선택하는 지혜가 필요합니다.
[이펙티브자바] 규칙27. 비검사 경고를 제거하라 (1) | 2024.03.12 |
---|---|
[이펙티브자바] 규칙26. 로 타입은 사용하지 말라 (0) | 2024.03.12 |
[이펙티브자바] 규칙21. 자바에서 전략 패턴 작성법 (0) | 2022.06.25 |
[이펙티브자바] 규칙20. 태그 클래스는 계층형 클래스로 변경하자 (0) | 2022.06.20 |
[이펙티브자바] 규칙19. 상수 인터페이스(Constant Interface)는 안티패턴이다 (0) | 2022.06.20 |
여러분이 차를 운전하고 있다고 상상해보세요. 갑자기 대시보드에 경고등이 켜집니다. 이때 대부분의 사람들은 경고등을 무시하고 계속 운전하지 않을 거예요. 차량의 안전과 성능을 위해 문제를 조사하고 해결할 필요가 있죠. 프로그래밍에서도 마찬가지입니다. 자바나 다른 프로그래밍 언어에서 컴파일러가 경고를 발생시킬 때, 그것은 우리에게 뭔가 문제가 있음을 알리는 신호입니다. "비검사 경고를 제거하라"는 이 경고들에 주목하고 해결해야 한다는 메시지를 전달합니다. 그렇다면 왜 이 경고들이 중요한 걸까요?
비검사 경고는 주로 제네릭스를 사용할 때 발생합니다. 컴파일러가 타입 안정성을 보장할 수 없을 때, 예를 들어 제네릭 타입을 명시하지 않고 사용할 때 이런 경고가 발생합니다. 비검사 경고는 런타임에 ClassCastException과 같은 오류로 이어질 수 있는 문제들을 미리 알려줍니다.
비검사 경고를 무시하면, 프로그램이 예상치 못한 방식으로 실패할 가능성이 있습니다. 이는 개발 과정에서 쉽게 발견되지 않는 버그를 의미할 수 있으며, 이런 버그는 나중에 찾아내고 수정하기 훨씬 어려울 수 있습니다.
비검사 경고를 제거하는 것은 코드의 안정성과 유지보수성을 높이는 중요한 단계입니다. 컴파일러의 경고를 무시하는 것은 잠재적인 문제를 무시하는 것과 같습니다. 따라서, 이러한 경고에 주목하고 해결하여 보다 안전하고 신뢰할 수 있는 코드를 작성해야 합니다. 마치 운전 중 대시보드의 경고등에 주목하고 필요한 조치를 취하는 것처럼, 프로그래밍에서도 비검사 경고에 주목하고 적절히 대응해야 합니다. 이렇게 함으로써, 우리는 더 나은 소프트웨어 개발자가 될 수 있습니다.
[이펙티브자바] 규칙28. 배열보다는 리스트를 사용하라 (0) | 2024.03.12 |
---|---|
[이펙티브자바] 규칙26. 로 타입은 사용하지 말라 (0) | 2024.03.12 |
[이펙티브자바] 규칙21. 자바에서 전략 패턴 작성법 (0) | 2022.06.25 |
[이펙티브자바] 규칙20. 태그 클래스는 계층형 클래스로 변경하자 (0) | 2022.06.20 |
[이펙티브자바] 규칙19. 상수 인터페이스(Constant Interface)는 안티패턴이다 (0) | 2022.06.20 |
상상해 보세요. 마법의 숲에 금지된 과일이 있는데, 이 과일을 먹으면 순간적으로 힘이 솟구치지만, 이내 큰 대가를 치르게 된다는 전설이 있습니다. 프로그래밍의 세계에서도 비슷한 금지된 과일이 있어요. 바로 '로 타입(raw types)'입니다. 이번 모험에서는 왜 로 타입을 사용하지 않아야 하는지, 그리고 그 대안은 무엇인지 함께 탐험해 보겠습니다.
자바에서 제네릭스(Generics)가 도입되기 전, 모든 컬렉션은 로 타입이었습니다. 예를 들어, List는 어떤 타입의 객체든지 저장할 수 있었죠. 제네릭스의 도입으로 List<String> 또는 List<Integer>와 같이 저장될 객체의 타입을 지정할 수 있게 되었습니다. 하지만 여전히 로 타입을 사용할 수는 있습니다. 문제는 여기서 시작됩니다.
로 타입을 사용하는 것은 마치 숲속의 금지된 과일을 먹는 것과 같습니다. 잠시 편리할 수 있지만, 결국엔 큰 대가를 치를 수 있어요. 반면, 제네릭스를 사용하는 것은 안전하고 명확한 길을 선택하는 것입니다. 코드의 안정성을 보장하고, 가독성을 높이며, 유연한 프로그래밍이 가능해집니다. 프로그래밍의 숲에서 금지된 과일인 로 타입을 멀리하고, 제네릭스라는 안전한 길을 선택해보세요. 여러분의 코드 여행이 더욱 즐겁고 안전해질 거예요.
[이펙티브자바] 규칙28. 배열보다는 리스트를 사용하라 (0) | 2024.03.12 |
---|---|
[이펙티브자바] 규칙27. 비검사 경고를 제거하라 (1) | 2024.03.12 |
[이펙티브자바] 규칙21. 자바에서 전략 패턴 작성법 (0) | 2022.06.25 |
[이펙티브자바] 규칙20. 태그 클래스는 계층형 클래스로 변경하자 (0) | 2022.06.20 |
[이펙티브자바] 규칙19. 상수 인터페이스(Constant Interface)는 안티패턴이다 (0) | 2022.06.20 |
디자인 패턴 중 일반적으로 가장 처음에 배우는 전략 패턴을 자바로 어떻게 작성해야 하는지에 대한 내용이다.
전략 패턴에 대한 설명은 생략한다.
문자열 비교를 해주는 Util이 있다고 가정해보자.
public class StringCompareUtil {
/**
* 일반 문자열 비교
*/
public static int compare(String s1, String s2) {
return s1.compareTo(s2);
}
/**
* 문자열 길이를 이용한 비교
*/
public static int compareLength(String s1, String s2) {
return s1.length() - s2.length();
}
}
만약 새로운 문자열 비교 기능이 필요하다면 위 클래스를 수정해야 한다. OCP를 위반하기 때문에 인터페이스를 이용하여 클래스를 변경해보자.
public interface Comparator {
public int compare(String s1, String s2);
}
public class NormalStringCompare implements Comparator {
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
}
public class LengthStringCompare implements Comparator {
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
}
인터페이스를 이용하였고 각 기능을 제공하는 클래스를 각각 구현하였다. 이런 식으로 작성한다면 해당 기능을 사용하는 클라이언트 입장에서는 어떠한 구현체여도 인터페이스를 사용하기만 하면 된다.
문자열만 비교하는 인터페이스로 사용하기에 아쉽기 때문에 제네릭을 추가하자.
public interface Comparator<T> {
public int compare(T s1, T s2);
}
public class NormalStringCompare implements Comparator<String> {
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
}
public class LengthStringCompare implements Comparator<String> {
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
}
새로운 비교 기능을 위해 클래스를 새롭게 구현해도 되지만, 기능이 추가될 때마다 클래스의 개수가 계속 늘어나게 된다. 만약 해당 비교 기능을 특정한 클라이언트 구현체에서만 사용한다면 익명 클래스를 이용해서 해당 위치에서 작성해주자.
new Comparator<String>() {
public int compare(String s1, String s2) {
// TODO
}
};
위와 같이 익명 클래스를 생성하면 해당 위치에서 새로운 객체가 계속 만들어지므로 해당 익명 클래스를 객체로 갖는 private static final 필드에 저장하고 재사용하는 것을 고려하자.
여기까지 책에 대한 설명이다. 참고로 블로그 저자는 2판을 보고 작성하고 있는데, 3판에서는 람다에 대한 챕터로 해당 내용을 새롭게 구성하여 설명하고 있다.
이제 익명 클래스 대신에 람다를 사용하는 것을 추천한다. 람다에 대한 설명은 다른 블로그 글을 참조하기 바란다.
[이펙티브자바] 규칙27. 비검사 경고를 제거하라 (1) | 2024.03.12 |
---|---|
[이펙티브자바] 규칙26. 로 타입은 사용하지 말라 (0) | 2024.03.12 |
[이펙티브자바] 규칙20. 태그 클래스는 계층형 클래스로 변경하자 (0) | 2022.06.20 |
[이펙티브자바] 규칙19. 상수 인터페이스(Constant Interface)는 안티패턴이다 (0) | 2022.06.20 |
[이펙티브자바] 규칙16. 계승보다 구성 (= extends보다 필드선언) (0) | 2022.03.20 |
이 챕터는 자바를 처음 배울 때 계층형(추상) 클래스를 사용하는 이유를 설명하는 내용이라고 생각해도 될 것 같다. 그만큼 내용이 어렵지 않으므로 간단하게 설명한다.
태그 클래스란 특정한 변수를 기준으로 분기하여 두 가지 이상의 기능을 제공하는 클래스를 의미한다.
public class Figure {
enum Shape {
RECTANGLE, CIRCLE
};
final Shape shape;
double length;
double width;
double redius;
Figure(double length, double width) {
this.shape = Shape.RECTANGLE;
this.length = length;
this.width = width;
}
Figure(double redius) {
this.shape = Shape.CIRCLE;
this.redius = redius;
}
double area() {
switch (shape) {
case RECTANGLE:
return length * width;
case CIRCLE:
return Math.PI * (redius * redius);
default:
throw new AssertionError();
}
}
}
A 기능을 위한 생성자와 인스턴스 필드들이 존재하며, B 기능을 위한 생성자와 인스턴스 필드들이 각각 존재한다.
이러한 경우 단점은 다음과 같다.
1. 가독성이 떨어진다.
2. 필요 없는 기능을 위한 필드도 함께 생성되므로 메모리 요구량이 늘어난다.
3. 또 다른 기능이 생긴다면 해당 클래스를 수정해야 하며, swtich문도 모두 수정해야 한다.
공통 기능은 추상(abstract) 클래스로 옮기고 서로 다른 기능은 하위 클래스로 구현한다.
[이펙티브자바] 규칙26. 로 타입은 사용하지 말라 (0) | 2024.03.12 |
---|---|
[이펙티브자바] 규칙21. 자바에서 전략 패턴 작성법 (0) | 2022.06.25 |
[이펙티브자바] 규칙19. 상수 인터페이스(Constant Interface)는 안티패턴이다 (0) | 2022.06.20 |
[이펙티브자바] 규칙16. 계승보다 구성 (= extends보다 필드선언) (0) | 2022.03.20 |
[이펙티브 자바] 규칙15. Immutable(불변) 클래스 (0) | 2022.03.09 |
[이펙티브자바] 규칙21. 자바에서 전략 패턴 작성법 (0) | 2022.06.25 |
---|---|
[이펙티브자바] 규칙20. 태그 클래스는 계층형 클래스로 변경하자 (0) | 2022.06.20 |
[이펙티브자바] 규칙16. 계승보다 구성 (= extends보다 필드선언) (0) | 2022.03.20 |
[이펙티브 자바] 규칙15. Immutable(불변) 클래스 (0) | 2022.03.09 |
[이펙티브 자바] 규칙14. getter 메서드 (0) | 2022.02.23 |