Jaxb2Marshaller

 

Jaxb2Marshaller

Jaxb2Marshaller는 Spring Framework 라이브러리이며 org.springframework.oxm의 하위 클래스입니다.

이번에는 Jaxb2Marshaller를 활용하여 restful 통신에 사용되는 Model(Object)를 XML로 변환하는 방법에 관하여 다루어 보도록 하겠습니다.

pom.xml

먼저 pom.xml을 열어 아래 두 의존성을 등록 하도록 합니다.

<dependencies>
    <!-- 스프링 라이브러리 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.6.RELEASE</version>
    </dependency>
    <!-- 스프링 oxm(xml) 라이브러리 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-oxm</artifactId>
        <version>5.2.6.RELEASE</version>
    </dependency>
</dependencies>

라이브러리 등록을 완료 하였다면 이제 패키지로 가서 자바 설정 파일을 하나 만들어 두도록 하겠습니다.

ApplicationConfiguration

설정 파일명은 ApplicationConfiguration 로 생성하겠습니다.

public class ApplicationConfiguration { }

설정파일이 생성 되었다면 이제 초기화(initializer) 클래스를 생성하도록 하겠습니다.

초기화 클래스는 ApplicationInitializer 로 생성한 다음

스프링 이벤트 리스너인 AbstractAnnotationConfigDispatcherServletInitializer를 구현하여

이전 생성한 ApplicationConfiguration을 서블릿 설정 클래스와 서블릿 매핑 정보(“/”)를 등록합니다.

public class ApplicationInitializer 
	extends AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	protected Class<?>[] getRootConfigClasses() {
		return null;
	}

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

	@Override
	protected String[] getServletMappings() {
		return new String[] { "/" };
	}

}

UserRepository와 User

이제 저장소(Repository)와 POJO클래스를 생성하겠습니다.

저장소는 UserRepository POJO 클래스로는 User로 생성하겠습니다.

public class User { }
public class UserRepository { }

POJO클래스의 속성값을 추가하도록 하겠습니다.

속성값으로는 id, name, address 3항목을 추가한 다음 setter를 추가 하겠습니다.

public class User {

	public User(String id, String name, String address) {
		this.id = id;
		this.name = name;
		this.address = address;
	}
    
	public void setId(String id) {
		this.id = id;
	}
	public void setName(String name) {
		this.name = name;
	}
	public void setAddress(String address) {
		this.address = address;
	}

    private String id;
    private String name;
    private String address;
}

이제 저장소의 사용자를 등록하고 조회 하는 기능을 추가하겠습니다.

특정 사용자를 조회하기 위하여 findUser, 전체 사용자를 조회하기 위하여 findUserList 기능을 추가 하도록 하겠습니다.

public class UserRepository {

	private Map<String, User> repository = new LinkedHashMap<String, User>();

	public User findUser(String id) {

		return repository.get(id);
	}
	
	public UserRepository findUserList() {

		return this;
	}
}

UserRepository의 초기 사용자를 등록 하겠습니다.

public class UserRepository {
	
	public UserRepository() {
		User user1 = new User("admin", "관리자" , "서울 특별시 영등포구 여의도동");
		User user2 = new User("dev01", "사용자1", "전라남도 부안군 청수리");
		User user3 = new User("dev02", "사용자2", "대전광역시 이수군 기립동");
		User user4 = new User("dev03", "사용자3", "대구광역시 중구 동인동");
		
		this.repository.put("admin", user1);
		this.repository.put("dev01", user2);
		this.repository.put("dev02", user3);
		this.repository.put("dev03", user4);
	}

	public UserRepository findUserList() {

		return this;
	}
	
	public List<User> findUserList() {

		return new ArrayList<User>(repository.values());
	}

	private Map<String, User> repository = new LinkedHashMap<String, User>();
}

이제 준비는 끝났습니다.

Jaxb2Marshaller를 활용하여 model(Object)를 xml로 된 view로 반환하도록 처리해보겠습니다.

이제 ApplicationConfiguration로 돌아가서 Jaxb2Marshaller와 Jaxb2Marshaller을 사용한 View 그리고 View를 이어줄 ViewResolver를 등록 하도록 하겠습니다.

(BeanNameViewResolver를 사용하는 이유는 후에 컨트롤러에서 ViewResolver를 xmlView로 뷰를 매핑하기 위합입니다.)

또한 @ComponentScan을 사용하여 빈 스캐닝을 자동으로 하도록 처리합니다.

@ComponentScan(basePackageClasses = ApplicationConfiguration.class)
public class ApplicationConfiguration {
	
	@Bean
	public View xmlView() {

		return new MarshallingView(jaxb2Marshaller());
	}

	@Bean
	public Jaxb2Marshaller jaxb2Marshaller() {

		return new Jaxb2Marshaller();
	}

	@Bean
	public ViewResolver viewResolver() {

		return new BeanNameViewResolver();
	}
}

AppController

이제 사용자의 요청을 받아 처리 해보도록 하겠습니다

사용자의 요청을 받아 처리하는 곳을 웹 레이어(웹 프리젠테이션 - 컨트롤러)라고 하며

웹 애플리케이션에서는 이 웹 레이어를 각각 모듈별로 엔트리 포인트로 분리하여 처리하기도 합니다.

그러며 먼저 AppController클래스를 하나 선언하고 요청을 받을 수 있는 요청 매핑(RequestMapping)을 하나 추가 하겠습니다.

(“xmlView”을 뷰 네임으로 매핑하는 것은 ApplicationConfiguration에서 MarshallingView로 처리하여서 가능합니다.)

@Controller
public class AppController {
	
	private UserRepository repository = new UserRepository();
	
	@RequestMapping(value= {"/", "/{id}"})
	public ModelAndView home(ModelAndView mv, @PathVariable(name="id",required=false) String id) {

		if(Objects.isNull(id)) {

			mv.addObject(repository.findUserList());
		} else {
			User user = repository.findUser(id);

			if(Objects.isNull(user)) {

				mv.addObject(new User("none","none","none"));
			} else {
				mv.addObject(user);
			}
		}
		mv.setViewName("xmlView");
		return mv;
	}
}

XML 매핑

마지막으로 XML을 매핑할 수 있도록 UserUserRepository의 xml 매핑을 추가하겠습니다.

먼저 User에서는 클래스에 xml루트를 알리는 @XmlRootElement를 등록하고 각 컬럼에는 @XmlElement을 추가 하여 명칭을 변경 하도록 하겠습니다.

@XmlRootElement
public class User {

	@XmlElement(name="_id")
	private String id;
	
	@XmlElement(name="_name")
	private String name;
	
	@XmlElement(name="_address")
	private String address;
    
	public User() { }

	public User(String id, String name, String address) {
		this.id = id;
		this.name = name;
		this.address = address;
	}
	
	public void setId(String id) {
		this.id = id;
	}
	public void setName(String name) {
		this.name = name;
	}
	public void setAddress(String address) {
		this.address = address;
	}
}

다음 UserRepository에서는 역시 루트를 알리는 @XmlRootElement을 클래스에 등록 하고 @XmlAccessorType을 지정하여 해당 필드를 직렬화 대상으로 등록 합니다.

XmlAccessorType은 아래표와 같습니다.

명칭 설명
NONE @XmlElement로 등록 된 필드만 직렬화 처리
FIELD 모든 필드(속성값)를 대상으로 직렬화 처리
PROPERTY 모든 프로퍼티(getter/setter)로 등록 된 필드는 직렬화 처리
PUBLIC_MEMBER 접근제한자가 publid인 필드와 프로퍼티를 대상으로 직렬화 처리
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class UserRepository {
	
	public UserRepository() {
		User user1 = new User("admin", "관리자", "서울 특별시 영등포구 여의도동");
		User user2 = new User("dev01", "사용자1", "전라남도 부안군 청수리");
		User user3 = new User("dev02", "사용자2", "대전광역시 이수군 기립동");
		User user4 = new User("dev03", "사용자3", "대구광역시 중구 동인동");
		
		this.repository.put("admin", user1);
		this.repository.put("dev01", user2);
		this.repository.put("dev02", user3);
		this.repository.put("dev03", user4);
	}

	public User findUser(String id) {

		return repository.get(id);
	}
	
	public UserRepository findUserList() {

		return this;
	}
	
	private Map<String, User> repository = new LinkedHashMap<String, User>();
}