{"id":19720285,"url":"https://github.com/cristianbonilla/api-reflection","last_synced_at":"2025-02-27T17:47:01.721Z","repository":{"id":122029895,"uuid":"180912988","full_name":"CristianBonilla/api-reflection","owner":"CristianBonilla","description":"Leer y modificar el contenido de un objeto en tiempo de ejecución sin un conocimiento previo de la estructura e incluso en tiempo de compilación, la API reflection de java ofrece esto y mucho más.","archived":false,"fork":false,"pushed_at":"2019-04-27T22:03:41.000Z","size":4119,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-10T15:51:34.130Z","etag":null,"topics":["annotations","h2-database","java-10","jdbc","pluralsight","reflection-api"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CristianBonilla.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-04-12T02:15:26.000Z","updated_at":"2019-09-29T00:01:13.000Z","dependencies_parsed_at":null,"dependency_job_id":"e2966100-8bab-4fae-b0e1-e7feb269f043","html_url":"https://github.com/CristianBonilla/api-reflection","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CristianBonilla%2Fapi-reflection","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CristianBonilla%2Fapi-reflection/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CristianBonilla%2Fapi-reflection/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CristianBonilla%2Fapi-reflection/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CristianBonilla","download_url":"https://codeload.github.com/CristianBonilla/api-reflection/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241040651,"owners_count":19898908,"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":["annotations","h2-database","java-10","jdbc","pluralsight","reflection-api"],"created_at":"2024-11-11T23:11:00.937Z","updated_at":"2025-02-27T17:47:01.698Z","avatar_url":"https://github.com/CristianBonilla.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# API Reflection\n\nLa API reflection se trata de leer y modificar el contenido de un objeto en tiempo de ejecución sin ningún conocimiento previo de la estructura o incluso la clase de ese objeto en el momento de la compilación. De hecho, la API de reflection trata de descubrir el contenido de un objeto y la estructura de la clase en tiempo de ejecución, que es una funcionalidad muy potente.\n\nHay varias clases fundamentales que se deben conocer en está API y el nombre esas clases es el siguiente, hay una clase llamada ‘Class’, que puede ser confuso, pero este es el caso. También una clase llamada ‘Field’ para modelar los campos dentro de una clase y una clase llamada ‘Method’ para modelar métodos, nuevamente dentro de una clase y hay una clase adicional llamada la clase ‘Constructor’, un constructor en la clase no es realmente un método, incluso si parece un método y los constructores se modelan utilizando su propia clase. También se usaría la clase ‘Annotation’ que modela las anotaciones.\n\n```java\nString hello = \"Hello\";\nClass helloClass = hello.getClass();\nClass\u003c?\u003e helloClass = \"Hello\".getClass();\n// la razón de esto radica en la forma en que los genéricos funcionan en java. De echo Class of String o Class of Object no son extensiones de Class of question mark.\nClass\u003cString\u003e helloClass = \"Hello\".getClass(); // compile error.\nClass\u003c? extends String\u003e helloClass = \"Hello\".getClass(); // is correct.\n// obtiene una referencia  de la clase misma.\nClass\u003c?\u003e stringClass = String.class;\n// recomienda porque Brinda muchas oportunidades de la reflection.\nString className = \"java.lang.String\";\nClass\u003c?\u003e stringClass = Class.forName(className);\n// forName puede producir varias excepciones por casos de seguridad y también puede ser por falta de derechos.\n```\n\n## Obtener los campos de una clase\n\nHay muchos métodos en la clase llamada ‘Class’, son métodos para obtener referencias en los campos, para obtener referencias en los métodos, en constructores y mucha más información.\n\n```java\nClass\u003c?\u003e getClass = Person.class;\nField field = getClass.getField(\"age\");\n// los campos declarados son los campos declarados dentro de esa clase, ya sean privados, protegidos, protegidos por paquetes o públicos.\nField[] declaredFields = getClass.getDeclaredFields();\n// Son los campos públicos de la clase, incluidos de la super clase.\nField[] fields = getClass.getFields();\n```\n\n## Obtener los métodos de una clase\n\nUna de las formas de obtener los métodos es usando el método getMethod(), devolverá un solo método, pasamos el nombre del método y luego los tipos de parámetros en el orden correcto. Si este método toma varios parámetros, luego agregamos los otros tipos de los otros parámetros después del primero. También hay un método GetDeclaredMethod() que devolverá todos los métodos dentro de esa clase ya sean públicos, protegidos o privados y un método getMethods() que devolverá todos los métodos públicos declarados en esa clase y todos los super clases de esa clase.\n\n```java\nClass\u003c?\u003e getClass = Person.class;\nMethod method = getClass.getMethod(\"setName\", String.class);\nMethod[] declaredMethods = getClass.getDeclaredMethods();\nMethod[] methods = getClass.getMethods();\n```\n\n## Obtener los constructores de una clase\n\nPara obtener los constructores de una clase, un constructor puede ser visto como una especie de método, pero de hecho no lo es y dentro del contexto de la API de reflection, esta modelado por una clase que es la clase constructora ‘Constructor’. El primer método es el mismo que el método o los campos. Es solo un método getConstructor() que toma los tipos de parámetros tomados por ese constructor.\n\n```java\nClass\u003c?\u003e getClass = Person.class;\nConstructor constructor = getClass.getConstructor(Class\u003c?\u003e... types);\nConstructor[] declaredConstructors = getClass.getDeclaredConstructors();\n//Los constructores de la super clase no estarán incluidos, es la diferencia con los métodos y campos.\nConstructor[] constructors = getClass.getConstructos();\n```\n\n## Accesibilidad\n\nSi el campo dado es privado entonces habría una excepción que es una excepción IllegalAccessException porque no hay permiso para leer un campo privado desde fuera de la clase ‘Person’, entonces la encapsulación no está rota y si hay una verificación de seguridad para acceder a un miembro privado a una clase Reflection, hay un método en la clase ‘Field’ que se llama setAccessible(). La llamada a setAccessible() en true realmente hace que suprima el control de acceso en ese campo.\n\n```java\nPerson o = ...;\nClass\u003c?\u003e getClass = o.getClass();\nField field = getClass.getDeclaredField(\"name\");\nfield.setAccessible(true);\nfield.setValue(o, \"Sarah\");\nString name = (String)field.getValue(o);\n```\n\n## Anotaciones\n\n```java\npublic class Person {\n    @PrimaryKey\n    private long id;\n    @Column\n    private int age;\n    @Column\n    private String name;\n\n    // getters and setters\n}\n\npublic @interface PrimaryKey { }\npublic @interface Column { }\n```\n\nEn la forma como funcionan las anotaciones en java, el compilador primero maneja una anotación y el compilador debe decidir en que momento del ciclo de vida de una clase estará disponible una anotación. Hay tres etapas cuando una anotación puede estar disponible.\n\n* La primera etapa es la propia etapa de compilación, por lo que solo el compilador podrá ver esa anotación si elegimos hacerlo.\n* La segunda etapa es la carga de clases por lo que la anotación solo verá el cargador de clases.\n* La tercera es el propio tiempo de ejecución.\n\n## Inyección de dependencias\n\nSi se está diseñando un objeto que necesita otro objeto para funcionar, un objeto delegado, entonces este primer objeto no debe intentar crear el objeto delegado, en lugar de eso cuando se construye el objeto, debería intentar pedir algún otro mecanismo para inyectar ese objeto delegado en el primer objeto. Entonces una forma de implementar esto es trabajar con un framework de inyección de dependencias, no es la única forma de implementar, pero probablemente sea la más clásica en las aplicaciones empresariales.\n\n![Alt text](/Reflection/assets/dependenciesInjection.png?raw=true \"Database Connection\")\n\n```java\nBeanManager beanManager = BeanManager.getInstance();\nEntityManager entityManager = beanManager.get(EntityManager.class);\n```\n\n## Invocación de métodos\n\n```java\npublic class ConnectionProvider {\n  Connection createConnection(String url) { ... }\n}\n\nClass\u003c?\u003e connectionType = ConnectionProvider.class;\nObject connectionProvider = connectionType.getConstructor().getInstance();\nMethod method = connectionType.getMethod(\"createConnection\", String.class);\nmethod.invoke(connectionProvider, \"jdbc:h2:mem:db_reflection\");\n```\n\n## API Reflection y su rendimiento\n\nCada vez que se llama a un método desde la API de reflection, se realizan varias comprobaciones de seguridad para verificar si el código puede acceder a una clase mediante reflection y todas esas comprobaciones se verifican nuevamente cada vez que se realiza un acceso, entonces si el mismo código se llama una y otra vez al mismo método de reflection, esas comprobaciones de seguridad se realizan una y otra vez solo para devolver el mismo resultado. Esto ha sido visto hace mucho tiempo, todos esos controles son costosos y tienen un impacto notable en el rendimiento.\n\n## API MethodHandle\n\nEs un objeto de búsqueda porque encapsula toda la información de seguridad, ya que se comprueba una vez y como esa información no va a cambiar se usará todo el tiempo cuando se acceda a una clase, un campo o un método. Desde el objeto de búsqueda se puede crear instancias de MethodHandle para acceder a la clase, los métodos dentro de esa clase y los campos dentro de esa clase, por lo tanto, este único identificador de método le dará acceso a todo el contenido de la clase que se distribuyó entre varios objetos en la API de reflection de java, es decir, objetos de método, constructor y campo.\n\n```java\n// Por cada subproceso con su rol de seguridad el objeto de \n// búsqueda será diferente.\nLookup lookup = MethodHandles.lookup();  // factory method\n// no debe compartirse con algún código no confiable,\n// porque se tendrá acceso a los objetos utilizando reflection\n// con la seguridad de otra persona.\n\nPerson person = ...;\n// devuelve una instancia de Lookup\nClass\u003c?\u003e personClass = MethodHandles.lookup()\n  .findClass(Person.class.getName());\n```\n\nUn tipo de método para un método que devuelve una cadena y no toma ningún argumento.\n\n```java\nLookup lookup = MethodHandles.lookup();\n// public String getName() { ... }\nMethodType getterType = MethodType.methodType(String.class);\n```\n\nUn tipo de método para un método que devuelve void y toma un argumento String.\n\n```java\n// public void setName(String name) { … }\nMethodType setterType = MethodType.methodType(void.class, String.class);\n```\n\nEn el caso de un constructor, el tipo devuelto es void.class\n\n```java\n// public Person() { ... }\nMethodType emptyConstructorType = MethodType.methodType(void.class);\n\n// public Person(String name, int age) { … }\nMethodType constructorType = MethodType.methodType(void.class,\nString.class, int.class);\n```\n\nObtener un controlador en un método de instancia regular\n\n```java\nLookup lookup = MethodHandles.lookup();\n\n// public String getName() { ... }\nMethodType getterType = MethodType.methodType(String.class);\nMethodHandle getterHandle = lookup.findVirtual(Person.class, \"getName\", getterType);\n\n// public void setName(String name) { ... }\nMethodType setterType = MethodType.methodType(void.class, String.class);\nMethodHandle setterHandle = lookup.findVirtual(Person.class, \"setName\", setterType);\n\n// public Person() { ... }\nMethodType emptyConstructorType = MethodType.methodType(void.class);  // not Void.class\nMethodHandle emptyConstructorHandle = lookup.findConstructor(Person.class, emptyConstructorType);\n\n// public Person(String name, int age) { ... }\nMethodType constructorType = MethodType.methodType(void.class, String.class, int.class);\nMethodHandle constructorHandle = lookup.findConstructor(Person.class, constructorType);\n```\n\nUn identificador devuelto por un findGetter da acceso de lectura en un campo y no llama al captador de ese campo.\n\n```java\nLookup lookup = MethodHandles.lookup();\n// reads name\nMethodHandle nameReader = lookup.findGetter(Person.class, \"name\", String.class);\n```\n\nUn identificador devuelto por un findSetter da acceso de escritura en un campo y no llama al configurador de ese campo.\n\n```java\nLookup lookup = MethodHandles.lookup();\n// writes name\nMethodHandle nameSetter = lookup.findSetter(Person.class, \"name\", String.class);\n```\n\nInvocar a un getter no requiere ningún argumento y devuelve una cadena.\n\n```java\nPerson person = ...;\nMethodHandle nameGetter = ...;\nString name = (String)nameGetter.invoke(person);\n```\n\nInvocar a un definidor requiere un argumento String y no devuelve nada.\n\n```java\nPerson person = ...;\nMethodHandle nameSetter = ...;\nnameSetter.invoke(person, \"John\");\n```\n\n### Accesos privados\n\nUna solución pre-Java 9 es usar los métodos unreflect() para accesos privados.\n\n```java\nPerson person = ...;\nField nameField = Person.class.getDeclaredField(\"name\");\nnameField.setAccessible(true);\n\nMethodHandle privateNameReader = lookup.unreflectGetter(field);\nString name = (String)privateNameReader.invoke(person);\n```\n\nJava 9 trae una solución mucho más limpia, usando un objeto de búsqueda para los elementos privados de una clase.\n\n```java\nPerson person = ...;\nLookup privateLookup = MethodHandles.privateLookupIn(Person.class, lookup);\n\nMethodHandle privateNameReader = privateLookup.findGetter(Person.class, \"name\", String.class);\nString name = (String)privateNameReader.invoke(person);\n\nMethodHandle privateNameWriter = privateLookup.findSetter(Person.class, \"name\", String.class);\nprivateNameWriter.invoke(person, \"John\");\n```\n\n## VarHandle\n\nTiene una funcionalidad especial que fue agregada en java 9, parece un MethodHandle para los campos, el MethodHandle ya puede acceder a un campo, dándole acceso completo e incluso a los campos privados ¿por qué se han agregado VarHandles a Java 9? Es porque VarHandle da tres tipos de acceso a los campos.\n\n* **Es solo el acceso sencillo, regular, leer y escribir en campos públicos y privados, esto se hace en MethodHandles**\n* **También brinda acceso volátil**\n* **Compara y configura el acceso**\n\nEl método get() invoca un manejador var en modo normal.\n\n```java\nLookup lookup = ...;\nPerson person = ...;\n\nVarHandle nameVarHandle = MethodHandles.privateLookupIn(Person.class, lookup)\n  .findVarHandle(Person.class, \"name\", String.class);\nString name = (String)nameVarHandle.get(person);\n```\n\nEl método getVolatile () invoca un manejador var en modo volátil.\n\n```java\nString name = (String)nameVarHandle.getVolatile(person);\n```\n\nEl método getAndAdd () agrega atómicamente el valor pasado y devuelve el valor anterior.\n\n```java\nInt newAge = (int)ageVarHandle.getAndAdd(person, 1);\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcristianbonilla%2Fapi-reflection","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcristianbonilla%2Fapi-reflection","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcristianbonilla%2Fapi-reflection/lists"}