Maven 编译 打包注意事项

1. 父子项目同时编译问题: 编译时找不到父项目的pom.xml文件

报错信息: Non-resolvable parent POM for XXXXX

解决: 在子项目pom.xml的parent中, 加入父项目的相对路径relativePath

<parent>
      <artifactId>demo-server</artifactId>
      <groupId>net.abc.demo</groupId>
      <version>1.0.0</version>
      <relativePath>../pom.xml</relativePath>
</parent>

2. 多个模块打包的依赖问题

报错信息: class net.abc.demo.XXXXX .java:[24,9] cannot find symbol: class XXX

解决: 在被依赖的模块项目pom.xml中, 加入maven-compiler-plugin插件

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <encoding>utf-8</encoding>
                    <fork>true</fork>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

如果依赖的是springboot模块, 则pom.xml为:

<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <classifier>exec</classifier>
                </configuration>
            </plugin>
        </plugins>
    </build>

Python 安装 – anaconda

anaconda是各种python库的集成环境, 可以虚拟多个python环境.

1. 安装后, 解决和原来python的冲突

修改环境变量: 将anaconda的环境变量(Library\bin, Scripts和Home目录)放在原来的python配置之前;

2. 升级工具包

conda upgrade --all
conda install requests

3. 升级pip版本

python -m pip install --upgrade pip 

4. 虚拟Python环境

1.创建
conda create -n learn python=3
备注: 环境在envs/learn目录下, 可以在Pycharm中用这个目录作为python环境
2.查看
conda env list
3.切换
activate learn

activate  // 切换到base环境
activate learn // 切换到learn环境
conda create -n learn python=3  // 创建一个名为learn的环境并指定python版本为3(的最新版本)
conda env list // 列出conda管理的所有环境
conda list // 列出当前环境的所有包
conda install requests 安装requests包
conda remove requests 卸载requets包
conda remove -n learn --all // 删除learn环境及下属所有包
conda update requests 更新requests包
conda env export > environment.yaml  // 导出当前环境的包信息
conda env create -f environment.yaml  // 用配置文件创建新的虚拟环境

5. 安装数据分析相关插件

# 分词
pip install wordcloud
# 统计
pip install jieba
# 机器学习
pip install gensim

6. 启动jupyter

#默认目录在user目录下
jupyter notebook

(转)Jersey2.x对REST请求处理流程的分析

一个REST请求,始于一个RESTful Web Service资源的地址,终于一个可接受的对资源的表述(比如JSON)。

因此,流程分析的关键点有2个:

  • 将请求地址对应到资源类的相应方法上,并触发该方法。
  • 将返回值转换成请求所需要的表述,并返回给客户端。

