序列化类型安全控制，对日常的应用开发尤其重要。（涉及序列化的框架，都会涉及安全问题）

### 情况1：默认只访问字段（避免触发行为，比较安全）

默认是这个状态：

* 不允许使用 setter, getter 
* 不允许使用 有参数的构造方法（但允许 `record` 或全部只读字段的类使用）

java17 后，模块化的类可能会有访问权限问题。（可通过 `--add-opens` 开放模块）

### 情况2：默认状态下启用 `Feature.Read_AutoType` （比较安全）

涉及特性（一般不需要启用）：

```java
Feature.Read_AutoType
```


### 情况3：只启用 setter, getter 和 parameterized constructor（比较安全）

启用这三个特性后，模块化类的有访问权限问题可以启用。涉及特性：

```java
Feature.Read_OnlyUseGetter //绝不访问字段读
Feature.Write_OnlyUseSetter //绝不访问字段写
Feature.Write_AllowParameterizedConstructor
```


### 情况4：（情况2 + 情况3）需要加黑白名单机制（不安全）

（比较）如果要序列化 `Exception` 类，需要启用 情况2 + 情况3 的多种特性，会比较不安全。需要加黑白名单机制


```java
//序列化
String json = ONode.ofBean(e,
        Feature.Write_ClassName, //write json
        Feature.Read_OnlyUseGetter //read bean
).toJson();

//反序列化
NullPointerException e = ONode.ofJson(json,
        Feature.Write_OnlyUseSetter,
        Feature.Write_AllowParameterizedConstructor,
        Feature.Read_AutoType
).toBean();
```


白黑名单结合参考：

```java
public class TypeSafety {
    @Test
    public void case1() {
        Options options = Options.of();
        
        //使用 ObjectFactory 模拟白名单（可选）
        options.addCreator(User.class, (opts, node, clazz) -> new User());
        options.addCreator(Order.class, (opts, node, clazz) -> new Order());

        //使用 ObjectPatternFactory 模拟黑名单（必选）
        options.addFactory(new ObjectPatternFactory<Object>() {
            @Override
            public boolean calCreate(Class<?> clazz) {
                return true;
            }

            @Override
            public Object create(Options opts, ONode node, Class<?> clazz) {
                if(Throwable.class.isAssignableFrom(clazz) == false) { //其它类，只以允许 Throwable
                    throw new SnackException("");
                } else {
                    return null; //交给框架自动处理
                }
            }
        });

        //效果测试
        Assertions.assertThrows(SnackException.class, () -> {
            ONode.deserialize("{id:1}", UserModel.class, options);
        });

        ONode.deserialize("{id:1}", Map.class, options);
    }
}
```

