设计模式之代理模式

静态代理

静态代理的本质,就是通过特有方法对指定方法,在调用前后进行程序处理

1.定义发送短信的接口
public interface SmsService {
    String send(String message);
}

2.实现发送短信的接口
public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}

3.创建代理类并同样实现发送短信的接口
public class SmsProxy implements SmsService {

    private final SmsService smsService;

    public SmsProxy(SmsService smsService) {
        this.smsService = smsService;
    }

    @Override
    public String send(String message) {
        //调用方法之前,我们可以添加自己的操作
        System.out.println("before method send()");
        smsService.send(message);
        //调用方法之后,我们同样可以添加自己的操作
        System.out.println("after method send()");
        return null;
    }
}

4.实际使用
public class Main {
    public static void main(String[] args) {
        SmsService smsService = new SmsServiceImpl();
        SmsProxy smsProxy = new SmsProxy(smsService);
        smsProxy.send("java");
    }
}

运行上述代码之后,控制台打印出:
before method send()
send message:java
after method send()

动态代理

动态代理分为 JDK动态代理 与 CGLib动态代理。相对于静态代理,动态代理更灵活。本质就是通过反射的方式创建代理对象。从JVM 角度讲,静态代理会生成一个指定的class文件,而动态代理是在运行时动态生成字节码,加载到JVM 中。JDK动态代理效率高,首选此方式代理。

JDK动态代理

注意点:

  1. CustomHandler 必须实现 InvocationHandler
  2. trageClass 必须实现接口
  3. 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象,CustomHandler 的invoke方法中自定义前后程序。
1.定义发送短信的接口
public interface SmsService {
    String send(String message);
}

2.实现发送短信的接口
public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}

3.定义一个 JDK 动态代理类
public class CustomHandler implements InvocationHandler {
    
    // 代理类中的真实对象
    private final Object target;

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

    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        //调用方法之前,我们可以添加自己的操作
        System.out.println("before method " + method.getName());
        Object result = method.invoke(target, args);
        //调用方法之后,我们同样可以添加自己的操作
        System.out.println("after method " + method.getName());
        return result;
    }
}

4.获取代理对象的工厂类
public class JdkProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(), // 目标类的类加载
                target.getClass().getInterfaces(),  // 代理需要实现的接口,可指定多个
                new CustomHandler (target)   // 代理对象对应的自定义 InvocationHandler
        );
    }
}

5.实际使用
SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
smsService.send("java");

运行上述代码之后,控制台打印出:
before method send
send message:java
after method send

CGLib 动态代理

注意点:

  1. trageClass 必须有父类
  2. CustomInterceptor 必须  MethodInterceptor,本质就是使用拦截器
  3. 通过 Enhancer的creator方法实现代理
  4. CGLib 是基于java的扩展功能,有自己的编译器
1.实现一个使用阿里云发送短信的类
package github.javaguide.dynamicProxy.cglibDynamicProxy;

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

2.自定义 MethodInterceptor(方法拦截器)
public class CustomInterceptor  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;
    }

}

3.获取代理类
public class CglibProxyFactory {

    public static Object getProxy(Class<?> clazz) {
        // 创建动态代理增强类
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        // 设置被代理类
        enhancer.setSuperclass(clazz);
        // 设置方法拦截器
        enhancer.setCallback(new CustomInterceptor());
        // 创建代理类
        return enhancer.create();
    }
}

4.实际使用
AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
aliSmsService.send("java");

运行上述代码之后,控制台打印出:
before method send
send message:java
after method send

Spring 中动态代理

Spring AOP 的本质就是动态代理,以下代码:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {


    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        // 这段代码用来判断选择哪种创建代理对象的方式
        // config.isOptimize()   是否对代理类的生成使用策略优化 其作用是和isProxyTargetClass是一样的 默认为false
        // config.isProxyTargetClass() 是否使用Cglib的方式创建代理对象 默认为false
        // hasNoUserSuppliedProxyInterfaces目标类是否有接口存在 且只有一个接口的时候接口类型不是SpringProxy类型
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            // 上面的三个方法有一个为true的话,则进入到这里
            // 从AdvisedSupport中获取目标类 类对象
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            // 判断目标类是否是接口 如果目标类是接口的话,则还是使用JDK的方式生成代理对象
            // 如果目标类是Proxy类型 则还是使用JDK的方式生成代理对象
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            // 配置了使用Cglib进行动态代理或者目标类没有接口,那么使用Cglib的方式创建代理对象
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            // 使用JDK的提供的代理方式生成代理对象
            return new JdkDynamicAopProxy(config);
        }
    }

    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        // 获取代理类型接口
        Class<?>[] ifcs = config.getProxiedInterfaces();
        return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
    }

}
posted @ 2022-04-10 16:33  之士咖啡  阅读(38)  评论(0编辑  收藏  举报