Fork me on GitHub

设计模式之一 代理模式

代理(Proxy)是一种设计模式,定义:为其他对象提供一个代理以控制对某个对象的访问,即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法

代理模式主要有三个角色, 及抽象接口,真实对象,代理对象,真实对象与代理对象实现接口订义方法,核心为代理对象维护真实对象,在调用中为真实对象添加额外功能

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//抽象接口
public interface UserMapper {
void findAll();
}
//真实对象
public class UserMapperImpl implements UserMapper {
@Override
public void findAll() {
System.out.println("数据库用户数量为200");
}
}

//代理对象
public class MapperProxy implements UserMapper {

private UserMapperImpl userMapper; // 真实对象添加属性

public MapperProxy(UserMapperImpl userMapper) {
this.userMapper = userMapper;
}

@Override
public void findAll() { //额外添加统计时间功能
long start = System.currentTimeMillis();
userMapper.findAll();
long end = System.currentTimeMillis();
System.out.println("查询数据库耗时"+(end-start)+"毫秒");
}
}

// test
public static void main(String[] args) {
UserMapperImpl userMapper = new UserMapperImpl();
MapperProxy mapperProxy = new MapperProxy(userMapper);
mapperProxy.findAll();

}

代理模式在框架中大量使用,如mybatis中mapper代理,spring中aop等,以下是mybatis代理模式demo实际应用

mybatis中mapper使用,可分为以下几个步骤,具体可查看Mybatis mapper执行sql源码过程分析

  1. 解析:XMLMapperBuilder 解析标签标签内容,与XMLConfigBuilder类似,XMLConfigBuilder解析标签

  2. 将mapper与configuration绑定:通过XMLMapperBuilder.configurationElement()解析mapper内容,并生成 MapperStatement 添加到 Configuration 中;使用namespace名称通过bindMapperForNamespace(),将mapper绑定到Configuration中mapperRegistry参数中,MapperRegistry 类是一个 Mapper 类注册工厂,把与 MapperProxyFactory 映射过的 Mapper 类添加到它的属性 knownMappers 中;,类型为Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();

  3. 获取mapper:当调用session.getMapper(Class type)方法时,实际调用的是MapperRegisty注册工厂,通过class获取的MapperProxyFactory,并通过反射与session关联生成mapper代理类,所以调用mapper所有方法都会调用MapperProxy.invoke(),再根据sql配置进行对应sql执行

以下代码模拟mybatis mapper代理创建过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//自定义mapper
public interface UserMapper {

List<Integer> getAllIds();
}
// mapper代理类 实现InvocationHandler会拦截真实对象的所有方法,及真实对象的对象会调用invoke()
public class MapperProxy<T> implements InvocationHandler {

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("===start====");
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(proxy, args);
}
//mybais中会解析sql,最后调用jdbc执行sql
Object result = method.invoke(proxy, args);
System.out.println("=======end==========");
return result;
}
}

public static void main(String[] args) {
MapperProxy<UserMapper> mapperProxy = new MapperProxy();// mybatis 实际由反射创建

//注释为模拟mybatis代码,
// Map<Class, MapperProxy> registMapper = new HashMap<>();
//
// registMapper.put(UserMapper.class, mapperProxy);
// Configuration configuration = new Configuration();
// configuration.addMapper(registMapper);//解析xml并添加到configuration中
//
// MapperProxy mapperProxy1 = configuration.getMapper(UserMapper.class);//mybatis实际由sqlSession获取

// UserMapper userMapper =(UserMapper) Proxy.newProxyInstance(UserMapper.class.getClassLoader(),
// new Class[]{UserMapper.class}, mapperProxy1);//创建代理对象

UserMapper userMapper =(UserMapper) Proxy.newProxyInstance(UserMapper.class.getClassLoader(),
new Class[]{UserMapper.class}, mapperProxy);//创建代理对象
userMapper.getAllIds();//调用方法,实际调用jdbc
}

// 模拟mybatis Configuration与代理模式无关
public class Configuration {

private Map<Class, MapperProxy> registMapper;

public void addMapper(Map<Class,MapperProxy> mapper) {
if (registMapper == null) {
registMapper = new HashMap<>();
}

registMapper.putAll(mapper);
}

public MapperProxy getMapper(Class mapperClass) {
if (registMapper.containsKey(mapperClass)) {
return registMapper.get(mapperClass);
}
return null;
}
}

显示 Gitment 评论