代理模式

1. 代理模式

1.1 何为代理?

代理是通过代理对象访问目标对象,这样可以在目标对象的逻辑进行增强,或者称之为改变。

代理分为静态代理动态代理两种。

1.2 为什么要代理?

原因一:你可能没有目标类的源码,也就是java文件,这样你如何修改逻辑?

原因二:若是直接在目标类上修改某方法逻辑,而项目中有多处都需要调用该类的该方法,会导致所有调用处的逻辑都发生改变,但假如我只想在某一处调用的地方添加自己的增强逻辑呢?

2. 静态代理

2.1 介绍

静态代理和动态代理都是实现代理的两种方式,区别在于:使用静态代理是需要编写代理类的,也就是最终你可以在你项目编译的文件中看到class文件,而动态代理是直接动态生成字节码文件(类加载器加载class文件到JVM中就变成了字节码文件)。

实现静态代理又包含两种方式,分别是继承聚合

2.2 继承

继承方式的核心思想是:使用代理类继承目标类,重写目标类的方法,并在重写的方法中调用目标类的方法,再添加代理的逻辑。

目标类:

package cn.it.show.inherit;

/**
 * 目标类

 */
public class Service {
    public void test(){
        System.out.println("---目标类逻辑---");
    }
}

 代理类:

package cn.it.show.inherit;

public class ProxyService extends Service{

    @Override
    public void test() {
        super.test();
        System.out.println("---代理类逻辑---");
    }
}

运行主类:

package cn.it.show;


public class TestMain {
    public static void main(String[] args) {
        Service Service = new ProxyService();
        Service.test();
    }
}

2.3 聚合

通过聚合实现代理,一般有一个前提,就是代理类和目标类都实现同一个接口。我们看下面一个代码样例。

公用接口

package cn.it.show.aggregation;


public interface Service {

    /**
     * 接口方法
     */
    public void test();
}

目标类

package cn.it.show.aggregation;

public class TargetService implements Service{

    public void test() {
        System.out.println("---目标类逻辑---");
    }
}

代理类

package cn.it.show.aggregation;


public class ProxyService implements Service {

    // target为目标对象
    private Service target;

    public ProxyService(Service target) {
        this.target = target;
    }

    public void test() {
        // 调用目标对象的逻辑
        target.test();
        // 编写代理逻辑
        System.out.println("---代理类逻辑---");
    }
}

运行主类

package cn.it.aggregation;

import cn.it.show.juhe.ProxyService;
import cn.it.show.juhe.Service;
import cn.it.show.juhe.TargetService;


public class TestMain {
    public static void main(String[] args) {
       
        Service service = new ProxyService(new TargetService());
        service.test();
    }
}

聚合的核心思想是:代理类和目标类都实现同一个接口,创建代理对象时,通过构造方法或者set方法将目标对象聚合进去。

3. 动态代理

3.1 介绍

         动态代理即动态的生成字节码文件到JVM中,并不会看到class文件。动态代理又分为JDK动态代理和Cglib动态代理,两种的区别在于:JDK动态代理都是对接口进行代理,而Cglib一般是对类进行代理。

        值得一提的是,动态代理技术在开源框架中经常被使用到。例如Mybatis、还有Spring源码中也在很多地方使用了动态代理,像@Configuration注解,还有SpringCloud的Feign组件等等。

3.2 JDK动态代理代码

 因为这个代理类和方法由JDK自带的,类名:Proxy

JdkService

package cn.it.show.jdk;

public interface JdkService {
    /**
     * 接口方法
     */
    public void test();
}

MyInvocationHandler

package cn.it.show.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;


public class MyInvocationHandler implements InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("-----代理逻辑-----");
        return null;
    }
}

Test类

package cn.it.show;

import cn.it.show.jdk.JdkService;
import cn.it.show.jdk.MyInvocationHandler;

import java.lang.reflect.Proxy;


public class TestMain {

    public static void main(String[] args) {
       
        // 作用:为JdkService接口动态生成一个代理类,并加载到JVM,返回一个JdkService类型的代理对象
        JdkService o = (JdkService) Proxy.newProxyInstance(JdkService.class.getClassLoader(),
                new Class[]{JdkService.class},
                new MyInvocationHandler());
        // 实际上会执行MyInvocationHandler的invoke方法
        o.test();
    }
}

 

4. CGLIB动态代理

为什么要使用CGLIB代理

特点:cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类或实现java接口,它广泛的被许多AOP的框架使用,为他们提供方法的拦截。

如果一个类并没有实现任何的接口,则无法使用上面所说的JDK动态代理,这时需要使用CGLIB代理,本质上是对原有类的继承,子类重新相应的方法。

使用cglib动态代理

  1. 直接引入Spring-Core核心包就可以,已经包含cglib功能

  2. 在spring-core中依赖的jar包中就包含了cglib

代码

真实角色

package com.it.proxy;

/**
 * 真实对象:被代理的对象
 */
public class SuperStar {

    /**
     * 唱歌
     * @param song
     */
    public void sing(String song) {
        System.out.println("小明唱:" + song);
    }

    /**
     * 演戏
     */
    public void act(double money) {
        System.out.println("拍戏出场费:" + money);
    }
}

Fans类

package com.it.proxy;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * 使用代理模式
 */
public class Fans {

    public static void main(String[] args) {
        System.out.println("真实对象:");
        //真实对象
        SuperStar s1 = new SuperStar();
        //唱歌的方法不变
        s1.sing("常回家看看");
        //对演戏的方法进行增强
        s1.act(800);

        System.out.println("代理对象:");
        /**
         * 创建代理对象
         * 参数1:被代理的类类型
         * 参数2:回调函数
         */
        SuperStar s2 = (SuperStar) Enhancer.create(SuperStar.class, new MethodInterceptor() {
            /**
             * 每个被代理的方法都会执行一次
             * @param proxy 生成的代理对象
             * @param method 被代理的方法对象
             * @param args 方法的参数
             * @param methodProxy 生成代理方法
             * @return 方法运行的返回值
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //获取方法名字
                String methodName = method.getName();
                //判断是不是act方法,对原有的功能进行增强
                if ("act".equals(methodName)) {
                    //获取出场费价格
                    double money = (double) args[0];
                    if (money < 1000) {
                        System.out.println("没有档期");
                        return null;
                    }
                }
                //调用原来的方法
                return method.invoke(s1, args);
            }
        });

        //调用代理对象方法
        s2.sing("绿光");
        s2.act(1700);
    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值