{"id":25182318,"url":"https://github.com/vipcxj/beanknife","last_synced_at":"2025-05-07T09:24:32.905Z","repository":{"id":57735129,"uuid":"316015123","full_name":"vipcxj/beanknife","owner":"vipcxj","description":"An annotation processor library to automatically generate the data transfer objects (DTO).","archived":false,"fork":false,"pushed_at":"2022-10-16T17:12:20.000Z","size":848,"stargazers_count":160,"open_issues_count":3,"forks_count":7,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-31T08:38:44.574Z","etag":null,"topics":["annotation-processor","data-transfer","dto","dto-bean","json-view","metadata","spring-support"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vipcxj.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-11-25T17:53:21.000Z","updated_at":"2025-01-16T01:31:09.000Z","dependencies_parsed_at":"2023-01-20T01:31:55.684Z","dependency_job_id":null,"html_url":"https://github.com/vipcxj/beanknife","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vipcxj%2Fbeanknife","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vipcxj%2Fbeanknife/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vipcxj%2Fbeanknife/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vipcxj%2Fbeanknife/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vipcxj","download_url":"https://codeload.github.com/vipcxj/beanknife/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252848828,"owners_count":21813725,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["annotation-processor","data-transfer","dto","dto-bean","json-view","metadata","spring-support"],"created_at":"2025-02-09T17:31:44.456Z","updated_at":"2025-05-07T09:24:32.877Z","avatar_url":"https://github.com/vipcxj.png","language":"Java","funding_links":[],"categories":["Bean映射\u0026复制"],"sub_categories":[],"readme":"BeanKnife\n===============\n\n[![Maven Release][maven-shield]][maven-link]\n\n[中文版](/README_CN.md)\n\nAn annotation processor library to automatically generate the data transfer objects (DTO).\n\n## Docs\n* [Quick Look](#quick-look)\n* [Requirement](#requirement)\n* [Quick Start](#quick-start)\n* [Introduction](#introduction)\n* [Basics](#basics)\n* [Advanced Usage](#advanced-usage)\n  * [Inheritance of configuration](#Inheritance-of-configuration)\n  * [Inheritance of annotation](#Inheritance-of-annotation)\n  * [change the generated class name or package](#change-the-generated-class-name)\n  * [filter the property](#filter-the-property)\n  * [generate the setter method](#generate-the-setter-method)\n  * [use non-static method to define new properties](#use-non-static-method-to-define-new-properties)\n  * [spring support](#spring-support)\n  * [serializable support](#serializable-support)\n  \n### Quick Look\nBase on\n```java\nclass Pojo1 {\n   private int a;\n   private List\u003cPojo2\u003e pojo2List;\n}\n\nclass Pojo2 {\n    private String b;\n    private Pojo1 pojo1;\n}\n```\nBeanknife is able to generate\n```java\nclass Pojo1View1 {\n   private int a;\n   private List\u003cPojo2View1\u003e pojo2List;\n}\n\nclass Pojo2View1 {\n    private String b;\n}\n```\nOr\n```java\nclass Pojo2View2 {\n    private String b;\n    private Pojo1View2 pojo1;\n}\n\nclass Pojo1View2 {\n   private int a;\n}\n\n```\n\n### Requirement\nJdk 1.8+ (include jdk 1.8)\n\n### Quick Start\nThis library is an annotation processor. Just use it like any other annotation processor.\n\nYou will need beanknife-runtime-${version}.jar in your runtime classpath, \nand you will need beanknife-core-${version}.jar in your annotation-processor classpath.\nIn fact, only The `PropertyConverter` interface in the beanknife-runtime-${version}.jar need be in runtime classpath, \nall others just need in compile classpath. In the future, I may split them.\n\nIn Maven, you can write:\n```xml\n\u003cdependencies\u003e\n  \u003cdependency\u003e\n    \u003cgroupId\u003eio.github.vipcxj\u003c/groupId\u003e\n    \u003cartifactId\u003ebeanknife-runtime\u003c/artifactId\u003e\n    \u003cversion\u003e${beanknife.version}\u003c/version\u003e\n  \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n```xml\n\u003cplugins\u003e\n  \u003cplugin\u003e\n    \u003cartifactId\u003emaven-compiler-plugin\u003c/artifactId\u003e\n    \u003cconfiguration\u003e\n      \u003cannotationProcessorPaths\u003e\n        \u003cpath\u003e\n          \u003cgroupId\u003eio.github.vipcxj\u003c/groupId\u003e\n          \u003cartifactId\u003ebeanknife-core\u003c/artifactId\u003e\n          \u003cversion\u003e${beanknife.version}\u003c/version\u003e\n        \u003c/path\u003e\n      \u003c/annotationProcessorPaths\u003e\n    \u003c/configuration\u003e\n  \u003c/plugin\u003e\n\u003c/plugins\u003e\n```\n\n### Introduction\nWhat's the problem?\n\nWhen writing a web service, we always need to transmit data to the client. \nWe generally call this data objects as DTO(Data Transfer Object. \nMany of them have similar formats and are derived from some internal data objects of the server, such as database entity classes. \nBut according to the specific needs of the service, they are slightly different. \nWriting the corresponding DTO for each service is boring and tedious. \nAnd in many cases, DTO needs to be maintained synchronously with the corresponding server internal classes. \nFor example, when the internal class of the server has a new attribute, the corresponding DTO will also need the corresponding attribute. \nMaintaining this relationship manually is tedious and error-prone. \nSo many people simply omit the DTO and use the server internal classes directly. \nBut most of the time this is not a good practice. \nServer internal classes often have many and complete attributes, but a service may not need so many attributes. \nSo this will bring additional bandwidth consumption. \nTo make matters worse, DTO must be serializable, such as converting to json. \nBut some internal classes are difficult to serialize. \nFor example, there are circular references, or very large deep references. \nThe workaround for many people is to customize its serialization process by configuring the json library. \nBut even jackson, the most widely used json library in the java world, is not doing very well in this regard. \nBased on the above facts, I brought you this library \u003cb\u003eBeanKnife\u003c/b\u003e\n\nWhat can \u003cb\u003eBeanKnife\u003c/b\u003e do for you?\n\nBasically, \u003cb\u003eBeanKnife\u003c/b\u003e will generate the DTO automatically for you.\nYou just need to tell the library which you need and which you not need by annotation.\nFurthermore, it has these powerful features:\n1. Automatically generate meta class which include all the property names of the target class. \nYou can use these property names in the configure annotation to avoid spelling mistakes.\n2. This is a non-invasive library. \nYou can let the target class alone and put the annotation on any other class.\nOf course, directly put the annotation on the target class is also supported.\n3. You can define the new property in the DTO class. \nIt should be configured on the class other than the target class.\n4. Support converter, which convert from one type to another type or a type to itself.\nThe runtime library provide some built-in converters. Such as converting a number object to zero when it is null.\n5. You can override the existed property.\nIt should be configured on the class other than the target class.\nYou can change the property type by converter or replace it with the DTO version. \nFurthermore, you can rewrite the property.\n6. Automatically convert the property to its DTO version, when you override it with the DTO type.\nThe convert feature support Array, List, Set, Stack and Map of the DTO too. And it even support more complex combination such as `List\u003cMap\u003cString, Set\u003cDTO\u003e[]\u003e\u003e[][]`\n\n### Basics\n```java\nimport io.github.vipcxj.beanknife.runtime.annotations.ViewOf;\n\n@ViewOf(includePattern=\".*\") // (1)\npublic class SimpleBean {\n    private String a;\n    private Integer b;\n    private long c;\n\n    public String getA() {\n        return a;\n    }\n\n    public Integer getB() {\n        return b;\n    }\n\n    public long getC() {\n        return c;\n    }\n}\n```\n1. Annotated the class with `@ViewOf` and set the appropriate attribute `includePattern`. \nThen the annotation processor will do what it should do. The attribute `includePattern` is regex pattern, means which properties are included. \nBy default, nothing is included. \nSo `includePattern=\".*\"` is necessary here, or the generated class will has no properties.\n\nAbove configure will generate:\n```java\nimport io.github.vipcxj.beanknife.runtime.annotations.internal.GeneratedMeta;\n\n@GeneratedMeta(\n    targetClass = SimpleBean.class,\n    configClass = SimpleBean.class,\n    proxies = {\n        SimpleBean.class\n    }\n)\npublic class SimpleBeanMeta {\n    public static final String a = \"a\";\n    public static final String b = \"b\";\n    public static final String c = \"c\";\n}\n\n```\nand\n```java\nimport io.github.vipcxj.beanknife.runtime.annotations.internal.GeneratedView;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.Stack;\n\n@GeneratedView(targetClass = SimpleBean.class, configClass = SimpleBean.class)\npublic class SimpleBeanView {\n\n    private String a;\n\n    private Integer b;\n\n    private long c;\n\n    public SimpleBeanView() { }\n\n    public SimpleBeanView(\n        String a,\n        Integer b,\n        long c\n    ) {\n        this.a = a;\n        this.b = b;\n        this.c = c;\n    }\n\n    public SimpleBeanView(SimpleBeanView source) {\n        this.a = source.a;\n        this.b = source.b;\n        this.c = source.c;\n    }\n\n    public static SimpleBeanView read(SimpleBean source) {\n        if (source == null) {\n            return null;\n        }\n        SimpleBeanView out = new SimpleBeanView();\n        out.a = source.getA();\n        out.b = source.getB();\n        out.c = source.getC();\n        return out;\n    }\n\n    public static SimpleBeanView[] read(SimpleBean[] sources) {\n        if (sources == null) {\n            return null;\n        }\n        SimpleBeanView[] results = new SimpleBeanView[sources.length];\n        for (int i = 0; i \u003c sources.length; ++i) {\n            results[i] = read(sources[i]);\n        }\n        return results;\n    }\n\n    public static List\u003cSimpleBeanView\u003e read(List\u003cSimpleBean\u003e sources) {\n        if (sources == null) {\n            return null;\n        }\n        List\u003cSimpleBeanView\u003e results = new ArrayList\u003c\u003e();\n        for (SimpleBean source : sources) {\n            results.add(read(source));\n        }\n        return results;\n    }\n\n    public static Set\u003cSimpleBeanView\u003e read(Set\u003cSimpleBean\u003e sources) {\n        if (sources == null) {\n            return null;\n        }\n        Set\u003cSimpleBeanView\u003e results = new HashSet\u003c\u003e();\n        for (SimpleBean source : sources) {\n            results.add(read(source));\n        }\n        return results;\n    }\n\n    public static Stack\u003cSimpleBeanView\u003e read(Stack\u003cSimpleBean\u003e sources) {\n        if (sources == null) {\n            return null;\n        }\n        Stack\u003cSimpleBeanView\u003e results = new Stack\u003c\u003e();\n        for (SimpleBean source : sources) {\n            results.add(read(source));\n        }\n        return results;\n    }\n\n    public static \u003cK\u003e Map\u003cK, SimpleBeanView\u003e read(Map\u003cK, SimpleBean\u003e sources) {\n        if (sources == null) {\n            return null;\n        }\n        Map\u003cK, SimpleBeanView\u003e results = new HashMap\u003c\u003e();\n        for (Map.Entry\u003cK, SimpleBean\u003e source : sources.entrySet()) {\n            results.put(source.getKey(), read(source.getValue()));\n        }\n        return results;\n    }\n\n    public String getA() {\n        return this.a;\n    }\n\n    public Integer getB() {\n        return this.b;\n    }\n\n    public long getC() {\n        return this.c;\n    }\n\n}\n```\n`SimpleBeanMeta` is the meta bean which list all the available property in `SimpleBean`. \nHere 'available' means all properties except private properties will be listed.\nThe protected and package properties are included as well because they can be accessed by the classes in same package.\nThe usage of the generated meta bean will be shown in next example.\n\n`SimpleBeanView` is the generated DTO class. \nIt always has a static `read` method to create a instance of itself from the original bean.\nwhich is `public static SimpleBeanView read(SimpleBean source)` here.\nIt also will has all the properties configured in the `@viewOf` annotation. \nIn this example, it inherit all the properties from `SimpleBean`.\nBy default. the DTO bean is readonly. So all properties of it have not setter method. \nOf course, you can change this behaviour.\n\nHere is another more complex example.\n```java\n// ignore all imports\npublic class BeanA {\n    public int a;  // (1)\n    protected long b; // (2)\n    public String c; // (3)\n    private boolean d; // (4)\n    private Map\u003cString, List\u003cBeanB\u003e\u003e beanBMap;\n    \n    public Date getC() { // (3)\n        return new Date();\n    }\n\n    // (5)\n    public Map\u003cString, List\u003cBeanB\u003e\u003e getBeanBMap() {\n        return beanBMap;\n    }\n}\n```\n```java\npublic class BeanB { // (6)\n    private String a;\n    private BeanA beanA;\n\n    public String getA() {\n        return a;\n    }\n    public BeanA getBeanA() {\n        return beanA;\n    }\n}\n```\n```java\n@ViewOf(value=BeanA.class, includes={BeanAMeta.a, BeanAMeta.b, BeanAMeta.c, BeanAMeta.beanBMap}) // (7)\npublic class ConfigBeanA {\n    @OverrideViewProperty(BeanAMeta.beanBMap) // (8)\n    private Map\u003cString, List\u003cBeanBView\u003e\u003e beanBMap;\n    @NewViewProperty(\"d\") // (9)\n    public static boolean d(BeanA source) { // (10)\n        return true; // Generally you should achieve the data from the source.\n    }\n    @NewViewProperty(\"e\") // (11)\n    @Dynamic // (12)\n    public static String e(@InjectProperty int a, @InjectProperty long b) { // (13)\n        return \"\" + a + b;\n    }\n}\n```\n```java\n@ViewOf(value=BeanB.class, includes={BeanBMeta.a}) // (14)\npublic class ConfigBeanB {\n}\n```\n`BeanA` is a native java bean, It has not any annotation from this library. \nWe configure it to generate the DTO by annotated a configure class `ConfigBeanA`.\n1. The field `a` is public, and there is no getter method named `getA`, so it is selected as an available property.\n2. The field `b` is protected, and there is no getter method named `getB`, so it is selected as an available property.\n3. The field `c` is public, however there is also a getter method named `getC`.\n In this library, the getter method always has a high priority. \n So the field `c` is ignored, and the getter method `getC` is selected as an available property. \n As a result the property `c` is a `Date`.\n4. The field `d` is private, it can not be accessed by any other class. So it is ignored.\n5. The getter method `getBeanBMap` is selected as an available property `beanBMap`. \nNote that it's type `BeanB` also has a related DTO type `BeanBView`.\n6. Another bean. we will configure it later.\n7. Rather than configure `BeanA` directly, this time we annotate a config class called `ConfigBeanA` with `@ViewOf`.\n Because `ConfigBeanA` is not the target class, we use the attribute `value` to tell the library the information of the target class.\n With the annotation `ViewOf`, the library will generate the meta class `BeanAMeta` and the DTO Class `BeanAView`.\n Here we use the attribute `includes` instead of `includePattern` to specialize the included properties. \n And the meta class `BeanAMeta` is used here.\n Although at this time, the class `BeanAMeta` still not generated and the compiler may complain that `BeanAMeta.xxx` is not a valid symbol, just use it.\n All will be ok after compile the whole project. If you really hate the error shown by IDE, you can annotated the `BeanB` with `@ViewOf` or `@ViewMeta` and compile it. \n Then the meta class `BeanBMeta` will be generated. Then the error will be gone.\n8. If we want to change a exist property, we should use `@OverrideViewProperty`. \nHere we want to change the type of `beanBMap` to `Map\u003cString, List\u003cBeanBView\u003e\u003e`. \nIt has the same shape with original type `Map\u003cString, List\u003cBeanB\u003e\u003e`. The only difference is `BeanB` become `BeanBView`.\nBecause `BeanBView` is the DTO version of `BeanB`, and the library can detect it. So the library has the ability to convert the type automatically.\nNo custom implementation is needed. So just use a field is enough. Or you have to define your own implementation with a method.\n9. Here we add a new custom property named `d` to the generated DTO class. \n10. There is no `@dynamic` annotated, So it is a *static* custom property. \nThe static property is initialized in the `read` method, so the implementation method may achieve the data direct from the target bean.\nThe implementation method should has the shape `public static PropertyType methodName(TargetBeanType source)`.\nHere the `PropertyType` is `boolean`, the `methodName` is `d` and the `TargetBeanType` is `BeanA`.\n11. Here we add a new custom property named `e` to the generated DTO class. \n12. `@Dynamic` means this custom property is a dynamic property. The dynamic property is calculated when called getter. \nSo the implementation method is not able to achieve the data from the target bean.\n13. The implementation method should has the shape `public static PropertyType methodName(@InjectProperty PropertyAType propertyAName, @InjectProperty PropertyBType propertyBName, ...)`.\nWe can use the annotation `@InjectProperty` to inject the property value of the DTO bean to the static method. Obviously, the parameter type should match the property type. \nBut you can use any parameter name only if you have specialized the property name in the `@InjectProperty` with `value` attribute.\n14. Configure to generate the meta class and DTO class of `BeanB` with only property `a`. So `beanA` is excluded.\n\nThe above class will generate these classes:\n\nThe meta class of `BeanA`\n```java\npublic class BeanAMeta {\n    public static final String a = \"a\";\n    public static final String a = \"b\";\n    public static final String a = \"c\";\n    public static final String beanBMap = \"beanBMap\";\n}\n```\nThe meta class of `BeanB`\n```java\npublic class BeanBMeta {\n    public static final String a = \"a\";\n    public static final String beanA = \"beanA\";\n}\n```\nThe DTO class of `BeanA`\n```java\n@GeneratedView(targetClass = BeanA.class, configClass = ConfigBeanA.class)\npublic class BeanAView {\n\n    private int a;\n\n    private long b;\n\n    private Date c;\n\n    private Map\u003cString, List\u003cBeanBView\u003e\u003e beanBMap;\n\n    private boolean d;\n\n    public BeanAView() { }\n\n    public BeanAView(\n        int a,\n        long b,\n        Date c,\n        Map\u003cString, List\u003cBeanBView\u003e\u003e beanBMap,\n        boolean d\n    ) {\n        this.a = a;\n        this.b = b;\n        this.c = c;\n        this.beanBMap = beanBMap;\n        this.d = d;\n    }\n\n    public BeanAView(BeanAView source) {\n        this.a = source.a;\n        this.b = source.b;\n        this.c = source.c;\n        this.beanBMap = source.beanBMap;\n        this.d = source.d;\n    }\n\n    public static BeanAView read(BeanA source) {\n        if (source == null) {\n            return null;\n        }\n        Map\u003cString, List\u003cBeanBView\u003e\u003e p0 = new HashMap\u003c\u003e();\n        for (Map.Entry\u003cString, List\u003cBeanB\u003e\u003e el0 : source.getBeanBMap().entrySet()) {\n            List\u003cBeanBView\u003e result0 = new ArrayList\u003c\u003e();\n            for (BeanB el1 : el0.getValue()) {\n                BeanBView result1 = BeanBView.read(el1);\n                result0.add(result1);\n            }\n            p0.put(el0.getKey(), result0);\n        }\n        BeanAView out = new BeanAView();\n        out.a = source.a;\n        out.b = source.b;\n        out.c = source.getC();\n        out.beanBMap = p0;\n        out.d = ConfigBeanA.d(source);\n        return out;\n    }\n\n    /* ... other read methods ... */\n\n    public int getA() {\n        return this.a;\n    }\n\n    public long getB() {\n        return this.b;\n    }\n\n    public Date getC() {\n        return this.c;\n    }\n\n    public Map\u003cString, List\u003cBeanBView\u003e\u003e getBeanBMap() {\n        return this.beanBMap;\n    }\n\n    public boolean isD() {\n        return this.d;\n    }\n\n    public String getE() {\n        return ConfigBeanA.e(this.a, this.b);\n    }\n}\n```\nThe DTO class of `BeanB`\n```java\n@GeneratedView(targetClass = BeanB.class, configClass = ConfigBeanB.class)\npublic class BeanBView {\n\n    private String a;\n\n    public BeanBView() { }\n\n    public BeanBView(\n        String a\n    ) {\n        this.a = a;\n    }\n\n    public BeanBView(BeanBView source) {\n        this.a = source.a;\n    }\n\n    public static BeanBView read(BeanB source) {\n        if (source == null) {\n            return null;\n        }\n        BeanBView out = new BeanBView();\n        out.a = source.getA();\n        return out;\n    }\n    \n    /* ... other read methods ... */\n    \n    public String getA() {\n        return this.a;\n    }\n\n}\n```\n\n### Advanced Usage\n\n#### Inheritance of configuration\nAlthough `@ViewOf` cannot be inherited, many other configuration elements can be inherited.\nSuch as the configuration bean itself.\nSee [Leaf11BeanViewConfigure](/beanknife-examples/src/main/java/io/github/vipcxj/beanknife/cases/beans/Leaf11BeanViewConfigure.java), \n[Leaf12BeanViewConfigure](/beanknife-examples/src/main/java/io/github/vipcxj/beanknife/cases/beans/Leaf12BeanViewConfigure.java) \nand [Leaf21BeanViewConfigure](/beanknife-examples/src/main/java/io/github/vipcxj/beanknife/cases/beans/Leaf21BeanViewConfigure.java)\n\nAlmost all attributes of `@ViewOf` has a standalone version annotation. They all can be inherited.\n\n| attribute               | standalone annotation           | merge method  | value on child  | value on base    | final value                 |\n|-------------------------|---------------------------------|---------------|-----------------|------------------|-----------------------------|\n| `access`                | `ViewAccess`                    | override      | `Access.NONE`   | `Access.PUBLIC`  | `Access.NONE`               |\n| `includes`              | `ViewPropertiesInclude`         | union         | `{\"a\", \"b\"}`    | `{\"b\", \"c\"}`     | `{\"b\", \"c\", \"a\", \"b\"}`      | \n| `excludes`              | `ViewPropertiesExclude`         | union         | `{\"a\", \"b\"}`    | `{\"b\", \"c\"}`     | `{\"b\", \"c\", \"a\", \"b\"}`      | \n| `includePattern`        | `ViewPropertiesIncludePattern`  | append        | `\"[aA]pple\\\\d\"` | `\"[oO]range\\\\d\"` | `\"[oO]range\\\\d [aA]pple\\\\d\"`|\n| `excludePattern`        | `ViewPropertiesExcludePattern`  | append        | `\"[aA]pple\\\\d\"` | `\"[oO]range\\\\d\"` | `\"[oO]range\\\\d [aA]pple\\\\d\"`|\n| `emptyConstructor`      | `ViewEmptyConstructor`          | override      | `Access.NONE`   | `Access.PUBLIC`  | `Access.NONE`               |\n| `fieldsConstructor`     | `ViewFieldsConstructor`         | override      | `Access.NONE`   | `Access.PUBLIC`  | `Access.NONE`               |\n| `copyConstructor`       | `ViewCopyConstructor`           | override      | `Access.NONE`   | `Access.PUBLIC`  | `Access.NONE`               |\n| `readConstructor`       | `ViewReadConstructor`           | override      | `Access.NONE`   | `Access.PUBLIC`  | `Access.NONE`               |\n| `getters`               | `ViewGetters`                   | override      | `Access.NONE`   | `Access.PUBLIC`  | `Access.NONE`               |\n| `setters`               | `ViewSetters`                   | override      | `Access.NONE`   | `Access.PUBLIC`  | `Access.NONE`               |\n| `errorMethods`          | `ViewErrorMethods`              | override      | `false`         | `true`           | `false`                     |\n| `serializable`          | `ViewSerializable`              | override      | `false`         | `true`           | `false`                     |\n| `serialVersionUID`      | `ViewSerialVersionUID`          | override      | `1L`            | `0L`             | `1L`                        |\n| `useDefaultBeanProvider`| `ViewUseDefaultBeanProvider`    | override      | `false`         | `true`           | `false`                     |\n| `configureBeanCacheType`| `ViewConfigureBeanCacheType`    | override      | `CacheType.NONE`| `CacheType.LOCAL`| `CacheType.NONE`            |\n\n---\n**NOTE**\n\nThe attribute of `ViewOf` has a higher priority than standalone annotation.\n\n---\n\nThrough configuration inheritance, we can extract the common configuration into a single class, which will greatly simplify our configuration work.\n\n#### Inheritance of annotation\nWe can use `@UseAnnotation` to make the generated class inheriting the annotations from configuration class and original class.\n```java\n@TypeAnnotation(Date.class)\n@DocumentedTypeAnnotation(enumValue = AEnum.B, enumValues = {AEnum.C, AEnum.B, AEnum.A})\npublic class AnnotationBean extends BaseAnnotationBean {\n\n    @FieldAnnotation1(doubleArray = 1.0)\n    private String a;\n    @FieldAnnotation2(annotation = @ValueAnnotation1(type = Date.class))\n    private String b;\n    @FieldAnnotation1(charValue = '0')\n    @FieldAnnotation2(stringArray = \"5\")\n    private String[] c;\n    @FieldAnnotation1(annotations = {\n            @ValueAnnotation1(),\n            @ValueAnnotation1(type = AnnotationBean.class)\n    })\n    @PropertyAnnotation1\n    private int d;\n    @PropertyAnnotation2(stringValue = \"field_e\")\n    private Date e;\n    @PropertyAnnotation2(stringValue = \"field_f\")\n    private List\u003cString\u003e f;\n    @PropertyAnnotation1(stringValue = \"field_g\")\n    private short g;\n\n    public String getA() {\n        return a;\n    }\n\n    public void setA(String a) {\n        this.a = a;\n    }\n\n    public String getB() {\n        return b;\n    }\n\n    public void setB(String b) {\n        this.b = b;\n    }\n\n    public String[] getC() {\n        return c;\n    }\n\n    public void setC(String[] c) {\n        this.c = c;\n    }\n\n    @MethodAnnotation1(\n            charValue = 'a',\n            annotations = {\n                    @ValueAnnotation1,\n                    @ValueAnnotation1(\n                            annotations = @ValueAnnotation2\n                    )\n            }\n    )\n    public int getD() {\n        return d;\n    }\n\n    public void setD(int d) {\n        this.d = d;\n    }\n\n    @PropertyAnnotation2(stringValue = \"getter_e\")\n    public Date getE() {\n        return e;\n    }\n\n    @PropertyAnnotation2(stringValue = \"getter_f\")\n    public List\u003cString\u003e getF() {\n        return f;\n    }\n\n    @PropertyAnnotation1(stringValue = \"getter_g\")\n    public short getG() {\n        return g;\n    }\n}\n\n@UseAnnotation(TypeAnnotation.class)\n@UseAnnotation(DocumentedTypeAnnotation.class)\n@UseAnnotation(InheritableTypeAnnotation.class)\n@UseAnnotation({ FieldAnnotation1.class, FieldAnnotation2.class })\n@UseAnnotation(MethodAnnotation1.class)\n@UseAnnotation(PropertyAnnotation1.class)\npublic class InheritedAnnotationBeanViewConfigure {\n}\n\n@ViewOf(value = AnnotationBean.class, includePattern = \".*\")\n@UnUseAnnotation(FieldAnnotation2.class)\n@UseAnnotation(value = PropertyAnnotation2.class, from = { AnnotationSource.CONFIG, AnnotationSource.TARGET_FIELD })\npublic class AnnotationBeanViewConfigure extends InheritedAnnotationBeanViewConfigure {\n\n    @UseAnnotation(FieldAnnotation2.class)\n    @OverrideViewProperty(AnnotationBeanMeta.c)\n    private String[] c;\n\n    // FieldAnnotation1 can not be put on a method, so this annotation is ignored.\n    @UseAnnotation(value = FieldAnnotation1.class, dest = AnnotationDest.GETTER)\n    // PropertyAnnotation1 is put on the field in the original class,\n    // but will be put on getter method in the generated class.\n    @UseAnnotation(value = PropertyAnnotation1.class, dest = AnnotationDest.GETTER)\n    @OverrideViewProperty(AnnotationBeanMeta.d)\n    private int d;\n\n    @PropertyAnnotation2(stringValue = \"config_e\")\n    @OverrideViewProperty(AnnotationBeanMeta.e)\n    private Date e;\n\n}\n```\nwill generate\n```java\n@GeneratedView(targetClass = AnnotationBean.class, configClass = AnnotationBeanViewConfigure.class)\n@InheritableTypeAnnotation(\n    annotation = @ValueAnnotation1(type = {\n        int.class\n    }),\n    annotations = {\n        @ValueAnnotation1(type = {\n            void.class\n        }),\n        @ValueAnnotation1(type = {\n            String.class\n        }),\n        @ValueAnnotation1(type = {\n            int[][][].class\n        }),\n        @ValueAnnotation1(type = {\n            Void.class\n        }),\n        @ValueAnnotation1(type = {\n            Void[].class\n        })\n    }\n)\n@TypeAnnotation(Date.class)\n@DocumentedTypeAnnotation(\n    enumValue = AEnum.B,\n    enumValues = {\n        AEnum.C,\n        AEnum.B,\n        AEnum.A\n    }\n)\npublic class AnnotationBeanView {\n\n    @FieldAnnotation1(enumClassArray = { AEnum.class, AEnum.class })\n    private Class\u003c?\u003e type;\n\n    @FieldAnnotation1(doubleArray = {1.0})\n    private String a;\n\n    private String b;\n\n    @FieldAnnotation1(charValue = '0')\n    @FieldAnnotation2(stringArray = { \"5\" })\n    private String[] c;\n\n    private int d;\n\n    @PropertyAnnotation2(stringValue = \"config_e\")\n    private Date e;\n\n    @PropertyAnnotation2(stringValue = \"field_f\")\n    private List\u003cString\u003e f;\n\n    @PropertyAnnotation1(stringValue = \"field_g\")\n    private short g;\n\n    // ignore other methods.\n\n    public Class\u003c?\u003e getType() {\n        return this.type;\n    }\n\n    public String getA() {\n        return this.a;\n    }\n\n    public String getB() {\n        return this.b;\n    }\n\n    public String[] getC() {\n        return this.c;\n    }\n\n    @PropertyAnnotation1\n    @MethodAnnotation1(\n        charValue = 'a',\n        annotations = {\n            @ValueAnnotation1,\n            @ValueAnnotation1(annotations = {\n                @ValueAnnotation2\n            })\n        }\n    )\n    public int getD() {\n        return this.d;\n    }\n\n    public Date getE() {\n        return this.e;\n    }\n\n    public List\u003cString\u003e getF() {\n        return this.f;\n    }\n\n    @PropertyAnnotation1(stringValue = \"getter_g\")\n    public short getG() {\n        return this.g;\n    }\n\n}\n```\nThe full [example](/beanknife-examples/src/main/java/io/github/vipcxj/beanknife/cases/beans/AnnotationBeanViewConfigure.java).\n\n#### change the generated class name\nBy default, the dto class is generated with the same package of the original class \nand the same name with a postfix 'View'. \nFor example, the class a.b.c.Bean will generate a dto class a.b.c.BeanView.\nWith the same package, the DTO class can access the properties with protected or default access modifier.\nHowever, if you really need to change the package or name of the generated class, you can configure like this:\n```java\n@ViewOf(value=a.b.c.Bean.class, genName=\"BeanDTO\", packageName=\"a.b.c.dto\")\npublic class ConfigureBean {}\n``` \nThen the generated dto class will be a.b.c.dto.BeanDTO.\n\n---\n**NOTE**\n\nIf the packageName is set to different from the package of the original class, \nnone of properties with protected or default modifier can be included in the generated DTO class.\n\n---\n\n#### filter the property\nWIP\n\n#### generate the setter method\n```java\n@ViewOf(setters=Access.PUBLIC)\npublic class ConfigureBean {}\n```\nBy default, no setter methods is generated, it means `setters=Access.NONE`.\nIf only special property need setter method, \n`@ViewProperty`, `@NewViewProperty`, `@OverrideViewProperty` all have a attribute `setter`.\n```java\npublic class OriginalBean {\n    @ViewProperty(setter=Access.PUBLIC)\n    private int a;\n    private String b;\n}\n\n@ViewOf(OriginalBean.class)\npublic class ConfigureBean {\n    @OverrideViewProperty(value=\"b\", setter=Access.PUBLIC)\n    private String b;\n    @NewViewProperty(value=\"c\", setter=Access.PUBLIC)\n    public static boolean c() {\n        return true;\n    }\n}\n```\n\n#### use non-static method to define new properties\nBy default, to define a new property in the configure bean, you need write a static method. \nAs a result, the configure bean is stateless in the generated class.\nSometimes, you really need configure bean hold some state, you can use a non-static method to add the property.\nThen the library runtime will scan the classpath to find a suitable implementation of service interface BeanProvider.\nIf it is found, the runtime will use the bean provider to get a configure bean instance and proxy the property method.\nBy default, there is no default bean provider. So an error will be thrown at runtime if you using a non-static method.\nHowever, you can activate a default bean provider by following configure:\n```java\n@ViewOf(value=OriginalBean.class, useDefaultBeanProvider=true)\npublic class ConfigureBean {\n    @NewViewProperty(value=\"c\", setter=Access.PUBLIC)\n    public boolean c() {\n        return true;\n    }\n}\n```\nThe default bean provider will use the empty constructor to instantiate the configure bean.\n So if there is no empty constructor or the empty constructor can't be accessed, a exception will be thrown at runtime.\n \n #### spring support\n In most cases, using non-static methods to configure new properties is not a good practice. \n If you really need some state binding to the configure class, you can make the configure as a spring component.\n Then add the spring support dependency to the class path\n ```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.vipcxj\u003c/groupId\u003e\n    \u003cartifactId\u003ebeanknife-spring\u003c/artifactId\u003e\n    \u003cversion\u003e${beanknife.version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nThis plugin provide a bean provider which lookup the bean in the spring application context. So with it, \nyou can use a spring component to configure the DTO generation.\n\n---\n**NOTE**\n\nTo make it work, you need a spring boot environment.\n\n---\n\n#### serializable support\n```java\n@ViewOf(value=OriginalBean.class, serializable=true, serialVersionUID=12345L)\npublic class ConfigureBean {\n    @OverrideViewProperty(value=\"b\", setter=Access.PUBLIC)\n    private String b;\n    @NewViewProperty(value=\"c\", setter=Access.PUBLIC)\n    public static boolean c() {\n        return true;\n    }\n}\n```\nBy default, the generated class does not implement any interface. \nSet serializable to true make the generated class to implement the `Serializable` interface,\nand add a static final long field `serialVersionUID` with 0L as initial value.\nYou can change its initial value by `ViewOf.serialVersionUID` attribute.\n\n[maven-shield]: https://img.shields.io/maven-central/v/io.github.vipcxj/beanknife-core.png\n[maven-link]: https://search.maven.org/artifact/io.github.vipcxj/beanknife-core","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvipcxj%2Fbeanknife","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvipcxj%2Fbeanknife","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvipcxj%2Fbeanknife/lists"}