Java使用序列化的私有方法巧妙解决部分属性持久化问题

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

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.io.Serializable;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Salary implements Serializable {
    private static final long serialVersionUID = 75632L;
    // 基本工资
    private Integer basePay;
    // 绩效工资
    private Integer bonus;
}
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.io.Serializable;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Serializable {
    private static final long serialVersionUID =45829L;
    // 员工姓名
    private String name;
    // 员工薪资
    private Salary salary;

}

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

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/demo")
public class UserController {

    @GetMapping("/get-user")
    public Person getUser() {
        // 基本工资 1000 绩效3000
        Salary salary = new Salary(1000,3000);
        Person person = new Person("小哇",salary);
        return person;
    }
}

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

import com.cp.security.user.common.JsonUtil;

public class TestController {

    public static void main(String[] args) {
        String personJson = HttpRequest.sendGet("http://localhost:8080/demo/get-user","");
        Person person = JsonUtil.str2Obj(personJson);
        StringBuffer sb = new StringBuffer();
        sb.append("name"+person.getName());
        sb.append("基本工资"+person.getSalary().getBasePay());
        sb.append("绩效工资"+person.getSalary().getBonus());
        System.out.println(sb);
    }

//运行结果如下:
// name 小哇 基本工资1000 绩效工资3000

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

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

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

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

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

3.在请求端进行过滤

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

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

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Serializable {
    private static final long serialVersionUID =45829L;
    // 员工姓名
    private String name;
    // 员工薪资
    private Salary salary;

    private void writeObject(ObjectOutputStream out)throws IOException {
        out.defaultWriteObject();
        out.writeInt(salary.getBasePay());
    }
    private void readObject(ObjectInputStream in)throws IOException,ClassNotFoundException{
        in.defaultReadObject();
        salary = new Salary(in.readInt(),0);
    }
}
// 此时运行的结果
// name 小哇 基本工资1000 绩效工资0

总结:

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

a> out.defaultWriteObject();

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

b>in.defaultReadObject();

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

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

原文地址:https://www.cnblogs.com/slymonkey/p/10549361.html

时间: 2024-10-09 02:50:00

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

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

建议11:养成良好习惯,显示声明UID 我们编写一个实现了Serializable接口(序列化标志接口)的类,Eclipse马上就会给一个黄色警告:需要添加一个Serial Version ID.为什么要增加?他是怎么计算出来的?有什么用?下面就来解释该问题. 类实现Serializable接口的目的是为了可持久化,比如网络传输或本地存储,为系统的分布和异构部署提供先决条件支持.若没有序列化,现在我们熟悉的远程调用.对象数据库都不可能存在,我们来看一个简单的序列化类: 1 import java

java基础 接口私有方法

1 /** 2 * 问题描述: 3 * 我们需要抽取一个共有方法,用来解决两个默认方法之间重复代码的问题 4 * 但是这个共有方法不应该让实现类使用,应该是私有化的. 5 * 6 * 解决方案: 7 * 从java 9开始,接口当中允许定义私有方法. 8 * 1.普通私有方法,解决多个默认方法之间重复代码问题 9 * 格式: 10 * private 返回值类型方法名称(参数列表){ 11 * 方法体 12 * } 13 * 2.静态私有方法,解决多个静态方法之间重复代码的问题 14 * 格式:

接口私有方法使用

1 package Port; 2 /* 3 问题描述: 4 我们需要抽取一个共有方法,用来解决两个默认方法之间的重复代码的问题. 5 但是这个共有方法不应该让实现类使用,应该是私有化的. 6 7 解决方案: 8 从java 9开始,接口当中允许定义私有方法. 9 1.普通私有方法,解决多个默认方法之间代码重复问题 10 格式: 11 private 返回值类型 方法名称 (参数列表){ 12 方法体 13 } 14 2.静态私有方法,解决多个静态方法之间重复代码的问题 15 private s

反射获取一个类的私有方法

今天在刷面试题的时候,发现一个题目是编写程序通过反射获取一个类的私有方法,因为之前学反射的时候也学的浅,没有考虑到这样的事情.今天敲了一下,虽然也就是那么几行代码,还是磕磕绊绊的,最后终于搞定了,这里总结一下 Java反射得到一个类的私有方法 获得私有方法的流程是 (1)获取目标类 (2)获取目标方法 Method method=clazz.getDeclaredMethod(name);//可以调用类中的所有方法(不包括父类中继承的方法) Method method=clazz.getMeth

java反射调用私有方法和修改私有属性

//调用私有方法package com.java.test; public class PrivateMethod { private String sayHello(String name) { return "hello "+name; } } 测试: package com.java.test; import java.lang.reflect.Method; public class PrivateMethodTest { public static void main(Str

java.lang.NoClassDefFoundError: org/hibernate/cfg/Configuration解决方法

Autowiring of fields failed; nested exception is...........Error creating bean with name 'siteOperaterFactory': Autowiring of fields fa ...........java.lang.NoClassDefFoundError: org/hibernate/cfg/Configuratio 看开头是以为是创建siteOperaterFactory实例出的错,再看后面,原

Struts2中使用execAndWait后,在 Action中调用getXXX()方法报告java.lang.NullPointerException异常的原因和解决方法

使用 Struts2 编写页面,遇到一个要长时间运行的接口,因此增加了一个execAndWait ,结果在 Action 中调用 getContext()的时候报告异常 1 ActionContext context = ActionContext.getContext(); 2 ServletContext servletContext = (ServletContext) context.get(ServletActionContext.SERVLET_CONTEXT); //抛空指针异常

java开发中遇到的问题及解决方法(持续更新)

摘自 http://blog.csdn.net/pony12/article/details/38456261 java开发中遇到的问题及解决方法(持续更新) 工作中,以C/C++开发为主,难免与其他服务和Web进行交换,Java开发必不可少,又不想动用Eclipse大家伙,只能自己动手编写脚本进行Java代码的编译和运行,期间遇到的一些问题,记录下来供自己和大家参考.1)软件包不存在/软件包 javax.jms 不存在    这是由于javac编译时找不到javax.jms所在的软件包,因此将

利用JAVA反射机制实现调用私有方法

1.fragment是AccessibilityFragment的對象.须要被調用的方法的類. setAccessible(true)并非将方法的訪问权限改成了public.而是取消java的权限控制检查.所以即使是public方法.其accessible属相默认也是false try { Class mClass = Class.forName("com.sonymobile.chameleon.workflow.step.AccessibilityFragment"); Metho