Spring framework XML Marshaller( Jaxb2Marshaller )
Spring framework에서 Jaxb2Marshaller를 사용하는 경우 관련된 classesToBeBound 또는 packagesToScan를 속성을 이용하여 XML 변환이 필요한 객체를 찾는다.
classToBeBound를 사용하는 경우 해당 Class를 주입해야하고, packageToScan을 사용하는 경우 해당 package를 여러개 선언해야 한다.
■ classToBeBound 와 packageToScan 설정 예
<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"> <property name="packagesToScan"--> <!-- set package name without * --> <list> <value>com.sample.vo</value> </list> </property> </bean> <bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"> <property name="classesToBeBound"> <list> <value>com.sample.vo.ApiUserVo</value> <value>com.sample.vo.ApiUserListVo</value> </list> </property> </bean>
|
상기의 방식을 적용하는 경우에는 XML 변환이 필요한 객체가 생성될 때마다 설정을 변경해야한다.
이와 같은 불편함을 해결하기 위해 package 단위로 관련 객체를 검색하는 방법을 구현한 소스를 인터넷에 찾아 약간의 변경을 하였다.
■Package 기준 객체 Scan 소스
public class PackageScanningJaxb2Marshaller extends Jaxb2Marshaller{ private static final Logger LOGGER = LoggerFactory.getLogger(ClasspathScanningJaxb2Marshaller.class);
/** base package location **/ private List<String> basePackages;
/** XML Filter for scanning package **/ private final TypeFilter[] jaxb2TypeFilters = new TypeFilter[]{ new AnnotationTypeFilter(XmlRootElement.class, false), new AnnotationTypeFilter(XmlType.class, false), new AnnotationTypeFilter(XmlSeeAlso.class, false), new AnnotationTypeFilter(XmlEnum.class, false) };
@Required public void setBasePackages(List<String> basePackages) { this.basePackages = basePackages; }
/** * This method should scan base package with XML related annotation filters to find classes */ private Class<?>[] getXmlRootElementClasses() throws Exception { ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); for (TypeFilter typeFilter : jaxb2TypeFilters) { scanner.addIncludeFilter(typeFilter); }
List<Class<?>> classes = new ArrayList<Class<?>>();
/** Find classes with base package **/ for (String basePackage : basePackages) { Set<BeanDefinition> definitions = scanner.findCandidateComponents(basePackage); for (BeanDefinition definition : definitions) { String className = definition.getBeanClassName();
LOGGER.info("Found class: {}", className);
classes.add(Class.forName(className)); } }
return classes.toArray(new Class[0]); }
@PostConstruct public void init() throws Exception { setClassesToBeBound(getXmlRootElementClasses()); } } |
PacakgeScanningJaxb2Marshaller는 Jaxb2Marshaller를 상속받아 구현하였으며, Bean이 생성된 후 정의된 Package에서 @XML annotation이 정의되어 있는 Class를 찾아 ClassesToBeBound에 해당 Class들을 Set하여 준다.
■ Bean 설정
<bean id="jaxbMarshaller" class="com.spring.xml.ClasspathScanningJaxb2Marshaller"> <property name="basePackages"> <list> <value>com.sample</value> </list> </property> </bean> |
■ xmlView 예제
<bean id="xmlView" class="org.springframework.web.servlet.view.xml.MarshallingView"> <!-- constructor-arg> <bean class="org.springframework.oxm.xstream.XStreamMarshaller" /> </constructor-arg--> <property name="marshaller" ref="jaxbMarshaller" /> </bean> |
xmlView는 marshller를 jaxbMarshller Bean을 참조한다.
■ Value Object 예제
import java.io.Serializable; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; @XmlAccessorType( XmlAccessType.FIELD ) @XmlType( name = "" , propOrder = { "clientId", "authCode", "authTime", } ) @XmlRootElement(name="ApiUser") public class ApiUserVo implements Serializable{
/** * */ private static final long serialVersionUID = -6929037986045167263L; private String clientId;
private String authCode;
private String authTime;
public String getClientId() { return clientId; } public void setClientId(String clientId) { this.clientId = clientId; } public String getAuthCode() { return authCode; } public void setAuthCode(String authCode) { this.authCode = authCode; } public String getAuthTime() { return authTime; } public void setAuthTime(String authTime) { this.authTime = authTime; }
} |
import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name="ApiUsers") @XmlAccessorType (XmlAccessType.FIELD) public class ApiUserListVo implements Serializable{ /** * */ private static final long serialVersionUID = 1L;
@XmlElement(name="apiUser") private List<ApiUserVo> apiUsers;
public ApiUserListVo() { apiUsers = new ArrayList<ApiUserVo>(); } public List<ApiUserVo> getApiUsers() { return apiUsers; } public void setApiUsers(List<ApiUserVo> apiUsers) { this.apiUsers = apiUsers; }
} |
■ Controller 예제
@RequestMapping(value="/sample/searchApiUserXml.do", method={RequestMethod.POST, RequestMethod.GET}, produces={MediaType.APPLICATION_XML_VALUE}) public ModelAndView selectApiUserXml(NexacroMapDTO dto, Model model) throws Exception {
ModelAndView modelAndView = NexacroUtil.getModelAndView(dto);
SBSessionVo sessionVo = SessionUtil.getSession(BaseConstants.DEFAULT_SESSION_NAME); ApiUserVo vo = new ApiUserVo(); vo.setClientId(sessionVo.getUserId());
List<ApiUserVo> resultList = apiUserService.selectApiUserXml(vo); ApiUserListVo voList = new ApiUserListVo(); voList.setApiUsers(resultList); modelAndView.setViewName("xmlView"); modelAndView.addObject("result", voList); return modelAndView;
} |
■ 결과
<ApiUsers> |
XML을 생성할 때 base package만 정의하면 간단하게 객체를 XML으로 변환이 가능하다.