我们使用Eclipse的断点调试服务器端(代码对应本例https://github.com/feuyeux/jax-rs2-guide/),使用cURL脚本作为客户端。

1 cURL测试

curl -H “Accept:application/json” http://localhost:8080/simple-service-webapp-spring-jpa-jquery/webapi/books/book?id=1

该脚本发送了一个REST GET请求,地址是:

http://localhost:8080/simple-service-webapp-spring-jpa-jquery/webapi/books/book?id=1

对应的服务器端方法是:

com.example.resource.BookResource.getBookByQuery(Integer)

要求返回的类型是JSON:

Accept:application/json

2 Jersey2.x流程

2.1 请求地址到REST方法

2.1.1 ServletContainer.service

ServletContainer.service(HttpServletRequest, HttpServletResponse) line: 248

  • baseUri http://localhost:8080/simple-service-webapp-spring-jpa-jquery/webapi/
  • requestUri http://localhost:8080/simple-service-webapp-spring-jpa-jquery/webapi/books/book?id=1

ServletContainer是HttpServlet的子类,位于Jersey容器包(.m2\repository\org\glassfish\jersey\containers\jersey-container-servlet-core\2.2\jersey-container-servlet-core-2.2.jar)。我们知道,Servlet的service方法是请求的入口,作为子类,HTTP任何方法的请求都要先经过该类的service方法。

断点堆栈中,重要的两个变量是请求地址信息的baseUri和requestUri。

dependencies 

Jersey包依赖关系图

ServletContainer.service(URI, URI, HttpServletRequest, HttpServletResponse) line: 372

  • requestContext.header {user-agent=[curl/7.26.0], host=[localhost:8080], accept=[application/json]}

在容器层级,请求上下文变量除了包括请求地址信息外,还包括请求头信息。这里我们关注accept信息。

2.1.2 对应方法

ApplicationHandler.handle(ContainerRequest) line: 982

ServerRuntime.process(ContainerRequest) line: 211 final Runnable task

new Runnable() {
 public void run() {
  final ContainerRequest data = Stages.process(request, requestProcessingRoot, endpointRef);
  final Endpoint endpoint = endpointRef.get();
  • endpoint ResourceMethodInvoker (id=2553)
    public com.example.domain.Book com.example.resource.BookResource.getBookByQuery(java.lang.Integer)

从上面的代码片段,可以看到请求被对应到了一个Endpoint对象,该对象是一个资源方法Invoker,从断点堆栈中可以看到,其内容就是我们期待的那个方法,此后的invoke将调用这个对应REST请求的处理方法。

2.1.3 调用方法

ResourceMethodInvoker.invoke(ContainerRequest, Object) line: 353
dispatcher.dispatch(resource, requestContext);

JavaResourceMethodDispatcherProvider$TypeOutInvoker(AbstractJavaResourceMethodDispatcher).invoke(Object, Object…) line: 158
invokeMethodAction.run();

ResourceMethodInvocationHandlerFactory$1.invoke(Object, Method, Object[]) line: 81

从堆栈中可以轻松定位这个方法:**BookResource.getBookByQuery(Integer) line: 71**

到此,请求地址到方法调用的流程就结束了。在处理业务逻辑方法BookResource.getBookByQuery后,Jersey将开始处理响应信息。

2.2 REST方法到表述

JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(Object, Request) line: 198

  • o Book (id=2686)
  • Response response = Response.ok().entity(o).build();

REST请求的返回信息应包括表述信息和HTTP响应代码(通常一个成功的请求应该得到200 OK)。这就是上面代码所做的事情。在设置好response的entity后,需要将该entity对象转化成请求所接受的表述,流程如下。

2.2.1 写向response

ServerRuntime$Responder.process(ContainerResponse) line: 351ServerRuntime$Responder.writeResponse(ContainerResponse) line: 486

  • entity Book (id=7076)
  • executor WriterInterceptorExecutor (id=7125)

从上面的堆栈中可以看到,这个阶段重点的两个对象是返回对象和写处理对象,就是使用后者将前者恰当地写入response中。

2.2.2 JSON表述

REST的表述不局限于JSON,这里我们以JSON作为常用的表述类型为例,来讨论Jersey的表述处理。 Jersey对JSON的支持有4种方式,在第3章会有详细的讲解,本例使用的是EclipseLink项目的MOXy。其底层依赖是JPA的实现之一(另一个著名的JPA实现是JBoss项目下大名鼎鼎的Hibernate)。

    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-moxy</artifactId>
        <version>${jersey.version}</version>
    </dependency>

moxy

MOXy依赖关系图

在下面的堆栈中,可以看到JAXB的身影,因为MOXy同样实现了JAXB,其内部实现是以XML的Marshal方式处理对象(OXM:Object-XML-Mapping),然后转化为JSON数据(XML-2-JSON)。

JsonWithPaddingInterceptor.aroundWriteTo(WriterInterceptorContext) line: 91

WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorContext) line: 188

ConfigurableMoxyJsonProvider(MOXyJsonProvider).**writeTo**(Object, Class<?>, Type, Annotation[], MediaType, MultivaluedMap<string,object style=”margin: 0px; padding: 0px;”>, OutputStream) line: 782

  • object Book (id=7076)
  • type Class (com.example.domain.Book) (id=568)
  • genericType Class (com.example.domain.Book) (id=568)
  • annotations Annotation3
  • mediaType AcceptableMediaType (id=7120)
  • httpHeaders StringKeyIgnoreCaseMultivaluedMap (id=7121)
  • entityStream CommittingOutputStream (id=7124)

开始从对象转化为JSON:

JAXBMarshaller.marshal(Object, OutputStream) line: 395

  • object Book (id=7076)
  • outputStream CommittingOutputStream (id=7124)

XMLMarshaller(XMLMarshaller<abstract_session,context,descriptor,media_type,namespace_prefix_mapper,object_builder style=”margin: 0px; padding: 0px;”>).marshal(Object, OutputStream, ABSTRACT_SESSION, DESCRIPTOR) line: 852

XMLMarshaller(XMLMarshaller<abstract_session,context,descriptor,media_type,namespace_prefix_mapper,object_builder style=”margin: 0px; padding: 0px;”>).marshal(Object, Writer, ABSTRACT_SESSION, DESCRIPTOR) line: 1031

  • object Book (id=7305)
  • writer OutputStreamWriter (id=7310)
    -> BufferedWriter (id=7333)

XMLMarshaller(XMLMarshaller<abstract_session,context,descriptor,media_type,namespace_prefix_mapper,object_builder style=”margin: 0px; padding: 0px;”>).marshal(Object, MarshalRecord, ABSTRACT_SESSION, DESCRIPTOR, boolean) line: 583

  • object Book (id=7305)
  • marshalRecord JSONWriterRecord (id=7342)
  • session DatabaseSessionImpl (id=7351)
  • descriptor XMLDescriptor (id=7353)
  • isXMLRoot false

几经辗转,写入对象变成了MarshalRecord。

JSONWriterRecord.startDocument(String, String) line: 171

TreeObjectBuilder.marshalAttributes(MarshalRecord, Object, CoreAbstractSession) line: 122

XPathObjectBuilder.marshalAttributes(MarshalRecord, Object, CoreAbstractSession) line: 552

这里是可以想见的流程…

ServerRuntime$Responder.processResponse(ContainerResponse) line: 362

最后是将response返回并释放。流程到此结束。

转自:http://feuyeux.iteye.com/blog/1938910

Jersey 过滤器

Jersey的过滤器分为:

ContainerRequestFilter: 请求阶段的过滤

ContainerResponseFilter: 响应阶段的过滤

一、使用注解名称绑定

1.1 创建绑定的注解

使用@NameBinding注解,可以定义一个运行时的自定义注解,该注解可以用于定义类级别名称和泪的方法。例如,我们定义一个用户访问的注解。

@NameBinding            //标识名称绑定的注解
@Target({ElementType.TYPE, ElementType.METHOD}) //表示该注解可以使用在类和方法上。
@Retention(value = RetentionPolicy.RUNTIME)
public @interface UserLogger {
}

上面代码我们定义了一个名称绑定注解UserLogger。

1.2 注解绑定过滤器

创建了注解之后,我们需要将注解和Jersey中的Provider 组件绑定,示例中我们使用的是过滤器。

@Provider
@UserLogger
@Priority(Priorities.USER)
public class LoggerFilter implements ContainerRequestFilter ,ContainerResponseFilter{
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
System.out.println("访问请求日志过滤器执行了>>>>>>>>>>>>");
}
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
System.out.println("访问响应日志过滤器执行了>>>>>>>>>>>");
}
}

