`
农村外出务工男JAVA
  • 浏览: 104734 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

Jersey的绑定机制

阅读更多

一:简介

    因为项目需要,最近研究了下restful风格的编程方式,这里也Jersey为例。Jersey是一个restful框架,其提供了面向切面的Providers功能,一般情况下我们可以手动注册到Application中,但是它支持更加灵活的方式,这就是jersey提供的绑定机制。

二:客户端封装

   Jersey Client的每次创建连接都必须耗资源,我们可以用连接池模式进行封装。

  

package com.jersey.client;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;

import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.glassfish.jersey.apache.connector.ApacheClientProperties;
import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
import org.glassfish.jersey.client.ClientConfig;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;

/**
 * 对Jersey客户端得封装 
  1、使用spring提供的几个bean实现 InitializingBean ,
 * FactoryBean 工厂bean,其返回的对象不是指定类的一个实例,其返回的是该FactoryBean的getObject方法所返回的对象
 *  DisposableBean
 *  bean的销毁
 * 
 * @author tanjie
 *
 */
@SuppressWarnings("deprecation")
@Service("jerseyClient")
public final class JerseyClient implements FactoryBean<Client>,
		InitializingBean, DisposableBean {

	/**
	 * 返回Client实例
	 */
	private Client client;

	private ClientConfig clientConfig;

	/**
	 * httpclient连接的最大连接数
	 */
	private int maxTotal = 1000;

	/**
	 * httpclient每个主机地址的并发数
	 */
	private int defaultMaxPerRoute = 100;
	
	/**
	 * 无参构造函数,spring管理的对象必须提供一个
	 */
	public JerseyClient(){
		
	}

	public JerseyClient(int maxTotal, int defaultMaxPerRoute) {
		this.maxTotal = maxTotal;
		this.defaultMaxPerRoute = defaultMaxPerRoute;
	}

	/**
	 * spring会在初始化bean之后执行该方法,优先于init-method执行,但是init-method不依赖spring,只是其采用
	 * 反射实现,效率上没有实现InitalizingBean高
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		if (null == clientConfig) {
			clientConfig = new ClientConfig();
			final PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager();
			connectionManager.setMaxTotal(maxTotal);
			connectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
			clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER,
					connectionManager);
			clientConfig.connectorProvider(new ApacheConnectorProvider());
			client = ClientBuilder.newClient(clientConfig);
		}else{
			// 默认创建一个
			client = ClientBuilder.newClient();
		}
	}

	/**
	 * 实例销毁
	 */
	@Override
	public void destroy() throws Exception {
		if (null != client) {
			client.close();
		}
	}

	@Override
	public Client getObject() throws Exception {
		return this.client;
	}

	@Override
	public Class<?> getObjectType() {
		return this.client.getClass();
	}

	@Override
	public boolean isSingleton() {
		return true;
	}

}

  2.1 按名称绑定

    使用@NameBinding

  

package com.tanjie.jersey.绑定机制;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.ws.rs.NameBinding;

/**
 * 通过名称绑定
 */
@NameBinding
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(value=RetentionPolicy.RUNTIME)
public @interface Banding {

}

  绑定@Provider

 

package com.jersey.绑定机制;

import java.io.IOException;

import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;

@Banding
@Priority(Priorities.USER)
@Provider
public class NameBindingFilter implements ContainerRequestFilter,
		ContainerResponseFilter {

	/**
	 * @param requestContext服务器请求过滤器
	 * 可以分为预处理:即当服务器接收到请求后先执行处理
	 * 后处理:当服务器接收到请求并处理后在进行处理(默认情况系)
	 * 二者可同时执行
	 */
	@Override
	public void filter(ContainerRequestContext requestContext)
			throws IOException {
		System.out.println("AirNameBindingFilter请求前"
				+ requestContext.getMethod());
	}
	
	/**
	 * 服务器响应请求后的过滤器
	 * @param requestContext 容器请求上下文
	 * @param responseContext 容器响应上下文
	 */
	@Override
	public void filter(ContainerRequestContext requestContext,
			ContainerResponseContext responseContext) throws IOException {
		System.out.println("AirNameBindingFilter请求后"
				+ requestContext.getMethod() + ",url:"
				+ responseContext.getStatus());
	}

}

  提供rest接口

package com.jersey.resources;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

import com.jersey.vo.User;
import com.jersey.绑定机制.Banding;

@Banding
@Path("/helloword")
public class RestfulHello {

	/**
	 * @GET 方法是幂等的,因为读取同一个资源,总是得到相同的数据,GET方法也是线程安全的
	 * 因为读取资源不会对其状态做改到
	 * 1、在接口中定义了资源请求的类型后,在实现类上不用再定义
	 * @return
	 */
	@GET
	@Produces(MediaType.TEXT_PLAIN)
	public String sayHello() {
		return "restful hello word";
	}

	@GET
	@Path("/{param}")
	@Produces("text/plain;charset=UTF-8")
	public String sayHello2UserByText(@PathParam("param") String username) {
		return "Hello " + username;
	}

	@GET
	@Path("/get")
	@Produces(MediaType.APPLICATION_JSON)
	public User HH(@QueryParam("username") String username) {
		User user = new User();
		user.setId(1);
		user.setName(username);
		return user;
	}
}

  单元测试:

 

package com.jersey_restful;

import javax.annotation.Resource;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.Response;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class NameBindTest{

	@Resource(name = "jerseyClient")
	private transient Client jerseyClient;

	@Test
	public void test() {
		WebTarget target = jerseyClient.target(
				"http://localhost:8080/jersey_restful/restful/helloword")
		Builder builder = target.request();
		Response response = builder.get();
		GenericType<String> genericType = new GenericType<String>(String.class);
		if (200 == response.getStatus()) {
			System.out.println("请求OK");
			System.out.println("返回值:" + response.readEntity(genericType));
		}
	}
}

  运行效果:

AirNameBindingFilter请求前GET
请求OK
返回值:restful hello world
AirNameBindingFilter请求后GET,url:200

 

  2.2 动态绑定

    Jersey支持动态绑定,可以更个性化的加载,在运行期只要匹配的动态绑定扩展的方法,面向切面的Provider就会被加载

   定义动态绑定:

  

package com.tanjie.jersey.绑定机制;

import javax.ws.rs.GET;
import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;

import com.tanjie.jersey.resources.RestfulHello;

@Provider
public final class 动态绑定  implements DynamicFeature{

	@Override
	public void configure(ResourceInfo resourceInfo, FeatureContext context) {
		    boolean 是否是这个类 = RestfulHello.class.isAssignableFrom(resourceInfo.getResourceClass());
	        boolean 是否是这个方法名 = resourceInfo.getResourceMethod().getName().contains("sayHello");
	        boolean 是否是这个请求类型 = resourceInfo.getResourceMethod().isAnnotationPresent(GET.class);
	        if (是否是这个类 && 是否是这个方法名 && 是否是这个请求类型) {
	            context.register(AirDynamicBindingFilter.class);
	        }
	}

}

  切面

 

public class AirDynamicBindingFilter implements ContainerRequestFilter {
	
    @Override
    public void filter(final ContainerRequestContext requestContext) throws IOException {
    	   System.out.println("动态绑定业务处理");
    }
}

  浏览器执行:

http://localhost:8080/jersey_restful/restful/helloword

  运行效果:

动态绑定业务处理

  如果继续执行:

http://localhost:8080/jersey_restful/restful/helloword/get?username=zs

  则不会有满足切面条件。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics