架构师成长之路-设计模式-09.代理模式、静态代理、jdk动态代理、cglib动态代理

代理模式 Proxy Pattern

代理模式是指为目标对象提供一种代理,以控制这个对象的访问,是一种结构型模式。
代理对象在目标对象与调用者之前起到中介的作用。
代理模式用于目标对象进行增强,在调用前后增加一些代码逻辑。
代理模式用于保护目标对象
代理模式可以理解为中介者模式。代理对象了解目标对象,负责与目标对象交互,并附加一些逻辑。
代理分为静态代理和动态代理。

静态代理模式的三种角色

在这里插入图片描述

  • ISubject : 接口层,主题
  • SubjectImpl: 实现层,目标对象
  • SubjectProxy: 代理层,持有目标对象,实现 ISubject 接口,并附加增强的逻辑。

静态代理实例

// 主题,接口抽象层
public interface ISubject {
    void doSomething();
}
// 实现,目标对象
public class SubjectImpl implements ISubject {
    @Override
    public void doSomething() {
        System.out.println("SubjectImpl do something");
    }
}
// 代理对象
public class SubjectProxy implements ISubject {
    // 持有目标对象
    private ISubject target;
    public SubjectProxy(ISubject target) {
        this.target = target;
    }
    @Override
    public void doSomething() {
        // 在调用目标对象前
        System.out.println("SubjectProxy . before do something ");
        // 调用目标对象
        target.doSomething();
        // 在调用目标对象后
        System.out.println("SubjectProxy . after do something ");
    }
}

动态代理

为什么要使用动态代理? 静态代理需要开发者静态编写代码,动态代理更灵活,开发者仅需关注需要增强、需要改变的逻辑。
动态代理分为jdk动态代理,和cglib动态代理。

jdk动态代理

先准备一个接口和实现类
// 接口
public interface ISubject {
    void doSomething();
    void eat();
}
// 实现类
public class SubjectImpl implements ISubject {
    @Override
    public void doSomething() {
        System.out.println("SubjectImpl do something");
    }
    @Override
    public void eat() {
        System.out.println("SubjectImpl eat..");
    }
}
创建 InvocationHandler
public class ProxyInvocationHandler implements InvocationHandler {
    // 目标实现类,这不是必须的
    private final ISubject target;
    public ProxyInvocationHandler(ISubject target) {
        this.target = target;
    }
    /**
     * @param proxy  代理对象
     * @param method 调用的方法
     * @param args   调用的方法参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodInfo = method.toString();
        // 处理前
        System.out.println("--before method [" + methodInfo + "] call");

        // 执行目标方法
        // 这里可以由开发者决定执行、或者不执行
        Object rt = method.invoke(target, args);

        // 调用后执行
        System.out.println("--after method [" + methodInfo + "] call");

        return rt;
    }
}
创建jdk动态代理对象
public static void main(String[] args) {
    final SubjectImpl target = new SubjectImpl();
    Class<?>[] interfaces = {ISubject.class};
    // 创建一个代理对象
    ISubject proxyObj = (ISubject) Proxy.newProxyInstance(
            // 类加载器
            ISubject.class.getClassLoader(),
            // 代理对象将实现的接口
            interfaces,
            // 调用处理器,对目标方法的调用,将转到这个处理器里面
            new ProxyInvocationHandler(target)
    );
    proxyObj.doSomething();
    proxyObj.eat();
    proxyObj.toString();
}

执行结果:

--before method [public abstract void org.smileface.designpattern.proxy.jdkProxy.ISubject.doSomething()] call
SubjectImpl do something
--after method [public abstract void org.smileface.designpattern.proxy.jdkProxy.ISubject.doSomething()] call
--before method [public abstract void org.smileface.designpattern.proxy.jdkProxy.ISubject.eat()] call
SubjectImpl eat..
--after method [public abstract void org.smileface.designpattern.proxy.jdkProxy.ISubject.eat()] call
--before method [public java.lang.String java.lang.Object.toString()] call
--after method [public java.lang.String java.lang.Object.toString()] call

可以看到,每次执行代理的对象的方法,都会触发 InvocationHandler.invoke 方法的调用,并执行了我们附加的执行前、执行后逻辑。

Proxy.newProxyInstance参数说明
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

第一个参数(ClassLoader loader):类加载器,使用那个类加载器加载代理对象
第二个参数(Class<?>[] interfaces):接口集合,新创建的代理对象,将实现这些给定的接口
第三个参数(InvocationHandler h):调用处理器,方法拦截器。代理对象调用的所有方法都将转到这个处理器中,此处理器可在方法调用前增加逻辑、可决定是否执行目标方法、可在调用后增加逻辑等等。

cglib动态代理

pom
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.7</version>
</dependency>
先准备一个接口和实现类
public interface ISubject {
    void doSomething();
}
public class SubjectImpl implements ISubject {
    @Override
    public void doSomething() {
        System.out.println("SubjectImpl do something");
    }
}
创建cglib动态代理对象
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class CglibMain {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(SubjectImpl.class);
        enhancer.setCallback(new MethodInterceptor() {
            /**
             *
             * @param o 代理对象(EnhancerByCGLIB )
             * @param method 原方法 SubjectImpl类的方法
             * @param args  方法参数
             * @param methodProxy 代理对象的方法
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

                // 方法执行前,执行的逻辑
                System.out.println("before ... " + method);

                // 执行目标方法(由开发者决定是否要调用目标方法)
                Object result = methodProxy.invokeSuper(o, args);

                // 方法执行后执行的逻辑
                System.out.println("after ..." + method);

                return result;
            }
        });
		// 创建代理对象
        ISubject subject = (ISubject) enhancer.create();
        subject.doSomething();
    }
}

执行结果如下:

before ... public void org.smileface.designpattern.proxy.cglib.SubjectImpl.doSomething()
SubjectImpl do something
after ...public void org.smileface.designpattern.proxy.cglib.SubjectImpl.doSomething()

Enhancer 是cglib 最核心的类,相当于jdk动态代理的proxy类。

jdk动态代理和cglib动态代理的区别

  1. JDK动态代理实现了被代理的接口,CGLIB动态代理继承了被代理对象
  2. 两者都在运行期生成字节码文件,JDK动态代理直接写字节码文件,CGLIB使用ASM框架写字节码文件,CGLIB实现更复杂,所以生成代理类这个过程CGLIB效率更低。
  3. JDK动态代理调用代理方法时通过反射机制调用的,CGLIB是通过FastClass机制调用的,所以CGLIB的执行效率更高。
FastClass机制

简单理解:为代理类和被代理类各生成一个类,这个类会为代理类和被代理类的方法分配一个index,通过这个index当做入参,FastClass直接定为到要调用的方法并直接调用,省去了反射的时间。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值