@Provider 注解Jersey注解,Jersey扫描到该注解,就会创建对应的组件对象。 @UserLogger 就是我们自定义的名称绑定注解 @Priority 是用于表示该过滤器的执行顺序,其中参数为long类型,对于请求过滤器,该数值越小越先执行,响应过滤器则相反。 ContainerRequestFilter 为请求过滤器 ContainerResponseFilter 为响应过滤器 在请求和响应的过滤方法中,我们简单的打印输出。 需要注意的是,我们创建了过滤器之后需要在Jersey中进行声明,我们在Jerysey的ResourceConfig 子类中,注册该过滤器,注册有两种方式,一种是扫描包的形式,这时需要在过滤器上加上@Provider注解,另一种是直接注册该过滤器。

packages("com.xxxx.xxxx.xxxx.filter"); //扫描包的形式 过滤器所在的包

register(LoggerFilter.class); //直接注册过滤器

1.3 注解绑定接口

上面我们创建好注解和过滤器之后,需要将在我们需要使用过滤器的接口方法上使用注解。

@Path("/test")
public class TestResource {
@GET
@Path("/1")
public String test1(){
return "不带过滤器";
}
@GET
@Path("/2")
@UserLogger
public String test2(){
return "带过滤器";
}
}

我们创建了两个接口,一个路径是/test/1 没有使用过滤器,一个路径是/test/2使用注解,按照我们的设计,当访问./test/1时,日志过滤器不起作用,访问/test/2时日志过滤器起作用。

二、动态绑定

上面介绍的名称保定的形式需要通过自定义注解的形式来实现过滤器绑定,而动态绑定则不需要新增注解,而是需要编码的形式,实现动态绑定。动态绑定需要实现动态特征接口javax.ws.rs.container,DynamiFeature,定义扩展点方法,请求方法类型等匹配信息,在运行期,一旦Provider匹配到当前处理类或方法,面向切面的Provider方法就是触发。

2.1 实现动态绑定特征

public class LoggerDynaimcFeature implements DynamicFeature {
@Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
String name = resourceInfo.getResourceMethod().getName();
if("test2".equals(name)){
context.register(LoggerFilter.class);
}
}
}

上面的代码实现的是当我们访问的是test2方法时,就会注册LoggerFilter,实现该过滤器的方法。    我们需要在同样我们需要注册该动态特征来    

  1. register(LoggerDynaimcFeature .class);

   这时我们重新启动项目时,分别访问test/1和test/2,就会看到只有test2时,日志过滤器才会起作用。    三. 名称绑定和动态绑定对比    动态绑定相比于名称绑定,不需要自定义注解,使用纯编码的形式实现。    名称绑定相比于动态绑定使用范围更广,因为我们使用注解的方式,可以对任意资源,任意方法进行控制。而使用动态绑定的形式,我们需要在动态特征类进行对应的匹配,适用范围较窄。

Jersey 方法拦截器

如果我们需要控制用户对接口的访问,例如登陆控制,权限控制等,就需要使用方法拦截器。
    由于Jersey中的AOP是基于HK2框架实现的,所以对应的拦截器的功能也是由HK2框架实现。
    现在我们模拟实现一个登陆拦截器的功能。
3.1 创建自定义注解
  1. @Documented
  2. @Target({ElementType.METHOD, ElementType.TYPE})
  3. @Retention(RetentionPolicy.RUNTIME)
  4. public @interface LoginTest {
  5. }
 
3.2 方法拦截器
  1. @LoginTest
  2. public class LoginTestMethodInterceptor implements MethodInterceptor {
  3. @Override
  4. public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  5. return "没有权限访问";
  6. }
  7. }
    
3.3 方法拦截器的绑定
    Jersey在调用方法拦截器的时候,需要InterceptionService的实现。
   该接口中有三个方法,在执行对应的接口方法之前会调用getMethodInteceptors()方法,获取对应的拦截器,并执行拦截器。
public class JerseyInterceptor implements InterceptionService {
private static Map<Annotation, MethodInterceptor> map = new HashMap<>();
static{
Annotation[] annotations = LoginTestMethodInterceptor.class.getAnnotations();
for(Annotation annotation : annotations){
map.put(annotation, new LoginTestMethodInterceptor());
}
}
@Override
public Filter getDescriptorFilter() {
return new Filter() {
public boolean matches(Descriptor descriptor) {
return true;
}
};
}
@Override
public List<MethodInterceptor> getMethodInterceptors(Method method) {
Annotation[] annotations = method.getAnnotations();
List<MethodInterceptor> list = new ArrayList<>();
for (Annotation annotation :annotations){
if(map.get(annotation) != null){
list.add(map.get(annotation));
}
}
return list;
}
@Override
public List<ConstructorInterceptor> getConstructorInterceptors(Constructor<?> constructor) {
return null;
}
}
上面代码可以看到,我们是将注解与拦截器绑定,通过方法上的注解,获取方法对应的拦截器,并执行拦截器。 我们创建的拦截服务实现,需要与拦截服务进行绑定,这时需要AbstracctBinding对象的实现‘。
public class JerseyBinding extends AbstractBinder {
@Override
protected void configure() {
this.bind(JerseyInterceptor.class).to(InterceptionService.class).in(Singleton.class);
}
}

为了使绑定生效,我们需要定义特征类,用于注册该绑定

 
  1. public class JerseyFeature implements Feature {
  2. @Override
  3. public boolean configure(FeatureContext context) {
  4. context.register(new JerseyBinding());
  5. return true;
  6. }
  7. }
同样我们需要在ResourceConfig中注册该特征类
  1. register(JerseyFeature.class);
