听说有人面试被问了设计模式?跟我学,外包Java后端开发三年

 public Map
  
   
    getMethodCache
    () 
   
  {
 
	
 return methodCache;
 
}
 

 

 @SuppressWarnings(
 "unchecked")
 

 protected T newInstance(MapperProxy
   
    
     mapperProxy)
    
    {
 
	
 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), 
 new Class[] { mapperInterface },
 
			mapperProxy);
 
}
 

 

 public T newInstance(SqlSession sqlSession) {
 
	
 final MapperProxy
 
  
   mapperProxy = 
   new MapperProxy
   
    
     (sqlSession, mapperInterface, methodCache);
     
	
     return newInstance(mapperProxy);
     
}

}

在这里,先通过T newInstance(SqlSession sqlSession)方法会得到一个MapperProxy对象,然后调用T newInstance(MapperProxy mapperProxy)生成代理对象然后返回。

而查看MapperProxy的代码,可以看到如下内容:

publicclass MapperProxy implements InvocationHandler, Serializable {

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	try {
		if (Object.class.equals(method.getDeclaringClass())) {
			return method.invoke(this, args);
		} elseif (isDefaultMethod(method)) {
			return invokeDefaultMethod(proxy, method, args);
		}
	} catch (Throwable t) {
		throw ExceptionUtil.unwrapThrowable(t);
	}
	final MapperMethod mapperMethod = cachedMapperMethod(method);
	return mapperMethod.execute(sqlSession, args);
}

}

非常典型的,该MapperProxy类实现了InvocationHandler接口,并且实现了该接口的invoke方法。

通过这种方式,我们只需要编写Mapper.java接口类,当真正执行一个Mapper接口的时候,就会转发给MapperProxy.invoke方法

而该方法则会调用后续的sqlSession.cud>executor.execute>prepareStatement等一系列方法,完成SQL的执行和返回。

##5、组合模式
组合模式组合多个对象形成树形结构以表示“整体-部分”的结构层次。

组合模式对单个对象(叶子对象)和组合对象(组合对象)具有一致性,它将对象组织到树结构中,可以用来描述整体与部分的关系。

同时它也模糊了简单元素(叶子对象)和复杂元素(容器对象)的概念,使得客户能够像处理简单元素一样来处理复杂元素,从而使客户程序能够与复杂元素的内部结构解耦。

在使用组合模式中需要注意一点也是组合模式最关键的地方:叶子对象和组合对象实现相同的接口。这就是组合模式能够将叶子节点和对象节点进行一致处理的原因。
![](https://upload-images.jianshu.io/upload_images/26182368-80947ebdf534b4fc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

Mybatis支持动态SQL的强大功能,比如下面的这个SQL:

   ![](https://upload-images.jianshu.io/upload_images/26182368-7aba4e6ecac4ea92.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
在这里面使用到了trim、if等动态元素,可以根据条件来生成不同情况下的SQL;

在DynamicSqlSource.getBoundSql方法里,调用了rootSqlNode.apply(context)方法,apply方法是所有的动态节点都实现的接口:

publicinterface SqlNode {
boolean apply(DynamicContext context);
}

对于实现该SqlSource接口的所有节点,就是整个组合模式树的各个节点:
![](https://upload-images.jianshu.io/upload_images/26182368-e08c1e85c7ecdbcf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
组合模式的简单之处在于,所有的子节点都是同一类节点,可以递归的向下执行,比如对于TextSqlNode,因为它是最底层的叶子节点,所以直接将对应的内容append到SQL语句中:

@Override
public boolean apply(DynamicContext context) {
GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter));
context.appendSql(parser.parse(text));
returntrue;
}

但是对于IfSqlNode,就需要先做判断,如果判断通过,仍然会调用子元素的SqlNode,即contents.apply方法,实现递归的解析。

@Override
public boolean apply(DynamicContext context) {
if (evaluator.evaluateBoolean(test, context.getBindings())) {
contents.apply(context);
returntrue;
}
returnfalse;
}

##6、模板方法模式
模板方法模式是所有模式中最为常见的几个模式之一,是基于继承的代码复用的基本技术。

模板方法模式需要开发抽象类和具体子类的设计师之间的协作。一个设计师负责给出一个算法的轮廓和骨架,另一些设计师则负责给出这个算法的各个逻辑步骤。

代表这些具体逻辑步骤的方法称做基本方法(primitive method);而将这些基本方法汇总起来的方法叫做模板方法(template method),这个设计模式的名字就是从此而来。

模板类定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

![](https://upload-images.jianshu.io/upload_images/26182368-a870d0b0fba9f7de.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
在Mybatis中,sqlSession的SQL执行,都是委托给Executor实现的,Executor包含以下结构:
![](https://upload-images.jianshu.io/upload_images/26182368-80ac4d8d5247cd5a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
其中的BaseExecutor就采用了模板方法模式,它实现了大部分的SQL执行逻辑,然后把以下几个方法交给子类定制化完成:

protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;

protected abstract List

doFlushStatements
(boolean isRollback)
throws SQLException

;

protectedabstract

List

doQuery
(MappedStatement ms, Object parameter, RowBounds rowBounds,
		ResultHandler resultHandler, BoundSql boundSql) 
throws SQLException

;

该模板方法类有几个子类的具体实现,使用了不同的策略:

简单SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。(可以是Statement或PrepareStatement对象)

重用ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map 内,供下一次使用。(可以是Statement或PrepareStatement对象)

批量BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理的;BatchExecutor相当于维护了多个桶,每个桶里都装了很多属于自己的SQL,就像苹果蓝里装了很多苹果,番茄蓝里装了很多番茄,最后,再统一倒进仓库。(可以是Statement或PrepareStatement对象)

比如在SimpleExecutor中这样实现update方法:

@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null,
null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}

##7、适配器模式
适配器模式(Adapter Pattern) :将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。

适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
![](https://upload-images.jianshu.io/upload_images/26182368-82cd06b0546c5dfd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
在Mybatsi的logging包中,有一个Log接口:

/**

  • @author Clinton Begin
    */
    publicinterface Log {

    boolean isDebugEnabled();

    boolean isTraceEnabled();

    void error(String s, Throwable e);

    void error(String s);

    void debug(String s);

    void trace(String s);

    void warn(String s);

}

该接口定义了Mybatis直接使用的日志方法,而Log接口具体由谁来实现呢?

Mybatis提供了多种日志框架的实现,这些实现都匹配这个Log接口所定义的接口方法,最终实现了所有外部日志框架到Mybatis日志包的适配:
![](https://upload-images.jianshu.io/upload_images/26182368-7fa742ee604de8aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
比如对于Log4jImpl的实现来说,该实现持有了org.apache.log4j.Logger的实例,然后所有的日志方法,均委托该实例来实现。

publicclass Log4jImpl implements Log {

privatestaticfinal String FQCN = Log4jImpl.class.getName();

private Logger log;

public Log4jImpl(String clazz) {
	log = Logger.getLogger(clazz);
}

@Override
public boolean isDebugEnabled() {
	return log.isDebugEnabled();
}

@Override
public boolean isTraceEnabled() {
	return log.isTraceEnabled();
}

@Override
public void error(String s, Throwable e) {
	log.log(FQCN, Level.ERROR, s, e);
}

@Override
public void error(String s) {
	log.log(FQCN, Level.ERROR, s, null);
}

@Override
public void debug(String s) {
	log.log(FQCN, Level.DEBUG, s, null);
}

@Override
public void trace(String s) {
	log.log(FQCN, Level.TRACE, s, null);
}

@Override
public void warn(String s) {
	log.log(FQCN, Level.WARN, s, null);
}

}

##8、装饰者模式
装饰模式(Decorator Pattern) :动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。

其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。

根据翻译的不同,装饰模式也有人称之为“油漆工模式”,它是一种对象结构型模式。
![image.png](https://upload-images.jianshu.io/upload_images/26182368-5fe54b42687f2361.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
在mybatis中,缓存的功能由根接口Cache(org.apache.ibatis.cache.Cache)定义。整个体系采用装饰器设计模式,数据存储和缓存的基本功能由PerpetualCache(org.apache.ibatis.cache.impl.PerpetualCache)永久缓存实现,然后通过一系列的装饰器来对PerpetualCache永久缓存进行缓存策略等方便的控制。

如下图:
![](https://upload-images.jianshu.io/upload_images/26182368-effe201e3efcc81e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
用于装饰PerpetualCache的标准装饰器共有8个(全部在org.apache.ibatis.cache.decorators包中):

FifoCache:先进先出算法,缓存回收策略

LoggingCache:输出缓存命中的日志信息

LruCache:最近最少使用算法,缓存回收策略

ScheduledCache:调度缓存,负责定时清空缓存

SerializedCache:缓存序列化和反序列化存储

SoftCache:基于软引用实现的缓存管理策略

SynchronizedCache:同步的缓存装饰器,用于防止多线程并发访问

WeakCache:基于弱引用实现的缓存管理策略

另外,还有一个特殊的装饰器TransactionalCache:事务性的缓存

正如大多数持久层框架一样,mybatis缓存同样分为一级缓存和二级缓存

一级缓存,又叫本地缓存,是PerpetualCache类型的永久缓存,保存在执行器中(BaseExecutor),而执行器又在SqlSession(DefaultSqlSession)中,所以一级缓存的生命周期与SqlSession是相同的。

二级缓存,又叫自定义缓存,实现了Cache接口的类都可以作为二级缓存,所以可配置如encache等的第三方缓存。二级缓存以namespace名称空间为其唯一标识,被保存在Configuration核心配置对象中。

二级缓存对象的默认类型为PerpetualCache,如果配置的缓存是默认类型,则mybatis会根据配置自动追加一系列装饰器。

Cache对象之间的引用顺序为:

SynchronizedCache–>LoggingCache–>SerializedCache–>ScheduledCache–>LruCache–>PerpetualCache

9、迭代器模式
迭代器(Iterator)模式,又叫做游标(Cursor)模式。GOF给出的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。

![](https://upload-images.jianshu.io/upload_images/26182368-beb61dcd3748fa26.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
Java的Iterator就是迭代器模式的接口,只要实现了该接口,就相当于应用了迭代器模式:
![](https://upload-images.jianshu.io/upload_images/26182368-4365d0dade311891.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
比如Mybatis的PropertyTokenizer是property包中的重量级类,该类会被reflection包中其他的类频繁的引用到。这个类实现了Iterator接口,在使用时经常被用到的是Iterator接口中的hasNext这个函数。

publicclass PropertyTokenizer implements Iterator {
private String name;
private String indexedName;
private String index;
private String children;

public PropertyTokenizer(String fullname) {
	int delim = fullname.indexOf('.');
	if (delim > -1) {
		name = fullname.substring(0, delim);
		children = fullname.substring(delim + 1);
	} else {
		name = fullname;
		children = null;
	}
	indexedName = name;
	delim = name.indexOf('[');
	if (delim > -1) {
		index = name.substring(delim + 1, name.length() - 1);
		name = name.substring(0, delim);
	}
}

public String getName() {
	return name;
}

public String getIndex() {
	return index;
}

public String getIndexedName() {
	return indexedName;
}

public String getChildren() {
	return children;
}

@Override
public boolean hasNext() {
	return children != null;
}

@Override
public PropertyTokenizer next() {
	returnnew PropertyTokenizer(children);
}

@Override
public void remove() {
	thrownew UnsupportedOperationException(
			"Remove is not supported, as it has no meaning in the context of properties.");
}

}

可以看到,这个类传入一个字符串到构造函数,然后提供了iterator方法对解析后的子串进行遍历,是一个很常用的方法类。

#小结
**以上内容希望可以给大家带来帮助,如果觉得有用的话,可以点赞关注我,后续会持续更新我对JAVA相关知识的见解。如有不同的见解欢迎在评论区留言。**



# 技术学习总结

学习技术一定要制定一个明确的学习路线,这样才能高效的学习,不必要做无效功,既浪费时间又得不到什么效率,大家不妨按照我这份路线来学习。

![](https://img-blog.csdnimg.cn/img_convert/d7a6ddd2bb9494ee1ad0132fc5304223.png)

![](https://img-blog.csdnimg.cn/img_convert/d7ac2d43ad4eedc63eb8a175760eecfa.png)

![](https://img-blog.csdnimg.cn/img_convert/b3e04893684082264f7e0c971badb6c0.png)

# 最后面试分享

大家不妨直接在牛客和力扣上多刷题,同时,我也拿了一些面试题跟大家分享,也是从一些大佬那里获得的,大家不妨多刷刷题,为金九银十冲一波!

![](https://img-blog.csdnimg.cn/img_convert/c930dfb3da75997e8471963def2c237a.png)

![](https://img-blog.csdnimg.cn/img_convert/5d43e26670daa67c989c4b71ff817688.png)

。如有不同的见解欢迎在评论区留言。**



# 技术学习总结

学习技术一定要制定一个明确的学习路线,这样才能高效的学习,不必要做无效功,既浪费时间又得不到什么效率,大家不妨按照我这份路线来学习。

[外链图片转存中...(img-yI0hMxgL-1628437720497)]

[外链图片转存中...(img-ZDLSk0kc-1628437720498)]

[外链图片转存中...(img-T59sJkqK-1628437720499)]

# 最后面试分享

大家不妨直接在牛客和力扣上多刷题,同时,我也拿了一些面试题跟大家分享,也是从一些大佬那里获得的,大家不妨多刷刷题,为金九银十冲一波!

[外链图片转存中...(img-NJokyCDg-1628437720501)]

[外链图片转存中...(img-YNrtZhVj-1628437720502)]

**最后,若需要完整pdf版,可以点赞本文后[点击这里免费领取](https://gitee.com/vip204888/java-p7)**
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>