public class HandlerFactory implements InitializingBean {
	private final ApplicationContext applicationContext;
	
	private static Map<String, RouteHandler> handlerMap = null;
	private static Map<String, Boolean> handlerBodyRefBoolMap = null;
	
	private static LocalDateTime lastModifiedDate = null;
	

	@Autowired
	private AdmHndlrDplyService admHndlrDplyService;

	public static void process(String handlerType, ServerWebExchange exchange, TransactionDto transaction) throws InternalRuntimeException {
		String errorHandler = null;
		try {
			// API ID extraction
			AdmApiDply apiVo = transaction.getComn().getApi();
			List<String> apiHandlerList = null;
			
			// 처리 대상 핸들러 그룹 추출 
			if (handlerType.equals(RequestHandlerFilter.REQUEST_HANDLER)) {
				if (apiVo.getReqHndlr() != null) {
					apiHandlerList = apiVo.getReqHndlr();
				}
			} else if (handlerType.equals(ResponseHandlerFilter.RESPONSE_HANDLER)) {
				if (apiVo.getResHndlr() != null) {
					apiHandlerList = apiVo.getResHndlr();
				}
			}else if (handlerType.equals(ErrorHandler.ERROR_HANDLER)) {
				if (apiVo.getErrHndlr() != null) {
					errorHandler = apiVo.getErrHndlr();
				}
			}

			// Loop를 수행하면서 핸들러 기능 처리
			if (apiHandlerList != null) {
				for (String routeHandlerId : apiHandlerList) {
					RouteHandler routeHandler = handlerMap.get(routeHandlerId);
					if (!routeHandler.process(transaction)) {
						break;
					}
				}
			} else if(StringUtils.hasText(errorHandler)){
				RouteHandler routeHandler = handlerMap.get(errorHandler);
				if (!routeHandler.process(transaction)) {
					transaction.setError(AgwErrorDto.INTERNAL_SERVER_ERROR);
				}
			} 

		} catch (InternalRuntimeException e) {
			log.error("Request handling processing failure", e);
			transaction.setError(AgwErrorDto.INTERNAL_SERVER_ERROR);
		} catch (Exception e) {
			log.error("Request handling processing failure", e);
			transaction.setError(AgwErrorDto.INTERNAL_SERVER_ERROR);
		}

		if (transaction.getErr() != null && !StringUtils.hasText(errorHandler)) {
			throw new InternalRuntimeException("API handling failure");
		}
	}

	@Override
	public void afterPropertiesSet() throws InternalException {
		log.info("HandlerFactory.afterPropertiesSet - Start");
		handlerMap = new ConcurrentHashMap<String, RouteHandler>();
		handlerBodyRefBoolMap = new ConcurrentHashMap<String, Boolean>();
		synchronizeDB();
		log.info("HandlerFactory.afterPropertiesSet - End");
	}
	
	/**
	 * Handler synchronization
	 * @throws InternalException 
	 */
	@Scheduled(fixedDelayString = "${common.handler.schedule.dbSync}", timeUnit = TimeUnit.SECONDS)
	public void synchronizeDB() throws InternalException {
		List<AdmHndlrDply> dbHndlrList = null;

		// 마지막 동기화 시점 시간을 비교하여 데이터 동기화 처리
		if (lastModifiedDate == null) {
			// 최초 데이터 조회
			dbHndlrList = admHndlrDplyService.getAdmHndlrList("DPLY");
		} else {
			// 변경분 데이터 조회
			dbHndlrList = admHndlrDplyService.getAdmHndlrList(lastModifiedDate);
		}
	
		try {
			// 핸들러 동기화
			if(dbHndlrList != null && dbHndlrList.size() > 0) {
				setHandler(dbHndlrList);

				AdmHndlrDply lastHndlr = dbHndlrList.stream().max((o1, o2) -> o1.getDplyDt().compareTo(o2.getDplyDt())).get();
				lastModifiedDate = lastHndlr.getDplyDt();
			}
	
		} catch (Exception e) {
			log.info("hndlr or hndlrGroup load fail", e);
		}
	}

