Java代理机制

Java代理

代理模式:提高真实对象的封装性、拓展性,采用代理对象来代替对真实对象的访问,在不修改原目标对象的基础上,进行额外的功能操作。

Java代理分为静态代理和动态代理

重点关注一下基于反射的动态代理,此部分为Java安全中的常见机制

静态代理

直接上代码比较好说明

首先我有一个Person的接口类, 定义苏醒和睡眠两种函数声明

// 接口提供函数声明,但不提供具体定义
public interface Person
{

    void wakeup();

    void sleep();
}

Student以Person为接口进行具体类实现

// 从Person接口进行二次开发
public class Student implements Person{

    public String name;

    // 构造函数
    public Student(){

    }

    public Student(String name){
        this.name = name;
    }

    @Override
    public void wakeup() {
        System.out.printf("%s wake up!\n" ,name);
    }

    @Override
    public void sleep() {
        System.out.printf("%s sleep!\n", name);
    }

}

再为Person设置代理类

public class PersonProxy implements Person {

    private Person person;

    public PersonProxy(Person person) {
        this.person = person;
    }

    @Override
    public void wakeup() {
        System.out.println("Good Morning!");
        person.wakeup();
    }

    @Override
    public void sleep() {
        System.out.println("Good Night!");
        person.sleep();
    }

}

main函数实现

public class Main {
    public static void main(String[] args)  throws Exception{
         Person student = new Student("icfh");
         PersonProxy studentProxy = new PersonProxy(student);

         studentProxy.wakeup();
         studentProxy.sleep();

    }
}

运行结果:

image-20230711210304172

上述一顿代码操作可能有点懵逼

但是对于同一个接口类而言, 需要为其实现一个特定的代理类, 此为静态代理的一个缺点

动态代理

1. JDK动态代理机制(反射代理)

  1. 定义一个接口及其实现类;
  2. 自定义 InvocationHandler 并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
  3. 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象

对于Person接口类的具体实现如上

JDK代理类

import java.lang.reflect.Method;

public class JdkProxy implements InvocationHandler {

    private Object bean;


    public JdkProxy(Object bean){
        this.bean = bean;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 获取运行时的函数
        String methodName = method.getName();
        
        // Hook指定的方法,完成代理工作逻辑
        if(methodName.equals("wakeup")){
            System.out.println("GoodMorning!");
        }else if(methodName.equals("sleep")){
            System.out.println("GoodNight!");
        }

        
       	// 再调用被代理的类的相应方法
        return method.invoke(bean, args);
    }
}

main函数

public class Main {

    public static void main(String[] args) throws Throwable{       
        // 代理类绑定需要代理的类实例
        JdkProxy proxy = new JdkProxy(new Student("icfh"));
        // 生成绑定对象实例
        Person student = (Person) Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[]{Person.class}, proxy);
        // 经过代理之后,调用原生方法时会先经过代理方法调用。然后在代理方法中再调用原生方法。
        student.wakeup();
        student.sleep();
    }

}

2. CGLIB动态代理机制(继承代理)

可以发现,使用JDK进行动态代理时,只能代理那些实现了接口的类。

CGLIBopen in new window(Code Generation Library)是一个基于ASM字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。

使用方法:

  1. 定义一个类
  2. 自定义实现MethodIntercepter接口类实现并重写intercept方法(原生方法调用时,intercept会拦截,有点像反射代理中的invoke)
  3. 通过Enhancer类的create()进行创建对象
  4. 在调用原生方法时会自动进入代理方法

原生类:

public class AliSmsService {

    public String send(String message){
        System.out.println("send message:"+ message);
        return message;
    }
}

实现MethodInterceptor接口类,然后重写intercept函数,完成代理

import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class DebugMethodInterceptor implements MethodInterceptor{
    @Override
    public Object intercept(Object o, Method method, Object[] args,MethodProxy methodProxy) throws Throwable{
        System.out.println("before method "+method.getName());
        // 原生方法调用
        Object object = methodProxy.invokeSuper(o, args);
        System.out.println("after method "+method.getName());
        return object;
    }

}

Main函数

import net.sf.cglib.proxy.Enhancer;

public class Main{
    public static void main(String[] args) throws Throwable{
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(AliSmsService.class.getClassLoader());
        enhancer.setSuperclass(AliSmsService.class);
        enhancer.setCallback(new DebugMethodInterceptor());
        AliSmsService a = (AliSmsService) enhancer.create();
        a.send("java");
    }
}

静态代理和动态代理的比较

  • 静态代理:
  1. 需要实现接口类
  2. 代理类中需要实现接口类中要进行代理的每个方法

image-20230712120002309

  • 基于反射的动态代理
  1. 仍旧需要实现接口类
  2. 需要重写invoke方法,所有需要代理的原生方法都会在invoke方法中被拓展调用
  • 基于继承的动态代理
  1. 可以代理未实现任何接口的类
  2. 代理类需要实现MethodInterceptor接口,重写拦截器intercept

参考链接

  1. https://segmentfault.com/a/1190000040680716

  2. https://javaguide.cn/java/basis/proxy.html

posted @ 2023-07-12 12:18  Icfh  阅读(19)  评论(0编辑  收藏  举报