http中有关缓存相关的几个字段

本文深入探讨了HTTP缓存机制中的Expires、Cache-Control、Last-Modified和ETag等关键字段的作用和配置方法,特别针对Apache2.0服务器进行了详细说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载自:http://blog.youkuaiyun.com/lifeibo/article/details/5979572


Expires、Cache-Control、Last-Modified、 ETag是RFC 2616(HTTP/1.1)协议中和网页缓存相关的几个字段。前两个用来控制缓存的失效日期,后两个用来验证网页的有效性。要注意的是, HTTP/1.0有一个功能比较弱的缓存控制机制:Pragma,使用HTTP/1.0的缓存将忽略Expires和Cache-Control头。我们这里以Apache2.0服务器为例,只讨论HTTP/1.1协议。

Expires


Expires字段声明了一个网页或URL地址不再被浏览器缓存的时间,一旦超过了这个时间,浏览器都应该联系原始服务器。RFC告诉我们:“由于推断的失效时间也许会降低语义透明度,应该被谨慎使用,同时我们鼓励原始服务器尽可能提供确切的失效时间。”

对于一般的纯静态页面,如html、gif、jpg、css、js,默认安装的Apache服务器,不会在响应头添加这个字段。Firefox浏览器接受到相应后,如果发现没有Expires字段,浏览器根据文件的类型和“Last-Modified”字段来推断出一个合适的失效时间,并存储在客户端。推测出的时间一般是接受到响应时间后的三天左右。

Apache的expires_module模块可以在Http响应头部自动加上Expires字段。在Apache的httpd.conf文件中进行如下配置:

#启用expires_module模块
LoadModule expires_module modules/mod_expires.so
# 启用有效期控制
ExpiresActive On
# GIF有效期为1个月
ExpiresByType image/gif A2592000
# HTML文档的有效期是最后修改时刻后的一星期
ExpiresByType text/html M604800
#以下的含义类似
ExpiresByType text/css “now plus 2 month”
ExpiresByType text/js “now plus 2 day”
ExpiresByType image/jpeg “access plus 2 month”
ExpiresByType image/bmp “access plus 2 month”
ExpiresByType image/x-icon “access plus 2 month”
ExpiresByType image/png “access plus 2 month”

对于动态页面,如果在页面内部没有通过函数强制加上Expires,例如header(”Expires: ” . gmdate(”D, d M Y H:i:s”) . ” GMT”),Apache服务器会把Wed, 11 Jan 1984 05:00:00 GMT作为Expires字段内容,返回给浏览器。即认为动态页面总是失效的。而浏览器仍然会保存已经失效的动态页面。

可以发现Firefox浏览器总是缓存所有页面,不管失效、不失效还是没有声明失效时间。即使缓存中声明了一个网页的实效日期是1970-01 - 01 08:00:00,浏览器仍然会发送该文件在缓存中的Last-Modified和ETag字段。如果在服务器端验证通过,返回304状态,浏览器就还会使用此缓存。

Cache-Control


Cache-Control字段中可以声明多些元素,例如no-cache, must-revalidate, max-age=0等。这些元素用来指明页面被缓存最大时限,如何被缓存的,如何被转换到另一个不同的媒介,以及如何被存放在持久媒介中的。但是任何一个 Cache-Control指令都不能保证隐私性或者数据的安全性。“private”和“no-store”指令可以为隐私性和安全性方面提供一些帮助,但是他们并不能用于替代身
份验证和加密。

Apache的mod_cern_meta模块允许文件级Http响应头部的控制,同时它也可以配置Cache-Control头(或任何其他头)。响应头文件是放在原始目录的子目录中,根据原始文件名所命名的一个文件。具体用法请参阅Apache的官方网站。其中Cache-Control : max-age表示失效日期。如果没有启动mod_cern_meta模块,Apache服务器会把Expires字段中的日期换算成以秒为单位的一个 delta值,赋值给max-age。如果启动mod_cern_meta模块,并且配置了max-age值,Apache会将这个覆盖Expires字段。同时,max-age隐含了Canche-Control: public。这样浏览器接受到的Cache-Control : max-age和Expires值就是一致的。

如果失效日期Cache-Control : max-ag=0或者是负值,浏览器会在对应的缓存中把Expires设置为1970-01-01 08:00:00。

Last-Modified

Last-Modified和ETag是条件请求(Conditional Request)相关的两个字段。如果一个缓存收到了针对一个页面的请求,它发送一个验证请求询问服务器页面是否已经更改,在HTTP头里面带上” ETag”和”If Modify Since”头。服务器根据这些信息判断是否有更新信息,如果没有,就返回HTTP 304(NotModify);如果有更新,返回HTTP 200和更新的页面内容,并且携带新的”ETag”和”LastModified”。

使用这个机制,能够避免重复发送文件给浏览器,不过仍然会产生一个HTTP请求。

一般纯静态页面本身都会有Last-Modified信息,Apache服务器会读取页面文件中的Last-Modified信息,并添加到http响应头部。

对于动态页面,如果在页面内部没有通过函数强制加上Last-Modified,例如header(”Last-Modified: ” . gmdate(”D, d M Y H:i:s”) . ” GMT”),Apache服务器会把当前时间作为Last-Modified,返回给浏览器。

无论是纯静态页面还是动态页面,Firefox浏览器巧妙地按照接受到服务器响应的时间设置缓存页面的Last-Modified,而不是按照http响应头部中的Last-Modified字段。

ETag

既然有了Last-Modified,为什么还要用ETag字段呢?因为如果在一秒钟之内对一个文件进行两次更改,Last-Modified就会不正确。因此,HTTP/1.1利用Entity Tag头提供了更加严格的验证。

Apache服务器默认情况下,会对所有的静态、动态文件的响应头添加ETag字段。在Apache的httpd.conf文件中可以通过FileETag指令配置该选项。

FileETag指令配置了当文档是基于一个文件时用以创建 Etag(entity tag)响应头的文件的属性。在Apache 1.3.22及以前,ETag的值是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。如果一个目录的配置包含了‘FileETag INode MTime Size’而其一个子目录包含了‘FileETag -INode’那么这个子目录的设置(并会被其下任何没有进行覆盖的子目录继承)将等价于‘FileETag MTime Size’。

在多台负载平衡的服务器环境下,同一个文件会有不同的etag或者文件修改日期,浏览器每次都会重新下载。设置‘FileETag None’可以使响应头不再包含ETag字段。

 

APACHE服务的内容过期设置

Apache配置摘录及解释
i. 过期相关设置
LoadModule headers_module modules/mod_headers.so
#Load 修改header的模块。
LoadModule expires_module modules/mod_expires.so
#Load 设定过期header的模块。
Header append Via: CCN-BJ-4-502
#增加一个Via header,值配置成设备的hostname。
KeepAliveTimeout 60
#设置连接的保持时间为60秒。
ExpiresActive On
#启用过期header功能。
ExpiresDefault A604800
#缺省过期时间为“访问后的604800秒”

<Directory /data/download>
Options FollowSymLinks
AllowOverride None
Order allow,deny
Allow from all
ExpiresByType text/html A300
#text/html类型文件的过期设置为“访问后的300秒”
ExpiresByType text/css A259200
#text/css类型文件的过期设置为“访问后的259200秒”
ExpiresByType application/x-javascript A300
# application/x-javascript类型文件的过期设置为“访问后的300秒”
ExpiresByType image/gif A2592000
#image/gif类型文件的过期设置为“访问后的2592000秒”

ExpiresByType application/x-shockwave-flash A2592000

# application/x-shockwave-flash类型文件的过期设置为“访问后的2592000秒”

</Directory>
上述配置文件中load的两个模块:mod_headers.so  和mod_expires.so 可以让Apache具有对header的一些定制功能。
ExpiresByType:     表示按照文件类型-MIME-TYPE设定过期策略;
A300: 表示在Access后300秒后过期;
ExpiresByType text/css A2592000:    表示Mime type是text/css的文件,在Access后2592000秒过期。
ExpiresDefault A604800:  表示除了单独制定的文件类型等过期策略外的其他内容,按照这个缺省的策略设定:访问后604800秒过期。
上面的方法可以实现根据web发布的不同文件类型,针对不同的发布目录进行过期策略设置。在按照如上方法设置后,Apache会自动的产生两个相关的http header,举例如下:
HTTP/1.1 200 OK
Date: Tue, 27 Mar 2007 17:44:21 GMT
Server: Apache/2.0.54 (Unix)
Last-Modified: Thu, 25 Jan 2007 07:45:45 GMT
ETag: “72df3a-93-99499c40”
Accept-Ranges: bytes
Content-Length: 147
Cache-Control: max-age=2592000
Expires: Thu, 26 Apr 2007 17:44:21 GMT
Via: CCN-BJ-4-575
Keep-Alive: timeout=60, max=100
Connection: Keep-Alive
Content-Type: image/gif