	public void setHandler(List<AdmHndlrDply> hndlrList) throws InternalException, ClassNotFoundException {

		ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
		DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();
		
		for(AdmHndlrDply hndlr : hndlrList) {
			if("DPLY".equals(hndlr.getDplyType())){
				if(!hndlr.getType().equals("SYS")) { // 커스텀 핸들러 로딩
					try {
						//CompilerUtils (즉, 자바 컴파일러로 컴파일 .java를 컴파일해서 .class로 변경시킬때 - 컴파일러를 호출)
						Class<?> cls = CompilerUtils.compileSource(hndlr.getCstmClassAdr(), hndlr.getCstmClassSrc());
						if(defaultListableBeanFactory.containsBeanDefinition(hndlr.getHndlrId())){
							defaultListableBeanFactory.removeBeanDefinition(hndlr.getHndlrId());
						}
						//스프링 컨테이너에 빈을 등록할때 일반적으로 블로킹 작업이 걸리지는 않지만, 의존성이 많거나 초기작업등이 길어지면 성능이 많이 저하되어 마치 블로킹과 같은 현상이 일어날수도 있다.
						defaultListableBeanFactory.registerBeanDefinition(hndlr.getHndlrId(), BeanDefinitionBuilder.genericBeanDefinition(cls).getRawBeanDefinition());
					} catch (Exception e) {
						throw new InternalException("Custom handler load failed", e);
					}
				}
	
				// 핸들러로 정의된 Bean 로딩
				RouteHandler routeHandler = (RouteHandler) applicationContext.getBean(hndlr.getHndlrId());
				
				if (!routeHandler.init()) {
					throw new InternalException("Handler initialization failed : " + hndlr.getHndlrId());
				}
	
				log.info("HandlerFactory.setHandler - Load : " + hndlr.toString());
				handlerMap.put(hndlr.getHndlrId(), routeHandler);
	
				handlerBodyRefBoolMap.put(hndlr.getHndlrId(),hndlr.isBodyRfrn());
			}else{
				if(!hndlr.getType().equals("SYS")) { // 커스텀 핸들러 로딩
					if(defaultListableBeanFactory.containsBeanDefinition(hndlr.getHndlrId())){
						defaultListableBeanFactory.removeBeanDefinition(hndlr.getHndlrId());
					}
				}
	
				
				handlerMap.remove(hndlr.getHndlrId());
				handlerBodyRefBoolMap.remove(hndlr.getHndlrId());
				log.info("HandlerFactory.setHandler - Remove : " + hndlr.toString());
	
			}
			
		}
	}

	public static boolean isHandlerGroupBodyRef(List<String> hndlrList) {
		if(CollectionUtils.isEmpty(hndlrList)){
			return false;
		}
		for(String hndlrId:hndlrList){
			if(handlerBodyRefBoolMap.get(hndlrId)){
				return true;
			}
		}
		return false;
	}

	@ReadOperation
    public Map<String, Object> handlerFeatures() {
		Map<String, Object> hndlrInfoMap = new HashMap<>();
		hndlrInfoMap.put("handler", handlerMap.keySet());
        return hndlrInfoMap;
    }

}

  1. Compiler를 이용할때 (다른 프로세스에게 요청 I/O 발생)
**//CompilerUtils (즉, 자바 컴파일러로 컴파일 .java를 컴파일해서 .class로 변경시킬때 - 컴파일러를 호출)**
Class<?> cls = CompilerUtils.compileSource(hndlr.getCstmClassAdr(), hndlr.getCstmClassSrc());

<aside> 💬 아무래도 컴파일러는 독립된 프로그램이다보니, 소스 중간에 호출하게되면 I/O작업이 되지 않을까 한다.

</aside>

  1. Bean을 등록할때 (성능 이슈)
**//스프링 컨테이너에 빈을 등록할때 일반적으로 블로킹 작업이 걸리지는 않지만, 의존성이 많거나 초기작업등이 길어지면 블로킹이 될 가능성이 있다.**
defaultListableBeanFactory.registerBeanDefinition(hndlr.getHndlrId(), BeanDefinitionBuilder.genericBeanDefinition(cls).getRawBeanDefinition());

<aside> 💬 빈 등록이 길어질경우 다른 스레드들에 과부하가 걸릴 경우도 생길수 있다. 최악의 경우 블로킹과 같은 현상이 일어날수도 있다고 본다. (but not blocking)

</aside>

참고 : 핸들러 내용은 상관하지 않는다.