412A-A1A1-JavaEE-编码

139 阅读5分钟

javase中的编码

public class TestEncoding {  
	public static void main(String[] args) {  
	System.out.println("你好,我是张三");  
	}  
}

控制台:

"C:\Program Files\Java\jdk-17.0.3.1\bin\java.exe" ...

你好,我是张三


Process finished with exit code 0

以上这段代码从编写到运行中,编码是如何变化的呢?

1.源文件编码

即创建文件时所选用的文件编码,由开发人员指定,通常设置为UTF-8

2.编译器编码
  • 使用哪种字符编码来读取解析源代码文件
  • 在编译java代码时底层使用的是javac命令,此命令存在可选项-encoding,用于指定编码时使用的格式,例如javac -encoding GBK HelloWorld.java
  • 如果在编译过程中未指定编码格式,将采用操作系统默认的编码格式

  • 在日常开发中我们并不会使用命令去编译程序,而是使用IDEA,在idea中你可以这样设置编码器的编码格式:
    • 方式一: 在File | Settings | Editor | File Encodings中设置
    • 412A-A1A1-JavaEE-image-20231115-220448.png
  • 方式二: 如果你使用了Maven,可以添加以下代码
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
3.字节码编码
  • 源代码中的字符串字面量(string literals)将以Modified UTF-8的编码格式存储在.class文件中
  • 字节码中字符串字面量的编码格式是固定的Modified UTF-8
##### 4.JVM内存编码 ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b6f5a780b607450085a15bf91a9ece36~tplv-k3u1fbpfcp-zoom-1.image) - JVM加载`.class`字节码类文件时会把`Modified UTF-8`格式的字符串字面量转换为UTF-16编码 - JVM中的内存编码为`UTF-16`,也是固定不变的
5.输出流编码
  • 输出到控制台的编码:
    • System.out使用的是JVM的默认编码,通常是操作系统的默认编码
    • 可以在JVM启动时通过JVM参数 -Dfile.encoding来指定
  • 输出到浏览器
    • HttpServletResponse:由开发人员指定
6.显示编码
  • 控制台显示的编码:通常是系统的默认编码
  • 浏览器显示的编码: 自动检测
    • Http响应的Content type
    • HTML文档中的 <meta charset>标签

注: 如果出现了乱码

  • 要么是1和2的编码不兼容
  • 要么是5和6的编码不兼容

javaWeb中的编码

以下将从 Chrome发出一个请求 → Tomcat服务器接收 → 运行程序 → 响应数据到浏览器的过程说明

请求url: http://localhost:8080/encode/编码?name=张三


1.浏览器进行uri字符编码

  • 浏览器发出请求(此处只说明get请求与post请求)
  • 请求uri中存在非ASCII的字符(例如中文,此处还有其他情况需要进行编码,暂不讨论)
  • 如果是get请求需要对字符进行百分号编码,Chrome的编码格式为UTF-8
  • 如果是post请求,application/x-www-form-urlencoded(需要对字符编码)multipart/form-data(不需要对字符编码)
  • 于是经过uri对字符的"百分号编码"(只讨论字符编码)后以上链接就转变为

http://localhost:8080/encode/%E7%BC%96%E7%A0%81/?name=%E5%BC%A0%E4%B8%89

  • 传输: 根据http协议以上的url将会转成字节数组进行传输

2.Tomcat进行解码

  • 步骤一:接收到字节数组后,会将字节码数组转成"百分号编码"形式的url
  • 步骤二:将"百分号编码"形式的url解码成原样

说明1:对于步骤二的解码格式说明:

  • 当前版本 < Tomcat8.0 : 采用ISO-8859-1解码
  • 当前版本 >= Tomcat8.0 : 采用UTF-8解码

官方原文:

这指定用于解码URI字节的字符编码, 在%xx解码URL之后。如果未指定,将使用UTF-8,除非 org.apache.catalina.STRICT_SERVLET_COMPLIANCE 系统属性设置为true 在这种情况下,将使用ISO-8859-1。


3.tomcat的JVM(启动Tomcat的jvm的file.encoding) 4.idea输出到控制台(启动Tomcat的jvm的file.encoding)

乱码问题的解决

请求乱码

post

方式一 (在Spring中): 在web.xml中添加

<!--中文乱码响应-->
<!--
    private String encoding;//编码格式
    private boolean forceRequestEncoding;//请求编码,默认false,不开启
    private boolean forceResponseEncoding;//响应编码,默认false,不开启
-->
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

方式二: 添加过滤器注解(在SpringMVC中)

public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    protected Class<?>[] getRootConfigClasses() {
        return new Class[0];
    }

    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMvcConfig.class};
    }

    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("UTF-8");
        return new Filter[]{filter};
    }
}
@Configuration
@ComponentScan("controller包路径例如com.hello.controller")
@EnableWebMvc
public class SpringMvcConfig {
}

get

因为自从Tomcat8.0之后URIEncoding就默认设置为UTF-8,所以这是的get请求乱码的问题基本被解决

如果依旧存在:

正确的做法是在Tomcat中设置URIEncoding 在Tomcat安装包的根路径下找到conf/server.xml 进入文件中修改Connector标签,添加URIEncoding="UTF-8"

注: get请求乱码设置request.setCharacterEncoding()是无效的

响应乱码

本质上是为了设置Content-Type

方式一:(单个方法有效)

response.setContentType("text/html;charset=UTF-8");

方式二:(单个方法有效)

@RequestMapping(value="/路径",produces = "application/json; charset=utf-8")

注:虽然是RequestMapping中的属性,但produces的作用是指定返回值类型和设定返回值的字符编码

jsp:(单个页面有效)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

@ResponseBody注解返回乱码

  • 第一种方法就是以上的方式二

  • 第二种是修改SpringMVC的配置文件,进行全局配置

使用HttpMessageConverter接口的相关实现类 基于spring3.2以后的配置文件。编码配置需要放在mvc:annotation-driven/之前,否则无效。

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" >  
    <property name="messageConverters">  
        <list>  
            <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />  
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">  
                <property name="supportedMediaTypes">  
                    <list>  
                        <value>text/plain;charset=utf-8</value>  
                        <value>text/html;charset=UTF-8</value>  
                    </list>  
                </property>  
            </bean>  
        </list>  
    </property>  
</bean>  

并且需要在Maven依赖中配置上Jackjson的依赖。

<dependency>  
    <groupId>org.codehaus.jackson</groupId>  
    <artifactId>jackson-mapper-asl</artifactId>  
    <version>1.9.13</version>  
</dependency>  
<dependency>  
    <groupId>org.codehaus.jackson</groupId>  
    <artifactId>jackson-core-asl</artifactId>  
    <version>1.9.13</version>  
</dependency>  
  • 方式三: 使用<mvc:message-converters>

还有一种方式是在SpringMVC的配置文件中的mvc:annotation-driven中加入mvc:message-converters的配置。具体配置内容如下:

<!-- SpringMVC注解驱动 -->
<mvc:annotation-driven>  
    <mvc:message-converters>  
        <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>  
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">  
            <property name="supportedMediaTypes">  
                <list>  
                    <value>text/plain;charset=utf-8</value>  
                    <value>text/html;charset=UTF-8</value>  
                </list>  
            </property>  
        </bean>  
    </mvc:message-converters>  
</mvc:annotation-driven> 

注意:始用这种配置的时候,需要去掉RequestMappingHandlerMapping、RequestMappingHandlerAdapter或者DefaultAnnotationHandlerMapping、AnnotationMethodHandlerAdapter的Bean配置,要不然可能会不生效


Citation:

References: