Saltar al contenido

JAVA: ¿Cuáles son las ventajas de un InvocationHandler sobre la implementación de una interfaz?

Esta reseña fue probado por especialistas así garantizamos la exactitud de nuestro post.

Solución:

Proxy es un dinámica proxy, lo que le permite alterar el comportamiento de los objetos en tiempo de ejecución en lugar de tener que decidirlo en tiempo de compilación.

Por ejemplo, digamos que queremos devolver solo valores nulos durante la noche. Si tuviera que implementarlo estáticamente, necesitaría escribir la lógica en todas las clases con algo como

if(isNight())
    return null;
return normalValue;

Esto requiere que realmente pueda cambiar la clase, y necesitaría cambiar todos las clases.

Sin embargo, con un Proxy, puede escribir la lógica anterior en el InvocationHandler y las clases normales ni siquiera sabrán que sus valores no se utilizan durante la noche. En lugar de la clase original, su código ahora usa el proxy dinámico, pero no notará la diferencia.

Esto también le permite tener múltiples InvocationHandlers, por lo que podría ejecutar su código con parámetros para decidir si desea registrar llamadas, evitar llamadas por razones de seguridad o cualquier otra cosa similar, lo que sería bastante imposible de hacer con static implementaciones.


Sin embargo, es poco probable que use esas clases directamente, ya que son de un nivel bastante bajo. Sin embargo, AOP utiliza proxies dinámicos o manipulación de código de bytes para lograr su tarea. Si alguna vez ha utilizado Spring, lo más probable es que haya utilizado un InvocationHandler sin saberlo. Cuando pones @Transactional en un método, un InvocationHandler es lo que interceptará la llamada al método e iniciará (y finalizará) la transacción por usted.

InvocationHandler Juntos con Proxy Permitir la implementación de una interfaz en tiempo de ejecución, sin tener que compilar código específico de la interfaz. A menudo se usa para mediar en el acceso a un objeto de una clase que implementa la misma interfaz. Proxyno permiten cambiar el comportamiento de objetos o clases existentes.

Por ejemplo, se puede utilizar en llamadas de métodos remotos en el lado del cliente, reenviando llamadas de métodos a través de una red a un servidor.

Mi primer uso de Proxy era para registrar llamadas de métodos a una amplia interfaz que representaba el comando recibido en un formato de cable. Esto fácilmente producido muy consistente depuración de salida, pero requirió poco mantenimiento cuando la interfaz cambió.

Las interfaces de anotación de Java pueden estar representadas por un Proxy objeto proxy en tiempo de ejecución, para evitar una explosión de clases.

java.beans.EventHandler era útil antes de que aparecieran lambdas y referencias de métodos, para implementar detectores de eventos sin frascos hinchados.

Según un ejemplo más específico o del mundo real, puede encontrarse con este tipo de usos de reflexión más utilizando una API de código abierto o de terceros. Un ejemplo muy popular de esto sería Minecraft, específicamente Bukkit / Spigot.

Esta API se utiliza para escribir complementos, que luego carga y ejecuta el servidor principal. Esto significa que no tiene el control al 100% de parte del código que existe en esa base de código, lo que invita a soluciones mediante la reflexión. Específicamente, cuando quieras interceptar llamadas que se realizan en la API (o incluso en la API de otro complemento, por ejemplo, Vault para los familiares), puede utilizar un Proxy.

Nos quedaremos con el ejemplo de Minecraft, pero nos estamos separando de la API de bukkit aquí (y fingiendo que no acepta relaciones públicas). Digamos que hay una parte de la API que simplemente no bastante trabaje de la manera que necesite.

public interface Player 
    //This method handles all damage! Hooray!
    public void damagePlayer(Player source, double damage);

Esto es genial, pero si queremos codificar algo donde podamos averiguar si un reproductor está dañado (¿tal vez para hacer efectos geniales?), Tendríamos que modificar la fuente (no es posible para complementos distribuidos), o lo haríamos Necesito encontrar una manera de averiguar cuándo #damagePlayer ha sido llamado y con qué valores. Entonces entra un Proxy:

public class PlayerProxy implements IvocationHandler 

    private final Player src;

    public PlayerProxy(Player src) 
        this.src = src;
    

    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable 
        //Proceed to call the original Player object to adhere to the API
        Object back = m.invoke(this.src, args);
        if (m.getName().equals("damagePlayer") && args.length == 2) 
            //Add our own effects!
            //Alternatively, add a hook so you can register multiple things here, and avoid coding directly inside a Proxy
            if (/* 50% random chance */) 
                //double damage!
                args[1] = (double) args[1] * 2;
                //or perhaps use `source`/args[0] to add to a damage count?
            
        
    

Con nuestro Proxy, hemos creado un falso Clase de jugador, una que simplemente llamará a los métodos en su lugar para Player. Si nuestro PlayerProxy se invoca con myPlayerProxy.someOtherMethod(...), luego pasará felizmente una llamada a myPlayerProxy.src.someOtherMethod(...) a través de la reflexión (el m#invoke en el método anterior).

En pocas palabras, puedes preparar los objetos de la biblioteca para que se adapten a tus necesidades:

//we'll use this to demonstrate "replacing" the player variables inside of the server
Map players = /* a reflected instance of the server's Player objects, mapped by name. Convenient! */;
players.replaceAll((name, player) -> 
    (PlayerProxy) Proxy.newProxyInstance(/* class loader */, new Class[]Player.class, new PlayerProxy(player)));

InvocationHandler también puede manejar múltiples interfaces. Utilizando un genérico Object para transmitir las invocaciones, puede escuchar una variedad de métodos diferentes en la API, todos dentro del mismo Proxy ejemplo.

Recuerda que puedes mostrar este escrito si te ayudó.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *