Maison > Java > javaDidacticiel > Comment implémenter le proxy dynamique JDK en Java

Comment implémenter le proxy dynamique JDK en Java

WBOY
Libérer: 2023-04-30 08:49:06
avant
1040 Les gens l'ont consulté

Concept

Agent : Afin de contrôler l'objet A, un nouvel objet B est créé et l'objet B effectue toutes les opérations de l'objet A à la place , que l'on appelle un agent. La mise en place d'un système d'agence implique trois rôles participants : l'objet réel (A), l'objet proxy (B) et le client.

L'objet proxy (B) joue un rôle intermédiaire, reliant l'objet réel (A) et le client. S'il est développé davantage, l'objet proxy peut implémenter une logique plus complexe, telle que le contrôle d'accès pour le réel. objet.

Case

Exigences : L'interface de la couche métier des employés nécessite l'autorisation de l'administrateur pour appeler et la liste d'appels ne nécessite pas d'autorisation. Une exception sera levée lors d'un appel sans autorisation.

static proxy

/**
 * 代理接口
 */
public interface IEmployeeService {
    void save();
 
    void list();
}
Copier après la connexion
/**
 * 真实对象
 */
public class EmployeeServiceImpl implements IEmployeeService {
    @Override
    public void save() {
        System.out.println("EmployeeServiceImpl-正常的save....");
    }
    @Override
    public void list() {
        System.out.println("EmployeeServiceImpl-正常的list....");
    }
}
Copier après la connexion
/**
 * 模拟当前登录用户对象
 */
public class SessionHolder {
    private static String currentUser;
    public static String  getCurrentUser(){
        return currentUser;
    }
    public static void   setCurrentUser(String currentUser){
        SessionHolder.currentUser = currentUser;
    }
}
Copier après la connexion
/**
 * 代理对象
 */
public class EmployeeProxy implements IEmployeeService {
    //真实对象
    private EmployeeServiceImpl employeeService;
    public EmployeeProxy(EmployeeServiceImpl employeeService){
        this.employeeService = employeeService;
    }
    @Override
    public void save() {
        //权限判断
        if("admin".equals(SessionHolder.getCurrentUser())){
            employeeService.save();
        }else{
            throw new RuntimeException("当前非admin用户,不能执行save操作");
        }
    }
    @Override
    public void list() {
        employeeService.list();
    }
}
Copier après la connexion
public class App {
    public static void main(String[] args) {
        System.out.println("----------------真实对象--------------------");
        EmployeeServiceImpl employeeService = new EmployeeServiceImpl();
        employeeService.list();
        employeeService.save();
        System.out.println("----------------代理对象--------------------");
        SessionHolder.setCurrentUser("dafei");  //设置权限(当前登录用户)
        EmployeeProxy employeeProxy = new EmployeeProxy(employeeService);
        employeeProxy.list();
        employeeProxy.save();
    }
}
Copier après la connexion
----------------真实对象--------------------
EmployeeServiceImpl-正常的list....
EmployeeServiceImpl-正常的save....
----------------代理对象--------------------
EmployeeServiceImpl-正常的list....
Exception in thread "main" java.lang.RuntimeException: 当前非admin用户,不能执行save操作
	at com.langfeiyes.pattern.proxy.demo.EmployeeProxy.save(EmployeeProxy.java:20)
	at com.langfeiyes.pattern.proxy.demo.App.main(App.java:16)
Copier après la connexion

Lors d'un appel direct à l'aide de l'objet réel EmployeeServiceImpl, la liste et la sauvegarde sont accessibles directement, mais elles ne répondent pas aux restrictions d'autorisation d'administrateur sur les exigences. Si vous utilisez l'objet proxy EmployeeProxy, vous pouvez remplir les conditions requises.

Complétez la logique proxy en créant directement une nouvelle classe d'objets proxy. Cette méthode est appelée mode proxy statique.

Mode proxy dynamique JDK

Les modes de proxy dynamique couramment utilisés en Java incluent le proxy dynamique JDK et le proxy dynamique cglib. Ici, nous nous concentrons sur le proxy dynamique JDK

#🎜. 🎜#Toujours l'exigence d'origine, le précédent IEmployeeService EmployeeServiceImpl SessionHolder n'a pas changé, un nouveau contrôleur proxy JDK-EmployeeInvocationHandler

/**
 * jdk动态代理控制类,由它牵头代理类获取,代理方法的执行
 */
public class EmployeeInvocationHandler  implements InvocationHandler {
    //真实对象-EmployeeServiceImpl
    private Object target;
    public EmployeeInvocationHandler(Object target){
        this.target = target;
    }
    //获取jvm在内存中生成代理对象
    public Object getProxy(){
        return  Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }
    //代理对象控制执行方法
    //参数1:代理对象
    //参数2:真实对象的方法(使用方式得到方法对象)
    //参数3:真实对象方法参数列表
    //此处是代理对象对外暴露的可编辑的方法处理场所,代理对象每调用一个次方法,就会执行一次invoke
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String name = method.getName();
        if("save".equals(name) && !"admin".equals(SessionHolder.getCurrentUser())){
            throw new RuntimeException("当前非admin用户,不能执行save操作");
        }
        return method.invoke(target, args);
    }
}
Copier après la connexion

La classe de l'application de test a été légèrement modifiée : # 🎜🎜#

public class App {
    public static void main(String[] args) {
        System.out.println("----------------真实对象--------------------");
        EmployeeServiceImpl employeeService = new EmployeeServiceImpl();
        employeeService.list();
        employeeService.save();
 
        System.out.println("----------------代理对象--------------------");
        SessionHolder.setCurrentUser("dafei");
        EmployeeInvocationHandler handler = 
            new EmployeeInvocationHandler(employeeService);
        IEmployeeService proxy = (IEmployeeService) handler.getProxy();
        proxy.list();
        proxy.save();
 
    }
}
Copier après la connexion
Le code ci-dessus peut également répondre aux exigences. La différence avec le proxy statique est que moins d'objets proxy sont créés. Il y a une question à ce stade : aucun objet proxy n'est créé. Pourquoi l'appel de classe proxy peut-il être implémenté ? ?

Analyse des principes

Tirons d'abord la conclusion. Le principe sous-jacent d'implémentation du proxy dynamique JDK : utilisez la méthode d'implémentation d'interface pour construire dynamiquement une classe dans la mémoire au moment de l'exécution, puis compiler et exécuter. Cette classe est à usage unique. Lorsque la JVM est arrêtée, la classe proxy disparaît.

Rôle participant

Pour comprendre le principe du proxy dynamique JDK, vous devez d'abord comprendre les classes impliquées dans le proxy dynamique JDK

#🎜🎜 #

Comment implémenter le proxy dynamique JDK en JavaInvocationHandler

 : gestionnaire d'invocation de méthode d'objet réel, méthode d'invocation intégrée, sa fonction : personnaliser la logique du proxy pour les objets réels

#🎜🎜 #EmployeeInvocationHandler#🎜 🎜# : Gestionnaire d'appel de méthode d'objet réel du service employé, cette classe a 3 objectifs : 1> Définir l'objet réel

     //真实对象-EmployeeServiceImpl
    private Object target;
    public EmployeeInvocationHandler(Object target){
        this.target = target;
    }
Copier après la connexion

2> 🎜# pour être réel La méthode de sauvegarde de l'objet ajoute une logique de vérification des autorisations

    //代理对象控制执行方法
    //参数1:代理对象
    //参数2:真实对象的方法(使用方式得到方法对象)
    //参数3:真实对象方法参数列表
    //此处是代理对象对外暴露的可编辑的方法处理场所,代理对象每调用一个次方法,就会执行一次invoke
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String name = method.getName();
        if("save".equals(name) && !"admin".equals(SessionHolder.getCurrentUser())){
            throw new RuntimeException("当前非admin用户,不能执行save操作");
        }
        return method.invoke(target, args);
    }
