[Spring] 스프링 기본설정 xml → @Configuration 변경

📌 Java Configuration

기본적으로 프로젝트를 생성하면 모든 web.xml, root-context.xml, app-servlet.xml 세가지의 기본 설정파일이 만들어진다.
Spring Framework 3.1부터는 이러한 xml 설정파일을 Java 기반으로 작성할 수 있도록 각종 클래스와 인터페이스, 어노테이션을 지원하고 있다.
둘 중 어떤 방식을 사용해도 괜찮지만, 다음과 같은 이유로 되도록 Java Config 방식으로 변경해 사용하는 것을 권장한다.

  • 더 많은 정보를 얻을 수 있다.
  • 컴파일 에러를 얻을 수 있다.
  • 설정 변경에 용이하다.

나는 현재 진행하고 있는 프로젝트의 기본 설정을 자바 클래스로 변경하기로 했다.
위와 같은 이유도 있지만, xml과 java config를 섞어 쓰니 헷갈리고 보기에도 좋지 않은 것 같아서(?) 한번 연습해보기로😗

📁 pom.xml

web.xml을 삭제하면 이를 인식하지 못해 pom.xml에 에러 표시가 생긴다.
따라서 플러그인을 추가해 xml을 사용하지 않는다는 설정을 해주어야 한다.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>3.2.0</version>
    <configuration>
        <failOnMissingWebXml>false</failOnMissingWebXml>
    </configuration>
</plugin>

📁 web.xml

기존에는 web.xml을 통해 설정파일 등록, 디스패처 서블릿 매핑, 필터 등록을 할 수 있었다.
자바 클래스로 변경하기 위해서는 다음 중 하나의 클래스를 상속하고 필요한 메서드를 오버라이딩해야 한다.

  • AbstractAnnotationConfigDispatcherServletInitializer
  • WebApplicationInitializer

이번 포스팅에서는 전자를 상속했다.

public class WebConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
	
	@Override
	protected Class<?>[] getRootConfigClasses() {
		return new Class[] {RootConfig.class, SecurityConfig.class};
	}
	
	@Override
	protected Class<?>[] getServletConfigClasses() {
		return new Class[] {ServletContext.class};
	}
	
	@Override
	protected String[] getServletMappings() {
		return new String[] {"/"};
	}
	
	@Override
	protected Filter[] getServletFilters() {
		CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
		characterEncodingFilter.setEncoding("UTF-8");
		characterEncodingFilter.setForceEncoding(true);
		return new Filter[] { characterEncodingFilter };
	}
}

1) Root Context 설정

xml에서는 contextConfigLocation에 루트 컨텍스트 설정파일 경로를 지정해주었다.

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
    	classpath:root-context.xml
        classpath:security-context.xml
    </param-value>
</context-param>

getRootConfigClasses()

Root Context와 관련된 설정 클래스들을 배열 형태의 리턴값으로 지정한다.

@Override
protected Class<?>[] getRootConfigClasses() {
    return new Class[] {RootConfig.class, SecurityConfig.class};
}

2) Dispatcher Servlet 설정

기존에는 DispatcherServlet 설정파일의 경로를 지정하고 서블릿 이름을 통해 url pattern과 매핑해주었다.

<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

getServletConfigClasses()

디스패처 서블릿 설정 클래스들을 배열 형태의 리턴값으로 지정한다.

@Override
protected Class<?>[] getServletConfigClasses() {
    return new Class[] {ServletContext.class};
}

getServletMappings()

위에서 등록한 디스패처 서블릿과 매핑할 url pattern을 설정한다.

<servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
@Override
protected String[] getServletMappings() {
    return new String[] {"/"};
}

3) 필터 설정

필터 클래스와 속성을 서술하고 필터 이름을 통해 매핑해 필터가 적용될 URL을 지정하는 부분이다.

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

getServletFilters()

new 연산자를 이용해 필터 클래스의 인스턴스를 생성하고 등록해준다.
시큐리티 필터 체인은 AbstractSecurityWebApplicationInitializer을 통해 등록해줄 것이므로 따로 작성하지 않았다.

@Override
protected Filter[] getServletFilters() {
    CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
    characterEncodingFilter.setEncoding("UTF-8");
    characterEncodingFilter.setForceEncoding(true);
    return new Filter[] { characterEncodingFilter };
}

📁 root-context.xml

기존의 root-context.xml을 대체할 RootConfig 클래스를 작성한다.
설정파일임을 알려주는 어노테이션 @Configuration을 사용한다.

@Configuration
@ComponentScan(basePackages = {"com.xxx.yyy.service", "com.xxx.yyy.repository"})
public class RootConfig {

	@Autowired
	private ApplicationContext applicationContext;
	
    @Bean
	public BasicDataSource dataSource() {
		BasicDataSource basicDataSource = new BasicDataSource();
		basicDataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
		basicDataSource.setUrl("jdbc:oracle:thin:@localhost:1521:orcl");
		basicDataSource.setUsername("name");
		basicDataSource.setPassword("password");
		return basicDataSource;
	}
    
