Jave Web使用的設計模型

Jave Web使用的設計模型

 

Mybatis 源碼解讀-設計模式總結

ImportNew 5月15日

(給ImportNew加星標,提升Java技能)html

 

做者:crazyantjava

www.crazyant.net/2022.htmlweb

 

雖然咱們都知道有26個設計模式,可是大多停留在概念層面,真實開發中不多遇到,Mybatis源碼中使用了大量的設計模式,閱讀源碼並觀察設計模式在其中的應用,可以更深刻的理解設計模式。算法

 

Mybatis至少遇到了如下的設計模式的使用:spring

 

  1. Builder模式,例如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder;sql

  2. 工廠模式,例如SqlSessionFactory、ObjectFactory、MapperProxyFactory;數據庫

  3. 單例模式,例如ErrorContext和LogFactory;apache

  4. 代理模式,Mybatis實現的核心,好比MapperProxy、ConnectionLogger,用的jdk的動態代理;還有executor.loader包使用了cglib或者javassist達到延遲加載的效果;編程

  5. 組合模式,例如SqlNode和各個子類ChooseSqlNode等;設計模式

  6. 模板方法模式,例如BaseExecutor和SimpleExecutor,還有BaseTypeHandler和全部的子類例如IntegerTypeHandler;

  7. 適配器模式,例如Log的Mybatis接口和它對jdbc、log4j等各類日誌框架的適配實現;

  8. 裝飾者模式,例如Cache包中的cache.decorators子包中等各個裝飾者的實現;

  9. 迭代器模式,例如迭代器模式PropertyTokenizer;

 

接下來挨個模式進行解讀,先介紹模式自身的知識,而後解讀在Mybatis中怎樣應用了該模式。

 

一、Builder模式

 

Builder模式的定義是「將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。」,它屬於建立類模式,通常來講,若是一個對象的構建比較複雜,超出了構造函數所能包含的範圍,就可使用工廠模式和Builder模式,相對於工廠模式會產出一個完整的產品,Builder應用於更加複雜的對象的構建,甚至只會構建產品的一個部分。

 

 

在Mybatis環境的初始化過程當中,SqlSessionFactoryBuilder會調用XMLConfigBuilder讀取全部的MybatisMapConfig.xml和全部的*Mapper.xml文件,構建Mybatis運行的核心對象Configuration對象,而後將該Configuration對象做爲參數構建一個SqlSessionFactory對象。

 

其中XMLConfigBuilder在構建Configuration對象時,也會調用XMLMapperBuilder用於讀取*Mapper文件,而XMLMapperBuilder會使用XMLStatementBuilder來讀取和build全部的SQL語句。

 

在這個過程當中,有一個類似的特色,就是這些Builder會讀取文件或者配置,而後作大量的XpathParser解析、配置或語法的解析、反射生成對象、存入結果緩存等步驟,這麼多的工做都不是一個構造函數所能包括的,所以大量採用了Builder模式來解決。

 

對於builder的具體類,方法都大都用build*開頭,好比SqlSessionFactoryBuilder爲例,它包含如下方法:

 

 

即根據不一樣的輸入參數來構建SqlSessionFactory這個工廠對象。

 

二、工廠模式

 

在Mybatis中好比SqlSessionFactory使用的是工廠模式,該工廠沒有那麼複雜的邏輯,是一個簡單工廠模式。

 

簡單工廠模式(Simple Factory Pattern):又稱爲靜態工廠方法(Static Factory Method)模式,它屬於類建立型模式。在簡單工廠模式中,能夠根據參數的不一樣返回不一樣類的實例。簡單工廠模式專門定義一個類來負責建立其餘類的實例,被建立的實例一般都具備共同的父類。

 

 

SqlSession能夠認爲是一個Mybatis工做的核心的接口,經過這個接口能夠執行執行SQL語句、獲取Mappers、管理事務。相似於鏈接MySQL的Connection對象。

 

 

能夠看到,該Factory的openSession方法重載了不少個,分別支持autoCommit、Executor、Transaction等參數的輸入,來構建核心的SqlSession對象。

 

在DefaultSqlSessionFactory的默認工廠實現裏,有一個方法能夠看出工廠怎麼產出一個產品:

 

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,
boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call
// close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

 

這是一個openSession調用的底層方法,該方法先從configuration讀取對應的環境配置,而後初始化TransactionFactory得到一個Transaction對象,而後經過Transaction獲取一個Executor對象,最後經過configuration、Executor、是否autoCommit三個參數構建了SqlSession。

 

在這裏其實也能夠看到端倪,SqlSession的執行,實際上是委託給對應的Executor來進行的。

 

而對於LogFactory,它的實現代碼:

 

public final class LogFactory {
private static Constructor<? extends Log> logConstructor;

private LogFactory() {
// disable construction
}

public static Log getLog(Class<?> aClass) {
return getLog(aClass.getName());
}

 

這裏有個特別的地方,是Log變量的的類型是Constructor<? extends Log>,也就是說該工廠生產的不僅是一個產品,而是具備Log公共接口的一系列產品,好比Log4jImpl、Slf4jImpl等不少具體的Log。

 

三、單例模式

 

單例模式(Singleton Pattern):單例模式確保某一個類只有一個實例,並且自行實例化並向整個系統提供這個實例,這個類稱爲單例類,它提供全局訪問的方法。

 

單例模式的要點有三個:一是某個類只能有一個實例;二是它必須自行建立這個實例;三是它必須自行向整個系統提供這個實例。單例模式是一種對象建立型模式。單例模式又名單件模式或單態模式。

 

 

在Mybatis中有兩個地方用到單例模式,ErrorContext和LogFactory,其中ErrorContext是用在每一個線程範圍內的單例,用於記錄該線程的執行環境錯誤信息,而LogFactory則是提供給整個Mybatis使用的日誌工廠,用於得到針對項目配置好的日誌對象。

 

ErrorContext的單例實現代碼:

 

public class ErrorContext {

private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>();

private ErrorContext() {
}

public static ErrorContext instance() {
ErrorContext context = LOCAL.get();
if (context == null) {
context = new ErrorContext();
LOCAL.set(context);
}
return context;
}

 

構造函數是private修飾,具備一個static的局部instance變量和一個獲取instance變量的方法,在獲取實例的方法中,先判斷是否爲空若是是的話就先建立,而後返回構造好的對象。

 

只是這裏有個有趣的地方是,LOCAL的靜態實例變量使用了ThreadLocal修飾,也就是說它屬於每一個線程各自的數據,而在instance()方法中,先獲取本線程的該實例,若是沒有就建立該線程獨有的ErrorContext。

 

四、代理模式

 

代理模式能夠認爲是Mybatis的核心使用的模式,正是因爲這個模式,咱們只須要編寫Mapper.java接口,不須要實現,由Mybatis後臺幫咱們完成具體SQL的執行。

 

代理模式(Proxy Pattern) :給某一個對象提供一個代 理,並由代理對象控制對原對象的引用。代理模式的英 文叫作Proxy或Surrogate,它是一種對象結構型模式。

 

代理模式包含以下角色:

 

  • Subject: 抽象主題角色

  • Proxy: 代理主題角色

  • RealSubject: 真實主題角色

 

 

 

這裏有兩個步驟,第一個是提早建立一個Proxy,第二個是使用的時候會自動請求Proxy,而後由Proxy來執行具體事務;

 

當咱們使用Configuration的getMapper方法時,會調用mapperRegistry.getMapper方法,而該方法又會調用mapperProxyFactory.newInstance(sqlSession)來生成一個具體的代理:

 

/**
* @author Lasse Voss
*/
public class MapperProxyFactory<T> {

private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}

public Class<T> getMapperInterface() {
return mapperInterface;
}

public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}

@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface },
mapperProxy);
}

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

}

 

在這裏,先經過T newInstance(SqlSession sqlSession)方法會獲得一個MapperProxy對象,而後調用T newInstance(MapperProxy<T> mapperProxy)生成代理對象而後返回。

 

而查看MapperProxy的代碼,能夠看到以下內容:

 

public class MapperProxy<T> 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);
} else if (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的執行和返回。

 

五、組合模式

 

組合模式組合多個對象造成樹形結構以表示「總體-部分」的結構層次。

 

組合模式對單個對象(葉子對象)和組合對象(組合對象)具備一致性,它將對象組織到樹結構中,能夠用來描述總體與部分的關係。同時它也模糊了簡單元素(葉子對象)和複雜元素(容器對象)的概念,使得客戶可以像處理簡單元素同樣來處理複雜元素,從而使客戶程序可以與複雜元素的內部結構解耦。

 

在使用組合模式中須要注意一點也是組合模式最關鍵的地方:葉子對象和組合對象實現相同的接口。這就是組合模式可以將葉子節點和對象節點進行一致處理的緣由。

 

 

Mybatis支持動態SQL的強大功能,好比下面的這個SQL:

 

<update id="update" parameterType="org.format.dynamicproxy.mybatis.bean.User">
UPDATE users
<trim prefix="SET" prefixOverrides=",">
<if test="name != null and name != ''">
name = #{name}
</if>
<if test="age != null and age != ''">
, age = #{age}
</if>
<if test="birthday != null and birthday != ''">
, birthday = #{birthday}
</if>
</trim>
where id = ${id}
</update>

 

在這裏面使用到了trim、if等動態元素,能夠根據條件來生成不一樣狀況下的SQL;

 

在DynamicSqlSource.getBoundSql方法裏,調用了rootSqlNode.apply(context)方法,apply方法是全部的動態節點都實現的接口:

 

public interface SqlNode {
boolean apply(DynamicContext context);
}

 

對於實現該SqlSource接口的全部節點,就是整個組合模式樹的各個節點:

 

 

組合模式的簡單之處在於,全部的子節點都是同一類節點,能夠遞歸的向下執行,好比對於TextSqlNode,由於它是最底層的葉子節點,因此直接將對應的內容append到SQL語句中:

 

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

 

可是對於IfSqlNode,就須要先作判斷,若是判斷經過,仍然會調用子元素的SqlNode,即contents.apply方法,實現遞歸的解析。

 

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

 

六、模板方法模式

 

模板方法模式是全部模式中最爲常見的幾個模式之一,是基於繼承的代碼複用的基本技術。

 

模板方法模式須要開發抽象類和具體子類的設計師之間的協做。一個設計師負責給出一個算法的輪廓和骨架,另外一些設計師則負責給出這個算法的各個邏輯步驟。表明這些具體邏輯步驟的方法稱作基本方法(primitive method);而將這些基本方法彙總起來的方法叫作模板方法(template method),這個設計模式的名字就是今後而來。

 

模板類定義一個操做中的算法的骨架,而將一些步驟延遲到子類中。使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。

 

 

在Mybatis中,sqlSession的SQL執行,都是委託給Executor實現的,Executor包含如下結構:

 

 

其中的BaseExecutor就採用了模板方法模式,它實現了大部分的SQL執行邏輯,而後把如下幾個方法交給子類定製化完成:

 

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

protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException;

protected abstract <E> List<E> 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<String, Statement>內,供下一次使用。(能夠是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);
}
}

 

七、適配器模式

 

適配器模式(Adapter Pattern) :將一個接口轉換成客戶但願的另外一個接口,適配器模式使接口不兼容的那些類能夠一塊兒工做,其別名爲包裝器(Wrapper)。適配器模式既能夠做爲類結構型模式,也能夠做爲對象結構型模式。

 

 

在Mybatsi的logging包中,有一個Log接口:

 

/**
* @author Clinton Begin
*/
public interface 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日誌包的適配:

 

 

好比對於Log4jImpl的實現來講,該實現持有了org.apache.log4j.Logger的實例,而後全部的日誌方法,均委託該實例來實現。

 

public class Log4jImpl implements Log {

private static final 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);
}

}

 

八、裝飾者模式

 

裝飾模式(Decorator Pattern) :動態地給一個對象增長一些額外的職責(Responsibility),就增長對象功能來講,裝飾模式比生成子類實現更爲靈活。其別名也能夠稱爲包裝器(Wrapper),與適配器模式的別名相同,但它們適用於不一樣的場合。根據翻譯的不一樣,裝飾模式也有人稱之爲「油漆工模式」,它是一種對象結構型模式。

 

 

在mybatis中,緩存的功能由根接口Cache(org.apache.ibatis.cache.Cache)定義。整個體系採用裝飾器設計模式,數據存儲和緩存的基本功能由PerpetualCache(org.apache.ibatis.cache.impl.PerpetualCache)永久緩存實現,而後經過一系列的裝飾器來對PerpetualCache永久緩存進行緩存策略等方便的控制。以下圖:

 

 

用於裝飾PerpetualCache的標準裝飾器共有8個(所有在org.apache.ibatis.cache.decorators包中):

 

  1. FifoCache:先進先出算法,緩存回收策略

  2. LoggingCache:輸出緩存命中的日誌信息

  3. LruCache:最近最少使用算法,緩存回收策略

  4. ScheduledCache:調度緩存,負責定時清空緩存

  5. SerializedCache:緩存序列化和反序列化存儲

  6. SoftCache:基於軟引用實現的緩存管理策略

  7. SynchronizedCache:同步的緩存裝飾器,用於防止多線程併發訪問

  8. WeakCache:基於弱引用實現的緩存管理策略

 

另外,還有一個特殊的裝飾器TransactionalCache:事務性的緩存

 

正如大多數持久層框架同樣,mybatis緩存一樣分爲一級緩存和二級緩存

 

  • 一級緩存,又叫本地緩存,是PerpetualCache類型的永久緩存,保存在執行器中(BaseExecutor),而執行器又在SqlSession(DefaultSqlSession)中,因此一級緩存的生命週期與SqlSession是相同的。

  • 二級緩存,又叫自定義緩存,實現了Cache接口的類均可以做爲二級緩存,因此可配置如encache等的第三方緩存。二級緩存以namespace名稱空間爲其惟一標識,被保存在Configuration核心配置對象中。

 

二級緩存對象的默認類型爲PerpetualCache,若是配置的緩存是默認類型,則mybatis會根據配置自動追加一系列裝飾器。

 

Cache對象之間的引用順序爲:

 

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

 

九、迭代器模式

 

迭代器(Iterator)模式,又叫作遊標(Cursor)模式。GOF給出的定義爲:提供一種方法訪問一個容器(container)對象中各個元素,而又不需暴露該對象的內部細節。

 

 

Java的Iterator就是迭代器模式的接口,只要實現了該接口,就至關於應用了迭代器模式:

 

 

好比Mybatis的PropertyTokenizer是property包中的重量級類,該類會被reflection包中其餘的類頻繁的引用到。這個類實現了Iterator接口,在使用時常常被用到的是Iterator接口中的hasNext這個函數。

 

public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
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() {
return new PropertyTokenizer(children);
}

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

 

能夠看到,這個類傳入一個字符串到構造函數,而後提供了iterator方法對解析後的子串進行遍歷,是一個很經常使用的方法類。

 

參考資料:

 

  • 圖說設計模式:http://design-patterns.readthedocs.io/zh_CN/latest/index.html

  • 深刻淺出Mybatis系列(十)—SQL執行流程分析(源碼篇):http://www.cnblogs.com/dongying/p/4142476.html

  • 設計模式讀書筆記—–組合模式 http://www.cnblogs.com/chenssy/p/3299719.html

  • Mybatis3.3.x技術內幕(四):五鼠鬧東京之執行器Executor設計本來 http://blog.csdn.net/wagcy/article/details/32963235

  • mybatis緩存機制詳解(一)——Cache https://my.oschina.net/lixin91/blog/620068

 

Spring源碼分析 之淺談設計模式

 

一直想專門寫個Spring源碼的博客,工做了,能夠全身性的投入到互聯網行業中。雖然加班很嚴重,可是依然很開心。趁着凌晨有時間,總結總結。

首先spring,相信你們都很熟悉了。 

 一、輕量級  零配置,API使用簡單

 二、面向Bean  只須要編寫普通的Bean(一個Bean表明一個對象)

三、鬆耦合 充分利用AOP思想 )(各自能夠獨立開發,而後整合起來運行)

四、萬能膠 與主流框架無縫集成 (Mybatis dubbo等等 )

五、設計模式 將Java中經典的設計模式運用的淋漓盡致

 

Spring解決企業級應用開發的負責設計,簡化開發。

   1,基於POJO的清理愛你國際和最小侵入性(代碼的嵌套,獨自開發合起來運行)

  2,經過依賴注入和麪向接口鬆耦合

  三、基於切面(典型的事務管理!!日誌)和慣性進行聲明式編程

  四、經過切面和模板減小版式代碼

  主要經過,面向Bean、依賴注入以及面向切面三種方式達成

 

Spring提供了IOC容器,經過配置文件或者註解的方式來管理對象之間的依賴關係

  A a = new A()//實例化後用一個變量保存下來(匿名對象)    ------------------》Spring用IOC容器 存儲起來~

 a.c()  //必須初始化才運行  -----------------------> Spring幫忙初始化,實例化(控制器給了spring) 

最終實現了 依賴注入:

@autowrite Interfa A a   //自動吧他的實現類注入進來

@Resource(「aa」)//IOC容器種類的id爲「aa」的對象自動注入到這裏

@autowrite A a  //根據類型自動注入

Spring的注入方式

一、 setter

二、 構造方法

三、強制賦值

面向切面,AOP核心思想--解耦! 把一個總體拆開,分別開發,發佈時候,再組裝到一塊兒運行,切面就是規則!

好比 事務;

     開啓事務 執行事務 事務回滾 關閉事務     這就是規則!!!!!!

     這種有規律的,就能夠認爲他是固定的,能夠單獨拿出來開發設計,做爲一個模塊(好比日誌啊)。

  AOP就是個編程思想而已

關於Spring的使用,特色,網上資料不少,你們能夠本身找找學習下。本博客主要對於源碼進行解讀。

 

在典型的面型對象開發方式中,可能要將日誌記錄語句放在全部方法和Java類種才能實現日誌功能。而在AOP方式中,能夠反過來將日誌服務模塊化,並以聲明的方式將他們應用到須要日誌的組件上。Java類不須要知道日誌服務的存在,也不想須要考慮相關的代碼。

AOP的功能徹底集成到了Spring事務管理、日誌和其餘各類特性的上下文中

authentication權限認證

Logging日誌

Transctions Manager事務

Lazy Loading懶加載

Contex Process 上下文處理

Error Handler 錯誤跟蹤(異常捕獲機制)

Cache緩存

一、除了AOP之外的設計模式

   a、 代理模式

   b、工廠模式

   c、單例模式

  d、委派模式

  e、策略模式

  f、策略模式

  g、原型模式

代理模式原理:

   一、拿到被代理對象的引用,而後獲取它的接口

   二、JDK代理從新生成一個類,同時實現咱們給的代理對象所實現的接口

   三、把代理對象的引用拿到

   四、從新動態生成一個class字節碼

   五、編譯

動態代理 調用哪一個方法就代理哪一個方法 

整個類 生成一個新 的類

 

你們認真仔細研究好代理模式,代理模式在Spring中 應用很是普遍!!!

JDK代理模式實現:

  一、定義接口

  二、定義實現接口的類

  三、 代理類  ,代理類須要實現  InvocationHandler 接口,而後實現 invoke方法

 回顧一下,知足代理模式應用場景的三個必要條件,窮取法
一、兩個角色:執行者、被代理對象
二、注重過程,必需要作,被代理對象沒時間作或者不想作(怕羞羞),不專業
三、執行者必須拿到被代理對象的我的資料(執行者持有被代理對象的引用)

 例:定義Persion接口

複製代碼
public interface Person {

    //尋找真愛、相親
    void findLove();
    
//    String getSex();
//
//    String getName();

}
複製代碼

 

實現這個接口

複製代碼
//小星星、單身
public class XiaoXingxing implements Person{

// private String sex = "女";
// private String name = "小星星";

@Override
public void findLove() {
// System.out.println("我叫" + this.name + ",性別:" + this.sex + "我找對象的要求是:");
System.out.println("高富帥");
System.out.println("有房有車的");
System.out.println("身高要求180cm以上,體重70kg");
}

// public String getSex() {
// return sex;
// }
//
// public void setSex(String sex) {
// this.sex = sex;
// }
//
// public String getName() {
// return name;
// }
//
// public void setName(String name) {
// this.name = name;
// }


}
複製代碼