3,4 测试
    测试接口如下
 
  1. @Path("/test")
  2. public class TestResource {
  3. @Path("/1")
  4. @GET
  5. public String test1(){
  6. return "不带拦截器";
  7. }
  8. @Path("/2")
  9. @GET
  10. @LoginTest
  11. public String test2(){
  12. return "不带拦截器";
  13. }
  14. }
我们访问test/1的时候可以看到不带拦截器,访问test/2的时候可以看到没有访问权限

Jersey 请求/响应拦截器

拦截器主要用来操作实体,操作是通过实体输入输出流来完成。例如对请求实体进行编码。有两种拦截器,ReaderInterceptor 与WriterInterceptor。 Reader拦截器用来操作输入实体流(inbound entity streams)。所以,使用reader拦截器,你可以在服务器端操作请求实体流,在客户端操作响应实体流。(这个实体是从server response读取)。Writer 拦截器,writer 拦截器用于将实体写入”wire”中。在服务端这表示,写出响应实体。在客户端表示为发送到服务器端的请求写请求实体。Writer 与 Reader拦截器 在  消息 readers或writers被执行前执行,其目的是包装将被用在消息reader与writer中的实体流。

下面演示writer拦截器,对整个实体进行GZIP压缩。

[java]  view plain  copy
  1. public class GZIPWriterInterceptor implements WriterInterceptor {  
  2.    
  3.     @Override  
  4.     public void aroundWriteTo(WriterInterceptorContext context)  
  5.                     throws IOException, WebApplicationException {  
  6.         final OutputStream outputStream = context.getOutputStream();  
  7.         context.setOutputStream(new GZIPOutputStream(outputStream));  
  8.         context.proceed();  
  9.     }  
  10. }  

GZIPReaderInterceptor通过GZIPInputStream包装原始的输入流。此后对实体流的读取或得到压缩后的流。aroundReadFrom拦截方法必须返回一个实体。实体是从ReaderInterceptorContext的proceed方法中返回。Proceed方法在内部调用包装的拦截器,这个拦截器必须返回一个实体。被在调用链中的上一个拦截器调用的proceed方法会将用message bodyreader,该方法会反序列化实体,并返回结果。如果需要,每次拦截器都能改变实体,但在大多数的情况下,拦截器仅返回从proceed方法中返回的实体。

上文已经提交,拦截器用来操作实体。与WriterInterceptorContext暴漏的方法类似,ReaderInterceptorContext也引入一些用来修改请求或响应属性的方法。例如,HTTP 头,URIs,以及HTTP 方法。

 与过滤器类似, 拦截器也可以通过命名注解的方式进行绑定, 或者实现DynamicFeature的方式进行动态绑定.

10.5 命名绑定

过滤器与拦截器可以指定名称。绑定后拦截器或过滤器仅仅对特殊的特定的资源方法执行。如果没有进行命名保定,那么过滤器或拦截器被称为全局过滤器或拦截器。

可以使用@NameBinding注解,将过滤器或拦截器可以被分配给一个资源方法。这个注解以元注解的形式使用,可以注解其它注解。例子如下:

[java]  view plain  copy
  1. …  
  2. import java.lang.annotation.Retention;  
  3. import java.lang.annotation.RetentionPolicy;  
  4. import java.util.zip.GZIPInputStream;  
  5.    
  6. import javax.ws.rs.GET;  
  7. import javax.ws.rs.NameBinding;  
  8. import javax.ws.rs.Path;  
  9. import javax.ws.rs.Produces;  
  10. …  
  11.    
  12.    
  13. // 声明注解 
  14. //@Compress annotation is the name binding annotation  
  15. @NameBinding  
  16. @Retention(RetentionPolicy.RUNTIME)  
  17. public @interface Compress {}  
  18.    
  19.    
  20. //将注解和业务方法绑定
  21. @Path(“helloworld”)  
  22. public class HelloWorldResource {  
  23.    
  24.     @GET  
  25.     @Produces(“text/plain”)  
  26.     public String getHello() {  
  27.         return “Hello World!”;  
  28.     }  
  29.    
  30.     @GET  
  31.     @Path(“too-much-data”)  
  32.     @Compress  
  33.     public String getVeryLongString() {  
  34.         String str = … // very long string  
  35.         return str;  
  36.     }  
  37. }  
  38.    
  1. // 将注解和拦截器实现类绑定
  2. // interceptor will be executed only when resource methods  
  3. // annotated with @Compress annotation will be executed  
  4. @Compress  
  5. public class GZIPWriterInterceptor implements WriterInterceptor {  
  6.     @Override  
  7.     public void aroundWriteTo(WriterInterceptorContext context)  
  8.                     throws IOException, WebApplicationException {  
  9.         final OutputStream outputStream = context.getOutputStream();  
  10.         context.setOutputStream(new GZIPOutputStream(outputStream));  
  11.         context.proceed();  
  12.     }  
  13. }  

上述代码定义了注解@Compress,并用在方法getVeryLongString()上,以及GZIPWriterInterceptor拦截器上。拦截器仅在被注解的方法执行时才执行。应用程序中可以有多个命名绑定注解。当provider(拦截器与过滤器)通过命名注解注解后,它仅仅在标注有对应注解的方法被执行后才执行。

10.6 动态绑定

动态绑定是一种以动态的方法将拦截器与过滤器与处理方法关联的方式。10.5中的绑定使用了静态方法,当改变绑定时,需要重新编译代码。通过动态绑定,你可以在程序初始化时实现绑定的代码。

[java]  view plain  copy
  1. …  
  2. import javax.ws.rs.core.FeatureContext;  
  3. import javax.ws.rs.container.DynamicFeature;  
  4. …  
  5.    
  6. @Path(“helloworld”)  
  7. public class HelloWorldResource {  
  8.    
  9.     @GET  
  10.     @Produces(“text/plain”)  
  11.     public String getHello() {  
  12.         return “Hello World!”;  
  13.     }  
  14.    
  15.     @GET  
  16.     @Path(“too-much-data”)  
  17.     public String getVeryLongString() {  
  18.         String str = … // very long string  
  19.         return str;  
  20.     }  
  21. }  
  22.    
  23. // This dynamic binding provider registers GZIPWriterInterceptor  
  24. // only for HelloWorldResource and methods that contain  
  25. // “VeryLongString” in their name. It will be executed during  
  26. // application initialization phase.  
  27. public class CompressionDynamicBinding implements DynamicFeature {  
  28.    
  29.     @Override  
  30.     public void configure(ResourceInfo resourceInfo, FeatureContext context) {  
  31.         if (HelloWorldResource.class.equals(resourceInfo.getResourceClass())  
  32.                 && resourceInfo.getResourceMethod()  
  33.                     .getName().contains(“VeryLongString”)) {  
  34.             context.register(GZIPWriterInterceptor.class);  
  35.         }  
  36.     }  
  37. }  

10.4 过滤器拦截器的执行顺序

接下来看一下过滤器与拦截器的执行上下文。下面的步骤描述了JAX-RS客户端发出POS请求到服务器端。服务器接收到实体,并返回响应。GZIP reader与writer拦截器注册在客户端与服务器端。过滤器也注册在服务器端与客户端,并用来修改请求、响应头。

1.      客户端请求调用: 在客户端发起POST请求。

2.      ClientRequestFilters: 客户端请求过滤器执行,修改请求头。

3.      Client WriterInterceptor:在客户端注册的writer interceptor在MessageBodyWriter执行前执行。它通过GZipOutputStream对实体输出流进行封装。

4.      Client MessageBody writer: 客户端的message body writer被执行,它将实体写入写的GZipOutput流中。这个流将数据压缩,并将数据发送到”wire”中。

5.      Server:服务器收到请求。收到的数据实体是压缩过的数据,如果直接读,则读取的是压缩的数据。

6.      Server pre-matching ContainerRequestFilters: ContainerRequestFilters执行,并可以修改请求,来匹配不同的处理方法。

7.      Server: matching: 资源方法匹配完成。(根据请求找到请求处理方法)

8.      Server: post-matching ContainerRequestFilters:ContainerRequestFilters post matching filters被执行。这包括所有全局filters(没有命名绑定)的执行及绑定名称的filters的执行。

9.      Server ReaderInterceptor:reader 拦截器在服务器端执行,GZIPReaderInterceptor对输入流进行包装,转换为GZipInputStream并将其添加到上下文中。

10.  Server MessageBodyReader: server message body reader 被执行,并通过GZipInputStream对实体数据解压缩。这表示,reader将读取解压缩后的数据。

11.  服务器端资源方法被执行:反序列化的实习对象以参数的形式传递给资源方法。方法以response实体的形式返回这个实体。

12.  Server ContainerResponseFilters被执行,响应过滤器在服务器端被执行,并将response headers修改。

13.  Server WriterInterceptor 在服务器端被执行,并通过GZIPOuptutStream对原始输出流进行包装。

14.  Server MessageBodyWriter:message body writer在服务器端被执行,并将实体序列化,写入GZIPOutputStream中。GZIPOutputStream会将数据压缩,随后压缩的数据被发送到客户端。

15.  客户端接收到响应:响应包含压缩的实体数据。

16.  Client ClientResponseFilters:客户端相应过滤器被执行,并且修改响应头。

17.  客户端响应返回。the javax.ws.rs.core.Response从请求调用中返回。

18.  客户端代码调用response.readEntity():从响应中提取出实体部分。

19.  Client ReaderInterceptor:客户端reader 拦截器在readEntity被调用时执行。拦截器通过GZIPInputStream对实体输入流封装。

20.  Client MessageBodyReaders:客户端消息体reader被调用,并从GZIPInputStream中读取解压缩后的数据,并将这些数据反序列化。

21.  客户端:方法readEntity()返回实体。

需要注意的是在上述的场景中,reader与writer拦截器仅仅当实体存在时才会被调用。(当没有实体流要被写时,封装实体流没有意义)。

message body reader有同样的行为。拦截器在message body reader/writer之前执行,在实体被读或写之前拦截器可以对实体进行包装。

当拦截器并没有在messagebodyreader/writers执行前执行,将会出现异常。当对使用内部缓冲区的客户端response的实体读取多次,数据仅仅会被拦截一次,然后解码后的数据被存储于缓冲区中。

10.7 优先级

如果有多个拦截器或过滤器,那么可以通过优先级指定执行的顺序。通过注解@Priority可以定义优先级。注解接受一个整数作为优先级。过滤器与拦截器执行顺序是按优先级(升序)顺序执行。因此@Priority(1000)或在@Priority(2000)之前执行。而优先级用在响应处理时候,顺序正好相反。@Priority(2000)将在@Priority(1000)之前执行。

[java]  view plain  copy
  1. …  
  2. import javax.annotation.Priority;  
  3. import javax.ws.rs.Priorities;  
  4. …  
  5.    
  6. @Priority(2000)  
  7. public class ResponseFilter implements ContainerResponseFilter {  
  8.    
  9.     @Override  
  10.     public void filter(ContainerRequestContext requestContext,  
  11.                     ContainerResponseContext responseContext)  
  12.                     throws IOException {  
  13.    
  14.         responseContext.getHeaders().add(“X-Powered-By”“Jersey :-)”);  
  15.     }  
  16. }  