	@Bean
	public SqlSessionTemplate sqlSession() throws Exception {
		return new SqlSessionTemplate((SqlSessionFactory) sqlSessionFactoryBean());
	}

	@Bean
	public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
		SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
		sqlSessionFactory.setConfigLocation(applicationContext.getResource("classpath:/mybatis-config.xml"));
		sqlSessionFactory.setDataSource(dataSource());
		return (SqlSessionFactory)sqlSessionFactory.getObject();
	}

	@Bean 
	public DataSourceTransactionManager transactionManager() {
		DataSourceTransactionManager transaction = new DataSourceTransactionManager();
		transaction.setDataSource(dataSource());
		return transaction;
	}
}

1) 컴포넌트 스캔

컴포넌트 스캔을 시작할 패키지를 명시해준다.

@Configuration
@ComponentScan(basePackages = {"com.xxx.yyy.service", "com.xxx.yyy.repository"})
public class RootConfig {

2) ApplicationContext

클래스경로에서 리소스를 가져오는 데에 사용되는 ApplicationContext 빈을 의존성주입 한다.

@Autowired
private ApplicationContext applicationContext;

3) 데이터베이스 연결

BasicDataSource

데이터베이스 연결을 위한 BasicDataSource 빈을 생성하고 드라이버 클래스 이름, URL, 사용자 이름, 암호 등 속성을 설정하는 부분이다.

어노테이션을 통해 생성하며 속성 설정은 xml 방식과 구조적으로 동일하다.

@Bean
public BasicDataSource dataSource() {
    BasicDataSource basicDataSource = new BasicDataSource();
    basicDataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
    basicDataSource.setUrl("jdbc:oracle:thin:@localhost:1521:orcl");
    basicDataSource.setUsername("name");
    basicDataSource.setPassword("password");
    return basicDataSource;
}

SqlSessionTemplate

MyBatis SQL 세션을 관리하는 데에 사용되는 SqlSessionTemplate, SqlSessionFactory 빈을 등록한다.

@Bean
public SqlSessionTemplate sqlSession() throws Exception {
    return new SqlSessionTemplate((SqlSessionFactory) sqlSessionFactoryBean());
}

@Bean
public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
    SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
    sqlSessionFactory.setConfigLocation(applicationContext.getResource("classpath:/mybatis-config.xml"));
    sqlSessionFactory.setDataSource(dataSource());
    return (SqlSessionFactory)sqlSessionFactory.getObject();
}

DataSourceTransactionManager

트랜잭션 관리를 위한 DataSourceTransactionManager 빈을 등록한다.

@Bean 
public DataSourceTransactionManager transactionManager() {
    DataSourceTransactionManager transaction = new DataSourceTransactionManager();
    transaction.setDataSource(dataSource());
    return transaction;
}

📁 Servlet-context.xml

기존의 Servlet-context.xml을 대체하기 위한 ServletContext 클래스를 생성한다.
WebMvcConfigurer 인터페이스를 상속하고, @EnableWebMvc 어노테이션을 붙여 스프링이 제공하는 웹 관련 빈을 자동으로 등록한다.

@EnableWebMvc
@ComponentScan(basePackages = {"com.sz.reminiscene.controller"})
public class ServletContext implements WebMvcConfigurer {

	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
		viewResolver.setViewClass(JstlView.class);
		viewResolver.setPrefix("/WEB-INF/views/");
		viewResolver.setSuffix(".jsp");
		registry.viewResolver(viewResolver);
	}

	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
	}
}

1) 컴포넌트 스캔

컴포넌트 스캔을 시작할 패키지를 명시해준다.

<context:component-scan base-package="com.XXX.YYY" />
@ComponentScan(basePackages = {"com.sz.reminiscene.controller"})
public class ServletContext implements WebMvcConfigurer {

2) View Resolver

응답 페이지와 관련한 뷰 리졸버를 설정하는 부분이다.

<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/views/" />
    <beans:property name="suffix" value=".jsp" />
</beans:bean>

configureViewResolvers()

InternalResourceViewResolver 인스턴스를 생성하고 prefix, suffix를 설정한다.
속성 설정을 마친 뷰 리졸버 객체는 ViewResolverRegistry에 등록한다.

@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
    InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
    viewResolver.setViewClass(JstlView.class);
    viewResolver.setPrefix("/WEB-INF/views/");
    viewResolver.setSuffix(".jsp");
    registry.viewResolver(viewResolver);
}

3) 정적 리소스 설정

이미지, 스타일시트, js파일 등 정적 리소스들의 정보를 기술하는 부분이다.

<resources mapping="/resources/**" location="/resources/" />

addResourceHandlers()

ResourceHandlerRegistry에 핸들러와 경로를 지정해준다.

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}