代理類

複製代碼
//媒婆
public class Meipo implements InvocationHandler {

    private Person target; //被代理對象的引用做爲一個成員變量保存下來了   在下面調用時候的 的   ///////////////////////// 下面的嗲用
 
    //獲取被代理人的我的資料爲,爲了能讓他代理任何對象
    public Object getInstance(Person target) throws Exception {
        this.target = target;
        Class clazz = target.getClass();  //利用反射機制(最終得到接口)
        System.out.println("被代理對象的class是:" + clazz);
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);  //this這個參數 指的是 代理人 this.h   就是調用了媒婆的 invoke方法    this指的是invoke這個回調方法
    }
        //代理對象 會自動調用下面invoke方法
        @Override
        public Object invoke (Object proxy, Method  method, Object[] args) throws Throwable{

            System.out.println("我是媒婆:" + "得給你找個異性才行");
            System.out.println("開始進行海選...");
            System.out.println("------------");
  ///////////////////////////////////////////////////////////
            //調用的時候   (利用反射機制調用)   對象名.方法名 
            method.invoke(this.target, args);  這個invoke不是方法名字的invoke  是的話 會陷入死循環  
            System.out.println("------------");
            System.out.println("若是合適的話,就準備辦事");

            return null;
        }

    }
複製代碼

測試:

複製代碼
public class TestFindLove {
    public static void main(String[] args) {
        
        try {
            
//            
//            Person obj = (Person)new Meipo().getInstance(new XiaoXingxing());
//            System.out.println(obj.getClass());
//            obj.findLove();
            
            //原理:
            //1.拿到被代理對象的引用,而後獲取它的接口
            //2.JDK代理從新生成一個類,同時實現咱們給的代理對象所實現的接口
            //3.把被代理對象的引用也拿到了
            //4.從新動態生成一個class字節碼
            //5.而後編譯
            
            //獲取字節碼內容 
//            byte[] data = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});  //生成字節碼文件
//            FileOutputStream os = new FileOutputStream("E:/GP_WORKSPACE/$Proxy0.class");  //將字節碼輸入到磁盤上
//            os.write(data);
//            os.close();
            
            //是什麼?
            //爲何?
            //怎麼作?
         //解釋: 字節碼 反編譯後 能夠查看
Person obj = (Person)new GPMeipo().getInstance(new XiaoXingxing()); //返回一個代理對象 代理出來的這個對象能夠強轉這個接口類 System.out.println(obj.getClass()); //這個Object對象 並非 lcy的引用了 徹底是一個新的對象 obj.findLove(); //動態代理 須要調用哪一個方法 就調用哪一個方法 整個類都是新的類了 新的字節碼 } catch (Exception e) { e.printStackTrace(); } } }
複製代碼

也能夠不用 JDK的任何東西 本身實現動態代理!!

不用jdk的任何東西!

首先規定有個InvocationHandler  有個 invoke方法

import java.lang.reflect.Method;

public interface GPInvocationHandler {
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

實現個Proxy  裏面有 InvocationHandler引用  有 newInstance的方法 classloader方法

複製代碼
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;


//生成代理對象的代碼
public class GPPorxy {
    
    private static String ln = "\r\n";
    
    public static Object newProxyInstance(GPClassLoader classLoader,Class<?>[] interfaces,GPInvocationHandler h){
        
        
        try{
            //一、生成源代碼
            String proxySrc = generateSrc(interfaces[0]);
            
            
            //二、將生成的源代碼輸出到磁盤,保存爲.java文件
            String filePath = GPPorxy.class.getResource("").getPath();
            File f = new File(filePath + "$Proxy0.java");
            FileWriter fw = new FileWriter(f);
            fw.write(proxySrc);
            fw.flush();
            fw.close();
        
            //三、編譯源代碼,而且生成.class文件   
            JavaCompiler  compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
            Iterable iterable = manager.getJavaFileObjects(f);
            
            CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
            task.call();
            manager.close();
        
            //4.將class文件中的內容,動態加載到JVM中來
            
            //5.返回被代理後的代理對象
            Class proxyClass = classLoader.findClass("$Proxy0");
            Constructor c = proxyClass.getConstructor(GPInvocationHandler.class);  //拿到構造方法
            f.delete();
            
            return c.newInstance(h);
            
        }catch (Exception e) {
            e.printStackTrace();
        }
        
        
        return null;
    }
    
    
    private static String generateSrc(Class<?> interfaces){
        StringBuffer src = new StringBuffer();
        src.append("package com.gupaoedu.vip.custom;" + ln);
        src.append("import java.lang.reflect.Method;" + ln);
        src.append("public class $Proxy0 implements " + interfaces.getName() + "{" + ln);
        
        src.append("GPInvocationHandler h;" + ln);
        
        src.append("public $Proxy0(GPInvocationHandler h) {" + ln);
        src.append("this.h = h;" + ln);
        src.append("}" + ln);
        
        for (Method m : interfaces.getMethods()) {    //那麼多方法 須要拿出來 
            src.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln);
            
            src.append("try{" + ln);
            src.append("Method m = " + interfaces.getName() + ".class.getMethod(\"" +m.getName()+"\",new Class[]{});" + ln);  //方法名 參數等等
            src.append("this.h.invoke(this,m,null);" + ln);  
            src.append("}catch(Throwable e){e.printStackTrace();}" + ln);
            src.append("}" + ln);    
        }  
        
        src.append("}");
        
        return src.toString();
    }
}
複製代碼

classloader類

複製代碼
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

//代碼生成、編譯、從新動態load到JVM
public class GPClassLoader extends ClassLoader{

    private File baseDir;
    
    public GPClassLoader(){
        String basePath = GPClassLoader.class.getResource("").getPath();
        this.baseDir = new java.io.File(basePath);   //保存路徑
    }
    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String className = GPClassLoader.class.getPackage().getName() + "." + name;   //找到這個class文件
        if(baseDir != null){
            File classFile = new File(baseDir,name.replaceAll("\\.", "/") + ".class");
            if(classFile.exists()){
                FileInputStream in = null;
                ByteArrayOutputStream out = null; 
                try{
                    in = new FileInputStream(classFile);
                    out = new ByteArrayOutputStream();
                    byte [] buff = new byte[1024];   //緩衝區
                    int len;
                    while ((len = in.read(buff)) != -1) {
                        out.write(buff, 0, len);
                    }  //所有讀完
                    return defineClass(className, out.toByteArray(), 0,out.size());  //搞到jvm中去
                    
                }catch (Exception e) {
                    e.printStackTrace();
                }finally{
                    if(null != in){
                        try {
                            in.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if(null != out){
                        try {
                            out.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    classFile.delete();
                }
                
            }
        }
        
        return null;
    }
    
}
複製代碼

代理類媒婆  必須實現這個類

複製代碼
import java.lang.reflect.Method;

import com.gupaoedu.vip.proxy.jdk.Person;

public class GPMeipo implements GPInvocationHandler{
    
    private Person target;
    
    //獲取被代理人的我的資料
    public Object getInstance(Person target) throws Exception{
        this.target = target;
        Class clazz = target.getClass();
        System.out.println("被代理對象的class是:"+clazz);
        return GPPorxy.newProxyInstance(new GPClassLoader(), clazz.getInterfaces(), this);
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是媒婆:得給你找個異性才行");
        System.out.println("開始進行海選...");
        System.out.println("------------");
        method.invoke(this.target, args);
        System.out.println("------------");
        System.out.println("若是合適的話,就準備辦事");
        return null;
    }

}
複製代碼

注意 Java中 $符號的 約定俗成 被代理的類

 JDK 必須 實現接口!!!!

知足代理模式應用場景的三個必要條件,

    一、須要有兩個角色  執行者 和 被代理對象

    二、注重過程,必需要作,被代理對象不作

   三、執行者必須拿到被代理對象的資料(執行者持有被代理對象的引用)

代理模式總結到底層就是:字節碼重組! (字節碼重組時候 對象要強制轉換,必需要實現一個接口)

 Java源代碼--->編譯---->字節碼(在原始的加了東西)-->加載到jvm中 

 

 

 

而後 cglib不須要,Spring主要用的cglib作動態代理 定義一個類   自動生成一個類 自動繼承這個類 子類引用指向父類   看下面:

(一樣作了字節碼重組 事情)

 是繼承關係 

複製代碼
public class YunZhongYu {
    
    
    public void findLove(){
        System.out.println("膚白貌美大長腿");
    }
    
}
複製代碼

定義代理類:

複製代碼
import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class GPMeipo implements MethodInterceptor{      // MethodInterceptor這是cglib裏面的

    //疑問?
    //好像並無持有被代理對象的引用
    public Object getInstance(Class clazz) throws Exception{
          //經過反射機制進行實例化

        Enhancer enhancer = new Enhancer();   //用來動態生成class
        //把父類設置爲誰?
        //這一步就是告訴cglib,生成的子類須要繼承哪一個類
        enhancer.setSuperclass(clazz);
        //設置回調
        enhancer.setCallback(this);   //業務邏輯 指的是下面的 intercept 回調方法
        
        //第一步、生成源代碼
        //第二步、編譯成class文件
        //第三步、加載到JVM中,並返回被代理對象
        return enhancer.create();
    }
    
    //一樣是作了字節碼重組這樣一件事情
    //對於使用API的用戶來講,是無感知
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {    //要乾的哪些事情   obj是生成之後的子類的引用   調用子類的引用就會調用這個子 intercept方法 調用super方法
        System.out.println("我是GP媒婆:" + "得給你找個異性才行");
        System.out.println("開始進行海選...");
        System.out.println("------------");
        //這個obj的引用是由CGLib給咱們new出來的
        //cglib new出來之後的對象,是被代理對象的子類(繼承了咱們本身寫的那個類)
        //OOP, 在new子類以前,實際上默認先調用了咱們super()方法的,
        //new了子類的同時,必須先new出來父類,這就至關因而間接的持有了咱們父類的引用
        //子類重寫了父類的全部的方法
        //咱們改變子類對象的某些屬性,是能夠間接的操做父類的屬性的
        proxy.invokeSuper(obj, args);  //能夠直接調用 調用的是父類哦
        System.out.println("------------");
        System.out.println("若是合適的話,就準備辦事");
        return null;
    }

}
複製代碼

測試類

複製代碼
public class TestGglibProxy {
    
    public static void main(String[] args) {
        
        //JDK的動態代理是經過接口來進行強制轉換的
        //生成之後的代理對象,能夠強制轉換爲接口
        
        
        //CGLib的動態代理是經過生成一個被代理對象的子類,而後重寫父類的方法
        //生成之後的對象,能夠強制轉換爲被代理對象(也就是用本身寫的類)
        //子類引用賦值給父類
        
        
        try {
            YunZhongYu obj = (YunZhongYu)new GPMeipo().getInstance(YunZhongYu.class);  //面向接口 對外開放 制定規範  接口就是規範  通常是是字符串 包名 類名 方法名之類的
            obj.findLove();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
}
複製代碼

 

代理能夠實現 在每個方法調用以前加一些代碼,方法嗲用以後加一些代碼

AOP: 事務代理、 日誌監聽

Service方法  開啓一個事務   事務的執行是由咱們本身的代碼完成的 

   一、監聽到是否有異常,可能須要根據異常的類型來決定這個事務是否好回滾or繼續提交?(commit or rollback?)

   二、事務要關閉掉 

經過動態代理給加了代碼   

 

-------------------------------------工廠模式

首先要區分 生產者 消費者     消費者不關心工廠、過程  只關心結果  從工廠取東西哈哈

簡單工廠:

定義接口:

複製代碼
//產品接口
//汽車須要知足必定的標準
public interface Car {
    
    //規定汽車的品牌
    String getName();
    
}
複製代碼

實現之:

複製代碼
public class Bmw implements Car{

    @Override
    public String getName() {
        return "BMW";
    }

}
複製代碼
複製代碼
public class Benz implements Car{

    @Override
    public String getName() {
        return "Benz";
    }

}
複製代碼
複製代碼
public class Audi implements Car{

    @Override
    public String getName() {
        return "Audi";
    }

}
複製代碼

定義工廠類

複製代碼
import com.gupaoedu.vip.factory.Audi;
import com.gupaoedu.vip.factory.Benz;
import com.gupaoedu.vip.factory.Bmw;
import com.gupaoedu.vip.factory.Car;

//對於這個工廠來講(太強大了)
//爲何?
//這個工廠啥都能幹(不符合現實)
//編碼也是一種藝術(融匯貫通),藝術來源於生活,迴歸到生活的
public class SimpleFactory {
    
    //實現統一管理、專業化管理
    //若是沒有工廠模式,小做坊,沒有執行標準的
    //若是買到三無產品(沒有標準)
    //衛生監督局工做難度會大大減輕
    
    //中國製造(按人家的標準執行)
    //中國製造向中國創造改變(技術不是問題了,問題是什麼?思惟能力)
    //碼農就是執行標準的人
    //系統架構師,就是制定標準的人
    
    //不僅作一個技術者,更要作一個思考者
    
    
    public Car getCar(String name){
        if("BMW".equalsIgnoreCase(name)){
            //Spring中的工廠模式
            //Bean
            //BeanFactory(生成Bean)
            //單例的Bean
            //被代理過的Bean
            //最原始的Bean(原型)
            //List類型的Bean
            //做用域不一樣的Bean
            
            //getBean
            //很是的紊亂,維護困難
            //解耦(鬆耦合開發)
            return new Bmw();
        }else if("Benz".equalsIgnoreCase(name)){
            return new Benz();
        }else if("Audi".equalsIgnoreCase(name)){
            return new Audi();
        }else{
            System.out.println("這個產品產不出來");
            return null;
        }
    }
    
}
複製代碼

測試類(消費者)

複製代碼
public class SimpleFactoryTest {

    
    
    public static void main(String[] args) {
    
        //這邊就是咱們的消費者
        Car car = new SimpleFactory().getCar("Audi");
        System.out.println(car.getName());
        
    }
    
}
複製代碼

接下來是 工廠方法模式

  定義工廠接口

  實現不一樣工廠

   消費者使用

一、定義工廠接口

 

複製代碼
import com.gupaoedu.vip.factory.Car;

//工廠接口,就定義了全部工廠的執行標準
public interface Factory {

    //符合汽車上路標準
    //尾氣排放標準
    //電子設備安全係數
    //必須配備安全帶、安全氣囊
    //輪胎的耐磨程度
    Car getCar();
    
}
複製代碼

二、實現這個工廠接口

複製代碼
import com.gupaoedu.vip.factory.Bmw;
import com.gupaoedu.vip.factory.Car;

public class BmwFactory implements Factory {

    @Override
    public Car getCar() {
        return new Bmw();
    }

}
複製代碼
複製代碼
import com.gupaoedu.vip.factory.Benz;
import com.gupaoedu.vip.factory.Car;

public class BenzFactory implements Factory {

    @Override
    public Car getCar() {
        return new Benz();
    }

}
複製代碼

三、測試類

複製代碼
public class FactoryTest {

    public static void main(String[] args) {
        
        //工廠方法模式
        //各個產品的生產商,都擁有各自的工廠
        //生產工藝,生成的高科技程度都是不同的
        Factory factory = new AudiFactory();
        System.out.println(factory.getCar());
        
        //須要用戶關心,這個產品的生產商
        factory = new BmwFactory();
        System.out.println(factory.getCar());
        
        //增長的代碼的使用複雜度
        
        
        //抽象工廠模式
        
    }
    
}
複製代碼

改進版的工廠方法模式,抽象工廠模式:

這個再也不是 接口了 而是 抽象類

 抽象類能夠引用本身的方法! 

默認的方法

複製代碼
import com.gupaoedu.vip.factory.Car;

public class DefaultFactory extends AbstractFactory {

    private AudiFactory defaultFactory = new AudiFactory();
    
    public Car getCar() {
        return defaultFactory.getCar();
    }

}
複製代碼

工廠方法

複製代碼
import com.gupaoedu.vip.factory.Car;

public abstract class AbstractFactory {

     protected abstract Car getCar();
     
     
     //這段代碼就是動態配置的功能
     //固定模式的委派
     public Car getCar(String name){
        if("BMW".equalsIgnoreCase(name)){
            return new BmwFactory().getCar();
        }else if("Benz".equalsIgnoreCase(name)){
            return new BenzFactory().getCar();
        }else if("Audi".equalsIgnoreCase(name)){
            return new AudiFactory().getCar();
        }else{
            System.out.println("這個產品產不出來");
            return null;
        }
    }

}
複製代碼
複製代碼
import com.gupaoedu.vip.factory.Audi;
import com.gupaoedu.vip.factory.Car;


//具體的業務邏輯封裝
public class AudiFactory extends AbstractFactory {

    @Override
    public Car getCar() {
        return new Audi();
    }

}
複製代碼
複製代碼
public class BenzFactory extends AbstractFactory {

    @Override
    public Car getCar() {
        return new Benz();
    }

}
複製代碼
複製代碼
public class BmwFactory extends AbstractFactory {

    @Override
    public Car getCar() {
        return new Bmw();
    }

}
複製代碼

 

單例模式:

整個系統從啓動到終止,自會有一個實例

在應用中遇到功能性衝突的時候,須要用到單例模式

單例模式有7種寫 法!!!

1.

複製代碼
1 public class Singleton implements java.io.Serializable {   
2     public static Singleton INSTANCE = new Singleton();   
3     protected Singleton() {  }   
4     private Object readResolve() {   
5         return INSTANCE;   
6     }
7 }
複製代碼

二、

複製代碼
//懶漢式單例類.在第一次調用的時候實例化本身
public class Singleton1 {
    //一、第一步先將構造方法私有化
    private Singleton1() {}
    //二、而後聲明一個靜態變量保存單例的引用
    private static Singleton1 single = null;
    //三、經過提供一個靜態方法來得到單例的引用
    //不安全的
    public static Singleton1 getInstance() {
        if (single == null) {
            single = new Singleton1();
        }
        return single;
    }
}
複製代碼

 

三、

複製代碼
//懶漢式單例.保證線程安全
public class Singleton2 {
    //一、第一步先將構造方法私有化
    private Singleton2() {}
    //二、而後聲明一個靜態變量保存單例的引用
    private static Singleton2 single=null;
    //三、經過提供一個靜態方法來得到單例的引用
    //爲了保證多線程環境下正確訪問,給方法加上同步鎖synchronized
    //慎用  synchronized 關鍵字,阻塞,性能很是低下的
    //加上synchronized關鍵字之後,對於getInstance()方法來講,它始終單線程來訪問
    //沒有充分利用上咱們的計算機資源,形成資源的浪費
    public static synchronized Singleton2 getInstance() {
        if (single == null) {
            single = new Singleton2();
        }
        return single;  
    }
}
複製代碼

四、

複製代碼
//懶漢式單例.雙重鎖檢查
public class Singleton3 {
    //一、第一步先將構造方法私有化
    private Singleton3() {}
    //二、而後聲明一個靜態變量保存單例的引用
    private static Singleton3 single=null;
    //三、經過提供一個靜態方法來得到單例的引用
    //爲了保證多線程環境下的另外一種實現方式,雙重鎖檢查
    //性能,第一次的時候
    public static Singleton3 getInstance() {  
      if (single == null) {
          synchronized (Singleton3.class) {
              if (single == null) {    
                  single = new Singleton3();
              }    
          }    
      }    
       return single;   
    }
}
複製代碼

五、

複製代碼
//懶漢式(靜態內部類)
//這種寫法,即解決安全問題,又解決了性能問題
//這個代碼,沒有浪費一個字
public class Singleton4 {
    //一、先聲明一個靜態內部類
    //private 私有的保證別人不能修改
    //static 保證全局惟一
    private static class LazyHolder {
        //final 爲了防止內部誤操做,代理模式,GgLib的代理模式
        private static final Singleton4 INSTANCE = new Singleton4();
    }
    //二、將默認構造方法私有化
    private Singleton4 (){}
    //至關於有一個默認的public的無參的構造方法,就意味着在代碼中隨時均可以new出來
        
    //三、一樣提供靜態方法獲取實例
    //final 確保別人不能覆蓋
    public static final Singleton4 getInstance() {  
        
        //方法中的邏輯,是要在用戶調用的時候纔開始執行的
        //方法中實現邏輯須要分配內存,也是調用時才分配的
        return LazyHolder.INSTANCE;
    }
    
//    static int a = 1;
//    //無論該class有沒有實例化,static靜態塊總會在classLoader執行完之後,就加載完畢
//    static{
//        //靜態塊中的內容,只能訪問靜態屬性和靜態方法
//        //只要是靜態方法或者屬性,直接能夠用Class的名字就能點出來
//        Singleton4.a = 2;
//        //JVM 內存中的靜態區,這一塊的內容是公共的 
//    }
}

//咱們所寫的全部的代碼,在java的反射機制面前,都是裸奔的
//反射機制是能夠拿到private修飾的內容的
//咱們能夠理解成即便加上private也不靠譜(按正常套路出牌,貌似能夠)


//類裝載到JVM中過程
//一、從上往下(必須聲明在前,使用在後)
//先屬性、後方法
//先靜態、後動態
複製代碼

六、

複製代碼
  //相似Spring裏面的方法,將類名註冊,下次從裏面直接獲取。  
public class Singleton6 {  
    private static Map<String,Singleton6> map = new HashMap<String,Singleton6>();  
    static {
        Singleton6 single = new Singleton6();
        map.put(single.getClass().getName(), single);
    }
    //保護的默認構造子  
    protected Singleton6(){}  
    //靜態工廠方法,返還此類唯一的實例  
    public static Singleton6 getInstance(String name) {  
        if(name == null) {  
             name = Singleton6.class.getName();  
        }  
        if(map.get(name) == null) {  
       try {  
           map.put(name, (Singleton6) Class.forName(name).newInstance());  
       } catch (InstantiationException e) {  
           e.printStackTrace();  
       } catch (IllegalAccessException e) {  
           e.printStackTrace();  
       } catch (ClassNotFoundException e) {  
           e.printStackTrace();  
       }  
}  
return map.get(name);  
}  
}
複製代碼

測試類

複製代碼
public class TestMain {
    public static void main(String[] args){  
        TestSingleton ts1 = TestSingleton.getInstance();  
        ts1.setName("james");  
        TestSingleton ts2 = TestSingleton.getInstance();  
        ts2.setName("tom");  
          
        ts1.printInfo();  
        ts2.printInfo();  
          
        if(ts1 == ts2){  
            System.out.println("建立的是同一個實例" + ts1.getName());  
        }else{  
            System.out.println("建立的不是同一個實例" + ts1.getName());  
        }  
    }
}
複製代碼
複製代碼
public class TestSingleton {  
    String name = null;  
    private TestSingleton() {}  
  
    //注意這裏用到了volatile關鍵字
    private static volatile TestSingleton instance = null;  
  
    public static TestSingleton getInstance() {  
       if (instance == null) {    
         synchronized (TestSingleton.class) {    
            if (instance == null) {    
               instance = new TestSingleton();   
            }    
         }    
       }   
       return instance;  
    }  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public void printInfo() {  
        System.out.println("the name is " + name);  
    }  
  
}
複製代碼
複製代碼
public class TestThread {
    
    public static void main(String[] args) {
        //啓動100線程同時去搶CPU
        int count = 100;
        
        //發令槍,測試併發常常用到
        CountDownLatch latch = new CountDownLatch(count);
        //Set默認去去重的,set是自己線程不安全的
        //
        final Set<Singleton1> syncSet = Collections.synchronizedSet(new HashSet<Singleton1>());
        
        for (int i = 0; i < count; i++) {
            new Thread(){

                @Override
                public void run() {
                    syncSet.add(Singleton1.getInstance());
                }
            }.start();
            
            latch.countDown();
        }
          
        try {
            latch.await();//等待全部線程所有完成,最終輸出結果
            System.out.println(syncSet.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
}
複製代碼

 

委派模式:

 1相似中介的功能(委託機制)

 2只有被委託人的引用 

兩個角色 受託人 委託人

 定義一個接口

複製代碼
public class Dispatcher implements IExector{
    IExector exector;
    
    Dispatcher(IExector exector){
        this.exector = exector;
    }
    
    
    //項目經理,雖然也有執行方法
    //可是他的工做職責是不同的
    public void doing() {
        this.exector.doing();
    }

}
複製代碼

兩個員工類實現這個接口

複製代碼
public class ExectorA implements IExector {

    @Override
    public void doing() {
        System.out.println("xxoo");
    }

}
複製代碼
複製代碼
public class ExectorB implements IExector{

    @Override
    public void doing() {
        System.out.println("員工B開始執行任務");
    }

}
複製代碼

項目經理類

複製代碼
public class Dispatcher implements IExector{
    IExector exector;
    
    Dispatcher(IExector exector){
        this.exector = exector;
    }
    
    
    //項目經理,雖然也有執行方法
    //可是他的工做職責是不同的
    public void doing() {
        this.exector.doing();
    }

}
複製代碼

測試

複製代碼
public class DispatcherTest {

    
    public static void main(String[] args) {
        Dispatcher dispatcher = new Dispatcher(new ExectorA());
        //看上去好像是咱們的項目經理在幹活
        //但實際幹活的人是普通員工
        //這就是典型,幹活是個人,功勞是你的
        dispatcher.doing();
    }
    
}
複製代碼

IOC容器中,有一個Register的東西(爲了告訴咱們的容器,在這個類被初始化的過程當中,須要作不少不一樣的邏輯處理,須要實現多個任務執行者,分別實現各自的功能 )

關於策略模式,參考系 Comparator方法就能夠啦 返回  -1 0 1這種的

  a、比較器接口

  b、調用時候有本身的實現

//比較器
public interface Comparator {
    
    int compareTo(Object obj1,Object obj2);
    
}
複製代碼
public class ObjectComparator implements Comparator{

    @Override
    public int compareTo(Object obj1, Object obj2) {
        return 0;
    }

}
複製代碼
複製代碼
public class NumberComparator implements Comparator{

    @Override
    public int compareTo(Object obj1, Object obj2) {
        return 0;
    }

}
複製代碼
複製代碼
public class MyList {
    
    public void sort(Comparator com){
//        com.compareTo(obj1, obj2);
        System.out.println("執行邏輯");
    }
    
}
複製代碼
複製代碼
public class MyListTest {
    
    public static void main(String[] args) {
        //new MyList().sort(new NumberComparator());
        
        
        //策略模式
//        List<Long> numbers = new ArrayList<Long>();
//        
//        Collections.sort(numbers, new Comparator<Long>() {
//
//            @Override
//            //返回值是固定的
//            //0 、-1 、1
//            //0 、 >0 、<0
//            public int compare(Long o1, Long o2) {
//                
//                //中間邏輯是不同的
//                
//                return 0;
//            }
//            
//            
//        });
    }
}
複製代碼

 原型模式:  

  首先要設計個原型

    實現 Cloneable接口

 

複製代碼
public class ConcretePrototype implements Cloneable{

    private int age;

    private String name;
    
    public ArrayList<String> list = new ArrayList<String>();
    
    protected Object clone() throws CloneNotSupportedException {
        ConcretePrototype prototype = null;
        try{
            prototype = (ConcretePrototype)super.clone();
            prototype.list = (ArrayList)list.clone();
            
            //克隆基於字節碼的
            //用反射,或者循環
        }catch(Exception e){
            
        }
        
        return prototype;
    }

    
    //定義上100個屬性
    
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    
    
    
    
}
複製代碼
複製代碼
public class CloneTest {

    
    
    public static void main(String[] args) {
        
        ConcretePrototype cp = new ConcretePrototype();
        cp.setAge(18);
        cp.setName("Tom");
        
        //cp.list.add("Tom");
        
        try {
            ConcretePrototype copy = (ConcretePrototype)cp.clone();
            
            System.out.println(copy.list  == cp.list);
            System.out.println(copy.getAge() + "," + copy.getName() + copy.list.size());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        
        //就是一個現成的對象,這個對象裏面有已經設置好的值
        //當我要新建一個對象,而且要給新建的對象賦值,並且賦值內容要跟以前的如出一轍
        
        
        //ConcretePrototype cp = new ConcretePrototype();
        //cp.setAge(18);
        
        //ConcretePrototype copy = new ConcretePrototype();
        //copy.setAge(cp.getAge());
        //copy.setName(cp.getName());
        //用循環,用反射,確實能夠的(反射性能並不高)
        //字節碼複製newInstance()
        
        //ConcretePrototype copy = cp;
        //ORM的時候常常用到的
        
        
        //可以直接拷貝其實際內容的數據類型/只支持9種,八大基本數據類型+String 淺拷貝
        //深拷貝
    }
    
}
複製代碼

原型模式:

複製代碼
//猴子
public class Monkey {
    //身高
    protected int height;//基本
    //體重
    protected int weight;
    //生日
    protected Date birthday;//不是基本類型
    
    public int getHeight() {
        return height;
    }
    public void setHeight(int height) {
        this.height = height;
    }
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }


    
    
}
複製代碼
複製代碼
public class TestPrototype {
    public static void main(String[] args) {
        TheGreatestSage sage = new TheGreatestSage();
        sage.change();
        
        //跟《西遊記》中描述的一致,怎麼辦?
    }
}
複製代碼
複製代碼
public class GoldRingedStaff implements Serializable{
    
    private float height = 100; //長度
    private float diameter = 10;//直徑
    
    
    
    /**
     * 金箍棒長大
     */
    public void grow(){
        this.diameter *= 2;
        this.height *= 2;
    }
    
    /**
     * 金箍棒縮小
     */
    public void shrink(){
        this.diameter /= 2;
        this.height /= 2;
    }
    
}
複製代碼
複製代碼
/**
 * 齊天大聖
 *
 */
public class   TheGreatestSage  extends Monkey implements Cloneable,Serializable{
    
    //金箍棒
    private GoldRingedStaff staff;
    
    //從石頭縫裏蹦出來
    public TheGreatestSage(){
        this.staff = new GoldRingedStaff();
        this.birthday = new Date();
        this.height = 150;
        this.weight = 30;
        System.out.println("------------------------");
    }
    
    //分身技能
    public Object clone(){
        //深度克隆
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            //return super.clone();//默認淺克隆,只克隆八大基本數據類型和String
            //序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            
            //反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            TheGreatestSage copy = (TheGreatestSage)ois.readObject();
            copy.birthday = new Date();
            
            return copy;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }finally{
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
                
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //變化
    public void change(){
        TheGreatestSage copySage = (TheGreatestSage)clone();
        System.out.println("大聖本尊生日是:" + this.getBirthday().getTime());
        System.out.println("克隆大聖的生日是:" + copySage.getBirthday().getTime());
        System.out.println("大聖本尊和克隆大聖是否爲同一個對象:" + (this == copySage));
        System.out.println("大聖本尊持有的金箍棒跟克隆大聖持有金箍棒是否爲同一個對象:" + (this.getStaff() == copySage.getStaff()));
    }
    
    public GoldRingedStaff getStaff() {
        return staff;
    }

    public void setStaff(GoldRingedStaff staff) {
        this.staff = staff;
    }
    
    
    
    
}
複製代碼

模板模式:

模板(固定的執行流程)

定義衝飲料的機器:

複製代碼
//衝飲料(拿出去賣錢了)
public abstract class Bevegrage {
    
    //不能被重寫
    public final void create(){
        //一、把水燒開
        boilWater();
        //二、把杯子準備好、原材料放到杯中
        pourInCup();
        //三、用水沖泡
        brew();
        //四、添加輔料
        addCoundiments();
    }
    
    public abstract void pourInCup();
    
    public abstract void addCoundiments();
    
    
    public void brew(){
        System.out.println("將開水放入杯中進行沖泡");
    };
    
    public void boilWater(){
        System.out.println("燒開水,燒到100度能夠起鍋了");
    }
    
}
複製代碼

實現爲衝咖啡的

複製代碼
public class Coffee  extends Bevegrage{

    //原材料放到杯中
    public void pourInCup() {
        System.out.println("將咖啡倒入杯中");
    }

    //房輔料
    public void addCoundiments() {
        System.out.println("添加牛奶和糖");
    }

}
複製代碼

實現爲泡茶的

複製代碼
public class Tea extends Bevegrage{

    //原材料放到杯中
    public void pourInCup() {
        System.out.println("將茶葉放入杯中");
    }

    //房輔料
    public void addCoundiments() {
        System.out.println("添加蜂蜜");
    }

}
複製代碼

測試類

複製代碼
public class TestTemplate {
    
    public static void main(String[] args) {
        
//        Coffee coffee = new Coffee();
//        coffee.create();
        
        Tea tea = new Tea();
        tea.create();
        
    }
    
    
    //SpringJDBC
    //是java規範,各個數據庫廠商本身去實現
    //一、加載驅動類DriverManager
    //二、創建鏈接
    //三、建立語句集(標準語句集、預處理語句集)(語句集?  MySQL、Oracle、SQLServer、Access)
    //四、執行語句集
    //五、結果集ResultSet 遊標
    //ORM(?)
    
}
複製代碼

Spring JDBC就是個模板模式

是 Java的規範  各個數據庫廠商去實現

  一、加載驅動類 DriverManager

   二、創建鏈接

   三、建立語句集(標準語句集、預處理語句集)(語句集合? Mysql oracle sqlserver access 語句不太同樣哦)

   四、執行語句集

    五、結果集ResultSet 遊標

   ORM (鏈接的是哪一個對象 映射哪一個結果 List or 自定義的類 仍是??運行時候才知道)

相關文章
相關標籤/搜索