Spring Framrwork

Spring framework XML Marshaller( Jaxb2Marshaller )

gregorio 2018. 3. 9. 13:19

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>
<apiUser>
<clientId>aaaa</clientId>
<authCode>db78f6a0-344f-485e-a8a9-71a5d54ee7a1</authCode>
<authTime>20184528130242</authTime>
</apiUser>
<apiUser>
<clientId>bbbb</clientId>
<authCode>38bf5d71-c422-42b4-a2f8-f5414fe6d6f0</authCode>
<authTime>20184628130217</authTime>
</apiUser>
</ApiUsers> 


XML을 생성할 때 base package만 정의하면 간단하게 객체를 XML으로 변환이 가능하다.