Jave Web使用的设计模型
Mybatis 源码解读-设计模式总结
(给ImportNew加星标,提高Java技能)
作者:crazyant
www.crazyant.net/2022.html
虽然我们都知道有26个设计模式,但是大多停留在概念层面,真实开发中很少遇到,Mybatis源码中使用了大量的设计模式,阅读源码并观察设计模式在其中的应用,能够更深入的理解设计模式。
Mybatis至少遇到了以下的设计模式的使用:
-
Builder模式,例如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder;
-
工厂模式,例如SqlSessionFactory、ObjectFactory、MapperProxyFactory;
-
单例模式,例如ErrorContext和LogFactory;
-
代理模式,Mybatis实现的核心,比如MapperProxy、ConnectionLogger,用的jdk的动态代理;还有executor.loader包使用了cglib或者javassist达到延迟加载的效果;
-
组合模式,例如SqlNode和各个子类ChooseSqlNode等;
-
模板方法模式,例如BaseExecutor和SimpleExecutor,还有BaseTypeHandler和所有的子类例如IntegerTypeHandler;
-
适配器模式,例如Log的Mybatis接口和它对jdbc、log4j等各种日志框架的适配实现;
-
装饰者模式,例如Cache包中的cache.decorators子包中等各个装饰者的实现;
-
迭代器模式,例如迭代器模式PropertyTokenizer;
接下来挨个模式进行解读,先介绍模式自身的知识,然后解读在Mybatis中怎样应用了该模式。
1、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这个工厂对象。
2、工厂模式
在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 logConstructor; private LogFactory() { // disable construction } public static Log getLog(Class aClass) { return getLog(aClass.getName()); }
这里有个特别的地方,是Log变量的的类型是Constructor<? extends Log>,也就是说该工厂生产的不只是一个产品,而是具有Log公共接口的一系列产品,比如Log4jImpl、Slf4jImpl等很多具体的Log。
3、单例模式
单例模式(Singleton Pattern):单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。
单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。单例模式是一种对象创建型模式。单例模式又名单件模式或单态模式。
在Mybatis中有两个地方用到单例模式,ErrorContext和LogFactory,其中ErrorContext是用在每个线程范围内的单例,用于记录该线程的执行环境错误信息,而LogFactory则是提供给整个Mybatis使用的日志工厂,用于获得针对项目配置好的日志对象。
ErrorContext的单例实现代码:
public class ErrorContext { private static final ThreadLocalLOCAL = new ThreadLocal (); 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。
4、代理模式
代理模式可以认为是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{ private final Class mapperInterface; private final Map methodCache = new ConcurrentHashMap (); public MapperProxyFactory(Class mapperInterface) { this.mapperInterface = mapperInterface; } public Class getMapperInterface() { return mapperInterface; } 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<T> mapperProxy)生成代理对象然后返回。
而查看MapperProxy的代码,可以看到如下内容:
public class MapperProxyimplements 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的执行和返回。
5、组合模式
组合模式组合多个对象形成树形结构以表示“整体-部分”的结构层次。
组合模式对单个对象(叶子对象)和组合对象(组合对象)具有一致性,它将对象组织到树结构中,可以用来描述整体与部分的关系。同时它也模糊了简单元素(叶子对象)和复杂元素(容器对象)的概念,使得客户能够像处理简单元素一样来处理复杂元素,从而使客户程序能够与复杂元素的内部结构解耦。
在使用组合模式中需要注意一点也是组合模式最关键的地方:叶子对象和组合对象实现相同的接口。这就是组合模式能够将叶子节点和对象节点进行一致处理的原因。
Mybatis支持动态SQL的强大功能,比如下面的这个SQL:
UPDATE users where id = ${id} name = #{name} , age = #{age} , birthday = #{birthday}
在这里面使用到了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; }
6、模板方法模式
模板方法模式是所有模式中最为常见的几个模式之一,是基于继承的代码复用的基本技术。
模板方法模式需要开发抽象类和具体子类的设计师之间的协作。一个设计师负责给出一个算法的轮廓和骨架,另一些设计师则负责给出这个算法的各个逻辑步骤。代表这些具体逻辑步骤的方法称做基本方法(primitive method);而将这些基本方法汇总起来的方法叫做模板方法(template method),这个设计模式的名字就是从此而来。
模板类定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
在Mybatis中,sqlSession的SQL执行,都是委托给Executor实现的,Executor包含以下结构:
其中的BaseExecutor就采用了模板方法模式,它实现了大部分的SQL执行逻辑,然后把以下几个方法交给子类定制化完成:
protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException; protected abstract ListdoFlushStatements(boolean isRollback) throws SQLException; protected abstract 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<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); } }
7、适配器模式
适配器模式(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); } }
8、装饰者模式
装饰模式(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包中):
-
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)对象中各个元素,而又不需暴露该对象的内部细节。
Java的Iterator就是迭代器模式的接口,只要实现了该接口,就相当于应用了迭代器模式:
比如Mybatis的PropertyTokenizer是property包中的重量级类,该类会被reflection包中其他的类频繁的引用到。这个类实现了Iterator接口,在使用时经常被用到的是Iterator接口中的hasNext这个函数。
public class 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() { 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,相信大家都很熟悉了。
1、轻量级 零配置,API使用简单
2、面向Bean 只需要编写普通的Bean(一个Bean代表一个对象)
3、松耦合 充分利用AOP思想 )(各自可以独立开发,然后整合起来运行)
4、万能胶 与主流框架无缝集成 (Mybatis dubbo等等 )
5、设计模式 将Java中经典的设计模式运用的淋漓尽致
Spring解决企业级应用开发的负责设计,简化开发。
1,基于POJO的清理爱你国际和最小侵入性(代码的嵌套,独自开发合起来运行)
2,通过依赖注入和面向接口松耦合
3、基于切面(典型的事务管理!!日志)和惯性进行声明式编程
4、通过切面和模板减少版式代码
主要通过,面向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的注入方式
1、 setter
2、 构造方法
3、强制赋值
面向切面,AOP核心思想--解耦! 把一个整体拆开,分别开发,发布时候,再组装到一起运行,切面就是规则!
比如 事务;
开启事务 执行事务 事务回滚 关闭事务 这就是规则!!!!!!
这种有规律的,就可以认为他是固定的,可以单独拿出来开发设计,作为一个模块(比如日志啊)。
AOP就是个编程思想而已
关于Spring的使用,特点,网上资料很多,大家可以自己找找学习下。本博客主要对于源码进行解读。
在典型的面型对象开发方式中,可能要将日志记录语句放在所有方法和Java类种才能实现日志功能。而在AOP方式中,可以反过来将日志服务模块化,并以声明的方式将他们应用到需要日志的组件上。Java类不需要知道日志服务的存在,也不想需要考虑相关的代码。
AOP的功能完全集成到了Spring事务管理、日志和其他各种特性的上下文中
authentication权限认证
Logging日志
Transctions Manager事务
Lazy Loading懒加载
Contex Process 上下文处理
Error Handler 错误跟踪(异常捕获机制)
Cache缓存
1、除了AOP以外的设计模式
a、 代理模式
b、工厂模式
c、单例模式
d、委派模式
e、策略模式
f、策略模式
g、原型模式
代理模式原理:
1、拿到被代理对象的引用,然后获取它的接口
2、JDK代理重新生成一个类,同时实现我们给的代理对象所实现的接口
3、把代理对象的引用拿到
4、重新动态生成一个class字节码
5、编译
动态代理 调用哪个方法就代理哪个方法
整个类 生成一个新 的类
大家认真仔细研究好代理模式,代理模式在Spring中 应用非常广泛!!!
JDK代理模式实现:
1、定义接口
2、定义实现接口的类
3、 代理类 ,代理类需要实现 InvocationHandler 接口,然后实现 invoke方法
回顾一下,满足代理模式应用场景的三个必要条件,穷取法 1、两个角色:执行者、被代理对象 2、注重过程,必须要做,被代理对象没时间做或者不想做(怕羞羞),不专业 3、执行者必须拿到被代理对象的个人资料(执行者持有被代理对象的引用)
例:定义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{ //1、生成源代码 String proxySrc = generateSrc(interfaces[0]); //2、将生成的源代码输出到磁盘,保存为.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(); //3、编译源代码,并且生成.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到JVMpublic 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 必须 实现接口!!!!
满足代理模式应用场景的三个必要条件,
1、需要有两个角色 执行者 和 被代理对象
2、注重过程,必须要做,被代理对象不做
3、执行者必须拿到被代理对象的资料(执行者持有被代理对象的引用)
代理模式总结到底层就是:字节码重组! (字节码重组时候 对象要强制转换,必须要实现一个接口)
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方法 开启一个事务 事务的执行是由我们自己的代码完成的
1、监听到是否有异常,可能需要根据异常的类型来决定这个事务是否好回滚or继续提交?(commit or rollback?)
2、事务要关闭掉
通过动态代理给加了代码
-------------------------------------工厂模式
首先要区分 生产者 消费者 消费者不关心工厂、过程 只关心结果 从工厂取东西哈哈
简单工厂:
定义接口:
//产品接口//汽车需要满足一定的标准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()); } }
接下来是 工厂方法模式
定义工厂接口
实现不同工厂
消费者使用
1、定义工厂接口
import com.gupaoedu.vip.factory.Car;//工厂接口,就定义了所有工厂的执行标准public interface Factory { //符合汽车上路标准 //尾气排放标准 //电子设备安全系数 //必须配备安全带、安全气囊 //轮胎的耐磨程度 Car getCar(); }
2、实现这个工厂接口
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(); }}
3、测试类
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 }
2、
//懒汉式单例类.在第一次调用的时候实例化自己public class Singleton1 { //1、第一步先将构造方法私有化 private Singleton1() {} //2、然后声明一个静态变量保存单例的引用 private static Singleton1 single = null; //3、通过提供一个静态方法来获得单例的引用 //不安全的 public static Singleton1 getInstance() { if (single == null) { single = new Singleton1(); } return single; }}
3、
//懒汉式单例.保证线程安全public class Singleton2 { //1、第一步先将构造方法私有化 private Singleton2() {} //2、然后声明一个静态变量保存单例的引用 private static Singleton2 single=null; //3、通过提供一个静态方法来获得单例的引用 //为了保证多线程环境下正确访问,给方法加上同步锁synchronized //慎用 synchronized 关键字,阻塞,性能非常低下的 //加上synchronized关键字以后,对于getInstance()方法来说,它始终单线程来访问 //没有充分利用上我们的计算机资源,造成资源的浪费 public static synchronized Singleton2 getInstance() { if (single == null) { single = new Singleton2(); } return single; }}
4、
//懒汉式单例.双重锁检查public class Singleton3 { //1、第一步先将构造方法私有化 private Singleton3() {} //2、然后声明一个静态变量保存单例的引用 private static Singleton3 single=null; //3、通过提供一个静态方法来获得单例的引用 //为了保证多线程环境下的另一种实现方式,双重锁检查 //性能,第一次的时候 public static Singleton3 getInstance() { if (single == null) { synchronized (Singleton3.class) { if (single == null) { single = new Singleton3(); } } } return single; }}
5、
//懒汉式(静态内部类)//这种写法,即解决安全问题,又解决了性能问题//这个代码,没有浪费一个字public class Singleton4 { //1、先声明一个静态内部类 //private 私有的保证别人不能修改 //static 保证全局唯一 private static class LazyHolder { //final 为了防止内部误操作,代理模式,GgLib的代理模式 private static final Singleton4 INSTANCE = new Singleton4(); } //2、将默认构造方法私有化 private Singleton4 (){} //相当于有一个默认的public的无参的构造方法,就意味着在代码中随时都可以new出来 //3、同样提供静态方法获取实例 //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中过程//1、从上往下(必须声明在前,使用在后)//先属性、后方法//先静态、后动态
6、
//类似Spring里面的方法,将类名注册,下次从里面直接获取。 public class Singleton6 { private static Mapmap = new HashMap (); 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 SetsyncSet = Collections.synchronizedSet(new HashSet ()); 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()); //策略模式// Listnumbers = new ArrayList ();// // Collections.sort(numbers, new Comparator () {//// @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 ArrayListlist = new ArrayList (); 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(){ //1、把水烧开 boilWater(); //2、把杯子准备好、原材料放到杯中 pourInCup(); //3、用水冲泡 brew(); //4、添加辅料 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规范,各个数据库厂商自己去实现 //1、加载驱动类DriverManager //2、建立连接 //3、创建语句集(标准语句集、预处理语句集)(语句集? MySQL、Oracle、SQLServer、Access) //4、执行语句集 //5、结果集ResultSet 游标 //ORM(?) }
Spring JDBC就是个模板模式
是 Java的规范 各个数据库厂商去实现
1、加载驱动类 DriverManager
2、建立连接
3、创建语句集(标准语句集、预处理语句集)(语句集合? Mysql oracle sqlserver access 语句不太一样哦)
4、执行语句集
5、结果集ResultSet 游标
ORM (连接的是哪个对象 映射哪个结果 List or 自定义的类 还是??运行时候才知道)