JDK内置工具使用-jstack命令(Java Stack Trace)

1、介绍

jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项”-J-d64″,Windows的jstack使用方式只支持以下的这种方式:

jstack [-l] pid

如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。

2、命令格式
jstack [ option ] pid
jstack [ option ] executable core
jstack [ option ] [server-id@]remote-hostname-or-IP

3、常用参数说明

1)、options: 

executable Java executable from which the core dump was produced.

(可能是产生core dump的java可执行程序)

core 将被打印信息的core dump文件

remote-hostname-or-IP 远程debug服务的主机名或ip

server-id 唯一id,假如一台主机上多个远程debug服务 

2)、基本参数:

-F当’jstack [-l] pid’没有相应的时候强制打印栈信息

-l长列表. 打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表.

-m打印java和native c/c++框架的所有栈信息.

-h | -help打印帮助信息

pid 需要被打印配置信息的java进程id,可以用jps查询.

4、使用示例

转自:csdn-jstack命令

JDK内置工具使用-jmap命令(Java Memory Map)

1、介绍

打印出某个java进程(使用pid)内存内的,所有‘对象’的情况(如:产生那些对象,及其数量)。

可以输出所有内存中对象的工具,甚至可以将VM 中的heap,以二进制输出成文本。使用方法 jmap -histo pid。如果连用SHELL jmap -histo pid>a.log可以将其保存到文本中去,在一段时间后,使用文本对比工具,可以对比出GC回收了哪些对象。jmap -dump:format=b,file=outfile 3024可以将3024进程的内存heap输出出来到outfile文件里,再配合MAT(内存分析工具(Memory Analysis Tool),使用参见:http://blog.csdn.net/fenglibing/archive/2011/04/02/6298326.aspx)或与jhat (Java Heap Analysis Tool)一起使用,能够以图像的形式直观的展示当前内存是否有问题。

64位机上使用需要使用如下方式:

jmap -J-d64 -heap pid

2、命令格式

SYNOPSIS

       jmap [ option ] pid

       jmap [ option ] executable core

       jmap [ option ] [server-id@]remote-hostname-or-IP

3、参数说明

1)、options: 

executable Java executable from which the core dump was produced.

(可能是产生core dump的java可执行程序)

core 将被打印信息的core dump文件

remote-hostname-or-IP 远程debug服务的主机名或ip

server-id 唯一id,假如一台主机上多个远程debug服务 

2)、基本参数:

-dump:[live,]format=b,file= 使用hprof二进制形式,输出jvm的heap内容到文件=. live子选项是可选的,假如指定live选项,那么只输出活的对象到文件. 

-finalizerinfo 打印正等候回收的对象的信息.

-heap 打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况.

-histo[:live] 打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量. 

-permstat 打印classload和jvm heap长久层的信息. 包含每个classloader的名字,活泼性,地址,父classloader和加载的class数量. 另外,内部String的数量和占用内存数也会打印出来. 

-F 强迫.在pid没有相应的时候使用-dump或者-histo参数. 在这个模式下,live子参数无效. 

-h | -help 打印辅助信息 

-J 传递参数给jmap启动的jvm. 

pid 需要被打印配相信息的java进程id,创业与打工的区别 – 博文预览,可以用jps查问.

4、使用示例

1)、[fenglb@ccbu-156-5 ~]$ jmap -histo 4939

[输出较多这里不贴了]

2)、[fenglb@ccbu-156-5 ~]$ jmap -dump:format=b,file=test.bin 4939

Dumping heap to /home/fenglb/test.bin …

Heap dump file created

转自:CSDN-jmap命令(Java Memory Map)

JVM内置工具-jstat命令(Java Virtual Machine Statistics Monitoring Tool)

1、介绍

Jstat用于监控基于HotSpot的JVM,对其堆的使用情况进行实时的命令行的统计,使用jstat我们可以对指定的JVM做如下监控:

– 类的加载及卸载情况

– 查看新生代、老生代及持久代的容量及使用情况

– 查看新生代、老生代及持久代的垃圾收集情况,包括垃圾回收的次数及垃圾回收所占用的时间

– 查看新生代中Eden区及Survior区中容量及分配情况等

jstat工具特别强大,它有众多的可选项,通过提供多种不同的监控维度,使我们可以从不同的维度来了解到当前JVM堆的使用情况。详细查看堆内各个部分的使用量,使用的时候必须加上待统计的Java进程号,可选的不同维度参数以及可选的统计频率参数。

它主要是用来显示GC及PermGen相关的信息,如果对GC不怎么了解,先看这篇文章:http://blog.csdn.net/fenglibing/archive/2011/04/13/6321453.aspx,否则其中即使你会使用jstat这个命令,你也看不懂它的输出。

 2、语法

     jstat [ generalOption | outputOptions vmid [interval[s|ms] [count]] ]

    generalOption – 单个的常用的命令行选项,如-help, -options, 或 -version。

    outputOptions –一个或多个输出选项,由单个的statOption选项组成,可以和-t, -h, and -J等选项配合使用。

    statOption

    根据jstat统计的维度不同,可以使用如下表中的选项进行不同维度的统计,不同的操作系统支持的选项可能会不一样,可以通过-options选项,查看不同操作系统所支持选项,如:

OptionDisplays…
class用于查看类加载情况的统计
compiler用于查看HotSpot中即时编译器编译情况的统计
gc用于查看JVM中堆的垃圾收集情况的统计
gccapacity用于查看新生代、老生代及持久代的存储容量情况
gccause用于查看垃圾收集的统计情况(这个和-gcutil选项一样),如果有发生垃圾收集,它还会显示最后一次及当前正在发生垃圾收集的原因。
gcnew用于查看新生代垃圾收集的情况
gcnewcapacity用于查看新生代的存储容量情况
gcold用于查看老生代及持久代发生GC的情况
gcoldcapacity用于查看老生代的容量
gcpermcapacity用于查看持久代的容量
gcutil用于查看新生代、老生代及持代垃圾收集的情况
printcompilationHotSpot编译方法的统计

    -h n

        用于指定每隔几行就输出列头,如果不指定,默认是只在第一行出现列头。

    -JjavaOption

        用于将给定的javaOption传给java应用程序加载器,例如,“-J-Xms48m”将把启动内存设置为48M。如果想查看可以传递哪些选项到应用程序加载器中,可以相看如下的文档:

        Linux and Solaris:http://docs.oracle.com/javase/1.5.0/docs/tooldocs/solaris/java.html

        Windows: http://docs.oracle.com/javase/1.5.0/docs/tooldocs/windows/java.html

    -t n

        用于在输出内容的第一列显示时间戳,这个时间戳代表的时JVM开始启动到现在的时间(注:在IBM JDK5中是没有这个选项的)。

    vmid  – VM的进程号,即当前运行的java进程号。

    还有两个关于显示频率的选项:

    interval–间隔时间,单位可以是秒或者毫秒,通过指定s或ms确定,默认单位为毫秒。

    count-打印次数,如果缺省则打印无数次。

3、不同的统计维度(statOption)及输出说明

    -class

列名说明
Loaded加载了的类的数量
Bytes加载了的类的大小,单为Kb
Unloaded卸载了的类的数量
Bytes卸载了的类的大小,单为Kb
Time花在类的加载及卸载的时间

     -compiler

列名说明
Compiled编译任务执行的次数
Failed编译任务执行失败的次数
Invalid编译任务非法执行的次数
Time执行编译花费的时间
FailedType最后一次编译失败的编译类型
FailedMethod最后一次编译失败的类名及方法名

     -gc

列名说明
S0C新生代中Survivor space中S0当前容量的大小(KB)
S1C新生代中Survivor space中S1当前容量的大小(KB)
S0U新生代中Survivor space中S0容量使用的大小(KB)
S1U新生代中Survivor space中S1容量使用的大小(KB)
ECEden space当前容量的大小(KB)
EUEden space容量使用的大小(KB)
OCOld space当前容量的大小(KB)
OUOld space使用容量的大小(KB)
PCPermanent space当前容量的大小(KB)
PUPermanent space使用容量的大小(KB)
YGC从应用程序启动到采样时发生 Young GC 的次数
YGCT从应用程序启动到采样时 Young GC 所用的时间(秒)
FGC从应用程序启动到采样时发生 Full GC 的次数
FGCT从应用程序启动到采样时 Full GC 所用的时间(秒)
GCTT从应用程序启动到采样时用于垃圾回收的总时间(单位秒),它的值等于YGC+FGC

    -gccapacity

列名说明
NGCMN新生代的最小容量大小(KB)
NGCMX新生代的最大容量大小(KB)
NGC当前新生代的容量大小(KB)
S0C当前新生代中survivor space 0的容量大小(KB)
S1C当前新生代中survivor space 1的容量大小(KB)
ECEden space当前容量的大小(KB)
OGCMN老生代的最小容量大小(KB)
OGCMX老生代的最大容量大小(KB)
OGC当前老生代的容量大小(KB)
OC当前老生代的空间容量大小(KB)
PGCMN持久代的最小容量大小(KB)
PGCMX持久代的最大容量大小(KB)
PGC当前持久代的容量大小(KB)
PC当前持久代的空间容量大小(KB)
YGC从应用程序启动到采样时发生 Young GC 的次数
FGC从应用程序启动到采样时发生 Full GC 的次数

    -gccause

     这个选项用于查看垃圾收集的统计情况(这个和-gcutil选项一样),如果有发生垃圾收集,它还会显示最后一次及当前正在发生垃圾收集的原因,它比-gcutil会多出最后一次垃圾收集原因以及当前正在发生的垃圾收集的原因。

列名说明
LGCC最后一次垃圾收集的原因,可能为“unknown GCCause”、“System.gc()”等
GCC当前垃圾收集的原因

    -gcnew

列名说明
S0C当前新生代中survivor space 0的容量大小(KB)
S1C当前新生代中survivor space 1的容量大小(KB)
S0US0已经使用的大小(KB)
S1US1已经使用的大小(KB)
TTTenuring threshold,要了解这个参数,我们需要了解一点Java内存对象的结构,在Sun JVM中,(除了数组之外的)对象都有两个机器字(words)的头部。第一个字中包含这个对象的标示哈希码以及其他一些类似锁状态和等标识信息,第二个字中包含一个指向对象的类的引用,其中第二个字节就会被垃圾收集算法使用到。
在新生代中做垃圾收集的时候,每次复制一个对象后,将增加这个对象的收集计数,当一个对象在新生代中被复制了一定次数后,该算法即判定该对象是长周期的对象,把他移动到老生代,这个阈值叫着tenuring threshold。这个阈值用于表示某个/些在执行批定次数youngGC后还活着的对象,即使此时新生的的Survior没有满,也同样被认为是长周期对象,将会被移到老生代中。
MTTMaximum tenuring threshold,用于表示TT的最大值。
DSSDesired survivor size (KB).可以参与这里:http://blog.csdn.net/yangjun2/article/details/6542357
ECEden space当前容量的大小(KB)
EUEden space已经使用的大小(KB)
YGC从应用程序启动到采样时发生 Young GC 的次数
YGCT从应用程序启动到采样时 Young GC 所用的时间(单位秒)

    -gcnewcapacity

