部分属性持久化问题看似很简单,只要把不需要的持久化的属性加上瞬态关键字(transient关键字)即可,没错,这也是一种解决方案,但在有的时候行不通,例如在一个计税系统和人力系统对接的时候,计税系统需要从人力系统获得人员的姓名和基本工资,作为纳税的一句,而人力系统的工资分成 分成两个部分:基本工资和绩效工资,基本工资没有什么秘密,一般都是直接跟年限挂钩,但是绩效工资一般来说是保密的,不能泄露到外系统,话不多说,上代码

  1. import lombok.AllArgsConstructor;
  2. import lombok.Getter;
  3. import lombok.NoArgsConstructor;
  4. import lombok.Setter;
  5.  
  6. import java.io.Serializable;
  7.  
  8. @Getter
  9. @Setter
  10. @NoArgsConstructor
  11. @AllArgsConstructor
  12. public class Salary implements Serializable {
  13. private static final long serialVersionUID = 75632L;
  14. // 基本工资
  15. private Integer basePay;
  16. // 绩效工资
  17. private Integer bonus;
  18. }
  1. import lombok.AllArgsConstructor;
  2. import lombok.Getter;
  3. import lombok.NoArgsConstructor;
  4. import lombok.Setter;
  5.  
  6. import java.io.Serializable;
  7.  
  8. @Getter
  9. @Setter
  10. @NoArgsConstructor
  11. @AllArgsConstructor
  12. public class Person implements Serializable {
  13. private static final long serialVersionUID =45829L;
  14. // 员工姓名
  15. private String name;
  16. // 员工薪资
  17. private Salary salary;
  18.  
  19. }

如上所示,他们都序列化了,都基本了持久化的条件,计税系统请求人力系统,然后人力系统后将人员和工资信息传递到计税系统中,做一个测试,代码如下:

  1. import org.springframework.web.bind.annotation.GetMapping;
  2. import org.springframework.web.bind.annotation.RequestMapping;
  3. import org.springframework.web.bind.annotation.RestController;
  4.  
  5. @RestController
  6. @RequestMapping("/demo")
  7. public class UserController {
  8.  
  9. @GetMapping("/get-user")
  10. public Person getUser() {
  11. // 基本工资 1000 绩效3000
  12. Salary salary = new Salary(1000,3000);
  13. Person person = new Person("小哇",salary);
  14. return person;
  15. }
  16. }

