使用jaxb注解的小技巧-给xml结构增加封装

1.使用maven引入jaxb依赖

jdk1.6以上自带了jaxb,但我还是推荐显式指定依赖:

<dependency>
  <groupid>javax.xml.bind</groupid>
  <artifactid>jaxb-api</artifactid>
  <version>2.2.7</version>
</dependency>

2.构造一个封装类

下面的类Wrapper,使用Wrapper.wrapper(yourObject),就能把yourObject的结构封装在<root><yourObject></yourObject></root>标签里。

@XmlRootElement(name = "root")
public class Wrapper {

	public static Wrapper wrapper(Object object) {
		Wrapper wrapper = new Wrapper();
		wrapper.addElement(object);
		return wrapper;
	}

	@XmlAnyElement
	private List<Object> root;

	private Wrapper() {
		root = new ArrayList<Object>();
	}

	public void addElement(Object object) {
		root.add(object);
	}
}

如果需要修改外层的封装标签,修改@XmlRootElement(name = “your_tag”)即可。

如何配置spring web-mvc返回资源的xml/json等不同视图

1.首先,列出两个很有用的资源:

  1. Spring 3 MVC ContentNegotiatingViewResolver Example

  2. Spring REST 3 to Support XML and JSON

我按照链接1说明下载了示例代码,根据链接2解决了示例代码中的问题。

2.spring web-mvc是怎样区分不同的视图请求(xml or json or ?)

spring web-mvc支持给资源返回不同形式的视图,现在有两种方式指定返回的视图类型:

a.通过URL后缀指定视图类型

  • http://sample.com/resource.xml 返回资源的xml视图
  • http://sample.com/resource.json 返回资源的json视图

b.通过在HTTP请求头的Accept参数里列出期望的资源类型(media-types)

  • Accept:application/xml 指定返回xml视图
  • Accept:application/json 指定返回json视图

3.配置ContentNegotiatingViewResolver来支持返回多种视图

下面是配置ContentNegotiatingViewResolver的一段spring配置文件:

<bean
  class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"
  p:order="1">
  <property name="mediaTypes">
    <map>
      <entry key="xml" value="application/xml" />
      <entry key="json" value="application/json" />
    </map>
  </property>
  <property name="defaultViews">
    <list>
      <bean class="org.springframework.web.servlet.view.xml.MarshallingView">
        <property name="marshaller">
          <bean class="org.springframework.oxm.xstream.XStreamMarshaller"
            p:autodetectAnnotations="true" />
        </property>
      </bean>
      <bean
        class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
    </list>
  </property>
</bean>

这段配置需要说明的有:

  1. 指定”后缀-资源类型”映射:<entry key=”xml” value=”application/xml” />,这样通过后缀就能对应到资源类型。
  2. 提供处理xml/json资源类型的bean,这里用来处理xml视图的bean是org.springframework.web.servlet.view.xml.MarshallingView,处理json视图的bean是
    org.springframework.web.servlet.view.json.MappingJacksonJsonView。

4.实现一个简单的Controller

class Somebody {
  private String name;
  private boolean isMale;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public boolean isMale() {
    return isMale;
  }

  public void setMale(boolean isMale) {
    this.isMale = isMale;
  }
}

@Controller
@RequestMapping("/hi")
public class Hi {
  @RequestMapping(value = "{name}", method = RequestMethod.GET)
  public @ModelAttribute
  Somebody getShopInJSON(@PathVariable String name) {
    Somebody somebody = new Somebody();
    somebody.setName(name);
    somebody.setMale(true);
    return somebody;
  }
}

注意!这里返回的对象,用的是@ModelAttribute注解。

5.部署和测试

把工程打成war包,部署到web容器下测试,访问http://host:port/hi/bob.json或是http://host:port/hi/bob.xml。

列下我的pom.xml的依赖:

...
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>3.1.2.RELEASE</version>
    </dependency>

    <!-- for xml -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-oxm</artifactId>
      <version>3.1.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>com.thoughtworks.xstream</groupId>
      <artifactId>xstream</artifactId>
      <version>1.4.3</version>
    </dependency>

    <!-- for json -->
    <dependency>
      <groupId>org.codehaus.jackson</groupId>
      <artifactId>jackson-mapper-asl</artifactId>
      <version>1.9.10</version>
    </dependency>

  </dependencies>
</project>

最后,感谢…!

使用maven WSDL2Code插件生成webservice代码

首先,配置我们的项目使用wsdl2code插件。

先看看pom.xml配置:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.sample</groupId>
  <artifactId>wesd2code-demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.axis2</groupId>
        <artifactId>axis2-wsdl2code-maven-plugin</artifactId>
        <version>1.6.2</version>
        <configuration>
          <packageName>com.sample</packageName>
          <wsdlFile>src/main/wsdl/webservice.wsdl</wsdlFile>
          <generateTestcase>true</generateTestcase>
          <syncMode>sync</syncMode>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

这里给大家解释下上面的wsdl2code插件配置

  1. packageName:生成的源码会放到指定的package里
  2. wsdlFile:webservice定义文件的路径,可以是相对于pom.xml所在目录的相对路径
  3. generateTestcase:是否生成单元测试
  4. syncMode:生成同步还是异步接口,有三种可选值:sync, async或者both,其中both是默认选项
  5. generateServerSide:默认是false,这里没有配置,所以是只生成客户端代码
  6. outputDirectory:默认是target/generated-sources/axis2/wsdl2code

然后,我们可以运行mvn命令生成代码:

mvn clean axis2-wsdl2code:wsdl2code

最后,给生成的代码添加依赖:

  <dependencies>
    <dependency>
      <groupId>org.apache.axis2</groupId>
      <artifactId>axis2-integration</artifactId>
      <version>1.6.2</version>
    </dependency>
  </dependencies>

Maven使用体会及一些最佳实践

maven官网的介绍太简略(Run Maven),对于一些初次接触maven的用户来说,实在是让人困惑。
本文主要介绍了maven的一些常见用法,大家籍着了解一些maven的功能。

1.使用maven标准的目录结构来组织项目

maven引入了convention over configuration(约定优先配置)的原则,如果你的项目是按照maven标准的目录结构来组织,很多时候都不用写自己的配置。

2.明确指定字符编码

特别是对于需要使用非ASCII字符的开发者来说,字符编码是很严肃的一件事。
建议在你的pom.xml里添加下面的配置:

<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

另外,单元测试时如果不明确指定字符编码,有可能输出乱码,甚至导致测试失败,建议配置:

<plugin>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.7.1</version>
  <configuration>
    <argLine>-Dfile.encoding=UTF-8</argLine>
  </configuration>
</plugin>

具体解释请参考maven-surefire-plugin乱码问题

待续…

Spring通过JNDI从Tomcat查询Datasource

首先,介绍下运行的软件环境:

  • tomcat 7.0
  • oracle 11.2
  • spring 3.0
  • c3p0 0.9.1

下面,我们开始配置。

1.准备好tomcat依赖的jar包
把下列的jar包拷贝到TOMCAT_HOME/lib/目录下(其他依赖可以放在web应用的lib目录下):

  • ojdbc6-11.2.0.jar: oracle jdbc驱动
  • c3p0-0.9.1.jar: c3p0连接池

2.在tomcat上下文里配置c3p0连接池
可以参考tomcat jndi datasource examples文档,和文档上不同的地方是这里使用了c3p0连接池。
在TOMCAT_HOME/conf/context.xml里添加下面的配置:

<Resource auth="Container"
          description="DB Connection"
          driverClass="oracle.jdbc.driver.OracleDriver"
          maxPoolSize="50"
          minPoolSize="3"
          acquireIncrement="3"
          name="jdbc/sample"
          user="user"
          password="password"
          factory="org.apache.naming.factory.BeanFactory"
          type="com.mchange.v2.c3p0.ComboPooledDataSource"
          jdbcUrl="jdbc:oracle:thin:@hostname:1521:sid" />

配置到这里,就可以使用JNDI查询到数据库连接池了。

3.在spring里注入数据源
使用JNDI查询数据源tomcat_DataSource:

<bean id="tomcat_DataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
	<property name="jndiName" value="java:comp/env/jdbc/sample" />
</bean>

现在,可以配置web应用使用tomcat_DataSource数据源了。

java读取classpath下资源的2种方法比较

A.使用ClassLoader.getResourceAsStream()读取资源:

@Test
public void test_ClassLoader_getResourceAsStream() {
	InputStream works = getClass().getClassLoader().getResourceAsStream(
			filename);
	assertNotNull(works);

	InputStream doesnt_work = getClass().getClassLoader()
			.getResourceAsStream("/" + filename);
	assertNull(doesnt_work);
}

B.使用spring上下文来加载资源:

@Test
public void test_ApplicationContext_getResource() {
	ApplicationContext ctx = new ClassPathXmlApplicationContext();
	// !!! dosen't work within tomcat
	Resource may_works = ctx.getResource("classpath:" + filename);
	assertTrue(may_works.exists());

	Resource always_works = ctx.getResource("classpath:/" + filename);
	assertTrue(always_works.exists());
}

下面来分析两种方法的适用情况:

方法A:
pros:不依赖额外的包,干净。
cons:资源和调用类必须由同一个(或有继承关系的)Classloader加载,否则找不到资源文件。例如在tomcat环境中,若资源文件和调用类的jar包分离,有可能出现找不到资源的情况。

方法B:
pros:只要在classpath下,就可以找到资源。
cons:需要依赖spring的包。

对于我来说,简单的应用,使用方法A就可以。
复杂些的应用,一般会使用spring,很多资源文件都使用依赖注入,使用方法B的情况很少。