Java中代理模式的一点理解

2023-03-01 13:53:12 浏览数 (2)

Java中代理模式的一点理解

1、什么是代理模式?

代理模式中可主要分为客户端、代理类、目标类这三种角色。

  • 客户端:发起请求方。
  • 目标类:特定行为的实现类,也就是真正工作的人
  • 代理类:可以调用目标类的所有功能,并可以在此基础上扩展额外的工作。通过在代理类内部持有目标类的对象来实现代理功能。

以租房为例:

  • 客户端:租客
  • 目标类:房东
  • 代理类:中介。中介不仅代理出租目标房东的房子,还会处理其他工作。
2、代理模式有哪些?

代理模式有分为两类。

  • 静态代理:顾名思义,关键字静态,代理关系一目了然。在编译之前就了然的代理关系。
  • 动态代理:既然了静态代理,为什么还需要动态代理呢?当有多个代理关系时,代码就会凸显代码的冗余。因此使用动态代理,一不变应万变,没错,就是,只需要一个动态代理类,就可以实现多了个代理关系。
    • jdk动态代理:基于接口实现,在创建代理对象时,需要指定生成代理类实现的接口。
    • cglib动态代理:基于继承实现,即使目标类没有实现接口也可以正常生成代理对象。
3、代理模式的作用?

代理的作用就是不改变目标类的情况下,对目标类进行增强。细品后发现和Spring的Aop有些相似,是的,Spring利用了动态代理实现了Aop的强大功能。

4、实操体会一下

下面通过三个简单的代理实现代码,进一步理解代理模式在Java中的基本实现。

4.1、静态代理

静态代理直接将目标对象targeti通过构造方法传递进去,构建出来了代理对象staticProxy。

如下代码:

代码语言:javascript复制
/**
 * 目标类接口
 */
public interface HelloService {
    public String sayHello(String msg);
}


/**
 * 目标类实现类
 */
public class HelloServiceImpl implements HelloService{
    public String sayHello(String msg){
        System.out.println("你好呀!!");
        return "HelloServiceImpl:  "   msg;
    }
}

/**
 * 代理类
 */
public class StaticProxy implements HelloService {

    /**
     * 目标对象
     */
    private HelloService target;

    public StaticProxy(HelloService target){
        this.target = target;
    }

    @Override
    public String sayHello(String msg){
        // 1、增强逻辑,记录入参
        System.out.println("方法名称:  sayHello, 入参:  "   msg);

        // 2、使用放射调用目标对象方法
        String result = target.sayHello(msg);

        // 3、增强逻辑,记录出参
        System.out.println("方法名称:  sayHello, 返回值:  "   result);
        return result;
    }
}

/**
 * 调用代理类实现目标类的功能
 */ 
public class StaticTest{
    
    public static void main(String args[]){
        HelloService helloService = new HelloServiceImpl();
        // 1、静态代理
        HelloService staticProxy = new StaticProxy(helloService);
        String result = staticProxy.sayHello("静态代理");
        System.out.println("MainTest==静态代理:  "   result);
    }
}
/**
 *  方法名称:  sayHello, 入参:  静态代理
    你好呀!!
    方法名称:  sayHello, 返回值:  HelloServiceImpl:  静态代理
    MainTest==静态代理:  HelloServiceImpl:  静态代理
 */
4.2、jdk动态代理

jdk动态代理是先将目标对象targeti通过构造方法传递进去,然后通过getProxy()方法完成了代理的创建,最后将代理对象强转为了接口类型HelloService,由于jdk动态代理是基于接口实现的,生成的代理类会实现这个HelloService接口,所以是可以这样强转的。

如下代码:

代码语言:javascript复制
/**
 * jdk动态代理
 */
public class JdkDynamicProxy implements InvocationHandler {

    /**
     * 目标对象
     */
    private Object target;

    public JdkDynamicProxy(Object target){
        this.target = target;
    }

    /**
     * 获取目标对象的代理
     * @return 返回代理对象
     */
    public Object getProxy(){
        // 创建代理对象
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),  // 指定当前目标对象使用类加载器
                target.getClass().getInterfaces(),   // 目标对象实现的接口和类型
                this                                 // 设置回调顺序
        );
    }

    /**
     * 对目标对象的方法进行增强
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1、增强逻辑,记录入参
        System.out.println("方法名称:  "   method.getName()   ", 入参:  "   Arrays.toString(args));

        // 2、使用放射调用目标对象方法
        Object result = method.invoke(target, args);

        // 3、增强逻辑,记录出参
        System.out.println("方法名称:  "   method.getName()   ", 返回值:  "   result);
        return result;
    }
}

/**
 * 调用代理类实现目标类的功能
 */ 
public class JdkTest{
    
    public static void main(String args[]){
        HelloService helloService = new HelloServiceImpl();
        HelloService jdkProxy = (HelloService) new JdkDynamicProxy(helloService).getProxy();
        String result = jdkProxy.sayHello("jdk动态代理");
        System.out.println("MainTest==jdk动态代理:  "   result);
        System.out.println("====================================");
    }
}
/*
    方法名称:  sayHello, 入参:  [jdk动态代理]
    你好呀!!
    方法名称:  sayHello, 返回值:  HelloServiceImpl:  jdk动态代理
    MainTest==jdk动态代理:  HelloServiceImpl:  jdk动态代理
 */
4.3、cglib动态代理

cglib动态代理是先将目标对象targeti通过构造方法传递进去,然后通过getProxy0方法完成了代理的创建,只不过这里是将代理对象强转为了HelloServicelmpl类型,因为cglib:是基于继承来的,生成的代理类本质是HelloServicelmpl类的子类,所以这里是可以强转为HelloServicelmpl类型的。

如下代码:

代码语言:javascript复制
/**
 * Cglib动态代理
 */
public class CglibDynamicProxy implements MethodInterceptor {

    /**
     * 目标对象
     */
    private Object target;

    public CglibDynamicProxy(Object target){
        this.target = target;
    }

    /**
     * 获取目标对象的代理
     * @return 返回代理对象
     */
    public Object getProxy(){
        // 字节码增强器, 可以为没有实现接口的类创建代理
        Enhancer enhancer = new Enhancer();
        // 因为Cglib的原理是动态生成要代理类的子类,然后子类重写父类方法,因此设置代理类的父类类型
        enhancer.setSuperclass(target.getClass());
        // 设置回调方法
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }

    /**
     * 对目标对象的方法进行增强
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 1、增强逻辑,记录入参
        System.out.println("方法名称:  "   method.getName()   ", 入参:  "   Arrays.toString(args));

        // 2、使用放射调用目标对象方法
        Object result = methodProxy.invoke(target, args);

        // 3、增强逻辑,记录出参
        System.out.println("方法名称:  "   method.getName()   ", 返回值:  "   result);
        return result;
    }
}


/**
 * 调用代理类实现目标类的功能
 */ 
public class JdkTest{
    
    public static void main(String args[]){
        HelloServiceImpl cglibProxy = (HelloServiceImpl) new CglibDynamicProxy(new HelloServiceImpl()).getProxy();
        String result = cglibProxy.sayHello("cglib动态代理");
        System.out.println("MainTest==cglib动态代理:  "   result);
        System.out.println("====================================");
    }
}
/*
    方法名称:  sayHello, 入参:  [cglib动态代理]
    你好呀!!
    方法名称:  sayHello, 返回值:  HelloServiceImpl:  cglib动态代理
    MainTest==cglib动态代理:  HelloServiceImpl:  cglib动态代理
 */
5、总结

1、代理模式分为两大类,分别是静态代理和动态代理,而动态代理又分为jdk动态代理和cglib动态代理。

2、单一实现可以使用静态代理,多个目标类需要被代理则需要使用动态代理模式,实现接口使用jdk动态代理,否则使用cglib动态代理。

3、动态代理是Spring中AOP实现的基础。

0 人点赞