代理模式

定义

  • 为其它对象提供一种代理,以控制对这个对象的访问
  • 代理对象在客户端和目标对象之间起到中介的作用

如何理解呢?就跟我们租房子是一样的,假设我们找中介租房子,租那种全托管的房子,出租出来的房子是房东的,房东是目标对象,合同草拟、水电费等都是代理类来做,代理类就是中介,但是我们可以直接和代理签租房合同,不需要和房东直接接触,也就是说中介代理房东,来和我们签合同,并且对目标对象做了一些增强,例如在签合同前先草拟合同,签完合同后,进行水电费结算等,这两点可以理解为对租房子方法的增强。

类型

结构型

使用场景

  • 保护目标对象
    例如,我们连房东叫什么都不知道,房东长什么样都不知道
  • 增强目标对象
    例如,增强目标对象的租房子这个行为方法,也是增强目标对象

优点

  • 代理对象能将代理对象和真实被调用的目标对象分离
  • 一定程度上降低了系统的耦合度,扩展性好
  • 保护目标对象
  • 增强目标对象

缺点

  • 代理模式会造成系统设计中类的数目增加
  • 在客户端和目标对象间增加一个代理对象,会造成请求处理速度变慢
  • 增加系统的复杂度

扩展

  • 静态代理(在代码中显示的指定代理)
  • 动态代理
    JDK中的动态代理,只能对实现的接口的类生成代理,并不能针对一个具体的实现类,jdk中也分静态代理、动态代理。JDK动态代理中,用到的代理类,是在程序调用到代理类的对象时,才由jvm真正创建,jvm根据传进来的业务实现类对象,以及方法名,动态的创建了一个class类的文件,并且这个class文件,被字节码引擎执行,然后通过该代理类的对象进行方法调用,那我们要做的就是把代理类的实现写好,比如说before、after怎么写,这些都是目标实现类方法层次的增强。
    如果我们业务实现类没有实现接口,而是直接定义业务方法的话,就没有办法直接使用jdk的动态代理了。同样,接口中有两个方法,实现接口的类中实现这两个方法,又定义了一个新的接口中没有的方法,这个方法也是无法动态代理的。
  • CGLib代理
    是可以代理类的,就是对类的代理实现,原理很简单:如果我们代理一个类,CGLib会生成一个被代理类的子类,覆盖其中的方法,也就是说通过继承、然后重写,因为是继承,如果这个类是final的,那这个类是无法别继承的,如果这个类不是继承的,里面的方法是fnial的,这个方法是无法被重写的,所以,使用CGLib代理的时候,对于final这个修饰符一定要格外关注,在code review时候,我们也会格外检查关于final这个修饰符的使用情况。

spring代理选择

  • 当bean有实现接口时,spring就会使用JDK动态代理
  • 当bean没有实现接口时,Spring会使用CGlib
  • 可以强制使用CGLib
    在spring的配置中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>
    spring-core的参考资料:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html

代理速度对比

  • CGLib
    底层通过ASM字节码生成的,跟lombook一样,比使用java反射效率高
  • jdk动态代理

在万次执行的性能对比下,jdk7、8比CGLIb快20%左右,这在工作中假如只能使用CGLib来代理的话,这点差别也可以忽略,但是,对要求特别苛刻的场景,这个代理方式也要注意,还是根据实际的业务需要,来决定选择哪种方式。

相关设计模式

  • 代理模式和装饰者模式
    实现上相似,但是目的不同,装饰者模式是给对象加上行为,而代理模式是控制访问,代理模式更加注重通过设置代理人的方式,来增强目标对象,它增强的方式一般是通过增强目标对象的某些行为
  • 代理模式和适配器模式
    适配器模式主要考虑改变所代理对象的接口,而代理模式是不能改变所代理类的接口的

coding

静态代理

在这里插入图片描述

package com.design.pattern.structural.proxy;

public class Order {
    private  Object orderInfo;
    private Integer userId;

    public Object getOrderInfo() {
        return orderInfo;
    }

    public void setOrderInfo(Object orderInfo) {
        this.orderInfo = orderInfo;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }
}
package com.design.pattern.structural.proxy;

public interface IOrderService {
    int saveOrder(Order order);
}
package com.design.pattern.structural.proxy;

public interface IOrderDao {
    int insert(Order order);
}
package com.design.pattern.structural.proxy;

public class OrderServiceImpl implements IOrderService {
    private IOrderDao iOrderDao;
    @Override
    public int saveOrder(Order order) {
        //spring会自动注入,我们demo中直接new了
        iOrderDao=new OrderDaoImpl();
        System.out.println("Service层调用dao层添加Order");
        return iOrderDao.insert(order);
    }
}
package com.design.pattern.structural.proxy;

public class OrderDaoImpl implements IOrderDao {
    @Override
    public int insert(Order order) {
        System.out.println("Dao层添加Order成功");
        return 1;
    }
}
package com.design.pattern.structural.proxy.db;

public class DataSourceContextHolder {
    private static final ThreadLocal<String> CONTEXT_HOLDER=new ThreadLocal<>();

    public static void setDBType(String dbType){
        CONTEXT_HOLDER.set(dbType);
    }

    public static String getDBType(){
        return CONTEXT_HOLDER.get();
    }
    public static void clearDBType(){
        CONTEXT_HOLDER.remove();
    }
}
package com.design.pattern.structural.proxy.db;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDBType();
    }
}
package com.design.pattern.structural.proxy.staticproxy;

import com.design.pattern.structural.proxy.IOrderService;
import com.design.pattern.structural.proxy.Order;
import com.design.pattern.structural.proxy.OrderServiceImpl;
import com.design.pattern.structural.proxy.db.DataSourceContextHolder;

public class OrderServciceStaticProxy {
    private IOrderService iOrderService;

    int saveOrder(Order order){
        beforeMethod(order);
        iOrderService=new OrderServiceImpl();
        int result=iOrderService.saveOrder(order);
        afterMethod();
        return result;
    }

    private void beforeMethod(Order order){
        int userId=order.getUserId();
        int dbRouter=userId%2;
        System.out.println("静态代理分配到【db"+dbRouter+"】处理数据");
        //todo 设置dataSource
        DataSourceContextHolder.setDBType("db"+String.valueOf(dbRouter));
        System.out.println("静态代理 before code");
    }
    private void afterMethod(){
        System.out.println("静态代理 after code");
    }

}

package com.design.pattern.structural.proxy.staticproxy;

import com.design.pattern.structural.proxy.Order;

public class Test {
    public static void main(String[] args) {
        Order order=new Order();
        order.setUserId(1);
        OrderServciceStaticProxy orderServciceStaticProxy=new OrderServciceStaticProxy();
        orderServciceStaticProxy.saveOrder(order);
    }
}

运行结果:

静态代理分配到【db1】处理数据
静态代理 before code
Service层调用dao层添加Order
Dao层添加Order成功
静态代理 after code

我们把Test中userId改为2,再次运行

静态代理分配到【db0】处理数据
静态代理 before code
Service层调用dao层添加Order
Dao层添加Order成功
静态代理 after code

我们一起看下UML图
在这里插入图片描述
看到有些类是分在包里的,我们看一个大包的时候,怎么看呢?很简单,里面有一个package我们右键,选择“Expend Node”,
在这里插入图片描述
然后把各项属性都打开,
在这里插入图片描述
首先,OrderDaoImpl、OrderrServiceImpl分别实现自己的接口,然后,OrderrServiceImpl里面组合了IOrderDao这个接口,OrderServiceStaticProxy静态代理类里组合了IOrderService接口,同时它增加了被代理类OrderServiceImpl的实现

动态代理

在静态代理的demo上新建一个类

package com.design.pattern.structural.proxy.dynamicproxy;
import com.design.pattern.structural.proxy.Order;
import com.design.pattern.structural.proxy.db.DataSourceContextHolder;

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

public class OrderServiceDynamicProxy implements InvocationHandler {
    /**声明下目标*/
    private Object target;

    public OrderServiceDynamicProxy(Object target) {
        this.target = target;
    }
    public Object bind(){
        Class cls=target.getClass();
        //生成了一个目标类的代理对象
        return Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),this);
    }

    /**
     * @param proxy  我们平时在写动态代理invoke方法实现的时候,几乎用不上
     * @param method 要被增强的方法对象
     * @param args   是第二个参数方法的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object argObject=args[0];
        beforeMethod(argObject);
        Object object=method.invoke(target,args);
        afterMethod();
        return object;
    }

    private void beforeMethod(Object obj){
        int userId=0;
        System.out.println("动态代理 before code");
        if(obj instanceof Order){
            Order order=(Order) obj;
            userId=order.getUserId();
        }
        int dbRouter=userId%2;
        System.out.println("动态代理分配到【db"+dbRouter+"】处理数据");
        //todo 设置dataSource
        DataSourceContextHolder.setDBType("db"+String.valueOf(dbRouter));
    }
    private void afterMethod(){
        System.out.println("动态代理 after code");
    }
}

package com.design.pattern.structural.proxy.dynamicproxy;

import com.design.pattern.structural.proxy.IOrderService;
import com.design.pattern.structural.proxy.Order;
import com.design.pattern.structural.proxy.OrderServiceImpl;

public class Test {
    public static void main(String[] args) {
        Order order=new Order();
        order.setUserId(1);
        IOrderService orderServiceDynamicProxy= (IOrderService) new OrderServiceDynamicProxy(new OrderServiceImpl()).bind();
        orderServiceDynamicProxy.saveOrder(order);
    }
}

运行结果:

动态代理 before code
动态代理分配到【db1】处理数据
Service层调用dao层添加Order
Dao层添加Order成功
动态代理 after code

SpringAop也是建立在jdk的动态代理之上的,当然还有CGLib,动态代理是非常重要的

源码解析

java.lang.reflect.Proxy

在这里插入图片描述

ProxyFactoryBean

在这里插入图片描述

JdkDynamicAopProxy、CglibAopProxy

在这里插入图片描述

在这里插入图片描述

MapperProxyFactory

在这里插入图片描述
从名字可以看出来,它是Mapper的代理工厂,我们主要看下newInstance方法,哪里调用它了呢,我们查下find usges
在这里插入图片描述
看到在MapperRegistry里有个getMapper方法,调用了它
在这里插入图片描述
那这个getMapper方法又在哪里调用了呢?
在这里插入图片描述
看到是在Configuration里调用了它,Configuration里都是对应的各种配置,
在这里插入图片描述
我们再回到MapperProxyFactory里,看它是怎么做的
在这里插入图片描述
首先创建一个MapperProxy,接着又调用了
在这里插入图片描述
生成一个代理对象,然后返回,我们首先要关注MapperProxy里面怎么写的
在这里插入图片描述
看invoker方法,是对InvocationHandler接口的实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值