在通过网络传输的计税系统中,进行反序列化,代码如下:

  1. import com.cp.security.user.common.JsonUtil;
  2.  
  3. public class TestController {
  4.  
  5. public static void main(String[] args) {
  6. String personJson = HttpRequest.sendGet("http://localhost:8080/demo/get-user","");
  7. Person person = JsonUtil.str2Obj(personJson);
  8. StringBuffer sb = new StringBuffer();
  9. sb.append("name"+person.getName());
  10. sb.append("基本工资"+person.getSalary().getBasePay());
  11. sb.append("绩效工资"+person.getSalary().getBonus());
  12. System.out.println(sb);
  13. }
  14.  
  15. //运行结果如下:
  16. // name 小哇 基本工资1000 绩效工资3000

很明显这不符合要求,存在严重的信息泄露问题,那么怎么解决呢?提供以下思路:

1.在bonus(绩效工资)前面加上transient关键字进行修饰

  这的确是一个办法,但不是一个好的办法,在分布式部署的时候性能很差,不推荐

2.新增一个业务对象(*)

  新增一个对象,该对象只有姓名和基本工资两个属性,符合开闭原则,对原来系统也没有侵入性,只是增加了工作量,相比较来说,这个是目前最受欢迎的方式了

3.在请求端进行过滤

  在计税系统得到Person对象之后,对立面的绩效薪资进行隐藏,可行但是很危险,业务交由其他系统处理,对外依旧是暴露的,差评

4.引出本文重点,采用Serializable接口中的两个私有方法writeObject和readObject,我们将Person稍作修改,上代码

  1. import lombok.AllArgsConstructor;
  2. import lombok.Getter;
  3. import lombok.NoArgsConstructor;
  4. import lombok.Setter;
  5.  
  6. import java.io.IOException;
  7. import java.io.ObjectInputStream;
  8. import java.io.ObjectOutputStream;
  9. import java.io.Serializable;
  10.  
  11. @Getter
  12. @Setter
  13. @NoArgsConstructor
  14. @AllArgsConstructor
  15. public class Person implements Serializable {
  16. private static final long serialVersionUID =45829L;
  17. // 员工姓名
  18. private String name;
  19. // 员工薪资
  20. private Salary salary;
  21.  
  22. private void writeObject(ObjectOutputStream out)throws IOException {
  23. out.defaultWriteObject();
  24. out.writeInt(salary.getBasePay());
  25. }
  26. private void readObject(ObjectInputStream in)throws IOException,ClassNotFoundException{
  27. in.defaultReadObject();
  28. salary = new Salary(in.readInt(),0);
  29. }
  1. }
  1. // 此时运行的结果
  2. // name 小哇 基本工资1000 绩效工资0

总结:

我们在Person类中增加了writeObject和readObject两个方法,并且访问权限都是私有级别的,为什么能够改变程序的运行结果呢?其实这里使用了序列化的独有机制,序列化毁掉,java中调用ObjectOutputStream类把一个对象转换成流数据时,会通过反射检查被序列化的类是否有writeObject方法,并且检查其是否符合私有、无返回的特性,若符合,则会委托该方法将对象进行序列化,若没有,则由ObjectOutputStream按照默认的规则继续序列化,同样,从流数据恢复成实力对象时,也会检查是否有一个私有的readObject方法,如果有,则会通过该方法读取属性值,此处有几个关键点需要说明:

a> out.defaultWriteObject();

  告知JVM按照默认的规则写入对象,惯例写法是写在第一句话里

b>in.defaultReadObject();

  告知JVM按照默认的规则读入对象,惯例写法是写在第一句话里

c>依然存在分布式部署的问题,只是提供一个思路而已,正常情况下依然推荐使用第二种方式,重新声明一个对象返回

Java使用序列化的私有方法巧妙解决部分属性持久化问题的更多相关文章

  1. Java常见序列化与反序列方法总结

    很多商业项目用到数据库.内存映射文件和普通文件来完成项目中的序列化处理的需求,但是这些方法很少会依靠于Java序列化.本文也不是用来解释序列化的,而是一起来看看面试中有关序列化的问题,这些问题你很有可 ...

  2. java中用反射访问私有方法和私有成员[转]

    转自: http://zhouyangchenrui.iteye.com/blog/470521 java的反射可以绕过访问权限,访问到类的私有方法和成员.可能这点会引起安全性的讨论.反射的使用帮助解 ...

  3. Java反射机制调用私有方法

    1.获取目标类: 每个类都有一个class属性,通过实体类的class属性获取: Class clazz = Person.class 通过对象获取.  Person p1 = new Person( ...

  4. 编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则)

    编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则) 目录 建议1: 不要在常量和变量中出现易混淆的字母 建议2: 莫让常量蜕变成变量 建议3: 三元操作符的类型务 ...

  5. java基础 接口私有方法

    /** * 问题描述: * 我们需要抽取一个共有方法,用来解决两个默认方法之间重复代码的问题 * 但是这个共有方法不应该让实现类使用,应该是私有化的. * * 解决方案: * 从java 9开始,接口 ...

  6. 我要告诉你:java接口中可以定义private私有方法

    在传统的Java编程中,被广为人知的一个知识点是:java Interface接口中不能定义private私有方法.只允许我们定义public访问权限的方法.抽象方法或静态方法.但是从Java 9 开 ...

  7. Python3学习之路~6.5 私有属性和私有方法

    属性分为静态属性和动态属性,静态属性就是变量,动态属性就是方法.但是一般我们说的属性就是变量,方法就是方法.私有属性/方法就是外面访问不了,只有自己能够访问的属性/方法.如何将属性和方法分别变成私有属 ...

  8. js——private 私有方法公有化

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. Python中的私有属性私有方法、类属性类方法以及单例设计模式

    私有属性是对象不希望公开的属性,私有方法是对象不希望公开的方法.在定义私有属性和私有方法时,在属性或者方法前,加上__(两个下划线) 公有方法可以通过对象名直接调用,私有方法不能通过对象名直接调用,只 ...

随机推荐

  1. mongoDB基础使用

    环境交代 操作系统: CentOS 6.8 64位 mongodb: 4.06 安装 官方下载地址:https://www.mongodb.org/dl/linux/x86_64-rhel62 阿里云 ...

  2. JAVA项目中常用的异常处理情况总结

    JAVA项目中常用的异常知识点总结 1. java.lang.nullpointerexception这个异常大家肯定都经常遇到,异常的解释是"程序遇上了空指针",简单地说就是调用 ...

  3. LeetCode(101):对称二叉树

    Easy! 题目描述: 给定一个二叉树,检查它是否是镜像对称的. 例如,二叉树 [1,2,2,3,4,4,3] 是对称的. 1 / \ 2 2 / \ / \ 3 4 4 3 但是下面这个 [1,2, ...

  4. 使用 declare 语句和strict_types 声明来启用严格模式:

    使用 declare 语句和strict_types 声明来启用严格模式: Caution: 启用严格模式同时也会影响返回值类型声明. Note: 严格类型适用于在启用严格模式的文件内的函数调用,而不 ...

  5. Happiness

    1575: Happiness 时间限制: 1 Sec  内存限制: 1280 MB 题目描述 Chicken brother is very happy today, because he atta ...

  6. Nginx详解一:Nginx基础篇之环境准备

    环境确认: 1.确认系统网络可用 2.确认yum源可用 3.确认关闭iptabkes规则 查看是否有iptabkes规则:iptables -L 如果有的话:iptables -F关闭 保险起见也看看 ...

  7. Python序列[1,2,3,4,5]

    序列是用于存放多个值得连续空间,并按一定顺序排列,每一个值(称为元素)都分配一个数,称为索引或位置.通过该索引可以取出相应的值. 索引 序列中的元素都是有序的.拥有自己编号(从0开始),我们可以通过索 ...

  8. 使用sysbench 0.5 对mysql 进行性能、压力测试

    sysbench是一个模块化的.跨平台.多线程基准测试工具,主要用于评估测试各种不同系统参数下的数据库负载情况.目前sysbench代码托管在launchpad上,项目地址:https://launc ...

  9. ubuntu下配置反向代理

    1. 环境 ubuntu:Ubuntu 13.04 x86-64 apache2: 2.2.22-6ubuntu5.1 amd64 2. 配置 2.1  配置应用 增加监听端口 打开/etc/apac ...

  10. Python(列表操作应用实战)方法二

    # 输入一个数据,删除一个列表中的所有指定元素# 给定的列表数据data = [1,2,3,4,5,6,7,8,9,0,5,4,3,5,"b","a",&quo ...