Server

[Spring] @Bean과 @Component의 차이

니용 2020. 6. 25. 22:46
반응형

 

스프링 프레임워크 사용하다가 궁금한 것이 생겼습니다.

다 같은 어노테이션인데 굳이 왜 나누어 놓았을까? 분명 이유가 있겠죠?

모르는 것은 바로 궁금증을 해결해보아야 되죠!!

 

이번 글에서는 @Bean과 @Component의 차이에 대해 살펴보려고 합니다.


@Bean

외부 라이브러리지만 개발자가 컨트롤이 불가능한 경우 사용한다고 합니다.

예를 들어, Redis를 사용하는 경우가 있습니다.

 

@Configuration
public class RedisConfig {

   private @Value("${spring.redis.host}") String redisHost;
   private @Value("${spring.redis.port}") int redisPort;

   @Bean
   public JedisConnectionFactory connectionFactory() {
      JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
      jedisConnectionFactory.setHostName(redisHost);
      jedisConnectionFactory.setPort(redisPort);
      jedisConnectionFactory.setUsePool(true);
      return jedisConnectionFactory;
   }
   
   @Bean
   public RedisTemplate<String, Object> redisTemplate() {
      RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
      redisTemplate.setKeySerializer(new StringRedisSerializer());
      redisTemplate.setValueSerializer(new StringRedisSerializer());
      redisTemplate.setHashKeySerializer(new StringRedisSerializer());
      redisTemplate.setHashValueSerializer(new StringRedisSerializer());
      redisTemplate.setConnectionFactory(connectionFactory());      
      return redisTemplate;
   }
}

Redis의 경우 application.yml에서 제공받은 변수를 할당받아 적용해야 하는데, 이때 사용합니다.

다른 경우도 한 번 보겠습니다.

 

@Configuration
@EnableAsync
public class AsyncConfig {

   @Bean("customExecutor")
   public TaskExecutor getAsyncExecutor() {
      ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
      executor.setCorePoolSize(10);
      executor.setMaxPoolSize(200);
      executor.setQueueCapacity(30);
      executor.setThreadNamePrefix("Async-");
      executor.setKeepAliveSeconds(5);
      executor.initialize();
      return executor;
   }
}

 이번에는 비동기 서비스에서 사용하는 AsynConfig 파일을 가져와보았습니다. 딱 정해진 값에만 세팅을 해서 return 하고 있네요.

@Configuration
@MapperScan(basePackages = "kr.abbo.app.api", annotationClass = abboDB.class, sqlSessionFactoryRef = "abboSqlSessionFactory")
public class MybatisDatasourceConfig {
   @Bean("abboSqlSessionFactory")
   public SqlSessionFactory abboSqlSessionFactory(@Qualifier("abboDataSource") DataSource dataSource)
         throws Exception {
      SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
      PathMatchingResourcePatternResolver pathResolver = new PathMatchingResourcePatternResolver();
      sessionFactoryBean.setDataSource(dataSource);
      sessionFactoryBean.setTypeAliasesPackage("kr.abbo.app.api.model");
      sessionFactoryBean.setConfigLocation(pathResolver.getResource("classpath:mybatis/MybatisConfiguration.xml"));
      sessionFactoryBean.setMapperLocations(pathResolver.getResources("classpath:**/*Mapper.xml"));
      sessionFactoryBean.setVfs(SpringBootVFS.class);
      return sessionFactoryBean.getObject();
   }
}

마지막으로 지정된 곳은 데이터 소스 부분인데요, 이 부분은 Database에 연결시키기 위해 스키마와 다른 부가 옵션을 설정하는 곳입니다. 위의 설정 파일들을 모두 확인해보니 @Configuration이 설정된 클래스 내에 모두 선언이 되어있고, 각 클래스마다 설정값이 다르게 매겨져 있는 것을 확인할 수 있습니다. 

 

@ComponentScan의 경우는 Spring Boot가 기동 되면서 Bean에 파일을 연결시켜줄 수 있는 어노테이션입니다.


@Component

이번에는 컴포넌트 어노테이션에 대해 정리해볼 텐데요, @Component는 검색해보니 개발자가 컨트롤이 가능한 클래스의 경우 사용한다고 합니다. 예시를 바로 보겠습니다.

@Slf4j
@Component
public class FileUploadUtil {

    // 추후에 application.yml에 value로 불러오도록 수정
    private String ACCESS_KEY = "...";
    private String SECRET_KEY = "......";
    private String BUCKET_NAME = "cdn-s3.abbo.kr";

    private static String DOMAIN = "http://images-cdn.abbo.kr";
    private static String ROOT = "/app/user";
    private static String PATH_FORMAT = "/yyyy/MM/dd";

    public String imageUpload(MultipartFile file) {
        try {
            byte[] fileData = IOUtils.toByteArray(file.getInputStream());
            String sourceFileName = file.getOriginalFilename();
            String fileExt = FilenameUtils.getExtension(sourceFileName).toLowerCase();
            String path = new SimpleDateFormat(PATH_FORMAT).format(new Date());
            String contentType = file.getContentType();
            String fileName = System.currentTimeMillis() + "." + fileExt;

            s3Upload(ROOT + path, fileName, contentType, fileData);

            return DOMAIN + ROOT + path + "/" + fileName;
        } catch (IOException e) {
            log.error(ExceptionUtils.getStackTrace(e));
            return null;
        }
    }
}

파일 업로드를 하는 유틸리티 클래스를 가져와 보았습니다. 오, 이번에는 내부에 로직이 정해져 있네요. 

하나 더 샘플을 봐야 감이 올 것 같습니다.

@Slf4j
@Component
@Profile("prd")
public class MemoryHealth {

   @Autowired ElasticsearchConfig logConfig;

   @PostConstruct
   public void init(){
      ManagementFactory.getMemoryPoolMXBeans().parallelStream().forEach(memoryPoolMXBean -> log.info("memoryPoolMXBean.getName() --- " + memoryPoolMXBean.getName()));
   }

   @Scheduled(initialDelay = 1000, fixedDelay = 60000)
   public void check(){
      Map<String, Object> memInfo = new HashMap<>();

      try {
         memInfo.put("server", InetAddress.getLocalHost().getCanonicalHostName());
      } catch (UnknownHostException e) {
         e.printStackTrace();
      }

      ManagementFactory.getMemoryPoolMXBeans().parallelStream().forEach(memoryPoolMXBean -> {
         switch (memoryPoolMXBean.getName()){
            case "Code Cache":
               memInfo.put("codeCache", memoryPoolMXBean.getUsage().getUsed());
               break;
            case "Metaspace":
               memInfo.put("metaSpace", memoryPoolMXBean.getUsage().getUsed());
               break;
            case "Compressed Class Space":
               memInfo.put("compressedClassSpace", memoryPoolMXBean.getUsage().getUsed());
               break;
            case "Eden Space":
            case "PS Eden Space":
               memInfo.put("psEdenSpace", memoryPoolMXBean.getUsage().getUsed());
               break;
            case "Survivor Space":
            case "PS Survivor Space":
               memInfo.put("psSurvivorSpace", memoryPoolMXBean.getUsage().getUsed());
               break;
            case "Tenured Gen":
            case "PS Old Gen":
               memInfo.put("psOldGen", memoryPoolMXBean.getUsage().getUsed());
               break;
         }
         memInfo.put("createdDatetime", System.currentTimeMillis());
      });
   }
}

이번에 작성된 유틸리티는 운영 중인 서버의 메모리 적재량을 확인하기 위해 구현된 소스입니다. 관리하고 있는 포인트가 꽤나 많기에 이러한 소스가 있다고만 짚고 넘어가 보겠습니다.

 

아무래도 @Bean과 @Component의 두 가지 어노테이션을 확인해 보았을 때 꽤나 차이가 있는 것을 확인하실 수 있을 거예요. 


정리

@Bean: 이전에 제공된 라이브러리를 Setter, Builder를 통해 사용자가 Properties를 변경하여 생성한 instance

@Component: 개발자가 생성한 클래스를 Spring Boot에 등록하기 위해 instance를 생성하고 Bean에 등록하는 과정

반응형