Length: 147 [image/gif]
其中:Date + Max-age = Expires.  Max-age是个时间长度,对应web server上面设置的过期时间;Expires是根据max-age算出来的过期时间点,两者是一致的,不同cache在判断内容是否过期时会严格比较系统时间和上述过期时间,或者比较age(在cache中存住的时间长度)和max-age的值。


<think>首先,用户的问题是:在Java中判断对象中非null字的数量。他们想知道如何统计一个Java对象中不为null的字个数。 我需要参考系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式使用$$...$$格式并单独成。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的落末尾自然地添加引用标识,如[^1]。 用户提供了几个引用,这些是关于判断对象是否所有字为null或空字符串的代码。这些引用不是直接关于统计非null字数量,但相关,我可以借鉴。 关键引用: - 引用[1]:忽略序列化字名,判断是否有属性不为null。 - 引用[2]:判断所有字是否都为null或空字符串。 - 引用[3]:排除serialVersionUID,判断所有字是否全为空。 - 引用[4]:讨论Java对象空间占用,但内容不完整。 用户的问题不是判断是否所有字为null,而是统计非null字的数量。所以,我需要修改这些代码来计数。 步骤: 1. 获取对象的类。 2. 获取所有声明的字。 3. 遍历每个字。 4. 设置字可访问。 5. 检查字值是否为null。 6. 如果不为null,则计数器加一。 7. 排除某些字,如serialVersionUID(如果需要)。 8. 返回计数。 在代码中,我需要处理字可访问性,并捕获异常。 用户可能想排除静态字或 transient 字,但问题没指定,所以我会考虑默认排除 serialVersionUID,就像引用中那样。 现在,构建回答: - 结构清晰:先解释方法,然后提供代码示例。 - 使用中文。 - 在末尾生成相关问题。 - 添加引用标识,如果适用。 代码示例: - 使用Java反射。 - 定义一个方法,如 `countNonNullFields(Object obj)`。 可能的代码: ```java import java.lang.reflect.Field; import java.util.Objects; public class ObjectUtils { public static int countNonNullFields(Object obj) { if (obj == null) { return 0; // 或者抛出异常,取决于需求 } int count = 0; try { Field[] fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { if ("serialVersionUID".equals(field.getName())) { continue; // 排除 serialVersionUID } field.setAccessible(true); Object value = field.get(obj); if (value != null) { count++; } } } catch (IllegalAccessException e) { e.printStackTrace(); // 或者处理异常 } return count; } } ``` 注意事项: - 这个方法只检查字是否为null,不包括空字符串或其他条件。用户只提到非null字。 - 如果字是原始类型(如int, double),它们不能为null,所以如果对象有原始类型字,它们总是非null。用户的问题是关于null,所以对于原始类型,它们永远不会是null,因此会计数。 - 在引用中,有检查空字符串的,但用户没要求,所以只检查null。 在引用[2]和[3]中,有检查空字符串或使用StringUtils,但用户问题明确是“不为 null”,所以只关注null。 现在,回答结构: 1. 介绍问题和方法。 2. 提供代码示例。 3. 解释代码。 4. 注意事项。 5. 生成相关问题。 最后,添加引用标识,如果我从引用中借鉴了想法。 生成相关问题: - 基于用户问题,如:如何排除特定字?如何处理继承的字?如何优化性能?等等。 完整回答:</think>在Java中,判断对象中非null字的数量可以通过反射机制实现。核心思路是:获取对象的所有字,遍历每个字,检查其值是否为null,并统计非null字的数量。通常需要排除一些特殊字(如`serialVersionUID`),因为它们是自动生成的序列化ID,不反映业务逻辑状态。下面我将逐步解释并提供一个完整代码示例。 ### 步骤说明 1. **获取对象类信息**:使用`getClass()`获取对象的`Class`实例。 2. **获取所有字**:通过`getDeclaredFields()`获取所有声明的字(包括私有字)。 3. **遍历字并检查**: - 设置字可访问(`setAccessible(true)`),以便读取私有字。 - 跳过需排除的字(如`serialVersionUID`)。 - 检查字值是否为null,若非null则计数器加一。 4. **处理异常**:反射操作可能抛出`IllegalAccessException`,需捕获并处理。 5. **返回计数**:最终返回非null字的数量。 ### 代码示例 ```java import java.lang.reflect.Field; import java.util.Objects; public class ObjectFieldUtils { /** * 统计对象中非null字的数量 * @param obj 目标对象 * @return 非null字的数量(排除serialVersionUID等特殊字) */ public static int countNonNullFields(Object obj) { if (obj == null) { return 0; // 对象为null时直接返回0 } int count = 0; try { Field[] fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { // 跳过序列化字(如serialVersionUID) if ("serialVersionUID".equals(field.getName())) { continue; } field.setAccessible(true); // 允许访问私有字 Object fieldValue = field.get(obj); if (fieldValue != null) { count++; // 字非null时计数器增加 } } } catch (IllegalAccessException e) { e.printStackTrace(); // 实际项目中建议使用日志记录 } return count; } } ``` ### 使用示例 ```java public class User { private Long id; // 可能为null private String name; // 可能为null private int age; // 原始类型,不能为null private static final long serialVersionUID = 1L; // 被排除 // 构造方法和Getter/Setter省略 } public class Main { public static void main(String[] args) { User user = new User(); user.setId(100L); user.setName(null); // name字为null // age字未设置,默认为0(非null) int nonNullCount = ObjectFieldUtils.countNonNullFields(user); System.out.println("非null字数量: " + nonNullCount); // 输出: 非null字数量: 2 (id和age) } } ``` ### 注意事项 1. **原始类型字**:如`int`、`double`等原始类型字不能为null(默认值为0或false),因此它们总是被计入非null字。这与引用类型(如`String`、`Long`)不同。 2. **排除字逻辑**:示例中排除了`serialVersionUID`,但你可以扩展`if`条件来排除其他字(如添加`|| "otherField".equals(field.getName())`)。 3. **性能考虑**:反射操作较慢,频繁调用可能影响性能。在高并发场景下,建议缓存`Class`的字信息。 4. **继承字**:`getDeclaredFields()`只获取当前类声明的字,不包括父类字。若需包含父类字,使用循环遍历`getSuperclass()`。 5. **空字符串处理**:本方法只检查null,不包括空字符串(如`""`)。如需检查空字符串,可添加条件如`if (fieldValue != null && !fieldValue.toString().trim().isEmpty())`。 ### 相关问题 1. 如何扩展此方法以同时排除空字符串字? 2. 如何处理父类继承的字? 3. 如何优化反射性能以提高统计效率? 4. 在哪些场景下统计非null字数量是必要的? [^1]: 引用[1]:【JAVA】判断对象是否为null,包括对象中的属性为null,可自定义判断的属性或值。解决:在判断条件中忽略序列化字名 //只要有一个属性值不为null 就返回false 表示对象不为null 忽略序列化 if (fieldValue != null && !"serialVersionUID".equals(fieldName)) { flag = false; break; } [^2]: 引用[2]:判断对象中的所有字属性是否都为null或空字符串。1 如果对象中没有基本数据类型的话,用这种方式就可以 //判断对象值是否为空 public static boolean isAllFieldsNull(Object obj) { for (Field f : obj.getClass().getDeclaredFields()) { f.setAccessible(true); try { if (f.get(obj) != null && !"".equals(f.get(obj).toString().trim())) { return false; } } catch (IllegalAccessException e) { e.printStackTrace(); } } return true; } [^3]: 引用[3]:Java 判断一个对象全部字属性是否为空,排除serialVersionUID。代码: /** * 判断对象中属性值是否全为空 * @param object * @return */ public static boolean checkObjAllFieldsIsNull(Object object) { if (null == object) { return true; } try { Field[] declaredFields = object.getClass().getDeclaredFields(); for (Field f :declaredFields) { if (f.getName().equals("serialVersionUID")){ continue; } f.setAccessible(true); if (Objects.nonNull(f.get(object)) && StringUtils.hasLength(f.get(object).toString())) { return false; } } } catch (Exception e) { e.printStackTrace(); } return true; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值