列名说明
NGCMN          新生代的最小容量大小(KB)
NGCMX    新生代的最大容量大小(KB)
NGC    当前新生代的容量大小(KB)
S0CMX新生代中SO的最大容量大小(KB)
S0C当前新生代中SO的容量大小(KB)
S1CMX新生代中S1的最大容量大小(KB)
S1C当前新生代中S1的容量大小(KB)
ECMX新生代中Eden的最大容量大小(KB)
EC当前新生代中Eden的容量大小(KB)
YGC从应用程序启动到采样时发生 Young GC 的次数
FGC从应用程序启动到采样时发生 Full GC 的次数

    -gcold

列名说明
PC当前持久代容量的大小(KB)
PU持久代使用容量的大小(KB)
OC当前老年代容量的大小(KB)
OU老年代使用容量的大小(KB)
YGC从应用程序启动到采样时发生 Young GC 的次数
FGC从应用程序启动到采样时发生 Full GC 的次数
FGCT从应用程序启动到采样时 Full GC 所用的时间(单位秒)
GCT从应用程序启动到采样时用于垃圾回收的总时间(单位秒),它的值等于YGC+FGC

    -gcoldcapacity

列名说明
OGCMN老生代的最小容量大小(KB)
OGCMX老生代的最大容量大小(KB)
OGC当前老生代的容量大小KB)
OC当前新生代的空间容量大小KB)
YGC从应用程序启动到采样时发生 Young GC 的次数
FGC从应用程序启动到采样时发生 Full GC 的次数
FGCT从应用程序启动到采样时 Full GC 所用的时间(单位秒)
GCT从应用程序启动到采样时用于垃圾回收的总时间(单位秒),它的值等于YGC+FGC

    -gcpermcapacity

    从应用程序启动到采样时发生 Full GC 的次数

列名说明
PGCMN持久代的最小容量大小(KB)
PGCMX持久代的最大容量大小(KB)
PGC当前持久代的容量大小KB)
PC当前持久代的空间容量大小KB)
YGC从应用程序启动到采样时发生 Young GC 的次数
FGC
FGCT从应用程序启动到采样时 Full GC 所用的时间(单位秒)
GCT从应用程序启动到采样时用于垃圾回收的总时间(单位秒),它的值等于YGC+FGC

    -gcutil

列名说明
S0Heap上的 Survivor space 0 区已使用空间的百分比
S1Heap上的 Survivor space 1 区已使用空间的百分比
EHeap上的 Eden space 区已使用空间的百分比
OHeap上的 Old space 区已使用空间的百分比
PPerm space 区已使用空间的百分比
YGC从应用程序启动到采样时发生 Young GC 的次数
YGCT从应用程序启动到采样时 Young GC 所用的时间(单位秒)
FGC从应用程序启动到采样时发生 Full GC 的次数
FGCT从应用程序启动到采样时 Full GC 所用的时间(单位秒)
GCT从应用程序启动到采样时用于垃圾回收的总时间(单位秒),它的值等于YGC+FGC

    -printcompilation

列名说明
Compiled编译任务执行的次数
Size方法的字节码所占的字节数
Type编译类型
Method指定确定被编译方法的类名及方法名,类名中使名“/”而不是“.”做为命名分隔符,方法名是被指定的类中的方法,这两个字段的格式是由HotSpot中的“-XX:+PrintComplation”选项确定的。

4、使用示例

示例1)、

示例2):

图中同时打印了young gc和full gc的总次数、总耗时。而,每次young gc消耗的时间,可以用相间隔的两行YGCT相减得到。每次full gc消耗的时间,可以用相隔的两行FGCT相减得到。例如红框中表示的第一行、第二行之间发生了1次young gc,消耗的时间为0.252-0.252=0.0秒。

常驻内存区(P)的使用率,始终停留在64.21%左右,说明常驻内存没有突变,比较正常。如果young gc和full gc能够正常发生,而且都能有效回收内存,常驻内存区变化不明显,则说明java内存释放情况正常,垃圾回收及时,java内存泄露的几率就会大大降低。但也不能说明一定没有内存泄露。

示例3)、

    以上,介绍了Jstat按百分比查看gc情况的功能。其实,它还有功能,例如加载类信息统计功能、内存池信息统计功能等,那些是以绝对值的形式打印出来的,比较少用,在此就不做介绍。

示例4)、jstat -class pid:显示加载class的数量,及所占空间等信息。

 示例5)、jstat -compiler pid:显示VM实时编译的数量等信息。

示例6)、查看远程服务器上的GC情况。

    这个需要先在远程服务器上面开启jstatd服务,可以选看这里如何开启jstatd服务,http://blog.csdn.net/fenglibing/article/details/17323515

    下面是一个执行示例:

C:\Documents and Settings\Administrator>jstat -gcutil 18272@the_ip:2021/jstatdName 1000

  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00   8.00   0.00  24.36      0    0.000     0    0.000    0.000
  0.00   0.00   8.00   0.00  24.36      0    0.000     0    0.000    0.000
  0.00   0.00   8.00   0.00  24.36      0    0.000     0    0.000    0.000
  0.00   0.00   8.00   0.00  24.36      0    0.000     0    0.000    0.000
  0.00   0.00   8.00   0.00  24.36      0    0.000     0    0.000    0.000
  0.00   0.00   8.00   0.00  24.36      0    0.000     0    0.000    0.000

    示例就不一一例举,有兴趣的可以自己尝试尝试。


Oracle上关于Jstat的使用说明:http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jstat.html

转自: CSDN-jstat命令(Java Virtual Machine Statistics Monitoring Tool)