设计模式之代理模式

引言

代理模式:给目标对象提供一个代理(中间商),并通过代理对象实现对目标对象的调用,这样客户端通过代理对象,间接实现对目标对象的引用,这样就可以在代理对象中实现除具体业务之外的额外功能。

代理模式的组成结构

Subject(目标对象): 原始对象和代理对象都需要实现的目标接口

RealSubject(具体目标对象): 需要代理的目标对象

Proxy(代理对象): 代理目标对象的代理类

静态代理

静态代理:代理类和目标类需要实现同样的目标方法,同时在代理对象中维护一个目标对象的引用,通过代理对象的方法调用目标对象的方法

静态代理的示例

java 复制代码
//目标接口
public interface TargetService {
    public void request();
}
// 具体目标实现
public class TargetServiceImpl implements TargetService{
    @Override
    public void request() {
        System.out.println("TargetServiceImpl.request()");
    }
}
// 代理对象
public class ProxyServiceImpl implements TargetService{
    private TargetService targetService;

    public ProxyServiceImpl(TargetService targetService) {
        this.targetService = targetService;
    }
    @Override
    public void request() {
        System.out.println("before request ...");
        targetService.request();
        System.out.println("after request ...");
    }

    public static void main(String[] args) {
        TargetService targetService = new TargetServiceImpl();
        ProxyServiceImpl proxyService = new ProxyServiceImpl(targetService);
        proxyService.request();
    }
}
// 期望实现
//before request ...
//TargetServiceImpl.request()
//after request ...

静态代理实现相对比较简单,但是相对的比较繁琐,如果有许多或者函数需要实现代理功能如果都去实现接口会产生类爆炸,同时也增加了大量重复代码

动态代理

动态代理:与静态代理不同,动态代理不需要像静态代理那样去实现相应的目标接口,也就是我们并不会去创建相应的Java代理对象,而是在程序运行时直接将代理对象创建并通过反射API运行目标函数

jdk的动态代理

jdk动态代理是通过动态字节码技术,在程序运行的时候直接将代理对象生成,而不是和正常的程序执行一样,正常的执行需要先将class字节码通过classloader加载到jvm中创建class对象,然后通过代理类的class对象创建代理对象,动态字节码省去了classloader加载字节码的过程。

java 复制代码
// classloader 代理对象的类加载器,但是动态代理并不会创建代理对象,也就没有class文件也就没有相对应的类加载器(jvm会为每个class文件创建一个与之相对应的类加载器),此时就可以借用一个类加载器
// interfaces 目标类实现的接口
// 调用处理器,主要是实现代理对象的额外功能以及是实现对目标对象的调用,并返回目标对象返回值
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                                      
                                      
                                      
//调用处理器
public interface InvocationHandler {
    // proxy代理对象
    // method 目标对象方法
    //args 目标对象方法参数
    // 返回目标对应调用返回值
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}                                   

jdk动态代理示例

java 复制代码
public class JdkProxy {
    public static void main(String[] args) {
        TargetService targetService =new TargetServiceImpl();
        TargetHandler targetHandler = new TargetHandler(targetService);
        TargetService proxy = (TargetService) Proxy.newProxyInstance(TargetService.class.getClassLoader(), TargetServiceImpl.class.getInterfaces(), targetHandler);
        proxy.request();
    }

    static class TargetHandler implements InvocationHandler{
        private TargetService target;
        public TargetHandler(TargetService target){
            this.target = target;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("代理对象:"+proxy.getClass().getName());
            System.out.println("before request");
            Object obj =  method.invoke(target,args);
            System.out.println("after request");
            return  obj;
        }
    }
}

//预期结果
//代理对象:com.sun.proxy.$Proxy0
//before request
//TargetServiceImpl.request()
//after request

cglib动态代理

jdk动态代理的缺点就是代理对象必须要实现目标对象的接口,着有很大的局限性,因为目标对象可能压根就没有实现接口,此时jdk动态代理就无法实现代理操作,cglib是通过继承的方式来实现的,代理对象继承目标对象,从而实现子类调用父类的方法来实现代理

cglib动态代理示例

java 复制代码
public class CglibProxy {
    public static void main(String[] args) {
        // 创建代理对象
        TargetServiceImpl targetServiceImpl = new TargetServiceImpl();
        // 实现额外功能
        MyMethodInterceptor interceptor =new MyMethodInterceptor(targetServiceImpl);
        // 创建代理类
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(targetServiceImpl.getClass().getClassLoader());
        enhancer.setSuperclass(targetServiceImpl.getClass());
        enhancer.setCallback(interceptor);
        TargetServiceImpl targetServiceImplProxy = (TargetServiceImpl) enhancer.create();
        targetServiceImplProxy.request();

    }
    static class  MyMethodInterceptor implements MethodInterceptor{
        private TargetServiceImpl targetService;

        public MyMethodInterceptor(TargetServiceImpl targetService){
            this.targetService= targetService;
        }
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("before request");
            Object obj = method.invoke(targetService, objects);
            System.out.println("after request");
            return obj;
        }
    }
}

jdk和cglib总结

  1. 实现代理的方式是类似的,首先创建目标对象,其次实现额外的功能,最后生成代理对象调用目标方法
  2. jdk是通过接口来实现代理,cglib是通过继承目标类来实现代理
  3. 二者都是采用动态字节码技术实现

基于Aspect的AOP编程

Aspect是基于注解来实现AOP切面编程,这样可以极大的简化开发工作量

Aspect aop编程示例

java 复制代码
// 表明这是一个切面类
@Aspect
@Component
public class MyLogAspect {
    //定义一个切入点
    @Pointcut("execution(* org.wh.proxy.TargetService.*(..))")
    public void logPointCut(){}

    //创建额外功能
    @Around("logPointCut()")
    public Object logAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("================join log====================");
        Object result = joinPoint.proceed();
        return result;
    }
}
//目标接口
public interface TargetService {
    public void request();
}
//具体目标实现
@Service
public class TargetServiceImpl implements TargetService{
    @Override
    public void request() {
        System.out.println("TargetServiceImpl.request()");
    }
}
//单元测试
@SpringBootTest
public class TargetServiceTest {
    @Autowired
    private TargetService targetService;

    @Test
    public void test1(){
        targetService.request();
    }
}
//预期结果
//================join log====================
//TargetServiceImpl.request()

springboot现在默认的jdk动态代理已经是cglib,之前是jdk动态代理

相关推荐
骆晨学长20 分钟前
基于springboot的智慧社区微信小程序
java·数据库·spring boot·后端·微信小程序·小程序
AskHarries25 分钟前
利用反射实现动态代理
java·后端·reflect
Flying_Fish_roe1 小时前
Spring Boot-Session管理问题
java·spring boot·后端
战神刘玉栋1 小时前
《程序猿之设计模式实战 · 观察者模式》
python·观察者模式·设计模式
蘑菇头爱平底锅2 小时前
十万条数据渲染到页面上如何优化
前端·javascript·面试
测试界柠檬2 小时前
面试真题 | web自动化关闭浏览器,quit()和close()的区别
前端·自动化测试·软件测试·功能测试·程序人生·面试·自动化
hai405872 小时前
Spring Boot中的响应与分层解耦架构
spring boot·后端·架构
nakyoooooo2 小时前
【设计模式】工厂模式、单例模式、观察者模式、发布订阅模式
观察者模式·单例模式·设计模式
Redstone Monstrosity2 小时前
字节二面
前端·面试
Adolf_19933 小时前
Flask-JWT-Extended登录验证, 不用自定义
后端·python·flask