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을 매핑할 수 있도록 User와 UserRepository의 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>();
}