Copier après la connexion
3> Renvoie l'objet proxy

Une fois la méthode exécutée, une classe proxy nommée : $ProxyX ( où X est le numéro de série, généralement la valeur par défaut est 0), cette classe proxy est construite dynamiquement par JDK.

    //获取jvm在内存中生成代理对象
    public Object getProxy(){
        return  Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }
Copier après la connexion

Proxy

 : La classe de contrôle proxy dynamique est la classe parent de la classe $ProxyX générée dynamiquement par JDK. Sa fonction est la suivante :

1>By. appeler la classe ProxyBuilder La méthode builder construit la classe d'objet proxy

private static Constructor<?> getProxyConstructor(Class<?> caller,
                                                      ClassLoader loader,
                                                      Class<?>... interfaces){
            return proxyCache.sub(intf).computeIfAbsent(
                loader,
                (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
            );
}
Copier après la connexion

2> renvoie une instance de la classe $ProxyX via la méthode newProxyInstance

   public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) {
    //...
   }
Copier après la connexion

$Proxy0  : App class runtime, la classe proxy construite dynamiquement par JDK, héritée de la classe Proxy

public class App {
    public static void main(String[] args) {
        //System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
        System.out.println("----------------真实对象--------------------");
        EmployeeServiceImpl employeeService = new EmployeeServiceImpl();
        employeeService.list();
        employeeService.save();
        System.out.println("----------------代理对象--------------------");
        SessionHolder.setCurrentUser("dafei");
        EmployeeInvocationHandler handler = 
                     new EmployeeInvocationHandler(employeeService);
        IEmployeeService proxy = (IEmployeeService) handler.getProxy();
        proxy.list();
        proxy.save();
 
    }
}
Copier après la connexion

Par défaut, la JVM n'enregistre pas les objets de bytecode de classe proxy créés dynamiquement. Vous pouvez configurer le. Paramètres proxy dans la méthode principale pour autoriser la rétention de code des octets

//JDK8之前
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//JDK8之后
System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
Copier après la connexion

Après l'exécution, l'objet bytecode de la classe proxy sera généré dans le répertoire racine du projet.

Afin de faciliter l'interprétation, après avoir supprimé certaines méthodes inutiles

$Proxy0 class#🎜🎜 #

public class $Proxy0 extends Proxy implements IEmployeeService {
    private static Method m4;
    private static Method m3;
    static {
        try {
            m4 = Class.forName("com.langfeiyes.proxy.demo.IEmployeeService")
                 .getMethod("save");
            m3 = Class.forName("com.langfeiyes.proxy.demo.IEmployeeService")
                 .getMethod("list");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public $Proxy0(InvocationHandler var1) throws Throwable {
        super(var1);
    }
    public final void save() throws Throwable {
        super.h.invoke(this, m4, (Object[])null);
    }
 
    public final void list() throws  Throwable{
        super.h.invoke(this, m3, (Object[])null);
    }
}
Copier après la connexion
Comment implémenter le proxy dynamique JDK en JavaD'après le code source, les caractéristiques de $Proxy0 :

1>Hérite de la classe Proxy , implémente l'interface IEmployeeService

2> reflète les méthodes de sauvegarde et de liste de l'interface IEmployeeService via des blocs statiques pour obtenir leurs objets de méthode Méthode

  • 3> Pour appeler le constructeur de la classe parent, vous devez passer le paramètre InvocationHandler

  • 4> L'interface IEmployeeService s'appuie sur la méthode .invoke de l'attribut h de la classe parent Proxy

  • La vérité est révélée

  • Toutes les classes participantes dans le proxy dynamique sont affichés ci-dessous : #🎜 🎜#

L'image ci-dessous est le diagramme de séquence de fonctionnement de l'image ci-dessus, suivez-la simplement#🎜 🎜#

# 🎜🎜#

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:yisu.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal