一、概述

  Proxy模式又叫做代理模式,是构造型的设计模式之一,它可以为其他对象提供一种代理(Proxy)以控制对这个对象的访问。

  可以详细控制访问某个类或对象的方法,在调用这个方法(流程代码放到代理类中处理)做前置处理,调用这个方法后做后置处理。(即aop的微观实现)

  所谓代理,是指具有与代理元(被代理的对象)具有相同的接口的类,客户端必须通过代理与被代理的目标类交互,而代理一般在交互的过程中(交互前后),进行某些特别的处理。

代理类型

  静态代理

    静态代理就是在代码中显示指定的代理

    静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类

  动态代理

    动态代理无法代理类,但是可以代理接口

    CGLib代理

      在使用CGLib代理的时候,因为要用到继承,还有重写,所以对final的这个关键字的时候一定要格外的注意

    JDK自带代理

      java.lang.relect.Proxy:动态生成代理类和对象

      java.lang.relect.InvocationHandler(处理器接口):可以通过invoke方法实现对真实角色的代理访问;每次通过Proxy生成代理类对象时都要指定对应的处理器对象。

    javasssist字节码操作库实现

    ASM(底层使用指令,可维护性差)

  代理速度:

    JDK7、JDK8动态代理比CGLib快

  Spring代理选择

    当Bean有实现接口时,Spring就会用JDK的动态代理。当Bean没有实现接口时,Spring使用CGIib。可以强制使用CGIib,在spring配置中加入

1.1、适用场景

  保护目标对象、增强目标对象

  安全代理:屏蔽对真实角色的直接访问。

  远程代理:通过代理类处理远程方法调用(RMI)

  延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象

1.2、优缺点

优点:

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

缺点:

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

1.3、类图角色及其职责

  

  代理模式的角色与职责

   1、subject(抽象主题角色):真实主题与代理主题的共同接口或抽象类。

   2、RealSubject(真实主题角色):定义了代理角色所代表的真实对象。

   3、Proxy(代理主题角色):含有对真实主题角色的引用,代理角色通常在将客户端调用传递给真是主题对象之前或者之后执行某些操作,而不是单纯返回真实的对象。

1.4、演进过程

订单分库

  

1.4.1、静态代理

(1)订单实体

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;
}
}

(2)Dao

Dao层的接口:

public interface IOrderDao {
int insert(Order order);

Dao层的实现:
假装订单添加成功。

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

(3)Service

Service层的接口:

public interface IOrderService {
/** 保存订单,参数为订单对象,返回值为生效行数 */
int saveOrder(Order order);

Service层的实现:

public class OrderServiceImpl implements IOrderService {

    private IOrderDao iOrderDao;

    @Override
public int saveOrder(Order order) {
/** Spring会自己注入,我们这里就直接new出来了 */
iOrderDao = new OrderDaoImpl();
System.out.println("Service调用Dao层添加Order层");
return iOrderDao.insert(order);
}

(4)实现分库与静态代理

DataSourceContextHolder 维护着数据库的信息

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 (String) CONTEXT_HOLDER.get();
} public static void clearDBType() {
CONTEXT_HOLDER.remove();
}
}

Spring里面的分库:

public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDBType();
}
}

还有一个静态代理类:

public class OrderServiceStaticProxy {
/** 在代理类里面注入目标对象 */
private IOrderService iOrderService; /** 我们要在这静态代理类里面增强这个方法 */
public int saveOrder(Order order){
beforeMethod();
/** 如果这里有spring容器的话,就不用显示的new了 */
iOrderService = new OrderServiceImpl();
int userId = order.getUserId();
/** 这里就是实现一个分库的功能,对userId取模2,这里就只会得到0或者是1 */
int dbRouter = userId % 2;
System.out.println("静态代理分配到【db"+dbRouter+"】处理数据"); //todo 设置dataSource;
DataSourceContextHolder.setDBType("db"+dbRouter); afterMethod();
return iOrderService.saveOrder(order);
} /** 我们要增强,我们就来写上一个before和after */
private void beforeMethod(){
System.out.println("静态代理 before code");
} private void afterMethod(){
System.out.println("静态代理 after code");
}
}

在代理类中让orderId对2取模,余数为0放入db0,余数为1放入db1。在spring的dataSource配置中,指定class为DynamicDataSourceDynamicDataSourceDataSourceContextHolder.getDBType()中获取需要存入那个数据库。

分库时spring的配置

//数据库0
<bean id="db0" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${db0.driverClassName}"/>
<property name="url" value="${db0.url}"/>
<property name="username" value="${db0.username}"/>
<property name="password" value="${db0.password}"/>
</bean> //数据库1
<bean id="db1" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${db1.driverClassName}"/>
<property name="url" value="${db1.url}"/>
<property name="username" value="${db1.username}"/>
<property name="password" value="${db1.password}"/>
</bean> <bean id="dataSource" class="com.design.pattern.structural.proxy.db.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry value-ref="db0" key="db0"></entry>
<entry value-ref="db1" key="db1"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="db0"></property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>

测试

@RunWith(SpringJUnit4ClassRunner.class)  //使用junit4进行测试
@ContextConfiguration({"classpath:applicationContext-proxy-static.xml"}) //加载配置文件
public class OrderServiceStaticProxyTest { @Test
public void saveOrder() {
Order order = new Order();
order.setUserId(0);
/** 这里没有采用spring自动注入的方式,而是采用了直接new的方式 */
OrderServiceStaticProxy orderServiceStaticProxy = new OrderServiceStaticProxy();
orderServiceStaticProxy.saveOrder(order);
}
}

结果

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

类图

  

(7)代理的进一步优化

通常将需要增强的部分放到beforeMethod和afterMethod中。要划分方法的界限

public class OrderServiceStaticProxy {
/** 在代理类里面注入目标对象 */
private IOrderService iOrderService; /** 我们要在这静态代理类里面增强这个方法 */
public int saveOrder(Order order){
beforeMethod(order);
/** 如果这里有spring容器的话,就不用显示的new了 */
iOrderService = new OrderServiceImpl();
int result = iOrderService.saveOrder(order);
afterMethod();
return result;
} /** 我们要增强,我们就来写上一个before和after */
private void beforeMethod(Order order){
int userId = order.getUserId();
/** 这里就是实现一个分库的功能,对userId取模2,这里就只会得到0或者是1 */
int dbRouter = userId % 2;
System.out.println("静态代理分配到【db"+dbRouter+"】处理数据"); //todo 设置dataSource;
DataSourceContextHolder.setDBType("db"+dbRouter);
System.out.println("静态代理 before code");
} private void afterMethod(){
System.out.println("静态代理 after code");
}
}

1.4.2、动态代理

主要增加动态代理类

public class OrderServiceDynamicProxy implements InvocationHandler {

    /** 这是目标类 */
private Object target; /** 通过构造器把目标类注入进来 */
public OrderServiceDynamicProxy(Object target) {
this.target = target;
} /** 进行绑定目标对象 */
public Object bind() {
Class clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
} /**
* proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
* method:我们所要调用某个对象真实的方法的Method对象
* args:指代代理对象方法传递的参数
*/
@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;
} public void beforeMethod(Object obj) {
int userId = 0;
System.out.println("动态代理before code");
if (obj instanceof Order) {
Order order = (Order) obj;
userId = order.getUserId();
}
/** 这里就是实现一个分库的功能,对userId取模2,这里就只会得到0或者是1 */
int dbRouter = userId % 2;
System.out.println("动态代理分配到【db"+dbRouter+"】处理数据");
//todo 设置dataSource;
DataSourceContextHolder.setDBType(String.valueOf(dbRouter));
}
public void afterMethod() {
System.out.println("动态代理after code");
}
}

测试

@RunWith(SpringJUnit4ClassRunner.class)  //使用junit4进行测试
@ContextConfiguration({"classpath:applicationContext-proxy-static.xml"}) //加载配置文件
public class OrderServiceDynamicProxyTest {
@Test
public void test() {
Order order = new Order();
order.setUserId(2);
/** 这里没有采用spring自动注入的方式,而是采用了直接new的方式 */
IOrderService orderServiceDynamicProxy = (IOrderService) new OrderServiceDynamicProxy(new OrderServiceImpl()).bind();
orderServiceDynamicProxy.saveOrder(order);
}
}

输出

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

二、扩展

2.1、jdk中的代理

2.1.1、java.lang.reflect.proxy

参看上文动态代理

2.2、spring中的代理

2.2.1、org.springframework.aop.framework.ProxyFactoryBean的getObject()

    public Class<?> getObjectType() {
synchronized(this) {
if (this.singletonInstance != null) {
return this.singletonInstance.getClass();
}
} Class<?>[] ifcs = this.getProxiedInterfaces();
if (ifcs.length == 1) {
return ifcs[0];
} else if (ifcs.length > 1) {
return this.createCompositeInterface(ifcs);
} else {
return this.targetName != null && this.beanFactory != null ? this.beanFactory.getType(this.targetName) : this.getTargetClass();
}
} public boolean isSingleton() {
return this.singleton;
} protected Class<?> createCompositeInterface(Class<?>[] interfaces) {
return ClassUtils.createCompositeInterface(interfaces, this.proxyClassLoader);
} private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
this.targetSource = this.freshTargetSource();
if (this.autodetectInterfaces && this.getProxiedInterfaces().length == 0 && !this.isProxyTargetClass()) {
Class<?> targetClass = this.getTargetClass();
if (targetClass == null) {
throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
} this.setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
} super.setFrozen(this.freezeProxy);
this.singletonInstance = this.getProxy(this.createAopProxy());
} return this.singletonInstance;
} private synchronized Object newPrototypeInstance() {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Creating copy of prototype ProxyFactoryBean config: " + this);
} ProxyCreatorSupport copy = new ProxyCreatorSupport(this.getAopProxyFactory());
TargetSource targetSource = this.freshTargetSource();
copy.copyConfigurationFrom(this, targetSource, this.freshAdvisorChain());
if (this.autodetectInterfaces && this.getProxiedInterfaces().length == 0 && !this.isProxyTargetClass()) {
copy.setInterfaces(ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass(), this.proxyClassLoader));
} copy.setFrozen(this.freezeProxy);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Using ProxyCreatorSupport copy: " + copy);
} return this.getProxy(copy.createAopProxy());
} protected Object getProxy(AopProxy aopProxy) {
return aopProxy.getProxy(this.proxyClassLoader);
}

2.2.2、spring对jdk和cglib代理封装

org.springframework.aop.framework.JdkDynamicAopProxy对jdk的动态代理进行封装
org.springframework.aop.framework.CglibAopProxy 对cglib的动态代理进行封装

2.3、mybatis

2.3.1、MapperProxy

org.apache.ibatis.binding.MapperProxyFactory的newInstance(SqlSession sqlSession)的MapperProxy实现了 InvocationHandler

在cachedMapperMethod方法中使用了享元模式

003-结构型-03-代理模式(Proxy)的更多相关文章

  1. python设计模式---结构型之代理模式

    主要想着nginx:) from abc import ABCMeta, abstractmethod # 结构型设计模式---代理模式 class Actor: def __init__(self) ...

  2. JAVA设计模式 5【结构型】代理模式的理解与使用

    今天要开始我们结构型 设计模式的学习,设计模式源于生活,还是希望能通过生活中的一些小栗子去理解学习它,而不是为了学习而学习这些东西. 结构型设计模式 结构型设计模式又分为 类 结构型 对象 结构型 前 ...

  3. 【设计模式】结构型01代理模式(Proxy Pattern)

    代理模式(Proxy Pattern) 定义:顾名思义,增加中间层,为其他对象提供一种代理以控制对这个对象的访问.核心在于代理二字. 1.和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理 ...

  4. 【设计模式】结构型03外观模式(Facade Pattern)

    [设计模式]结构型02装饰模式(Decorator Pattern) 意图:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 主要解决:降低访问 ...

  5. 结构型:代理模式 Vs 适配器模式 Vs 门面模式(外观模式)

    先上UML图 代理模式: 适配器模式: 门面模式(外观模式): 打了例子……呃……举个比方 代理模式: 水浒街,西门庆看上潘金莲,想和她嘿咻嘿咻,但是自己有不能去找潘金莲去说,于是他找到了金牌代理人王 ...

  6. 《精通Python设计模式》学习结构型之代理模式

    这种模式,总会让人想到SRPING中的AOP, 不同语言有不同的实现方式吧. class SensitiveInfo: def __init__(self): self.users = ['nick' ...

  7. 代理模式/proxy模式/结构型模式

    代理模式proxy 定义 为其他对象提供一种代理,并以控制对这个对象的访问.最简单的理解,买东西都是要去商店的,不会去工厂. java实现三要素 proxy(代理)+subject(接口)+realS ...

  8. 【转】设计模式(十一)代理模式Proxy(结构型)

    设计模式(十一)代理模式Proxy(结构型) 1.概述 因为某个对象消耗太多资源,而且你的代码并不是每个逻辑路径都需要此对象, 你曾有过延迟创建对象的想法吗 ( if和else就是不同的两条逻辑路径) ...

  9. 代理模式 PROXY Surrogate 结构型 设计模式(十四)

    代理模式 PROXY 别名Surrogate 意图 为其他的对象提供一种代理以控制对这个对象的访问. 代理模式含义比较清晰,就是中间人,中介公司,经纪人... 在计算机程序中,代理就表示一个客户端不想 ...

  10. 设计模式系列之代理模式(Proxy Pattern)——对象的间接访问

    说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...

随机推荐

  1. 扫雷小游戏PyQt5开发【附源代码】

    也没啥可介绍哒,扫雷大家都玩过. 雷的分布算法也很简单,就是在雷地图(map:二维数组)中,随机放雷,然后这个雷的8个方位(上下左右.四个对角)的数字(非雷的标记.加一后不为雷的标记)都加一. 如何判 ...

  2. Mysql存储引擎中InnoDB与Myisam的区别

    1. 事务处理innodb 支持事务功能,myisam 不支持.Myisam 的执行速度更快,性能更好. 2. select ,update ,insert ,delete 操作MyISAM:如果执行 ...

  3. jdk、jre、jvm三者联系

    JDK(Java Development Kit)是针对Java开发员的产品,是整个Java的核心,包括了Java运行环境JRE.Java工具和Java基础类库.Java Runtime Enviro ...

  4. 'CSRFCheck' object has no attribute 'process_request' 报错

    环境:Python3.5 way 1: way 2: 在项目的setting.py中设置

  5. RCNN,Fast RCNN,Faster RCNN 的前生今世:(1) Selective Search

    Selective Search for Object Recoginition 这篇论文是J.R.R. Uijlings发表在2012 IJCV上的一篇文章,主要介绍了选择性搜索(Selective ...

  6. [NgRx] Optimistically Editing Entity Data

    First thing first, let's define a action to update entity: import { createAction, props } from " ...

  7. 2019-2020-1 20199302《Linux内核原理与分析》第二周作业

    一.实验记录 1.实验代码截屏 本次实验中遇到的一个小问题是: (1)在进行汇编语言编译时,命令行本应是"g/.s*/d ",因为做实验的时候还没有看视频,只是看了书,把" ...

  8. Kubernetes 学习23 kubernetes资源指标API及自定义指标API

    一.概述 1.上集中我们说到,官方文档提示说从k8s 1.11版本开始,将监控体系指标数据获取机制移向新一代的监控模型.也就意味着对于我们的k8s来讲现在应该有这样两种资源指标被使用.一种是资源指标, ...

  9. C语言中【变量】的存储类型共有4种类型

    在C语言中,对变量的存储类型说明有以下四种:   auto          自动变量 (动态存储) register     寄存器变量(动态存储) extern       外部变量(静态存储) ...

  10. Hdu 2157 How many ways??(DP||矩阵乘法)

    How many ways?? Time Limit:1000 MS Memory Limit: 32768 K Problem Description 春天到了, HDU校园里开满了花, 姹紫嫣红, ...