<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[ray blog]]></title><description><![CDATA[喜欢编程，擅长j2ee web开发，php网站开发，node全栈开发 8年以上一线开发经历，在企业信息化领域有丰富经验，同样热衷于微信公众平台，钉钉平台等移动领域 无论是技术交流，项目咨询，欢迎各位！]]></description><link>http://blog.ycminglei.cn/</link><image><url>http://blog.ycminglei.cn/favicon.png</url><title>ray blog</title><link>http://blog.ycminglei.cn/</link></image><generator>Ghost 1.9</generator><lastBuildDate>Wed, 24 Jun 2026 22:50:00 GMT</lastBuildDate><atom:link href="http://blog.ycminglei.cn/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[vuepress wiki系统]]></title><description><![CDATA[<div class="kg-card-markdown"><p>支持markdown的wiki系统</p>
<h3 id>一.背景</h3>
<p>VuePress 由两部分组成：第一部分是一个极简静态网站生成器，它包含由 Vue 驱动的主题系统和插件 API，另一个部分是为书写技术文档而优化的默认主题，它的诞生初衷是为了支持 Vue 及其子项目的文档需求。</p>
<p>每一个由 VuePress 生成的页面都带有预渲染好的 HTML，也因此具有非常好的加载性能和搜索引擎优化（SEO）。同时，一旦页面被加载，Vue 将接管这些静态内容，并将其转换成一个完整的单页应用（SPA），其他的页面则会只在用户浏览到的时候才按需加载。</p>
<p><strong>优势</strong></p>
<ul>
<li>markdown编写</li>
<li>静态化页面</li>
<li>轻量化,不需要数据库等的支持,可以与github wiki系统集成</li>
</ul>
<h3 id>二.基本使用</h3>
<ol>
<li>安装
<ul>
<li>安装node &gt;8</li>
<li>npm vuepress i -g</li>
</ul>
</li>
<li>搭建<br>
VuePress 遵循 “约定优于配置” 的原则，推荐的目录结构如下：</li></ol></div>]]></description><link>http://blog.ycminglei.cn/vuepress-wikixi-tong/</link><guid isPermaLink="false">6367f33bd1154b05b82c04ce</guid><dc:creator><![CDATA[ray]]></dc:creator><pubDate>Sun, 06 Nov 2022 17:48:01 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>支持markdown的wiki系统</p>
<h3 id>一.背景</h3>
<p>VuePress 由两部分组成：第一部分是一个极简静态网站生成器，它包含由 Vue 驱动的主题系统和插件 API，另一个部分是为书写技术文档而优化的默认主题，它的诞生初衷是为了支持 Vue 及其子项目的文档需求。</p>
<p>每一个由 VuePress 生成的页面都带有预渲染好的 HTML，也因此具有非常好的加载性能和搜索引擎优化（SEO）。同时，一旦页面被加载，Vue 将接管这些静态内容，并将其转换成一个完整的单页应用（SPA），其他的页面则会只在用户浏览到的时候才按需加载。</p>
<p><strong>优势</strong></p>
<ul>
<li>markdown编写</li>
<li>静态化页面</li>
<li>轻量化,不需要数据库等的支持,可以与github wiki系统集成</li>
</ul>
<h3 id>二.基本使用</h3>
<ol>
<li>安装
<ul>
<li>安装node &gt;8</li>
<li>npm vuepress i -g</li>
</ul>
</li>
<li>搭建<br>
VuePress 遵循 “约定优于配置” 的原则，推荐的目录结构如下：</li>
</ol>
<pre><code>.
├── docs
│   ├── .vuepress (可选的)
│   │   ├── components (可选的)
│   │   ├── theme (可选的)
│   │   │   └── Layout.vue
│   │   ├── public (可选的)
│   │   ├── styles (可选的)
│   │   │   ├── index.styl
│   │   │   └── palette.styl
│   │   ├── templates (可选的, 谨慎配置)
│   │   │   ├── dev.html
│   │   │   └── ssr.html
│   │   ├── config.js (可选的)
│   │   └── enhanceApp.js (可选的)
│   │ 
│   ├── README.md
│   ├── guide
│   │   └── README.md
│   └── config.md
│ 
└── package.json
</code></pre>
<ol start="3">
<li>config.js 核心配置文件</li>
</ol>
<blockquote>
<p>主要维护导航菜单配置</p>
</blockquote>
<ul>
<li>nav</li>
</ul>
<blockquote>
<p>顶部导航栏</p>
</blockquote>
<pre><code>nav: [
    {text: '主页', link: '/'},
    {text: 'JAVA', link: '/java/',
        items: [
            {text: 'spring', link: '/java/spring/'},
            {text: 'jpa', link: '/java/jpa/'},
        ]
    },
    {text: 'VUE', link: '/vue/'},
    {text: 'Hadoop', link: '/hadoop/'},
],
</code></pre>
<blockquote>
<p>四个一级菜单,通过items嵌套二级菜单</p>
</blockquote>
<ul>
<li>sidebar</li>
</ul>
<blockquote>
<p>侧边导航栏</p>
</blockquote>
<pre><code> sidebar: {
    '/java/': [
        ['/java/', 'java简介'],
        {
            title: &quot;spring&quot;,
            collapsable: false,
            children: [
                ['/java/spring/Security.md', 'spring安全框架'],
                ['/java/spring/Social.md', 'spring social'],
            ]
        }],
}
</code></pre>
<blockquote>
<p>1.侧边栏通过children进行嵌套<br>
2.通过link地址匹配与nav保持同步</p>
</blockquote>
<h3 id>三.场景</h3>
<ul>
<li>项目文档系统</li>
<li>个人博客系统</li>
</ul>
<h3 id>参考资料</h3>
<p><a href="https://www.vuepress.cn/">VuePress 中文网</a></p>
</div>]]></content:encoded></item><item><title><![CDATA[java web测试体系]]></title><description><![CDATA[<div class="kg-card-markdown"><h3 id>引言</h3>
<p>-. 从<strong>测试目的</strong>来讲,分为功能测试,性能测试,安全测试,特性测试<br>
-. 从<strong>测试V模型</strong>范围来讲,分为单元测试,集成测试,系统测试,验收测试<br>
-. 从<strong>mvc架构</strong>来讲,测试分为dao,service,controller,view</p>
<p>本文旨在为程序开发人员在java web开发项目中的测试进行分析归纳</p>
<h3 id>程序开发人员场景</h3>
<ol>
<li>
<p>单元测试<br>
单元测试更多使用mock测试,减少外部依赖影响,仅测试当前对象</p>
</li>
<li>
<p>切片测试<br>
slice是指一些在特定环境下才能执行的模块，比如MVC中的Controller、JDBC数据库访问、Redis客户端等，这些模块大多脱离特定环境后不能独立运行,需要提供测试上下文环境进行测试,一般</p>
<ol>
<li>提供jpa上下文,对dao层进行测试</li>
<li>提供bean上下文,对服务层进行测试</li>
<li>提供bean与servlet上下文,对controller进行测试</li>
</ol>
</li>
<li>
<p>功能测试<br>
使用相对完整的系统上下文进行测试,针对web服务一些特殊的期望测试,一般可以</p></li></ol></div>]]></description><link>http://blog.ycminglei.cn/java-webce-shi-ti-xi/</link><guid isPermaLink="false">6367f2f7d1154b05b82c04cd</guid><dc:creator><![CDATA[ray]]></dc:creator><pubDate>Sun, 06 Nov 2022 17:46:47 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><h3 id>引言</h3>
<p>-. 从<strong>测试目的</strong>来讲,分为功能测试,性能测试,安全测试,特性测试<br>
-. 从<strong>测试V模型</strong>范围来讲,分为单元测试,集成测试,系统测试,验收测试<br>
-. 从<strong>mvc架构</strong>来讲,测试分为dao,service,controller,view</p>
<p>本文旨在为程序开发人员在java web开发项目中的测试进行分析归纳</p>
<h3 id>程序开发人员场景</h3>
<ol>
<li>
<p>单元测试<br>
单元测试更多使用mock测试,减少外部依赖影响,仅测试当前对象</p>
</li>
<li>
<p>切片测试<br>
slice是指一些在特定环境下才能执行的模块，比如MVC中的Controller、JDBC数据库访问、Redis客户端等，这些模块大多脱离特定环境后不能独立运行,需要提供测试上下文环境进行测试,一般</p>
<ol>
<li>提供jpa上下文,对dao层进行测试</li>
<li>提供bean上下文,对服务层进行测试</li>
<li>提供bean与servlet上下文,对controller进行测试</li>
</ol>
</li>
<li>
<p>功能测试<br>
使用相对完整的系统上下文进行测试,针对web服务一些特殊的期望测试,一般可以</p>
<ol>
<li>使用restTemplate,模拟http请求进行测试</li>
<li>使用htmlunit,模拟ui层面的交互和验证<br>
3.使用mockmvc模拟httpServlet,系统测试controller,service,jpa</li>
</ol>
</li>
</ol>
<blockquote>
<p>配合使用dbunit,flyway进行数据初始化配合测试进行验证</p>
</blockquote>
<h3 id="junit">一. junit</h3>
<p>推荐最佳实践</p>
<ul>
<li>测试方法上必须使用@Test进行修饰</li>
<li>测试方法必须使用public void 进行修饰，不能带任何的参数</li>
<li>新建一个源代码目录来存放我们的测试代码，即将测试代码和项目业务代码分开</li>
<li>测试类所在的包名应该和被测试类所在的包名保持一致</li>
<li>测试单元中的每个方法必须可以独立测试，测试方法间不能有任何的依赖</li>
<li>测试类使用Test作为类名的后缀（不是必须）</li>
<li>测试方法使用test作为方法名的前缀（不是必须）<br>
常用注解</li>
</ul>
<pre><code>*`@BeforeClass`*   在所有测试方法前执行一次，一般在其中写上整体初始化的代码 
*`@AfterClass`*   在所有测试方法后执行一次，一般在其中写上销毁和释放资源的代码 
*`@Before`*   在每个测试方法前执行，一般用来初始化方法（比如我们在测试别的方法时，类中与其他测试方法共享的值已经被改变，为了保证测试结果的有效性，我们会在@Before注解的方法中重置数据） 
*`@After`*  在每个测试方法后执行，在方法执行完成后要做的事情 
*`@Test(timeout = 1000)`* 测试方法执行超过1000毫秒后算超时，测试将失败 
*`@Test(expected = Exception.class)`* 测试方法期望得到的异常类，如果方法执行没有抛出指定的异常，则测试失败 
*`@Ignore(“not ready yet”)`*        执行测试时将忽略掉此方法，如果用于修饰类，则忽略整个类 
 *`@Test `*  编写一般测试用例
 *`@RunWith`*    在JUnit中有很多个Runner，他们负责调用你的测试代码，每一个Runner都有各自的特殊功能，你要根据需要选择不同的Runner来运行你的测试代码。 
如果我们只是简单的做普通Java测试，不涉及Spring Web项目，你可以省略@RunWith注解，这样系统会自动使用默认Runner来运行你的代码。

</code></pre>
<p>与testNg对比<br>
a.两个框架的不同在于核心设计。JUnit 一直 是一个单元测试框架，也就是说，其构建目的是促进单个对象的测试，它确实能够极其有效地完成此类任务。而 TestNG 则是用来解决更高 级别的测试问题，<br>
b.testNg在依赖性测试,失败和重运行,参数化测试具备一定优势,同样这些都是大规模测试准备的</p>
<h3 id="springtest">二. spring test</h3>
<p>Spring Test与JUnit等其他测试框架结合起来，提供了便捷高效的测试手段</p>
<h4 id>基础</h4>
<p>-. 依赖:org.springframework.spring-test.version<br>
-. SpringJUnit4ClassRunner spring提供的junit测试环境<br>
-. @ContextConfiguration加载上下文注解<br>
1. locations属性:引入xml文件,<br>
2. classes属性:引入bean对象</p>
<p>-. @TestExecutionListeners<br>
定义了一个类级别的元数据，用于配置需要用TestContextManager进行注册的TestExecutionListener实现</p>
<p>-. @WebAppConfiguration<br>
用于声明一个ApplicationContext,集成测试加载WebApplicationContext,模拟ServletContext</p>
<p>-. @DirtiesContext<br>
使用@DirtiesContext，可以保证每个test class的执行上下文的独立性、隔离性，但是也会有让测试运行速度变慢的副作用。所以在使用@DirtiesContext前，弄清楚你是否真的需要使用它</p>
<p>-. @TestPropertySource<br>
is a class-level annotation that configures locations of property files and inlined properties in Spring integration test.</p>
<h4 id>进阶</h4>
<p>-. TestContext<br>
TestContext 测试框架的核心由 org.springframework.test.context 中三个类组成，分别是</p>
<ul>
<li>TestContext：它封装了运行测试用例的上下文</li>
<li>TestContextManager：它是进入 Spring TestContext 框架的程序主入口，它管理着一个 TestContext 实例，并在适合的执行点上向所有注册在 TestContextManager 中的 TestExecutionListener 监听器发布事件：比如测试用例实例的准备，测试方法执行前后方法的调用等</li>
<li>TestExecutionListener：该接口负责响应 TestContextManager 发布的事件</li>
</ul>
<p>-. TestExecutionListener<br>
+. DependencyInjectionTestExecutionListener：该监听器提供了自动注入的功能，它负责解析测试用例中 @Autowried 注解并完成自动注入</p>
<ul>
<li>DirtiesContextTestExecutionListener：一般情况下测试方法并不会对 Spring 容器上下文造成破坏（改变 Bean 的配置信息等），如果某个测试方法确实会破坏 Spring 容器上下文，你可以显式地为该测试方法添加 @DirtiesContext 注解，以便 Spring TestContext 在测试该方法后刷新 Spring 容器的上下文，而 DirtiesContextTestExecutionListener 监听器的工作就是解析 @DirtiesContext 注解<br>
+. TransactionalTestExecutionListener：它负责解析 @Transaction、@NotTransactional 以及 @Rollback 等事务注解的注解<br>
+. MockitoTestExecutionListener  解析:@Mock:模拟bean,@InjectMocks:注入模拟bean<br>
+. DbUnitTestExecutionListener 解析:@DatabaseSetup, @DatabaseTearDown 和 @ExpectedDatabase</li>
</ul>
<h3 id="springboottest">三. springboot test</h3>
<p>Spring Boot Test 是在Spring Test之上的再次封装，增加了切片测试，增强了mock能力。</p>
<ul>
<li>基于junit测试框架,延续spring test,使用一些断言库,mock库完成测试工作</li>
<li>提供两个核心模块:1.核心测试支持,2.spring-boot-test-autoconfig</li>
<li>其依赖有junit.spring test,assertj,hamcrest,mockito,jsonpath,jsonAssert</li>
<li></li>
</ul>
<h4 id="1">1. 基础</h4>
<ol>
<li>依赖<br>
<dependency><br>
<groupid>org.springframework.boot</groupid><br>
<artifactid>spring-boot-starter-test</artifactid><br>
<scope>test</scope><br>
</dependency></li>
</ol>
<p>+. @SpringRunner<br>
继承SpringJUnit4ClassRunner,可能是为了缩减命名长度吧</p>
<p>+. @SpringBootTest<br>
- 此注解替代了spring-test中的@ContextConfiguration注解，目的是加载ApplicationContext，启动spring容器,<br>
- @SpringBootTest注解会自动检索程序的配置文件，检索顺序是从当前包开始，逐级向上查找被@SpringBootApplication或@SpringBootConfiguration注解的类<br>
+. @AutoConfigureWebMvc<br>
- 启用与Web层相关的所有自动配置,并且仅启用Web层.这是整体自动配置的子集</p>
<p>+. @AutoConfigureMockMvc<br>
- 启用与MockMvc和ONLY MockMvc相关的所有自动配置.同样,这是整体自动配置的子集.</p>
<p>+. @WebMvcTest<br>
- 包括@AutoConfigureWebMvc和@AutoConfigureMockMvc以及其他功能.<br>
-<br>
+. @MockBean<br>
- 提供mock支持,在运行时会扫描到你注释的 MockBean ，并自动装配到被测试的 controller 里面。这也是和 @Mock 注释不同的地方，后者只能生成一个 Mock 类，但是并不能自动装配到其它类里面。<br>
- @SpyBean 与 @Spy 的关系类似于 @MockBean 与 @Mock 的关系。和 @MockBean 不同的是，它不会生成一个 Bean 的替代品装配到类中，而是会监听一个真正的 Bean 中某些特定的方法，并在调用这些方法时给出指定的反馈。<br>
- @Mock: 创建一个Mock.<br>
- @InjectMocks: 创建一个实例，其余用@Mock（或@Spy）注解创建的mock将被注入到用该实例中</p>
<p>+. @TestConfiguration<br>
- 提供一些测试相关的配置入口</p>
<h3 id>四. 实践</h3>
<p>测试过程中的几个关键要素<br>
项目结构:maven提供依赖与结构管理<br>
测试框架:junit<br>
容器管理:springboot<br>
mock能力：Mockito提供了强大mock功能。<br>
断言能力：AssertJ、Hamcrest、JsonPath提供了强大的断言能力</p>
<h4 id>单元测试</h4>
<pre><code>使用junit+mock进行测试
</code></pre>
<p>ps:单元测试仅仅测试当前单元类,其依赖均使用mock模拟实现;集成测试时也会用mock模拟一些边界来配合测试,如HttpRequest等,但是会测试到相关依赖类的实现是否正确</p>
<h4 id>集成测试</h4>
<p>支撑方式：通过@RunWith 和 @SpringBootTest启动spring容器</p>
<p>切片测试：一般面向难于测试的边界功能，介于单元测试和功能测试之间。涉及到的注解 @WebMvcTest,@DataJpaTest等。</p>
<p>功能测试：一般面向某个完整的业务功能，同时也可以使用切面测试中的mock能力,使用Mockmvc进行测试，推荐使用。涉及到的注解有@RunWith @SpringBootTest等。</p>
<ol>
<li>关于web<br>
在spring没有为此提供测试支持，开发者只能启动完整服务对这些模块进行测试，这在一些复杂的系统中非常不方便，所以springboot为这些模块提供了测试支持，使开发者有能力单独对这些模块进行测试。</li>
</ol>
<ul>
<li>
<p>@SpringBootTest注解中，给出了webEnvironment参数指定了web的environment</p>
<ol>
<li>MOCK：此值为默认值，该类型提供一个mock环境，可以和@AutoConfigureMockMvc或@AutoConfigureWebTestClient搭配使用，开启Mock相关的功能。注意此时内嵌的服务（servlet容器）并没有真正启动，也不会监听web服务端口。</li>
<li>RANDOM_PORT：启动一个真实的web服务，监听一个随机端口。</li>
<li>DEFINED_PORT：启动一个真实的web服务，监听一个定义好的端口（从application.properties读取）。</li>
<li>NONE：启动一个非web的ApplicationContext，既不提供mock环境，也不提供真实的web服务</li>
</ol>
</li>
<li>
<p>@WebMvcTest(IndexController.class)<br>
使用@WebMvcTest和MockMvc搭配使用，可以在不启动web容器的情况下，对Controller进行测试</p>
</li>
</ul>
<blockquote>
<p>仅仅只是对controller进行简单的测试，如果Controller中依赖用@Autowired注入的service、dao等则不能这样测试,如果要测试,则必须mock相应的service,dao来进行测试</p>
</blockquote>
<h3 id>五. 其它</h3>
<ol>
<li>
<p>mock测试<br>
打桩（Stub）或则模拟，当你调用一个不好在测试中创建的对象时，Mock框架为你模拟一个和真实对象类似的替身来完成相应的行为<br>
在进行单元测试时，经常遇到被测方法依赖外部对象和环境，如需要数据库连接，网络通信依赖等，需要进行大量的初始化工作，这时可以采用powermock+mockito对被测对象进行模拟，通过录放的形式解决此类问题。</p>
</li>
<li>
<p>Mockito<br>
它与 EasyMock 和 jMock 很相似，都是为了简化单元测试过程中测试上下文 ( 或者称之为测试驱动函数以及桩函数 ) 的搭建而开发的工具。<br>
方法名 描述<br>
Mockito.mock(classToMock)   模拟对象<br>
Mockito.verify(mock)    验证行为是否发生<br>
Mockito.when(methodCall).thenReturn(value1).thenReturn(value2)  触发时第一次返回value1，第n次都返回value2<br>
Mockito.doThrow(toBeThrown).when(mock).[method] 模拟抛出异常。<br>
Mockito.mock(classToMock,defaultAnswer) 使用默认Answer模拟对象<br>
Mockito.when(methodCall).thenReturn(value)  参数匹配<br>
Mockito.doReturn(toBeReturned).when(mock).[method]  参数匹配（直接执行不判断）<br>
Mockito.when(methodCall).thenAnswer(answer))    预期回调接口生成期望值<br>
Mockito.doAnswer(answer).when(methodCall).[method]  预期回调接口生成期望值（直接执行不判断）<br>
Mockito.spy(Object) 用spy监控真实对象,设置真实对象行为<br>
Mockito.doNothing().when(mock).[method] 不做任何返回<br>
Mockito.doCallRealMethod().when(mock).[method] <a href="//xn--Mockito-lr4ko4ay553a.when">//等价于Mockito.when</a>(mock.[method]).thenCallRealMethod();   调用真实的方法<br>
reset(mock) 重置mock</p>
</li>
<li>
<p>PowerMock<br>
在其它单元测试模拟框架的基础上做出的扩展。通过提供定制的类加载器以及一些字节码篡改技巧的应用，对静态方法、构造方法、私有方法以及 Final 方法的模拟支持，对静态初始化过程的移除等强大的功能。<br>
现如今比较流行的Mock工具如jMock 、EasyMock 、Mockito等都有一个共同的缺点：不能mock静态、final、私有方法等。而PowerMock能够完美的弥补以上三个Mock工具的不足。<br>
它有两个重要的依赖：javassist和objenesis。<br>
javassist是一个修改java字节码的工具包，<br>
objenesis是一个绕过构造方法来实例化一个对象的工具包<br>
由此看来，PowerMock的本质是通过修改字节码来实现对静态和final等方法的mock的。当某个测试方法被注解@PrepareFordytest标注以后，在运行测试用例时，会创建一个新的org.powermock.core.classloader.MockClassLoader实例，<br>
然后加载该测试用例使用到的类（系统类除外）。<br>
• PowerMock会根据你的mock要求，去修改写在注解@PrepareFordytest里的class文件（当前测试类会自动加入注解中），以满足特殊的mock需求。例如：去除final方法的final标识，在静态方法的最前面加入自己的虚拟实现等。<br>
• 如果需要mock的是系统类的final方法和静态方法，PowerMock不会直接修改系统类的class文件，而是修改调用系统类的class文件，以满足mock需求。</p>
</li>
<li>
<p>EasyMock<br>
提供了根据指定接口动态构建 Mock 对象的方法，避免了手工编写 Mock 对象</p>
</li>
<li>
<p>Hamcrest<br>
是一个书写匹配器对象时允许直接定义匹配规则的框架.有大量的匹配器是侵入式的</p>
</li>
<li>
<p>AseertJ:<br>
JAVA 流式断言器，支持一条断言语句对实际值同时断言多个校验点</p>
</li>
</ol>
</div>]]></content:encoded></item><item><title><![CDATA[Flyway入门]]></title><description><![CDATA[<div class="kg-card-markdown"><h2 id="flyway">Flyway</h2>
<pre><code> Flyway 的中文文档近乎为零，英文文档也凤毛麟角，但它却是我们最理想的数据库版本管理工具，
</code></pre>
<h3 id>特性</h3>
<ul>
<li>自动升级（自动发现更新项）：Flyway 会将任意版本的数据库升级到最新版本。Flyway 可以脱离JVM 环境通过命令行执行，可以通过Ant 脚本执行，通过Maven 脚本执行（这样就可以在集成环境自动执行），并且可以在应用中执行（比如在应用启动时执行）。</li>
<li>规约优于配置：Flyway 有一套默认的规约，所以不需要修改任何配置就可以正常使用</li>
<li>既支持SQL 脚本，又支持Java 代码：可以使用SQL 脚本执行数据库更新，也可以使用Java 代码来进行一些高级数据升级操作</li>
<li>高可靠性：在集群环境下进行数据库升级是安全可靠的</li>
<li>支持清除已存在的库表结构：Flyway 可以清除已存在的库表结构，可以从零开始搭建您的库表结构，并管理您的数据库版本升级工作</li>
<li>支持失败修复。新的2.0 版本提供了repair 功能，用于解决数据库更新操作失败问题</li>
</ul>
<h3 id>运行</h3>
<ol>
<li>cmd</li>
</ol>
<pre><code>    1.  修改conf/flyway.</code></pre></div>]]></description><link>http://blog.ycminglei.cn/flywayru-men/</link><guid isPermaLink="false">6367f168d1154b05b82c04ca</guid><category><![CDATA[other]]></category><dc:creator><![CDATA[ray]]></dc:creator><pubDate>Sun, 06 Nov 2022 17:41:25 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><h2 id="flyway">Flyway</h2>
<pre><code> Flyway 的中文文档近乎为零，英文文档也凤毛麟角，但它却是我们最理想的数据库版本管理工具，
</code></pre>
<h3 id>特性</h3>
<ul>
<li>自动升级（自动发现更新项）：Flyway 会将任意版本的数据库升级到最新版本。Flyway 可以脱离JVM 环境通过命令行执行，可以通过Ant 脚本执行，通过Maven 脚本执行（这样就可以在集成环境自动执行），并且可以在应用中执行（比如在应用启动时执行）。</li>
<li>规约优于配置：Flyway 有一套默认的规约，所以不需要修改任何配置就可以正常使用</li>
<li>既支持SQL 脚本，又支持Java 代码：可以使用SQL 脚本执行数据库更新，也可以使用Java 代码来进行一些高级数据升级操作</li>
<li>高可靠性：在集群环境下进行数据库升级是安全可靠的</li>
<li>支持清除已存在的库表结构：Flyway 可以清除已存在的库表结构，可以从零开始搭建您的库表结构，并管理您的数据库版本升级工作</li>
<li>支持失败修复。新的2.0 版本提供了repair 功能，用于解决数据库更新操作失败问题</li>
</ul>
<h3 id>运行</h3>
<ol>
<li>cmd</li>
</ol>
<pre><code>    1.  修改conf/flyway.properties  配置文件
    2.  拷贝数据库jdbc  驱动jar  到jars/  目录
    3. 在sql/  目录下创建配置好的sql  脚本文件目录路径，如flyway 默认的sql  文件路径为db/migration ，我们就需要在sql/  目录下创建/db/migration  目录结构
    4.  将数据库维护脚本放到创建好的sql  脚本文件目录中（维护脚本文件名需要遵循命名规范）
    5.  在命令行执行命令（从flyway 安装目录开始执行）flyway init （初始化Flyway metadata ）、flyway migrate（执行Flyway 升级操作）、flyway validate （校验Flyway 数据正确性）
</code></pre>
<p>2.maven</p>
<ul>
<li>mvn flyway:init</li>
<li>mvn flyway:migrate</li>
<li>mvn flyway:validate</li>
</ul>
<pre><code> &lt;plugin&gt;  
       &lt;groupId&gt;com.googlecode.flyway&lt;/groupId&gt;  
       &lt;artifactId&gt;flyway-maven-plugin&lt;/artifactId&gt;  
       &lt;version&gt;3.1&lt;/version&gt;  
        &lt;dependencies&gt;  
              &lt;dependency&gt;  
                     &lt;groupId&gt;mysql&lt;/groupId&gt;  
                     &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;  
                     &lt;version&gt;${mysql.connector.version}&lt;/version&gt;  
              &lt;/dependency&gt;  
       &lt;/dependencies&gt;  
       &lt;configuration&gt;  
              &lt;driver&gt;com.mysql.jdbc.Driver&lt;/driver&gt;  
              &lt;url&gt;jdbc:mysql://localhost/flywaydemo?useUnicode=true&amp;amp;characterEncoding=utf-8&lt;/url&gt;  
              &lt;user&gt;root&lt;/user&gt;  
              &lt;password&gt;&lt;/password&gt;  
              &lt;!-- 设置接受flyway进行版本管理的数据库，多个数据库以逗号分隔 --&gt;  
              &lt;schemas&gt;flywaydemo&lt;/schemas&gt;  
              &lt;!-- 设置存放flyway metadata数据的表名 --&gt;  
              &lt;table&gt;schema_version&lt;/table&gt;  
              &lt;!-- 设置flyway扫描sql升级脚本、java升级脚本的目录路径或包路径 --&gt;  
              &lt;locations&gt;  
                     &lt;location&gt;flyway/migrations&lt;/location&gt;  
                     &lt;location&gt;com.kedacom.flywaydemo.migrations&lt;/location&gt;  
              &lt;/locations&gt;  
              &lt;!-- 设置sql脚本文件的编码 --&gt;  
              &lt;encoding&gt;UTF-8&lt;/encoding&gt;  
              &lt;!-- 设置执行migrate操作之前的validation行为 --&gt;  
              &lt;validationMode&gt;ALL&lt;/validationMode&gt;  
              &lt;!-- 设置当validation失败时的系统行为 --&gt;  
              &lt;validationErrorMode&gt;FAIL&lt;/validationErrorMode&gt;  
       &lt;/configuration&gt;  
	33. 
&lt;/plugin&gt; 
</code></pre>
<p>3.java</p>
<pre><code>public class FlywayMigration {  
    private DataSource dataSource;  
    public void setDataSource(DataSource dataSource) {  
        this.dataSource = dataSource;  
    }  
  
    public void migrate() {  
        Flyway flyway = new Flyway();  
        flyway.setDataSource(dataSource);  
  
        flyway.setSchemas(&quot;flywaydemo&quot;); // 设置接受flyway进行版本管理的多个数据库  
        flyway.setTable(&quot;schema_version&quot;); // 设置存放flyway metadata数据的表名  
        flyway.setLocations(&quot;flyway/migrations&quot;, &quot;com.kedacom.flywaydemo.migrations&quot;); // 设置flyway扫描sql升级脚本、java升级脚本的目录路径或包路径  
        flyway.setEncoding(&quot;UTF-8&quot;); // 设置sql脚本文件的编码  
        flyway.setValidationMode(ValidationMode.ALL); // 设置执行migrate操作之前的validation行为  
        flyway.setValidationErrorMode(ValidationErrorMode.FAIL); // 设置当validation失败时的系统行为  
  
        flyway.migrate();  
    }  
  
} 
</code></pre>
<p>4.spring</p>
<pre><code>&lt;bean id=&quot;flywayMigration&quot; class=&quot;com.kedacom.flywaydemo.FlywayMigration&quot; init-method=&quot;migrate&quot;&gt;  
    &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot; /&gt;  
&lt;/bean&gt;  
</code></pre>
<blockquote>
<p>flywayMigration 这个bean 实例注入了一个数据源，Flyway 的所有操作将针对这个数据源进行；同时我们通过init-method 属性指定了Spring 在实例化该bean 以后，主动执行该bean的migrate 方法，而该方法内会执行Flyway 更新数据库的操作。至此，我们达到了在应用启动时，Spring 实例化上下文的时候，在Spring 实例化flywayMigration 这个bean 的时候，自动执行Flyway 更新数据库的操作。<br>
问题1： 当flyway 还在更新数据库，没有完成更新操作之前，应用程序的其他逻辑已经开始使用数据库进行其他操作了，会导致应用程序产生很多bug</p>
</blockquote>
<blockquote>
<p>解决办法：</p>
</blockquote>
<pre><code>&lt;bean id=&quot;jdbcTemplate&quot; class=&quot;org.springframework.jdbc.core.JdbcTemplate&quot; depends-on=&quot;flywayMigration&quot;&gt;  
    &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot; /&gt;  
&lt;/bean&gt;  
  
&lt;bean id=&quot;txManager&quot; class=&quot;org.springframework.jdbc.datasource.DataSourceTransactionManager&quot; depends-on=&quot;flywayMigration&quot;&gt;  
    &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot; /&gt;  
&lt;/bean&gt;  
</code></pre>
<p>总结</p>
</div>]]></content:encoded></item><item><title><![CDATA[Git 原理简介]]></title><description><![CDATA[<div class="kg-card-markdown"><blockquote>
<p>本质上，Git是一套内容寻址（content-addressable）文件系统，而和我们直接接触的Git界面，只不过是封装在其之上的一个应用层。</p>
</blockquote>
<h2 id="git">Git工作区、暂存区和版本库</h2>
<p>工作区：项目文件夹即工作区 working directory<br>
版本库：在初始化git版本库之后会生成一个隐藏的文件 .git ，可以将该文件理解为git的版本库 repository，<br>
暂存区：在.git 文件夹里面还有很多文件，其中有一个index 文件 就是暂存区也可以叫做 stage</p>
<p>git还为我们自动生成了一个分支master以及指向该分支的指针head ,如下图<br>
<img src="http://blog.ycminglei.cn/content/images/2018/05/20151030110206984.png" alt="20151030110206984"></p>
<blockquote>
<p>git add file 是把文件从工作区提交到暂存区,暂存区的目录树被更新，同时工作区修改（或新增）的文件内容被写入到对象库中的一个新的对象中，而该对象的ID被记录在暂存区的文件索引中<br>
git commit -m &quot;prompty&quot; file 是把文件从暂存区提交到了分支master下面,暂存区的目录树写到版本库（对象库）中，master 分支会做相应的更新<br>
git</p></blockquote></div>]]></description><link>http://blog.ycminglei.cn/git-yuan-li-jian-jie/</link><guid isPermaLink="false">5b027767732a5961f290ea1e</guid><dc:creator><![CDATA[ray]]></dc:creator><pubDate>Tue, 18 Sep 2018 16:32:51 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><blockquote>
<p>本质上，Git是一套内容寻址（content-addressable）文件系统，而和我们直接接触的Git界面，只不过是封装在其之上的一个应用层。</p>
</blockquote>
<h2 id="git">Git工作区、暂存区和版本库</h2>
<p>工作区：项目文件夹即工作区 working directory<br>
版本库：在初始化git版本库之后会生成一个隐藏的文件 .git ，可以将该文件理解为git的版本库 repository，<br>
暂存区：在.git 文件夹里面还有很多文件，其中有一个index 文件 就是暂存区也可以叫做 stage</p>
<p>git还为我们自动生成了一个分支master以及指向该分支的指针head ,如下图<br>
<img src="http://blog.ycminglei.cn/content/images/2018/05/20151030110206984.png" alt="20151030110206984"></p>
<blockquote>
<p>git add file 是把文件从工作区提交到暂存区,暂存区的目录树被更新，同时工作区修改（或新增）的文件内容被写入到对象库中的一个新的对象中，而该对象的ID被记录在暂存区的文件索引中<br>
git commit -m &quot;prompty&quot; file 是把文件从暂存区提交到了分支master下面,暂存区的目录树写到版本库（对象库）中，master 分支会做相应的更新<br>
git reset HEAD 暂存区的目录树会被重写，被 master 分支指向的目录树所替换，但是工作区不受影响<br>
git rm --cached file 直接从暂存区删除文件，工作区则不做出改变<br>
git checkout -- file 用暂存区全部或指定的文件替换工作区的文件。这个操作很危险，会清除工作区中未添加到暂存区的改动<br>
git checkout HEAD file 用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件</p>
</blockquote>
<h2 id="git">Git的目录结构</h2>
<ul>
<li>config文件：该文件主要记录针对该项目的一些配置信息，例如是否以bare方式初始化、remote的信息等，通过git remote add命令增加的远程分支的信息就保存在这里；</li>
<li>objects文件夹：该文件夹主要包含git对象。关于什么是git对象，将会在下一节进行详细介绍。Git中的文件和一些操作都会以git对象来保存，git对象分为BLOB、tree和commit三种类型，例如git commit便是git中的commit对象，而各个版本之间是通过版本树来组织的，比如当前的HEAD会指向某个commit对象，而该commit对象又会指向几个BLOB对象或者tree对象。objects文件夹中会包含很多的子文件夹，其中Git对象保存在以其sha-1值的前两位为子文件夹、后38位位文件名的文件中；除此以外，Git为了节省存储对象所占用的磁盘空间，会定期对Git对象进行压缩和打包，其中pack文件夹用于存储打包压缩的对象，而info文件夹用于从打包的文件中查找git对象；</li>
<li>HEAD文件：该文件指明了git branch（即当前分支）的结果，比如当前分支是master，则该文件就会指向master，但是并不是存储一个master字符串，而是分支在refs中的表示，例如ref: refs/heads/master。</li>
<li>index文件：该文件保存了暂存区域的信息。该文件某种程度就是缓冲区（staging area），内容包括它指向的文件的时间戳、文件名、sha1值等；</li>
<li>Refs文件夹：该文件夹存储指向数据（分支）的提交对象的指针。其中heads文件夹存储本地每一个分支最近一次commit的sha-1值（也就是commit对象的sha-1值），每个分支一个文件；remotes文件夹则记录你最后一次和每一个远程仓库的通信，Git会把你最后一次推送到这个remote的每个分支的值都记录在这个文件夹中；tag文件夹则是分支的别名，这里不需要对其有过多的了解；</li>
</ul>
<h2 id>存储对象</h2>
<p>一般来讲记录版本信息的方式主要有两种：</p>
<ol>
<li>记录文件每个版本的快照；</li>
<li>记录文件每个版本之间的差异</li>
</ol>
<blockquote>
<p>GIT采用第一种方式。像Subversion和Perforce等版本控制系统都是记录文件每个版本之间的差异，这就需要对比文件两版本之间的具体差异，但是GIT不关心文件两个版本之间的具体差别，而是关心文件的整体是否有改变，若文件被改变，在添加提交时就生成文件新版本的快照，而判断文件整体是否改变的方法就是用SHA-1算法计算文件的校验和。GIT能正常工作完全信赖于这种SHA-1校验和，当一个文件的某一个版本被记录之后会生成这个版本的一个快照，但是一样要能引用到这个快照，GIT中对快照的引用，对每个版本的记录标识全是通过SHA-1校验和来实现的。</p>
</blockquote>
<p>GIT对象<br>
　　每个对象(object) 包括三个部分：类型，大小和内容。大小就是指内容的大小，内容取决于对象的类型，有四种类型的对象：&quot;blob&quot;、&quot;tree&quot;、 &quot;commit&quot; 和&quot;tag&quot;。</p>
<ul>
<li>“blob”用来存储文件数据，通常是一个文件。</li>
<li>“tree”有点像一个目录，它管理一些“tree”或是 “blob”（就像文件和子目录）</li>
<li>一个“commit”指向一个&quot;tree&quot;，它用来标记项目某一个特定时间点的状态。它包括一些关于时间点的元数据，如提交时间、提交说明、作者、提交者、指向上次提交（commits）的指针等等。</li>
<li>一个“tag”是来标记某一个提交(commit) 的方法</li>
</ul>
<p>示例1：</p>
<pre><code>$ git add README test.rb LICENSE2
$ git commit -m 'initial commit of my project'
</code></pre>
<p>现在,Git 仓库中有五个对象:三个表示文件快照内容的 blob 对象;一个记录着目录树内容及其中各个文件对应 blob 对象索引的 tree 对象;以及一个包含指向 tree 对象(根目录)的索引和其他提交信息元数据的 commit 对象。概念上来说,仓库中的各个对象保存的数据和相互关系看起来如下图：<br>
<img src="http://blog.ycminglei.cn/content/images/2018/05/20193835-53a201b735d34b1e98cd882a40396932.png" alt="20193835-53a201b735d34b1e98cd882a40396932"><br>
　如果进行多次提交，仓库的历史会像这样：<br>
<img src="http://blog.ycminglei.cn/content/images/2018/05/20194015-e690fe536bd94cde81f132bb5e2470c2.png" alt="20194015-e690fe536bd94cde81f132bb5e2470c2"><br>
<strong>分支引用</strong><br>
　　所谓的GIT分支，其实就是一个指向某一个Commit对象的指针，像下面这样，有两个分支，master与testing：<br>
<img src="http://blog.ycminglei.cn/content/images/2018/05/20194528-f5bf7e93960148a782f9d72f93a134bc.png" alt="20194528-f5bf7e93960148a782f9d72f93a134bc"></p>
<h2 id>分布式版本库</h2>
<blockquote>
<p>git是一个分布式的结构，意味着本地和远程是一个相对的名称</p>
</blockquote>
<ul>
<li>git remote 本地的repo仓库要与远程的repo配合完成版本对应必须要有 git remote子命令</li>
<li>git branch [-r] 操作本地分支或远程分支</li>
<li>git push [-remote] [-branch] [-branch],将本地版本库的分支推送到远程服务器上对应的分支</li>
<li>git fetch [-remote] [-remote_branch:local branch] 更新git remote中远程仓库提交</li>
<li>git pull 比对本地的FETCH_HEAD记录与远程仓库的版本号，然后git fetch 获得当前指向的远程分支的后续版本的数据，然后再利用git merge将其与本地的当前分支合并</li>
</ul>
<h2 id>版本工作差别</h2>
<p>与svn差别<br>
1.GIT是分布式的，SVN不是：<br>
2.GIT把内容按元数据方式存储，而SVN是按文件：<br>
3.GIT分支和SVN的分支不同：<br>
4.GIT没有一个全局的版本号，而SVN有：<br>
5.GIT的内容完整性要优于SVN<br>
缺点: 复杂的信息模型,提交麻烦，命令复杂，<br>
操作流程对比如下图：<br>
<img src="http://blog.ycminglei.cn/content/images/2018/05/TIM--20180525163025.png" alt="TIM--20180525163025"></p>
</div>]]></content:encoded></item><item><title><![CDATA[IO设计模式：Reactor和Proactor]]></title><description><![CDATA[<div class="kg-card-markdown"><h3 id>基本概念</h3>
<ol>
<li>
<p>同步 vs. 异步</p>
<ul>
<li>同步I/O　每个请求必须逐个地被处理，一个请求的处理会导致整个流程的暂时等待，这些事件无法并发地执行。用户线程发起I/O请求后需要等待或者轮询内核I/O操作完成后才能继续执行。</li>
<li>异步I/O　多个请求可以并发地执行，一个请求或者任务的执行不会导致整个流程的暂时等待。用户线程发起I/O请求后仍然继续执行，当内核I/O操作完成后会通知用户线程，或者调用用户线程注册的回调函数。</li>
</ul>
</li>
<li>
<p>阻塞 vs. 非阻塞</p>
<ul>
<li>阻塞某个请求发出后，由于该请求操作需要的条件不满足，请求操作一直阻塞，不会返回，直到条件满足。</li>
<li>非阻塞请求发出后，若该请求需要的条件不满足，则立即返回一个标志信息告知条件不满足，而不会一直等待。一般需要通过循环判断请求条件是否满足来获取请求结果。</li>
</ul>
</li>
</ol>
<blockquote>
<p>阻塞并不等价于同步，而非阻塞并非等价于异步。事实上这两组概念描述的是I/O模型中的两个不同维度。<br>
<strong>同步和异步</strong>重点在于多个任务执行过程中，后发起的任务是否必须等先发起的任务完成之后再进行。而不管先发起的任务请求是阻塞等待完成，还是立即返回通过循环等待请求成功。<br>
<strong>阻塞和非阻塞</strong>重点在于请求的方法是否立即返回（或者说是否在条件不满足时被阻塞）</p>
</blockquote>
<p>3.Unix中的五种I/O模型</p>
<ul>
<li>
<p>阻塞I/</p></li></ul></div>]]></description><link>http://blog.ycminglei.cn/ioshe-ji-mo-shi-reactorhe-proactor/</link><guid isPermaLink="false">5a0aa972732a5961f290e9fe</guid><category><![CDATA[软件设计]]></category><dc:creator><![CDATA[ray]]></dc:creator><pubDate>Tue, 18 Sep 2018 16:32:37 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><h3 id>基本概念</h3>
<ol>
<li>
<p>同步 vs. 异步</p>
<ul>
<li>同步I/O　每个请求必须逐个地被处理，一个请求的处理会导致整个流程的暂时等待，这些事件无法并发地执行。用户线程发起I/O请求后需要等待或者轮询内核I/O操作完成后才能继续执行。</li>
<li>异步I/O　多个请求可以并发地执行，一个请求或者任务的执行不会导致整个流程的暂时等待。用户线程发起I/O请求后仍然继续执行，当内核I/O操作完成后会通知用户线程，或者调用用户线程注册的回调函数。</li>
</ul>
</li>
<li>
<p>阻塞 vs. 非阻塞</p>
<ul>
<li>阻塞某个请求发出后，由于该请求操作需要的条件不满足，请求操作一直阻塞，不会返回，直到条件满足。</li>
<li>非阻塞请求发出后，若该请求需要的条件不满足，则立即返回一个标志信息告知条件不满足，而不会一直等待。一般需要通过循环判断请求条件是否满足来获取请求结果。</li>
</ul>
</li>
</ol>
<blockquote>
<p>阻塞并不等价于同步，而非阻塞并非等价于异步。事实上这两组概念描述的是I/O模型中的两个不同维度。<br>
<strong>同步和异步</strong>重点在于多个任务执行过程中，后发起的任务是否必须等先发起的任务完成之后再进行。而不管先发起的任务请求是阻塞等待完成，还是立即返回通过循环等待请求成功。<br>
<strong>阻塞和非阻塞</strong>重点在于请求的方法是否立即返回（或者说是否在条件不满足时被阻塞）</p>
</blockquote>
<p>3.Unix中的五种I/O模型</p>
<ul>
<li>
<p>阻塞I/O</p>
<ul>
<li>阻塞I/O下请求无法立即完成则保持阻塞。阻塞I/O分为如下两个阶段。</li>
<li>阶段1：等待数据就绪。网络 I/O 的情况就是等待远端数据陆续抵达；磁盘I/O的情况就是等待磁盘数据从磁盘上读取到内核态内存中。</li>
<li>阶段2：数据拷贝。出于系统安全，用户态的程序没有权限直接读取内核态内存，因此内核负责把内核态内存中的数据拷贝一份到用户态内存中。</li>
</ul>
</li>
<li>
<p>非阻塞I/O<br>
非阻塞I/O请求包含如下三个阶段</p>
<ul>
<li>socket设置为 NONBLOCK（非阻塞）就是告诉内核，当所请求的I/O操作无法完成时，不要将线程睡眠，而是返回一个错误码(EWOULDBLOCK) ，这样请求就不会阻塞。</li>
<li>I/O操作函数将不断的测试数据是否已经准备好，如果没有准备好，继续测试，直到数据准备好为止。整个I/O 请求的过程中，虽然用户线程每次发起I/O请求后可以立即返回，但是为了等到数据，仍需要不断地轮询、重复请求，消耗了大量的 CPU 的资源。</li>
<li>数据准备好了，从内核拷贝到用户空间。</li>
</ul>
</li>
</ul>
<blockquote>
<p>一般很少直接使用这种模型，而是在其他I/O模型中使用非阻塞I/O 这一特性。这种方式对单个I/O 请求意义不大，但给I/O多路复用提供了条件。</p>
</blockquote>
<ul>
<li>
<p>I/O多路复用（异步阻塞 I/O）<br>
I/O多路复用会用到select或者poll函数，这两个函数也会使线程阻塞，但是和阻塞I/O所不同的是，这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作，多个写操作的I/O函数进行检测，直到有数据可读或可写时，才真正调用I/O操作函数。从流程上来看，使用select函数进行I/O请求和同步阻塞模型没有太大的区别，甚至还多了添加监视Channel，以及调用select函数的额外操作，增加了额外工作。但是，使用 select以后最大的优势是用户可以在一个线程内同时处理多个Channel的I/O请求。用户可以注册多个Channel，然后不断地调用select读取被激活的Channel，即可达到在同一个线程内同时处理多个I/O请求的目的。而在同步阻塞模型中，必须通过多线程的方式才能达到这个目的。<br>
调用select/poll该方法由一个用户态线程负责轮询多个Channel，直到某个阶段1的数据就绪，再通知实际的用户线程执行阶段2的拷贝。 通过一个专职的用户态线程执行非阻塞I/O轮询，模拟实现了阶段一的异步化。</p>
</li>
<li>
<p>信号驱动I/O（SIGIO）<br>
首先我们允许socket进行信号驱动I/O，并安装一个信号处理函数，线程继续运行并不阻塞。当数据准备好时，线程会收到一个SIGIO 信号，可以在信号处理函数中调用I/O操作函数处理数据。</p>
</li>
<li>
<p>异步I/O<br>
调用aio_read 函数，告诉内核描述字，缓冲区指针，缓冲区大小，文件偏移以及通知的方式，然后立即返回。当内核将数据拷贝到缓冲区后，再通知应用程序。所以异步I/O模式下，阶段1和阶段2全部由内核完成，完成不需要用户线程的参与。</p>
</li>
</ul>
<ol start="4">
<li>
<p>Java中四种I/O模型<br>
除信号驱动I/O外，Java对其它四种I/O模型都有所支持。其中Java最早提供的blocking I/O即是阻塞I/O，而NIO即是非阻塞I/O，同时通过NIO实现的Reactor模式即是I/O复用模型的实现，通过AIO实现的Proactor模式即是异步I/O模型的实现。</p>
</li>
<li>
<p>IO到NIO</p>
<ul>
<li>面向流 vs. 面向缓冲<br>
Java IO是面向流的，每次从流（InputStream/OutputStream）中读一个或多个字节，直到读取完所有字节，它们没有被缓存在任何地方。另外，它不能前后移动流中的数据，如需前后移动处理，需要先将其缓存至一个缓冲区。<br>
Java NIO面向缓冲，数据会被读取到一个缓冲区，需要时可以在缓冲区中前后移动处理，这增加了处理过程的灵活性。但与此同时在处理缓冲区前需要检查该缓冲区中是否包含有所需要处理的数据，并需要确保更多数据读入缓冲区时，不会覆盖缓冲区内尚未处理的数据。</li>
<li>阻塞 vs. 非阻塞<br>
Java IO的各种流是阻塞的。当某个线程调用read()或write()方法时，该线程被阻塞，直到有数据被读取到或者数据完全写入。阻塞期间该线程无法处理任何其它事情。<br>
Java NIO为非阻塞模式。读写请求并不会阻塞当前线程，在数据可读/写前当前线程可以继续做其它事情，所以一个单独的线程可以管理多个输入和输出通道。</li>
</ul>
</li>
</ol>
<h3 id="nio">NIO</h3>
<ol>
<li>
<p>选择器（Selector）<br>
Java NIO的选择器允许一个单独的线程同时监视多个通道，可以注册多个通道到同一个选择器上，然后使用一个单独的线程来“选择”已经就绪的通道。这种“选择”机制为一个单独线程管理多个通道提供了可能。</p>
</li>
<li>
<p>零拷贝<br>
Java NIO中提供的FileChannel拥有transferTo和transferFrom两个方法，可直接把FileChannel中的数据拷贝到另外一个Channel，或者直接把另外一个Channel中的数据拷贝到FileChannel。该接口常被用于高效的网络/文件的数据传输和大文件拷贝。在操作系统支持的情况下，通过该方法传输数据并不需要将源数据从内核态拷贝到用户态，再从用户态拷贝到目标通道的内核态，同时也避免了两次用户态和内核态间的上下文切换，也即使用了“零拷贝”，所以其性能一般高于Java IO中提供的方法。</p>
</li>
</ol>
<h3 id>反应器模式</h3>
<pre><code>  reactor设计模式，是一种基于事件驱动的设计模式。Reactor框架是ACE各个框架中最基础的一个框架，其他框架都或多或少地用到了Reactor框架。 
  在事件驱动的应用中，将一个或多个客户的服务请求分离（demultiplex）和调度（dispatch）给应用程序。在事件驱动的应用中，同步地、有序地处理同时接收的多个服务请求。 
  reactor模式与外观模式有点像。不过，观察者模式与单个事件源关联，而反应器模式则与多个事件源关联 。当一个主体发生改变时，所有依属体都得到通知。
</code></pre>
<h4 id="what">what</h4>
<ol>
<li>场景</li>
</ol>
<blockquote>
<p>举个例子：餐厅服务问题<br>
* 传统线程池做法：来一个客人(请求)去一个服务员(线程)<br>
* 反应器模式做法：当客人点菜的时候，服务员就可以去招呼其他客人了，等客人点好了菜，直接招呼一声“服务员”</p>
</blockquote>
<ol start="2">
<li>结构<br>
<img src="http://owmdyd8zo.bkt.clouddn.com/reactor_structure.jpg" alt="reactor"></li>
</ol>
<ul>
<li>描述符（handle）：由操作系统提供，用于识别每一个事件，如Socket描述符、文件描述符等。在Linux中，它用一个整数来表示。<br>
事件可以来自外部，如来自客户端的连接请求、数据等。事件也可以来自内部，如定时器事件。</li>
<li>同步事件分离器（demultiplexer）：是一个函数，用来等待一个或多个事件的发生。调用者会被阻塞，直到分离器分离的描述符集上有事件发生。Linux的select函数是一个经常被使用的分离器。</li>
<li>事件处理器接口（event handler）：是由一个或多个模板函数组成的接口。这些模板函数描述了和应用程序相关的对某个事件的操作。<br>
具体的事件处理器：是事件处理器接口的实现。它实现了应用程序提供的某个服务。每个具体的事件处理器总和一个描述符相关。<br>
它使用描述符来识别事件、识别应用程序提供的服务。</li>
<li>Reactor 管理器（reactor）：定义了一些接口，用于应用程序控制事件调度，以及应用程序注册、删除事件处理器和相关的描述符。<br>
它是事件处理器的调度核心。 Reactor管理器使用同步事件分离器来等待事件的发生。一旦事件发生，Reactor管理器先是分离每个事件，<br>
然后调度事件处理器，最后调用相关的模 板函数来处理这个事件</li>
</ul>
<h4 id="how">how</h4>
<h4 id="why">why</h4>
<h3 id="proactor">Proactor模式</h3>
<p>Proactor主动器模式包含如下角色</p>
<ul>
<li>Handle 句柄；用来标识socket连接或是打开文件；<br>
Asynchronous Operation Processor：异步操作处理器；负责执行异步操作，一般由操作系统内核实现；</li>
<li>Asynchronous Operation：异步操作</li>
<li>Completion Event Queue：完成事件队列；异步操作完成的结果放到队列中等待后续使用</li>
<li>Proactor：主动器；为应用程序进程提供事件循环；从完成事件队列中取出异步操作的结果，分发调用相应的后续处理逻辑；</li>
<li>Completion Handler：完成事件接口；一般是由回调函数组成的接口；</li>
<li>Concrete Completion Handler：完成事件处理逻辑；实现接口定义特定的应用处理逻辑；</li>
</ul>
<p>主动和被动</p>
<p>以主动写为例：<br>
Reactor将handle放到select()，等待可写就绪，然后调用write()写入数据；写完处理后续逻辑；<br>
Proactor调用aoi_write后立刻返回，由内核负责写操作，写完后调用相应的回调函数处理后续逻辑；</p>
<p>可以看出，Reactor被动的等待指示事件的到来并做出反应；它有一个等待的过程，做什么都要先放入到监听事件集合中等待handler可用时再进行操作；<br>
Proactor直接调用异步读写操作，调用完后立刻返回；</p>
<p>实现</p>
<p>Reactor实现了一个被动的事件分离和分发模型，服务等待请求事件的到来，再通过不受间断的同步处理事件，从而做出反应；</p>
<p>Proactor实现了一个主动的事件分离和分发模型；这种设计允许多个任务并发的执行，从而提高吞吐量；并可执行耗时长的任务（各个任务间互不影响）</p>
<p>优点</p>
<p>Reactor实现相对简单，对于耗时短的处理场景处理高效；<br>
操作系统可以在多个事件源上等待，并且避免了多线程编程相关的性能开销和编程复杂性；<br>
事件的串行化对应用是透明的，可以顺序的同步执行而不需要加锁；<br>
事务分离：将与应用无关的多路分解和分配机制和与应用相关的回调函数分离开来，</p>
<p>Proactor性能更高，能够处理耗时长的并发场景；</p>
<p>缺点</p>
<p>Reactor处理耗时长的操作会造成事件分发的阻塞，影响到后续事件的处理；</p>
<p>Proactor实现逻辑复杂；依赖操作系统对异步的支持，目前实现了纯异步操作的操作系统少，实现优秀的如windows IOCP，但由于其windows系统用于服务器的局限性，目前应用范围较小；而Unix/Linux系统对纯异步的支持有限，应用事件驱动的主流还是通过select/epoll来实现；</p>
<p>适用场景</p>
<p>Reactor：同时接收多个服务请求，并且依次同步的处理它们的事件驱动程序；<br>
Proactor：异步接收和同时处理多个服务请求的事件驱动程序；</p>
</div>]]></content:encoded></item><item><title><![CDATA[GIT 最佳实践]]></title><description><![CDATA[<div class="kg-card-markdown"><h2 id>一. 分支规划</h2>
<ul>
<li>master: 主分支，主要用来版本发布。</li>
<li>develop：日常开发分支，该分支正常保存了开发的最新代码。</li>
<li>feature：具体的功能开发分支，只与 develop 分支交互。</li>
<li>release：release 分支可以认为是 master 分支的未测试版。比如说某一期的功能全部开发完成，那么就将 develop 分支合并到 release 分支，测试没有问题并且到了发布日期就合并到 master 分支，进行发布。</li>
<li>hotfix：线上 bug 修复分支。</li>
</ul>
<h3 id>各分支使用场景</h3>
<ol>
<li>master创建开发分支</li>
</ol>
<pre><code>git branch develop
git push -u origin develop  
</code></pre>
<ol start="2">
<li>功能开发</li>
</ol>
<h4 id="21developuserdev">2.1 develop - &gt; user-dev</h4>
<pre><code>git</code></pre></div>]]></description><link>http://blog.ycminglei.cn/git-zui-jia-shi-jian/</link><guid isPermaLink="false">5b07b495732a5961f290ea22</guid><category><![CDATA[j2ee]]></category><dc:creator><![CDATA[ray]]></dc:creator><pubDate>Mon, 28 May 2018 04:56:33 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><h2 id>一. 分支规划</h2>
<ul>
<li>master: 主分支，主要用来版本发布。</li>
<li>develop：日常开发分支，该分支正常保存了开发的最新代码。</li>
<li>feature：具体的功能开发分支，只与 develop 分支交互。</li>
<li>release：release 分支可以认为是 master 分支的未测试版。比如说某一期的功能全部开发完成，那么就将 develop 分支合并到 release 分支，测试没有问题并且到了发布日期就合并到 master 分支，进行发布。</li>
<li>hotfix：线上 bug 修复分支。</li>
</ul>
<h3 id>各分支使用场景</h3>
<ol>
<li>master创建开发分支</li>
</ol>
<pre><code>git branch develop
git push -u origin develop  
</code></pre>
<ol start="2">
<li>功能开发</li>
</ol>
<h4 id="21developuserdev">2.1 develop - &gt; user-dev</h4>
<pre><code>git checkout develop
git pull origin develop
git checkout -b user-dev //创建新的功能分支并切换
</code></pre>
<p>分支创建完毕之后,开始在branch上进行编码,它的主要任务就是完成task的编码工作，并最终将代码push到当前分支对应的远程分支上去。</p>
<h4 id="22">2.2 提交代码</h4>
<pre><code>git status
git add * //改动的代码放入index
git commit -m &quot;the first commit message&quot;
</code></pre>
<h4 id="23develop">2.3 拉取develop代码，合并至功能分支</h4>
<pre><code>git checkout develop
git pull origin develop //获取远程分支代码
git checkout user-dev 
git rebase -i develop // 合并代码至deveplop分支
</code></pre>
<blockquote>
<p>-i : 当前分支之前的commit压缩成为一个commit，这样做的好处在于当我们之后创建pull request并进行相应的code review的时候，代码的改动会集中在一个commit</p>
</blockquote>
<h4 id="24push">2.4 功能完成后，push远程分支</h4>
<pre><code>git push origin user-dev //可选择push功能分支，直接合并，如需code review可push
git checkout master
git merge --no-ff user-dev 
git push origin master
git branch -d user-dev //删除分支
</code></pre>
<blockquote>
<p>--no-ff</p>
</blockquote>
<ol start="3">
<li>版本发布</li>
</ol>
<pre><code>git checkout -b release-0.1.0 develop //创建测试版本
... 完成测试功能之后准备发布
git checkout master
git merge --no-ff releash-0.1.0
git push
git branch -d releash-0.1.0
git push origin --delete release-0.1.0 //清除分支
git tag -a v0.1.0 master //标记版本
git push --tags
</code></pre>
<ol start="4">
<li>主版本bug修复</li>
</ol>
<pre><code>git checkout -b hotfix-0.1.1 master 
... 完成bug修复
git checkout master //合并至稳定版本
git merge --no-ff hotfix-0.1.1
git push

git checkout develop //合并至开发版本
git merge --no-ff hotfix-0.1.1
git push
</code></pre>
<h2 id>二. 常用场景</h2>
<ol>
<li>恢复单个文件</li>
</ol>
<ul>
<li>查看文件日志:<code>git log lost_file</code></li>
<li>从指定版本恢复工作目录：<code>git checkout xxxxx lost_file</code> xxxx就是git 库中的sha-1码</li>
<li>从缓存区恢复工作目录：<code>git checkout lost_file</code></li>
<li>从指定版本恢复缓存区：<code>git reset xxxx lost_file</code></li>
<li>撤销合并   <code>git reset --hard HEAD</code></li>
</ul>
<ol start="2">
<li>历史版本切换</li>
</ol>
<pre><code>git reset --hard xxxxx 恢复到指定版本
git reset --hard ORIG_HEAD 用来撤销已经commit 的merge.
git reset --hard HEAD 用来撤销还没commit 的merge,其实原理就是放弃index和工作区的改动
git reset --hard orign/master #将本地的状态回退到和远程的一样
</code></pre>
<ol start="3">
<li>创建版本库</li>
</ol>
<ul>
<li>查看远程仓库地址：<code>git remote -v</code></li>
<li>创建仓库</li>
</ul>
<pre><code>cd my-project
git init
git remote add origin git@code.aliyun.com:anneng/ybss-manager.git
git add .
git commit -m &quot;init project&quot;
git push -u origin master
</code></pre>
<ol start="4">
<li>更改托管服务器</li>
</ol>
<pre><code>git clone --bare git://github.com/username/project.git //目标库克隆一份裸版本库
cd project.git
git push --mirror git@gitcafe.com/username/newproject.git //以镜像方式上传代码
cd ..
rm -rf project.git
</code></pre>
<p>若只更换地址<br>
<code>git remote set-url origin remote_git_address</code></p>
<ol start="5">
<li>配置帐号</li>
</ol>
<pre><code>git config --global gui.encoding utf-8 Git Gui中文乱码
git config --global user.name &quot;leidy&quot; Git 全局设置
git config --global user.email &quot;zichen616@163.com&quot;
</code></pre>
<ol start="6">
<li>更新提交场景</li>
</ol>
<pre><code>git diff 比较的是工作区和暂存区的差别
git diff --cached 比较的是暂存区和版本库的差别
git diff HEAD 可以查看工作区和版本库的差别
git add 工作区提交至stage
git commit -a -m &quot;command&quot; -a 把修改的文件先提交到stage,然后再从stash提交到branch
git rm --cached readme.txt 从stage和版本库中删除，保留物理文件,本地不受影响，团队更新后删除文件
git rm readme.txt 不但从stage中删除，同时删除物理文件，
git mv a.txt b.txt 把a.txt改名为b.tx

</code></pre>
<ol start="7">
<li>合并场景</li>
</ol>
<pre><code>git ls-files -u 显示冲突的文件，-s是显示标记为冲突已解决的文件
git ls-files --stage 检查保存在stage的文件
</code></pre>
<p>将开发中的分支（develop），合并到稳定分支（master）</p>
<pre><code>git checkou master
git merge --noff develop
</code></pre>
<p>分支衍合：分支衍合不会保留合并的日志，不留痕迹，而 分支合并则会保留合并的日志</p>
<pre><code>git checkou master
git rebase develop
git rebase --continue
</code></pre>
<p>有时候只需要合并部分提交的代码可以</p>
<pre><code>git checkou master
git cherry-pick 62ecb3
</code></pre>
<p>但是cherry pick 虽好，但一次只能合并一个commit。 连续多个commit就要用到 rebase</p>
<pre><code>git checkout -b develop 62ecb3
git rebase —onto master 76cada^ #从 76cada 的 commit 开始合并（作为新的commit）
git checkou master
git cherry-pick 62ecb3
</code></pre>
<ol start="8">
<li>ignore无效</li>
</ol>
<blockquote>
<p>.gitignore 文件的用途，该文件只能作用于 Untracked Files，也就是那些从来没有被 Git 记录过的文件（自添加以后，从未 add 及 commit 过的文件）。之所以你的规则不生效，是因为那些 .log 文件曾经被 Git 记录过，因此 .gitignore 对它们完全无效</p>
</blockquote>
<pre><code>git rm --cached logs/xx.log #从 Git 的数据库中删除对于该文件的追踪  
vim .gitignore logs/xx.log #把对应的规则写入.gitignore
git commit #提交＋推送
</code></pre>
<blockquote>
<p>另一种方式是.git/info/exclude 这里设置的则是你自己本地需要排除的文件，但同样只能作用于Untracked Files</p>
</blockquote>
<ol start="9">
<li>git提交时忽略已提交过但本地已修改的文件,ignore针对于untrack File,对于已经添加追踪的文件使用如下</li>
</ol>
<pre><code>$ git update-index --assume-unchanged [file-path]  #忽略跟踪
$ git update-index --no-assume-unchanged [file-path]  #恢复跟踪
$ git ls-files -v | grep '^h\ ' 查找忽略文件
</code></pre>
<blockquote>
<p>应用了该标识之后，Git 停止查看工作区文件可能发生的改变，所以你必须 手动 重置该标识以便 Git 知道你想要恢复对文件改变的追踪。当你工作在一个大型项目中，这在文件系统的调用非常迟钝的时候会很有用。</p>
</blockquote>
<p>但带来的不良后果：<br>
1 所有的团队成员都必须对目标文件执行：git update-index --assume-unchanged <path>。这是因为即使你让 Git 假装看不见目标文件的改变，但文件本身还是在 Git 的历史记录里的，所以团队的每个人在 fetch 的时候都会拉到目标文件的变更。（但实际上目标文件是根本不想被 Git 记录的，而不是假装看不见它发生了改变）<br>
2 一旦有人改变目标文件之后没有 git update-index --assume-unchanged <path> 就直接 push 了,merge会发生错误，需要恢复跟踪进行合并</path></path></p>
<p>10.删除untracked files</p>
<pre><code># 删除 untracked files
git clean -f
 
# 连 untracked 的目录也一起删掉
git clean -fd
 
# 连 gitignore 的untrack 文件/目录也一起删掉 （慎用，一般这个是用来删掉编译出来的 .o之类的文件用的）
git clean -xfd
 
# 在用上述 git clean 前，墙裂建议加上 -n 参数来先看看会删掉哪些文件，防止重要文件被误删
git clean -nxfd
git clean -nf
git clean -nfd
</code></pre>
<h2 id>三. 命令详解</h2>
<h3 id="31">3.1 分支操作</h3>
<ul>
<li>git branch 默认查看本地分支 -a 查看所有分支 -r 查看远程分支</li>
<li>git branch [branchName] 创建名为branchName的branch</li>
<li>git checkout [branchName] 切换到branchName的branch</li>
<li>git checkout -b [branchName] 创建并切换，也就是上面两个命令的合并</li>
<li>git checkout -b [branchName] origin/develop 获取远程分支并分化一个新分支</li>
<li>git brach [branchName] ef71 从commit ef71创建名为branchName的branch</li>
<li>git branch 查看分支，-a 查看远程分支</li>
<li>git branch -d branchName 删除分支</li>
<li>git branch -r -d branchName 删除远程分支</li>
<li>git push origin [branchName] 将本地分支branchName提交到推送远程分支上</li>
<li>git push origin :[branchName] 将branchname删除</li>
<li>git merge origin/branchName 将远程分支合并到当前分支</li>
</ul>
<h3 id="32">3.2 代码下载</h3>
<ul>
<li>git fetch 将会取到所有你本地没有的数据,所有取下来的分支可以被叫做remote branches,它们和本地分支一样(可以看diff,log等,也可以merge到其他分支),但是Git不允许你checkout到它们</li>
<li>git pull 会首先执行git fetch,然后执行git merge,把取来的分支的head merge到当前分支.这个merge操作会产生一个新的commit.</li>
<li>git push  [alias] [branch] 将会把当前分支merge到alias上的[branch]分支.如果分支已经存在,将会更新,如果不存在,将会添加这个分支.</li>
</ul>
</div>]]></content:encoded></item><item><title><![CDATA[maven将项目war包编译为docker镜像发布至远程linux服务器]]></title><description><![CDATA[<div class="kg-card-markdown"><h2 id="1">1.开发端</h2>
<h3 id="11maven">1.1maven插件</h3>
<pre><code> &lt;plugin&gt;
    &lt;groupId&gt;com.spotify&lt;/groupId&gt;
    &lt;artifactId&gt;docker-maven-plugin&lt;/artifactId&gt;
    &lt;version&gt;0.4.12&lt;/version&gt;
    &lt;configuration&gt;
        &lt;imageName&gt;${project.name}:${project.version}&lt;/imageName&gt;
        &lt;dockerDirectory&gt;${project.basedir}/src/</code></pre></div>]]></description><link>http://blog.ycminglei.cn/mavenjiang-xiang-mu-bian-yi-wei-dockerjing-xiang-fa-bu-zhi-yuan-cheng-linuxfu-wu-qi/</link><guid isPermaLink="false">5aebd1e0732a5961f290ea17</guid><category><![CDATA[docker]]></category><dc:creator><![CDATA[ray]]></dc:creator><pubDate>Fri, 04 May 2018 03:42:21 GMT</pubDate><media:content url="http://blog.ycminglei.cn/content/images/2018/05/u-2484179835-909096129-fm-27-gp-0.jpg" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><h2 id="1">1.开发端</h2>
<h3 id="11maven">1.1maven插件</h3>
<pre><code> &lt;plugin&gt;
    &lt;groupId&gt;com.spotify&lt;/groupId&gt;
    &lt;artifactId&gt;docker-maven-plugin&lt;/artifactId&gt;
    &lt;version&gt;0.4.12&lt;/version&gt;
    &lt;configuration&gt;
        &lt;imageName&gt;${project.name}:${project.version}&lt;/imageName&gt;
        &lt;dockerDirectory&gt;${project.basedir}/src/main/docker&lt;/dockerDirectory&gt;
        &lt;skipDockerBuild&gt;false&lt;/skipDockerBuild&gt;
        &lt;resources&gt;
            &lt;resource&gt;
                &lt;directory&gt;${project.build.directory}&lt;/directory&gt;
                &lt;include&gt;${project.build.finalName}.war&lt;/include&gt;
            &lt;/resource&gt;
        &lt;/resources&gt;
    &lt;/configuration&gt;
&lt;/plugin&gt;
</code></pre>
<h3 id="12dockerfile">1.2 dockerfile</h3>
<pre><code>FROM docker.io/tomcat ##基础镜像
MAINTAINER ray-ldy
ENV DIR_WEBAPP /usr/local/tomcat/webapps/ ## 环境变量
ADD  study-jfinal.war $DIR_WEBAPP/study-jfinal.war ## 发布jar包
EXPOSE 8080 ## 开发端口
CMD [&quot;catalina.sh&quot;, &quot;run&quot;] ## 入口命令
</code></pre>
<h3 id="13">1.3 远程地址</h3>
<img src="http://blog.ycminglei.cn/content/images/2018/05/u-2484179835-909096129-fm-27-gp-0.jpg" alt="maven将项目war包编译为docker镜像发布至远程linux服务器"><p>在windows系统环境变量中新建DOCKER_HOST,值为tcp://192.168.20.129:2375</p>
<h3 id="14">1.4 发布镜像</h3>
<pre><code>mvn clean package docker:build -DskipTests
</code></pre>
<h2 id="2docker">2.docker服务端配置</h2>
<h3 id="21">2.1 远程配置</h3>
<pre><code>vi /usr/lib/systemd/system/docker.service
</code></pre>
<p>ExecStart这一行后面加上</p>
<pre><code>    ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock \
</code></pre>
<p>刷新配置</p>
<pre><code>systemctl daemon-reload
systemctl restart docker
</code></pre>
<blockquote>
<p>输入#netstat -anp|grep 2375 显示docker正在监听2375端口，<br>
输入#curl 127.0.0.1:2375/info  显示信息，证明远程api配置成功</p>
</blockquote>
<h3 id="22mysql">2.2 mysql镜像</h3>
<p>1 拉取mysql镜像，采用网易加速地址</p>
<pre><code>docker pull hub.c.163.com/library/mysql:5.7
</code></pre>
<p>2 重命名镜像名</p>
<pre><code>docker tag hub.c.163.com/library/mysql:5.7 mysql:5.7
</code></pre>
<p>3 创建用于挂载的目录</p>
<pre><code>sudo mkdir /my/mysql/datadir #用于挂载mysql数据文件
sudo mkdir /my/mysql/conf.d #用于挂载mysql配置文件
sudo chown yaoren:docker /my #修改/my目录拥有者
</code></pre>
<p>4 使用镜像创建容器</p>
<pre><code>docker run --name mysql5.7 -p 3306:3306 -v /my/mysql/datadir:/var/lib/mysql -v /my/mysql/conf.d:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
命令解析：
--name：容器名
-p：映射宿主主机端口
-v：挂载宿主目录到容器目录
-e：设置环境变量，此处指定root密码，mysql必须指定
-d：后台运行容器
</code></pre>
<h3 id="23">2.3 启动实例</h3>
<pre><code>docker run -d -p 8080:8080  -name study-jfinal -link ray-mysql:db study-jfinal:1.0-SNAPSHOT
命令解析：
--name：容器名
--link：链接mysql镜像实例
-p：映射宿主主机端口
-d：后台运行容器
</code></pre>
<blockquote>
<p>jdbc使用链接串：url=jdbc:mysql://db:3306/study-jfinal，db为链接实例别名<br>
进入容器bash:docker exec -it study-jfinal bash</p>
</blockquote>
<h2 id="3">3.其它</h2>
<h3 id="31dockerfile">3.1 dockerfile可以自由化定制</h3>
<h3 id="32mavenprofilepprofile">3.2 利用maven profile实例多环境自动打包，编译是指定-Pprofile环境参数</h3>
<h3 id="33">3.3 源码请联系作者</h3>
</div>]]></content:encoded></item><item><title><![CDATA[JPA: 4种触发懒加载的方式]]></title><description><![CDATA[<div class="kg-card-markdown"><h2 id="tableofcontents">Table of contents:</h2>
<ol>
<li><a title="引言" href="http://blog.ycminglei.cn/jpa-4chong-hong-fa-lan-jia-zai-de-fang-shi/#1">引言</a></li>
<li><a title="场景" href="http://blog.ycminglei.cn/jpa-4chong-hong-fa-lan-jia-zai-de-fang-shi/#2">场景</a></li>
<li><a title="方法" href="http://blog.ycminglei.cn/jpa-4chong-hong-fa-lan-jia-zai-de-fang-shi/#3">方法调用触发</a></li>
<li><a title="Join Fetch" href="http://blog.ycminglei.cn/jpa-4chong-hong-fa-lan-jia-zai-de-fang-shi/#4">Join Fetch触发</a></li>
<li><a title="NamedEntityGraph" href="http://blog.ycminglei.cn/jpa-4chong-hong-fa-lan-jia-zai-de-fang-shi/#5">NamedEntityGraph触发</a></li>
<li><a title="EntityGraph" href="http://blog.ycminglei.cn/jpa-4chong-hong-fa-lan-jia-zai-de-fang-shi/#6">动态的EntityGraph触发</a></li>
<li><a title="补充" href="http://blog.ycminglei.cn/jpa-4chong-hong-fa-lan-jia-zai-de-fang-shi/#7">补充</a></li>
</ol>
<h3 id="1aname1id1a">1.<a name="1" id="1">引言</a></h3>
<p>在一个JPA应用中，可以通过懒加载来提高应用的性能。这一点毋庸置疑，但是懒加载不等于不加载，在某个时刻还是需要加载这些数据的，那么如何触发这个加载的行为才能够事半功倍呢？</p>
<blockquote>
<p>大家对于JPA/Hibernate的看法，感觉很慢；具体有JPA Provider(例如Hibernate)会生成非常多效率低下的SQL，于是看起来性能就不行了。每种技术都有自身的优缺点，完美的技术是不存在的。具体问题具体分析，不要人云亦云是一个开发人员应该拥有的基本能力。JPA在目前互联网海量数据的环境下，确实有很多的问题，最典型的比如对于数据分片，分表分库上支持的欠缺。但是，并不是所有的应用都有那么大的数据量，也不是所有的项目都需要去分表分库。更多的中小型项目，如果能够合理地运用好JPA，开发效率和项目的服务性能绝对不会差。</p>
</blockquote>
<p>所以，这篇文章我想从触发懒加载这个角度，分析几种不同的实现方式，来看看应该如何提高应用的性能。</p>
<h3 id="2aname2id2a">2.<a name="2" id="2">场景</a></h3>
<ol start="0">
<li>数据关联关系的假设<br>
在具体分析4种触发方式之前，我们先来假设一组关联关系：</li></ol></div>]]></description><link>http://blog.ycminglei.cn/jpa-4chong-hong-fa-lan-jia-zai-de-fang-shi/</link><guid isPermaLink="false">5a74c3f7732a5961f290ea12</guid><category><![CDATA[JPA]]></category><dc:creator><![CDATA[ray]]></dc:creator><pubDate>Fri, 02 Feb 2018 20:21:40 GMT</pubDate><media:content url="http://blog.ycminglei.cn/content/images/2018/02/u-727620409-1641106268-fm-27-gp-0.jpg" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><h2 id="tableofcontents">Table of contents:</h2>
<ol>
<li><a title="引言" href="http://blog.ycminglei.cn/jpa-4chong-hong-fa-lan-jia-zai-de-fang-shi/#1">引言</a></li>
<li><a title="场景" href="http://blog.ycminglei.cn/jpa-4chong-hong-fa-lan-jia-zai-de-fang-shi/#2">场景</a></li>
<li><a title="方法" href="http://blog.ycminglei.cn/jpa-4chong-hong-fa-lan-jia-zai-de-fang-shi/#3">方法调用触发</a></li>
<li><a title="Join Fetch" href="http://blog.ycminglei.cn/jpa-4chong-hong-fa-lan-jia-zai-de-fang-shi/#4">Join Fetch触发</a></li>
<li><a title="NamedEntityGraph" href="http://blog.ycminglei.cn/jpa-4chong-hong-fa-lan-jia-zai-de-fang-shi/#5">NamedEntityGraph触发</a></li>
<li><a title="EntityGraph" href="http://blog.ycminglei.cn/jpa-4chong-hong-fa-lan-jia-zai-de-fang-shi/#6">动态的EntityGraph触发</a></li>
<li><a title="补充" href="http://blog.ycminglei.cn/jpa-4chong-hong-fa-lan-jia-zai-de-fang-shi/#7">补充</a></li>
</ol>
<h3 id="1aname1id1a">1.<a name="1" id="1">引言</a></h3>
<img src="http://blog.ycminglei.cn/content/images/2018/02/u-727620409-1641106268-fm-27-gp-0.jpg" alt="JPA: 4种触发懒加载的方式"><p>在一个JPA应用中，可以通过懒加载来提高应用的性能。这一点毋庸置疑，但是懒加载不等于不加载，在某个时刻还是需要加载这些数据的，那么如何触发这个加载的行为才能够事半功倍呢？</p>
<blockquote>
<p>大家对于JPA/Hibernate的看法，感觉很慢；具体有JPA Provider(例如Hibernate)会生成非常多效率低下的SQL，于是看起来性能就不行了。每种技术都有自身的优缺点，完美的技术是不存在的。具体问题具体分析，不要人云亦云是一个开发人员应该拥有的基本能力。JPA在目前互联网海量数据的环境下，确实有很多的问题，最典型的比如对于数据分片，分表分库上支持的欠缺。但是，并不是所有的应用都有那么大的数据量，也不是所有的项目都需要去分表分库。更多的中小型项目，如果能够合理地运用好JPA，开发效率和项目的服务性能绝对不会差。</p>
</blockquote>
<p>所以，这篇文章我想从触发懒加载这个角度，分析几种不同的实现方式，来看看应该如何提高应用的性能。</p>
<h3 id="2aname2id2a">2.<a name="2" id="2">场景</a></h3>
<ol start="0">
<li>数据关联关系的假设<br>
在具体分析4种触发方式之前，我们先来假设一组关联关系：</li>
</ol>
<pre><code>@Entity
public class Department {
    // 主键等字段
    // ......

    @OneToMany(mappedBy = &quot;department&quot;)
    private List&lt;Employee&gt; employees;
}
</code></pre>
<h3 id="3aname3id3a">3.<a name="3" id="3">通过方法调用触发</a></h3>
<p>这是使用频率最高的一种触发方式，几乎所有JPA开发人员一般情况下都会使用这种方式。</p>
<p>顾名思义，这种方式通过在employee集合对象上调用方法来完成触发，比如下面的代码：</p>
<pre><code>Department dept = em.find(Department.class, deptId);
int count = dept.getEmployees().size();
// ......
</code></pre>
<p>通过调用size方法来触发懒加载，这个size的执行会让JPA的Provider生成具体去获取集合数据的SQL并执行之。这种方法看似没什么问题，在很多场景下确实也非常好用。但是它太简单粗暴了。在下面两种情况下，都会造成较为严重的性能问题：</p>
<p>集合数据量大。比如关联数据有上成百上千条记录时。<br>
一个实体类型需要触发懒加载的关系很多。比如当上述Department类型还需要加载更多一对多的关系时。<br>
第一种情况很好理解，数据量越大，SQL执行的时间越久，这一点毫无疑问。<br>
第二种情况，假设Department类型有10个一对多关系，现在都需要触发懒加载行为来得到完整的数据。那么针对每个关系都会产生一条SQL命令。加上它自身的，一共就是11条命令。当然你的应用往往不会只有一个用户在使用，假设有100个用户同时使用，那么就是1100条SQL会被执行！这能不慢吗？</p>
<p>所以对于这种触发方式，在确定不会发生上述两种情况时，是可以使用的。一旦有发生它们的风险，就不要使用了。</p>
<h3 id="4aname4id4joinfetcha">4.<a name="4" id="4">通过Join Fetch触发</a></h3>
<p>通过在JPQL中添加JOIN FETCH来完成关联关系的获取：</p>
<pre><code>Query q = em.createQuery(&quot;SELECT d FROM Department d JOIN FETCH d.employees e WHERE d.id = :id&quot;);
q.setParameter(&quot;id&quot;, deptId);
Department dept = (Department) q.getSingleResult();
</code></pre>
<p>这种方式，主要解决了在通过方法调用触发时面临的第二个问题：执行的SQL命令数量过多。<br>
使用JOIN FETCH时，执行的SQL命令只有一条。因此，需要触发加载行为的关系越多，使用JOIN FETCH带来的性能优势就越明显。</p>
<p>但是这种方式并非一本万利，如果不同业务场景下需要触发加载的关系不一样，就会产生非常多的组合。而每种组合的JPQL都是不一样的。此时可以结合实际的业务需求通过字符串的拼接操作完成JPQL的准备工作。而这个准备工作在组合情况很多的情况下，往往会十分复杂。不过相比它能够带来的性能提升，这些麻烦都是可以克服的。</p>
<p>除了直接提供JPQL，在Criteria API中也能使用：</p>
<pre><code>CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery q = cb.createQuery(Department.class);
Root d = q.from(Department.class);
d.fetch(&quot;employees&quot;, JoinType.INNER);
q.select(d);
q.where(cb.equal(d.get(&quot;id&quot;), deptId));
Department dept = (Department)em.createQuery(q).getSingleResult();
</code></pre>
<p>这种方式只是定义查询的方式不同而已，在性能层面上和直接写JPQL是一样的。</p>
<h3 id="5aname5id5namedentitygrapha">5.<a name="5" id="5">通过NamedEntityGraph触发</a></h3>
<p>这种方式实际上是JPA 2.1中新增的一项特性。借助它也能够完成懒加载的触发。我们先来看看如何定义一个命名的EntityGraph，即NamedEntityGraph。从命名方式上看，是不是很接近于NamedEntityQuery？所以引申到定义方式而言，它们也是很接近的：</p>
<pre><code>@Entity
@NamedEntityGraph(name = &quot;graph.Department.employees&quot;, 
      attributeNodes = @NamedAttributeNode(&quot;employees&quot;))
public class Department {
    // ......
}
</code></pre>
<p>使用它进行关系的加载：</p>
<pre><code>EntityGraph graph = em.getEntityGraph(&quot;graph.Department.employees&quot;);
Map&lt;String, Object&gt; props = new HashMap&lt;&gt;();
props.put(&quot;javax.persistence.fetchgraph&quot;, graph);
Department dept = em.find(Department.class, deptId, props);
</code></pre>
<p>此时查询得到的Department对象就包含了我们需要的Employee集合。同样地，使用这种方式的时候也只会生成并执行一条SQL命令。</p>
<p>但是在组合情况比较多的时候，和使用Join Fetch一样，也是需要根据业务场景进行一些准备工作的，只不过这个准备工作更加麻烦，每个组合都需要添加一个专门的@NamedEntityGraph注解用来定义。所以，在组合关系很多的时候，使用@NamedEntityGraph是很不划算的。因此也就有了下面的动态EntityGraph。</p>
<h3 id="6aname6id6entitygrapha">6.<a name="6" id="6">通过动态的EntityGraph触发</a></h3>
<p>动态EntityGraph的定义方式更加灵活：</p>
<pre><code>EntityGraph graph = this.em.createEntityGraph(Department.class);
Subgraph employeesGraph = graph.addSubgraph(&quot;employees&quot;);
Map&lt;String, Object&gt; props = new HashMap&lt;&gt;();
props.put(&quot;javax.persistence.loadgraph&quot;, graph);
Department dept = em.find(Department.class, deptId, props);
</code></pre>
<p>动态EntityGraph根据需要加载的关系，通过addSubgraph方法进行指定。</p>
<ol start="5">
<li>EntityGraph与JOIN FETCH是一样的？<br>
细心的同学看到这里，也许会发现目前介绍的所谓EntityGraph，怎么跟JOIN FETCH那么那么像呢？难道只是换了个马甲？</li>
</ol>
<p>很显然，并没有这么简单。</p>
<p>注意一下上面的这两行代码：</p>
<pre><code>// #1 loadgraph
props.put(&quot;javax.persistence.loadgraph&quot;, graph);

// #2 fetchgraph
props.put(&quot;javax.persistence.fetchgraph&quot;, graph);
</code></pre>
<p>这两者有什么具体的区别呢？这里我不打算长篇累牍地介绍。捡重点说就是：</p>
<p>loadgraph：在原有Entity的定义的基础上，定义还需要获取什么字段/关系<br>
fetchgraph：完全放弃原有Entity的定义，定义仅需要获取什么字段/关系<br>
注意上面的”还”和”仅”，表达了两者最大的不同点。</p>
<p>举个例子，如果我们的Department类型中还有一个name字段：</p>
<p>loadgraph：被加载的数据为name以及employees<br>
fetchgraph：被加载的数据仅为employees<br>
所以，在使用EntityGraph的时候配合fetchgraph，可以精准的完成所需要数据的加载，可谓是指哪打哪。在一些对性能尤其敏感的业务场景，不妨来试试看仅仅加载所需要的数据的那种酸爽吧。</p>
<h3 id="7aname7id7a">7.<a name="7" id="7">补充说明</a></h3>
<p>还有两种方式解决懒加载问题<br>
一种是通过OpenEntityManagerInViewFilter来延迟关闭session,在页面开启session，待页面渲染完毕后才关闭session</p>
<pre><code>web.xml
&lt;filter&gt;
     &lt;filter-name&gt;OpenEntityManagerInViewFilter&lt;/filter-name&gt;
     &lt;filter-class&gt;org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter&lt;/filter-class&gt;
    &lt;/filter&gt;
    &lt;filter-mapping&gt;
        &lt;filter-name&gt;OpenEntityManagerInViewFilter&lt;/filter-name&gt;
        &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
    &lt;/filter-mapping&gt;
</code></pre>
<p>然后再在service中开启事务支持，这种方式问题是EntityManager的生命周期会比较长（因为请求开始到请求结束这段时间长），EntityManager不会立刻关闭，导致连接池连接数占用的问题。因此高并发的系统最好不要使用OpenEntityManagerInView模式</p>
<p>第二种是将关联关系调整为立即加载，显示这个操作应用场景不符，总是立即加载关联数据影响性能</p>
<pre><code> @ManyToMany(fetch = FetchType.EAGER, targetEntity = User.class)
</code></pre>
</div>]]></content:encoded></item><item><title><![CDATA[thymeleaf 自定义标签&方言和处理器简介]]></title><description><![CDATA[<div class="kg-card-markdown"><h2 id="1">1.概念</h2>
<h3 id="11dialects">1.1 Dialects</h3>
<blockquote>
<p>thymeleaf是一个容易扩展的库，大部分面向用户的功能不是直接构建在他的核心中，而是通过打包和组件化到一个称谓Dialect(方言)的功能集合中,同时可以自定义一组attribute或者tag在thymeleaf中用来处理自定义的模板。</p>
</blockquote>
<p>Dialects是实现了org.thymeleaf.dialect.IDialect接口的对象,具体如下：</p>
<pre><code>public interface IDialect {
    public String getName();
}
</code></pre>
<blockquote>
<p>基础的接口有：</p>
</blockquote>
<ul>
<li>IProcessorDialect 处理器方言</li>
</ul>
<pre><code>public interface IProcessorDialect extends IDialect {
    public String getPrefix();//应用于匹配元素和属性的前缀
    public int getDialectProcessorPrecedence();//定义方言优先级
    public Set&lt;IProcessor&gt; getProcessors(final String dialectPrefix);//定义一组处理器集合
}
</code></pre>
<ul>
<li>IPreProcessorDialect 预处理方言</li></ul></div>]]></description><link>http://blog.ycminglei.cn/thymeleaf-zi-ding-yi-biao-qian-fang-yan-he-chu-li-qi-jian-jie/</link><guid isPermaLink="false">5a5a447c732a5961f290ea0b</guid><category><![CDATA[thymeleaf]]></category><category><![CDATA[模板引擎]]></category><dc:creator><![CDATA[ray]]></dc:creator><pubDate>Sat, 13 Jan 2018 18:10:20 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><h2 id="1">1.概念</h2>
<h3 id="11dialects">1.1 Dialects</h3>
<blockquote>
<p>thymeleaf是一个容易扩展的库，大部分面向用户的功能不是直接构建在他的核心中，而是通过打包和组件化到一个称谓Dialect(方言)的功能集合中,同时可以自定义一组attribute或者tag在thymeleaf中用来处理自定义的模板。</p>
</blockquote>
<p>Dialects是实现了org.thymeleaf.dialect.IDialect接口的对象,具体如下：</p>
<pre><code>public interface IDialect {
    public String getName();
}
</code></pre>
<blockquote>
<p>基础的接口有：</p>
</blockquote>
<ul>
<li>IProcessorDialect 处理器方言</li>
</ul>
<pre><code>public interface IProcessorDialect extends IDialect {
    public String getPrefix();//应用于匹配元素和属性的前缀
    public int getDialectProcessorPrecedence();//定义方言优先级
    public Set&lt;IProcessor&gt; getProcessors(final String dialectPrefix);//定义一组处理器集合
}
</code></pre>
<ul>
<li>IPreProcessorDialect 预处理方言</li>
</ul>
<pre><code>public interface IPreProcessorDialect extends IDialect {
    public int getDialectPreProcessorPrecedence();
    public Set&lt;IPreProcessor&gt; getPreProcessors();
}
</code></pre>
<p>ps:<strong>处理器方言是在单个时间或者模板片段上执行。而预处理方言和后处理方言是作为引擎处理过程中的附加步骤，应用在整个模板的执行过程中。</strong></p>
<ul>
<li>IPostProcessorDialect 后处理方言</li>
<li>IExpressionObjectDialect 表达式对象方言</li>
</ul>
<pre><code>public interface IExpressionObjectDialect extends IDialect {
    public IExpressionObjectFactory getExpressionObjectFactory();
}
</code></pre>
<p>ps:<strong>Dialect可以提供新的表达式对象或者表达式应用程序对象，例如#strings,#numbers等</strong></p>
<ul>
<li>IExecutionAttributeDialect 可执行属性方言,即在模板处理期间执行的每个处理器可用的对象</li>
</ul>
<pre><code>public interface IExecutionAttributeDialect extends IDialect {
    public Map&lt;String,Object&gt; getExecutionAttributes();
}
</code></pre>
<h3 id="12processors">1.2 Processors处理器</h3>
<p>处理器的对象全部实现org.thymeleaf.processor.IProcessor接口,具体如下：</p>
<pre><code>public interface IProcessor {
    public TemplateMode getTemplateMode();
    public int getPrecedence();
}
</code></pre>
<blockquote>
<p>基础的接口有：</p>
</blockquote>
<ul>
<li>IElementProcessor 元素处理器,<br>
在open element 或者独立元素上执行,IElementProcessor处理器并不是直接实现这个接口，它还包含了两个子接口
<ul>
<li>IElementTagProcessor</li>
<li>IElementModelProcessor</li>
</ul>
</li>
</ul>
<h2 id="2">2. 自定义标签方言</h2>
<p>步骤：</p>
<ol>
<li>定义方言</li>
<li>定义方言处理器</li>
<li>添加到thymeleaf引擎中</li>
</ol>
<pre><code>//spring boot配置启用
    @Bean
    @ConditionalOnMissingBean
    public WorkFocusDialect wlfDialect() {
        return new WorkFocusDialect();
    }
</code></pre>
<h3 id="21">2.1 表格属性实例</h3>
<blockquote>
<p>eg:table表格中某一列显示内容为是否启用，具体的值为0和1.如果是0，该单元格是红色,否则为绿色。</p>
</blockquote>
<ul>
<li>自定义方言</li>
</ul>
<pre><code>public class WorkFocusDialect extends AbstractProcessorDialect {
    private final IExpressionObjectFactory EXPRESSION_OBJECTS_FACTORY = new WorkFocusExpressionFactory();
    private static final String DIALECT_NAME = &quot;workfocus&quot;;
    private static final String PREFIX = &quot;wlf&quot;;
    public static final int PROCESSOR_PRECEDENCE = 1000;

    public WorkFocusDialect() {
        // We will set this dialect the same &quot;dialect processor&quot; precedence as
        // the Standard Dialect, so that processor executions can interleave.
        super(DIALECT_NAME, PREFIX, PROCESSOR_PRECEDENCE);
    }
    @Override
    public Set&lt;IProcessor&gt; getProcessors(final String dialectPrefix) {
        final Set&lt;IProcessor&gt; processors = new HashSet&lt;IProcessor&gt;();
        ... 在这里增加自定义的处理器
        processors.add(new SampleAttributeTagProcessor(dialectPrefix));
        processors.add(new SampleElementTagProcessor(dialectPrefix));
        return processors;
    }
}
</code></pre>
<ul>
<li>自定义定义处理器1：属性处理器</li>
</ul>
<pre><code>public class SampleAttributeTagProcessor extends AbstractAttributeTagProcessor {

    private static final String ATTR_NAME = &quot;sample1&quot;;
    private static final int PRECEDENCE = 10000;

    public SampleAttributeTagProcessor(final String dialectPrefix) {
        super(
            TemplateMode.HTML, // This processor will apply only to HTML mode
            dialectPrefix,     // Prefix to be applied to name for matching
            null,              // No tag name: match any tag name
            false,             // No prefix to be applied to tag name
            ATTR_NAME,         // Name of the attribute that will be matched
            true,              // Apply dialect prefix to attribute name
            PRECEDENCE,        // Precedence (inside dialect's own precedence)
            true);             // Remove the matched attribute afterwards
    }

    @Override
    protected void doProcess(
        final ITemplateContext context, final IProcessableElementTag tag,
        final AttributeName attributeName, final String attributeValue,
        final IElementTagStructureHandler structureHandler) {
        final IEngineConfiguration configuration = context.getConfiguration();
        /*
         * Obtain the Thymeleaf Standard Expression parser
         */
        final IStandardExpressionParser parser =
                StandardExpressions.getExpressionParser(configuration);
        /*
         * Parse the attribute value as a Thymeleaf Standard Expression
         */
        final IStandardExpression expression = parser.parseExpression(context, attributeValue);
        /*
         * Execute the expression just parsed
         */
        final Integer position = (Integer) expression.execute(context);
        if(position.equals(1)) {
            structureHandler.setAttribute(&quot;style&quot;, &quot;background:green&quot;);
        } else {
            structureHandler.setAttribute(&quot;style&quot;, &quot;background:red&quot;);
        }
    }
</code></pre>
<ul>
<li>使用定义自定义标签1</li>
</ul>
<pre><code>&lt;table&gt;
&lt;tr&gt;
    &lt;td&gt;....&lt;/td&gt;
    &lt;td wlf:sample1=&quot;${user.status}&quot; th:text=&quot;${user.status}&quot;&gt;状态&lt;/td&gt;
    //根据具体的值，改变了td元素的属性值
&lt;/tr&gt;
&lt;/table&gt;
</code></pre>
<ul>
<li>自定义定义处理器2：元素处理器</li>
</ul>
<pre><code>public class SampleElementTagProcessor extends AbstractElementTagProcessor {

    private static final String TAG_NAME = &quot;sample3&quot;;
    private static final int PRECEDENCE = 1000;

    public Sample3ElementTagProcessor(final String dialectPrefix) {
        super(
            TemplateMode.HTML, // This processor will apply only to HTML mode
            dialectPrefix,     // Prefix to be applied to name for matching
            TAG_NAME,          // Tag name: match specifically this tag
            true,              // Apply dialect prefix to tag name
            null,              // No attribute name: will match by tag name
            false,             // No prefix to be applied to attribute name
            PRECEDENCE);       // Precedence (inside dialect's own precedence)
    }


    @Override
    protected void doProcess(
        final ITemplateContext context, final IProcessableElementTag tag,
        final IElementTagStructureHandler structureHandler) {

        /*
         * Read the 'order' attribute from the tag. This optional attribute in our tag 
         * will allow us to determine whether we want to show a random headline or
         * only the latest one ('latest' is default).
         */
        final String statusValue = tag.getAttributeValue(&quot;status&quot;);

        final IEngineConfiguration configuration = context.getConfiguration();
        /*
         * Obtain the Thymeleaf Standard Expression parser
         */
        final IStandardExpressionParser parser = StandardExpressions.getExpressionParser(configuration);
        final IStandardExpression expression = parser.parseExpression(context, statusValue);
        final Integer parseStatus = (Integer) expression.execute(context);
        /*
         * Create the DOM structure that will be substituting our custom tag.
         */
        final IModelFactory modelFactory = context.getModelFactory();
        final IModel model = modelFactory.createModel();
        if(parseStatus.equals(0)) {
            model.add(modelFactory.createOpenElementTag(&quot;td&quot;, &quot;style&quot;, &quot;background:green&quot;));
            model.add(modelFactory.createText(HtmlEscape.escapeHtml5(&quot;停用&quot;)));
        }else {
            model.add(modelFactory.createOpenElementTag(&quot;td&quot;, &quot;style&quot;, &quot;background:red&quot;));
            model.add(modelFactory.createText(HtmlEscape.escapeHtml5(&quot;启用&quot;)));
        }
        model.add(modelFactory.createCloseElementTag(&quot;td&quot;));
        /*
         * Instruct the engine to replace this entire element with the specified model.
         */
        structureHandler.replaceWith(model, false);
    }
}
</code></pre>
<ul>
<li>使用定义自定义标签2</li>
</ul>
<pre><code>&lt;table&gt;
&lt;tr&gt;
    &lt;td&gt;....&lt;/td&gt;
    &lt;wlf:sample status=&quot;${user.status}&quot;/&gt;
    //根据具体的值，改变了td元素的属性值
&lt;/tr&gt;
&lt;/table&gt;
</code></pre>
</div>]]></content:encoded></item><item><title><![CDATA[架构设计]]></title><description><![CDATA[<div class="kg-card-markdown"><h3 id>目录</h3>
<ol>
<li>软件架构设计</li>
<li>层次式架构设计</li>
<li>企业集成架构设计</li>
<li>面向服务架构设计</li>
<li>面向构件的架构设计</li>
<li>系统安全架构设计</li>
<li>系统可靠性架构设计</li>
</ol>
<hr>
<h3 id="1">1. 软件架构设计</h3>
<h4 id="11">1.1 定义</h4>
<blockquote>
<p>软件体系结构是指系统的一个或者多个结构，结构中包括软件的构件，构件的外部可见属性以及它们之间的相互关系。数据设计可表示出传统系统中体系结构的数据构件和面向对象系统中类的定义，体系结构设计则主要关注软件构件的结构，属性和交互作用</p>
</blockquote>
<p>系统架构师：系统的设计责任人，是一个负责理解和管理并最终确认和评估非功能性系统需求（可维护性，性能，可靠性，测试性）给出开发规范，搭建实现的核心构架，对整个软件架构、关键构件、接口进行总体设计。</p>
<h4 id="12absd">1.2 ABSD</h4>
<blockquote>
<p>ASBD指构成体系结构的商业、质量和功能需求的组合驱动的，设计活动从项目总体功能框架明确就开台，ABSD有三个基础，第一个基础是功能的分解，第二个基础是通过选择体系结构风格来实现质量和商业需求，第三个基础是软件模板的使用。</p>
</blockquote>
<ol>
<li>概念
<ul>
<li>设计元素：ABSD方法是一个自顶向下，递归细化的方法，直到能产生软件构件和类。在最顶层，系统被分解为若干<strong>概念子系统</strong>和一个或多个<strong></strong></li></ul></li></ol></div>]]></description><link>http://blog.ycminglei.cn/jia-gou-she-ji/</link><guid isPermaLink="false">59e1e1aa732a5961f290e9c9</guid><dc:creator><![CDATA[ray]]></dc:creator><pubDate>Thu, 09 Nov 2017 15:44:11 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><h3 id>目录</h3>
<ol>
<li>软件架构设计</li>
<li>层次式架构设计</li>
<li>企业集成架构设计</li>
<li>面向服务架构设计</li>
<li>面向构件的架构设计</li>
<li>系统安全架构设计</li>
<li>系统可靠性架构设计</li>
</ol>
<hr>
<h3 id="1">1. 软件架构设计</h3>
<h4 id="11">1.1 定义</h4>
<blockquote>
<p>软件体系结构是指系统的一个或者多个结构，结构中包括软件的构件，构件的外部可见属性以及它们之间的相互关系。数据设计可表示出传统系统中体系结构的数据构件和面向对象系统中类的定义，体系结构设计则主要关注软件构件的结构，属性和交互作用</p>
</blockquote>
<p>系统架构师：系统的设计责任人，是一个负责理解和管理并最终确认和评估非功能性系统需求（可维护性，性能，可靠性，测试性）给出开发规范，搭建实现的核心构架，对整个软件架构、关键构件、接口进行总体设计。</p>
<h4 id="12absd">1.2 ABSD</h4>
<blockquote>
<p>ASBD指构成体系结构的商业、质量和功能需求的组合驱动的，设计活动从项目总体功能框架明确就开台，ABSD有三个基础，第一个基础是功能的分解，第二个基础是通过选择体系结构风格来实现质量和商业需求，第三个基础是软件模板的使用。</p>
</blockquote>
<ol>
<li>概念
<ul>
<li>设计元素：ABSD方法是一个自顶向下，递归细化的方法，直到能产生软件构件和类。在最顶层，系统被分解为若干<strong>概念子系统</strong>和一个或多个<strong>软件模板</strong>。</li>
<li>视角与视图：从不同的视角来检查体系结构的不同属性，展示功能组织的静态视角判断质量特性，展示并发行为的动态视角判断系统行为特性，逻辑视图来记录设计元素的功能和接口概念</li>
<li>用例和质量场景：用例用来捕获功能需求，定义特定场景来捕获质量需求（变更，性能，可靠性和交互性），非预期场景可能不能真正实现，但它们在决定设计的边界条件时很有用。</li>
<li>开发过程：体系结构需求，设计，文件化，复审，实现和演化等六个子过程</li>
</ul>
</li>
<li>体系结构需求
<ul>
<li>需求过程主要是获取用户需求，标识系统中所要用到的构件</li>
<li>需求来自三方面：系统的质量目标，系统的商业目标和系统开发人员的商业目标</li>
<li>标识构件：大致分三步，1.生成类图，2.对类进行分组，3.把类打包成构件</li>
<li>需求评审：组织分析人员，客户，设计人员，测试人员审查，需求是否真实反映了用户的需求，类的分组是否合理，构件合并是否合理。</li>
</ul>
</li>
<li>体系结构设计
<ul>
<li>提出软件体系结构模型</li>
<li>把已标识的构件映射到软件体系结构中，将产生一个中间结构</li>
<li>分析构件之间的相互使用，为把所有标识的构件集成到体系结构做准备</li>
<li>最后就是产生软件体系结构，并邀请独立于系统开发的外部人员对体系结构进行设计评审</li>
</ul>
</li>
<li>其它
<ul>
<li>体系结构文档化：输出体系结构规格说明书和测试体系结构需求的质量设计说明书</li>
<li>体系结构的演化：需求变化进行归类，使变化的需求与已有构件对应，制订体系结构演化计划，修改、增加或删除构件，更新构件的相互作用，构件组装与测试</li>
</ul>
</li>
</ol>
<blockquote>
<p>软件体系结构设计的一个核心目标是重复的体系结构模式，即达到体系结构级的软件重用</p>
</blockquote>
<h4 id="13">1.3 软件架构风格</h4>
<p>体系结构风格定义一个词汇表和一组约束，词汇表包含一些构件和连接件类型，需约束指出系统是如何将构件和连接件组合起来。体系结构风格反映了领域中众多系统所共有的结构和语义特性，并指导如何将各个模块和子系统有效组织成一个完整的系统。</p>
<h4 id="131">1.3.1 数据流风格</h4>
<ol>
<li>管理和过滤器，良好内聚，低耦合，具有重用性，维护性，并行，并不适合处理交互应用
<ul>
<li>构件：一组输入与输出的<strong>过滤器</strong>，数据输入构件，经过内部处理，然后产生数据输出</li>
<li>连接件：数据流传输的管道</li>
</ul>
</li>
<li>批处理序列，其约束就是数据完整性
<ul>
<li>构件：固定顺序计算单元</li>
<li>连接件：数据传递</li>
</ul>
</li>
</ol>
<h4 id="132">1.3.2 调用/返回风格</h4>
<ol>
<li>数据抽象和面向对象组织，良好内聚，模块分解 ，但依赖性强且具有传递依赖
<ul>
<li>构件：对象</li>
<li>连接件：对象的调用</li>
</ul>
</li>
<li>层次结构：模块分解，影响相邻两层扩展和重用
<ul>
<li>构件：每层上的应用</li>
<li>连接件：协议</li>
</ul>
</li>
<li>主程序/子程序：CS架构或三层CS架构，BS风格，调用关系具有层次性，取决于它调用的正确性
<ul>
<li>构件：主、子程序</li>
<li>连接件：过程调用</li>
</ul>
</li>
</ol>
<h4 id="133">1.3.3 独立构件风格</h4>
<ol>
<li>事件驱动，软件重用性，方便事件注册和维护；缺点是放弃计算控制，数据交换，和上下文约束
<ul>
<li>构件：事件</li>
<li>连接件：事件触发</li>
</ul>
</li>
<li>进程通信
<ul>
<li>构件：进程</li>
<li>连接件：消息传递</li>
</ul>
</li>
</ol>
<h4 id="134">1.3.4 虚拟机风格</h4>
<ol>
<li>解释器
<ul>
<li>构件：解释引擎</li>
<li>连接件：解释代码存储区</li>
</ul>
</li>
<li>基于规则，主要应用于人工智能
<ul>
<li>构件：规则集，解释器，选择器</li>
<li>连接件：工作内存</li>
</ul>
</li>
</ol>
<h4 id="135">1.3.5 仓库风格</h4>
<ol>
<li>黑板系统，中央数据结构的当前状态触发进程执行的选择；缺点是没有确定性算法的软件
<ul>
<li>构件：中央仓库</li>
<li>连接件：</li>
</ul>
</li>
<li>数据库系统，构件控制共享数据
<ul>
<li>构件：中央仓库，多个独立处理单元</li>
<li>连接件：</li>
</ul>
</li>
<li>超文本系统，构件以网状链接方式连接用户任意跳转相关构件
<ul>
<li>构件：</li>
<li>连接件：</li>
</ul>
</li>
</ol>
<h4 id="14">1.4 特定领域软件体系结构</h4>
<p>DSSA是专用于一类特定类型的任务的，在整个领域中能有效地使用的，为成功构造应用系统限定了标准的组合结构的软件构件的集合</p>
<ol>
<li>DSSA特征有：一个严格定义的问题域和问题解域，具有普遍性，对整个领域的构件组织模型的恰当抽象，具备领域固定的、典型的在开发过程中可重用元素</li>
<li>基本活动：领域分析，领域设计，领域实现</li>
<li>角色：
<ul>
<li>领域专家：提供关于领域中系统的需求规约和实现的知识，领域字典，选择样本系统</li>
<li>领域分析师：由具有知识工具背景的分析员担任，任务有控制领域分析过程，进行知识获取，并组织到领域模型中</li>
<li>领域设计人员：由有经验的软件设计人员来担任，任务有控制整个软件设计过程，根据领域模型和现有的系统开发出DSSA</li>
<li>领域实现人员</li>
</ul>
</li>
<li>建立过程：定义领域范围，定义领域特定的元素，定义领域特定的设计和实现需求约束，定义领域模型和体系结构，产生或搜集可重用的产品单元</li>
</ol>
<h4 id="15">1.5 系统架构的评估</h4>
<p>质量属性</p>
<ol>
<li>性能，系统的响应能力，常指单位事件内处理事务的数量或所需时间</li>
<li>可靠性，在意外或错误使用的情况下维护软件系统的功能特性的基本能力，包含容错和健壮性</li>
<li>可用性，正常运行的时间比例</li>
<li>安全性，向合法用户提供服务同阻止非授权用户使用</li>
<li>可修改性：较高的性能价格对系统进行变更的能力，包含可维护性，可扩展性，结构重组及可移植性</li>
<li>功能性：完成期望工作的能力</li>
<li>可变性：体系结构经扩充或变更而成为新体系结构的能力</li>
<li>互操作性：为外部功能提供软件入口</li>
</ol>
<p>概念<br>
1.敏感点：构件的特性，实现质量目标时应关注什么<br>
2.权衡点：影响多个质量属性的特性，是多个质量属性的敏感点<br>
3.场景：在进行体系结构评估时，首先要精确地得出具体的质量目标，为得出这些目标而采用的机制叫做和场景，一般从风险承担者的角度对与系统的交互的简短描述，采用刺激，环境，响应三方面描述</p>
<p>方法<br>
1.SAAM,采用场景技术，把任何形式的质量属性都具体化为场景，分5个步骤：场景开发，体系结构描述，单个场景评估，场景交互和总体评估<br>
2.ATAM,</p>
<h3 id="2">2.层次式架构设计</h3>
<h4 id="21">2.1 表现层框架设计</h4>
<blockquote>
<p>处理用户接口部分，担负用户与系统对话功能，检查，控制，转换</p>
</blockquote>
<ol>
<li>MVC，把一个应用的输入，处理，输出流程按照视图、控制、模型的方式进行分离，形成了控制器、模型、视图三个核心模块<br>
<img src="https://gss3.bdstatic.com/7Po3dSag_xI4khGkpoWK1HF6hhy/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=886019c7af345982d187edc06d9d5ac8/ac6eddc451da81cb26660e7e5066d01608243184.jpg" alt="mvc"></li>
<li>使用XML设计表现层，整个xml表现层解析的机制是一种策略模式，XML标记用于定义数据本身的结构和数据类型，在调用显示 GUI时，不直接的调用特定的表现技术的API，而是装载GUI对应的XML配置文件，再根据特定的表现技术的解析器解析xml,得到GUI视图实现对象</li>
<li>UIP,分UIC,UIPC两层,UIP管理经过UIC的信息流，管理UIP中各个事件之间的事务，修改用户过程的流程以响应异常；将概念上的用户交互流程从实现 或者涉及的设备上分离出来，保持内部的事务关联状态，通常是持有一个或者多个的与用户交互的事务实体。</li>
</ol>
<h4 id="22">2.2 业务逻辑层框架设计</h4>
<p>一般分为接口和实现两部分</p>
<ul>
<li>接口用于定义业务逻辑组件，定义业务逻辑组件必须实现的方法是整个系统运行核心，通常按模块来设计业务逻辑组件，每个模块设计一个业务逻辑组件，并且每个业务逻辑组件以多个DAO组件作为基础。</li>
<li>业务逻辑组件以DAO组件为基础，实现所有的业务逻辑，并且向外提供了统一的Facade接口，前台WEB仅仅依赖这个Facade接口</li>
</ul>
<h4 id="23">2.3 数据访问层框架设计</h4>
<ol>
<li>在线访问，基于数据库连接，读取数据等操作不断与后端数据源交互</li>
<li>Data Access Object,数据库访问对象，主要的功能就是用于进行数据操作的，在程序的标准开发架构中属于数据层的操作，定义一系列数据库的原子性操作</li>
<li>Data Transfer Object,经典EJB模式，DTO是一组对象或数据容器，跨不同的进程或网络的边界来传输数据</li>
<li>离线数据模式，以数据为中心，数据从数据源获取之后，将按照某种预定义的结构存放在系统中，成为应用的中心。</li>
<li>O/R Mapping,领域模型与ER模型转换</li>
</ol>
<p>相关概念</p>
<ul>
<li>工厂模式：定义一个用于创建对象的接口，让子类决定实例化哪一个类</li>
<li>Hibernate：本质上是一个提供数据库服务的中间件，对JDBC轻量级的对象封装，可使用对象编程思维操纵数据库</li>
<li>事务处理：ACID原则，atomicity,consistency,isolation,durabiltyp0<br>
petshop体系架构<img src="http://images.cnblogs.com/cnblogs_com/aspxer/ps05.gif" alt="petshop"></li>
</ul>
<h3 id="3">3.企业集成架构设计</h3>
<ol>
<li>是什么
<ul>
<li>是一个支持复杂信息环境下信息系统开发、集成和协同运行的软件支撑环境</li>
</ul>
</li>
<li>做什么
<ul>
<li>通信服务，提供分布环境下透明的同步/异步通信服务功能</li>
<li>信息集成服务，实现异种数据库系统之间数据的交换，互操作，分布数据管理和共享信息模型定义</li>
<li>应用集成服务，通过高层应用编程接口来实现对相应应用程序的访问</li>
<li>二次开发工具</li>
<li>平台运行管理工具</li>
</ul>
</li>
<li>怎么做
<ul>
<li>网络集成:语法互联</li>
<li>数据集成：语义互联<br>
解决不同应用和系统间的数据共享和交换需求，包括共享信息管理，共享模型管理，共享信息访问机制；主要有三种模式：数据联绑，数据复制，适配器</li>
<li>应用集成：语用互操作<br>
指多个应用系统根据业务逻辑的需要而进行的功能之间的相互调用和互操作。<br>
包括集成适配器，集成信使，集成面板，集成代理4种。
<ul>
<li>信使集成：系统这间的通信和数据交换通过消息代理来实现 ，每个应用需要建立与集成信使之间的接口连接</li>
<li>集成面板：从应用交互实现的层面来描述客户端应用和服务器端应用集成的方法，对服务器应用功能的抽象和简化，为客户端应用调用服务器应用提供了一种简化的公共接口。</li>
<li>代理集成:装待集成的应用间的交互逻辑从应用中分离出来，并对应用间的交互逻辑进行封装，进而由集成代理来引导多个应用之间的交互。</li>
</ul>
</li>
<li>会聚集成：集成化运行</li>
</ul>
</li>
<li>集成技术
<ul>
<li>EDI:用一种国际公认的标准格式进行数据交换与处理</li>
<li>xml</li>
<li>step:一个描述如何表达和交换数字化产品信息的ISO标准</li>
</ul>
</li>
</ol>
<h3 id="4">4. 面向服务架构设计</h3>
<h3 id="41what">4.1 概念（what）</h3>
<h3 id="42how">4.2 参考架构、主要技术、实施过程（how）</h3>
<h3 id="43why">4.3 设计原则与模式（why）</h3>
<h3 id="5">5. 面向构件的架构设计</h3>
<h3 id="51what">5.1 概念（what）</h3>
<ol>
<li>构件：是一种组装单元，具有规范的接口规约和显示的语境依赖
<ul>
<li>独立部署单元:封装全部内部特征，具有原子性，外部不能访问构件内部细节信息</li>
<li>作为第三方的组装单元：通过良好定义的接口与外部环境交互</li>
<li>没有外部的可见状态：一般只有一个实例，构件与其拷贝无区别</li>
</ul>
</li>
<li>对象：一个实例单元，具有唯一标志，可具有外部可见状态，封装了自已的状态和行为。对象必然属性一个构件，构件可以有对象或其它过程体。</li>
<li>模块：</li>
<li>接口：一个已命名的一组操作的集合</li>
</ol>
<h3 id="52how">5.2 构件标准（how）</h3>
<h3 id="53why">5.3 构件结构（why）</h3>
<h3 id="54">5.4 典型架构</h3>
<h3 id="6">6. 系统安全架构设计</h3>
<h3 id="7">7. 系统可靠性架构设计</h3>
<hr>
<h3 id>附录 名词</h3>
<p>1.ASBD-Architecture Based Software Design 基于体系结构的软件设计<br>
2.DSSA-Domian Specific Software Architecture 特定领域软件体系结构<br>
3.SAAM-Scenarios-based Architecture Analysis Method<br>
4.ATAM-Architecture Tradeoff Analysis Method 体系结构权衡分析方法</p>
</div>]]></content:encoded></item><item><title><![CDATA[ray个人简介]]></title><description><![CDATA[<div class="kg-card-markdown"><h4 id="rayblog">ray个人<a href="http://blog.ycminglei.cn/">blog</a></h4>
<hr>
<h4 id>技能</h4>
<ol>
<li>精通java,常用技术有spring,jpa,hibernate,mybatis,jquery,bootstarp,mvn,docker等</li>
<li>熟练使用php thinkphp,node angularjs1.*,vue</li>
<li>了解c#,delphi,vb</li>
</ol>
<h4 id>工作经验</h4>
<p>先后在杭州松下电器，武汉烽火科技，三峡高科等企业担任软件工程师、架构师、项目经理等，主要从事企事业单位业务应用系统，移动html,微信，钉钉领域开发。在多年的软件行业奋战历程中，沉淀下来了对 J2ee 平台相关技术的掌握和理解；在业务领域和 IT 项目建设之间，围绕项目建设目标来定义项目范围，控制进度，分配资源，降低成本来完成有质量项目的管理和经验； 在架构上考虑复用性、灵活性，可维护性，抽取可复用的组件模型，积累资源提高复用，快速迭代产品原型等设计知识。</p>
<h3 id>个人信念</h3>
<p>对代码的有轻微洁癖，对于出于己手的源码，</p></div>]]></description><link>http://blog.ycminglei.cn/raycv/</link><guid isPermaLink="false">59f2ab3e732a5961f290e9e3</guid><dc:creator><![CDATA[ray]]></dc:creator><pubDate>Fri, 27 Oct 2017 04:06:51 GMT</pubDate><media:content url="http://blog.ycminglei.cn/content/images/2017/10/splid1.jpg" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><h4 id="rayblog">ray个人<a href="http://blog.ycminglei.cn/">blog</a></h4>
<hr>
<h4 id>技能</h4>
<ol>
<li>精通java,常用技术有spring,jpa,hibernate,mybatis,jquery,bootstarp,mvn,docker等</li>
<li>熟练使用php thinkphp,node angularjs1.*,vue</li>
<li>了解c#,delphi,vb</li>
</ol>
<h4 id>工作经验</h4>
<img src="http://blog.ycminglei.cn/content/images/2017/10/splid1.jpg" alt="ray个人简介"><p>先后在杭州松下电器，武汉烽火科技，三峡高科等企业担任软件工程师、架构师、项目经理等，主要从事企事业单位业务应用系统，移动html,微信，钉钉领域开发。在多年的软件行业奋战历程中，沉淀下来了对 J2ee 平台相关技术的掌握和理解；在业务领域和 IT 项目建设之间，围绕项目建设目标来定义项目范围，控制进度，分配资源，降低成本来完成有质量项目的管理和经验； 在架构上考虑复用性、灵活性，可维护性，抽取可复用的组件模型，积累资源提高复用，快速迭代产品原型等设计知识。</p>
<h3 id>个人信念</h3>
<p>对代码的有轻微洁癖，对于出于己手的源码，完美是一种不能放弃的执念，服务用户，进无止境。对各种技术有无穷的贪欲，但毕竟术业有专攻。</p>
<h3 id>团队支持</h3>
<p>宜昌铭雷信息技术有限责任公司，成立于2016年4月，我们基于微信公众平台为企业提供咨询，开发，运营综合解决方案，提供微信公众号管理系统服务、基于j2ee+bootstarp提供企业软件项目定制两大业务分支。让我们乘着互联网浪潮，微信互联天下商机，用我们专业的技术和至诚的努力为您提供精致的软件和服务。</p>
<blockquote>
<p><strong>联系我们</strong><br>
通讯地址;湖北省宜昌市西陵区西湖路6号<br>
联系电话：雷先生 15871590233，139072004449</p>
</blockquote>
</div>]]></content:encoded></item><item><title><![CDATA[面向方面编程(Aspect Oriented Programming)]]></title><description><![CDATA[<div class="kg-card-markdown"><h3 id="1">1. 概念</h3>
<h4 id="11">1.1 背景</h4>
<ol>
<li>面向过程编程问题<br>
面向过程是一种自顶向下的编程方法，适用于小型软件系统，在大型应用系统中，自顶向下逐步求精的方法，在系统的进化和维护，以及软件重用性方面都存在其不足之处</li>
<li>面向对象编程问题<br>
由于其良好的封装性，层次化性以及继承性，且对象模型很好映射到实际领域。但是在设计阶段，由于以类为单位组织建模，不能全面反映软件系统的需求；在编码阶段，将数据和方法封装到类中增强了数据的安全性和模块化，但减少了代码重用可能性；维护阶段，类中夹杂各种特定于应用的代码，难以理解和维护</li>
</ol>
<h4 id="12aop">1.2 AOP</h4>
<ol>
<li><strong>将传统的按功能或按对象划分程序模块的方法转化为按系统特征划分程序模块，这是AOP基本思想</strong>，OOP引入封装、继承和多态性等概念来建立一种对象层次结构，允许定义从上到下的关系，AOP则定义从左到右的关系，为分散的对象引入公共行为。</li>
<li>核心名词
<ul>
<li>aspect方面:切面是横切性关注点的抽象，切入点和通知结合起来就是aspect</li>
<li>joinpoint连接点是指那些被拦截到的点，在spring中，这些点指的是方法</li>
<li>pointcut切入点：指对joinpoint进行拦截的定义，本质上是一个捕获连接点的结构,</li>
</ul>
<blockquote>
<p>其表述方式有：1、直接指定Joinpoint所在方法名称，2、正则表达式，</p></blockquote></li></ol></div>]]></description><link>http://blog.ycminglei.cn/mian-xiang-fang-mian-bian-cheng/</link><guid isPermaLink="false">59e19232732a5961f290e9c5</guid><category><![CDATA[软件设计]]></category><category><![CDATA[spring]]></category><dc:creator><![CDATA[ray]]></dc:creator><pubDate>Tue, 17 Oct 2017 17:12:53 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><h3 id="1">1. 概念</h3>
<h4 id="11">1.1 背景</h4>
<ol>
<li>面向过程编程问题<br>
面向过程是一种自顶向下的编程方法，适用于小型软件系统，在大型应用系统中，自顶向下逐步求精的方法，在系统的进化和维护，以及软件重用性方面都存在其不足之处</li>
<li>面向对象编程问题<br>
由于其良好的封装性，层次化性以及继承性，且对象模型很好映射到实际领域。但是在设计阶段，由于以类为单位组织建模，不能全面反映软件系统的需求；在编码阶段，将数据和方法封装到类中增强了数据的安全性和模块化，但减少了代码重用可能性；维护阶段，类中夹杂各种特定于应用的代码，难以理解和维护</li>
</ol>
<h4 id="12aop">1.2 AOP</h4>
<ol>
<li><strong>将传统的按功能或按对象划分程序模块的方法转化为按系统特征划分程序模块，这是AOP基本思想</strong>，OOP引入封装、继承和多态性等概念来建立一种对象层次结构，允许定义从上到下的关系，AOP则定义从左到右的关系，为分散的对象引入公共行为。</li>
<li>核心名词
<ul>
<li>aspect方面:切面是横切性关注点的抽象，切入点和通知结合起来就是aspect</li>
<li>joinpoint连接点是指那些被拦截到的点，在spring中，这些点指的是方法</li>
<li>pointcut切入点：指对joinpoint进行拦截的定义，本质上是一个捕获连接点的结构,</li>
</ul>
<blockquote>
<p>其表述方式有：1、直接指定Joinpoint所在方法名称，2、正则表达式，3、使用特定的Pointcut表述语言,4、pointcut运算：可以涵盖逻辑运算语法，如spring的配置文件中使用and,or来作为逻辑运算符</p>
</blockquote>
<ul>
<li>advice通知 ：是指拦截到joinpoint之后所要做的事情就是通知，分为前置通知，后置通知，异常通知，最终通知，环绕通知</li>
<li>target目标对象：代理的目标对象</li>
<li>weawe织入：指将aspect应用到target对象并导致proxy对象创建的过程称为织入</li>
<li>introduction引入:在不修改类代码的前提下，introduction可以在运行期为类动态地添加一些方法或field,本质也是一种advice,但其不是根据横切逻辑在Joinpoint处的执行时机来区分的，而是根据它可以完成的功能而区别于其他的advice类型<br>
<img src="http://owmdyd8zo.bkt.clouddn.com/aopexample.png" alt="形象图"></li>
</ul>
</li>
<li>aop技术：AspectJ,AspectWerkz,JBoss AOP,Spring Aop
<ul>
<li>语法方面: aspectJ对java进行了扩展，其它则没有改变</li>
<li>声明方面: aspectJ使用代码声明，其它则使用xml或注解</li>
<li>性能方面: AspectJ通过编译对目标二进制类的增加，其它则基于运行时拦截技术</li>
</ul>
</li>
</ol>
<h4 id="13">1.3 发展历史</h4>
<ul>
<li>第一代：静态aop时代<br>
以AspectJ为代表，相应的横切关注点以Aspec形式实现后，会通过特定的编辑器，将实现后的Aspect编译并织入到系统的静态类中，以EJB所提供的声明性事务等AOP实现为代表。Aspect直接以Java字节码的形式编译到java类中，由java虚拟机加载class文件得以运行。</li>
<li>第二代：动态aop时代<br>
aop的织入过程在系统运行开始之后进行，而不是预先编译到系统类中，而且织入信息大都采用外部xml文件格式保存，可以在调整以入及织入逻辑单元的同时，不必变更系统其它模块。采用对系统字节码进行操作的方式来完成 aspect到系统织入，带到一定性能消耗，而jvm的提升，对反射以及字节码操作技术的更好支持，少量的性能损失可以容忍。</li>
</ul>
<h4 id="14javaaop">1.4 java平台AOP机制</h4>
<ol>
<li>动态代理机制，可以在运行期间，为相应的接口动态生成对应的代理对象，将横切关注点逻辑封装到动态代理的InvocationHandler中，以动态代理类为载体来实现横切逻辑，但这种机制只针对接口有效，性能稍逊静态aop</li>
<li>动态字节码增加，使用asm或cglib，动态构建字节码的class文件，通过动态字节码增强技术，对目标对象扩展继承生成相应的子类，而实现MethodInterceptor接口将横切逻辑加到这些子类中，通过Enhancer类（参数父类类型和callback实现类）创建代理类，从而达到将横切逻辑织入系统的目的。缺点在于final的声明则无法对其进行子类化的扩展。</li>
<li>java代码生成(静态代理)，早期EJB容器根据部署描述符文件提供的织入信息，会为相应的功能模块类生成对应的java代码，然后通过部署工具编译生成相应java类，然后在ejb容器下工作.主要由三个角色构成，subject接口，subject实例，subject代理，客户端使用代理来调用对象，使用代理对方法拦截进行部分业务处理。subject代理和subject实例都实现了subject接口，代理类持有对实例类的引用，在调用实例类业务方法的时候添加横切逻辑，缺点是虽然横切逻辑相同，但对应的目标对象类型(subject接口)是不一样的都需要编写对应代理类，加入相同的横切逻辑，代码过于臃肿。</li>
<li>自定义类加载器，通过自定义类加载器的方式完成横切逻辑到系统的织入，自定义类加载器通过读取外部文件规定的必要信息，在加载 class文件期间就可以将横切逻辑添加到系统模块类的现有逻辑中。jboss aop采用这种方式实现，某些应用服务器会控制整个类的加载体系。</li>
</ol>
<h3 id="2aspectj">2 AspectJ</h3>
<p>AspectJ既是语言规范，又是语言实现,AspectJ也许是已知最好的，并且应用最广泛的AOP实现，</p>
<ol>
<li>连接点：可用的连接点有
<ul>
<li>方法的调用或执行</li>
<li>构造器的调用和执行</li>
<li>对属性的读写访问</li>
<li>异常处理的执行</li>
<li>对象和类的初始化执行<br>
备注：不能在像if条件检查或for循环这样细粒度语言构造上的连接点</li>
</ul>
</li>
<li>方面<br>
方面把切入点和通知包在一起。AspectJ允许在类中声明切入点，但在类中只能声明static的切入点，且不允许通知。</li>
</ol>
<h3 id="3springaop">3 Spring AOP</h3>
<h4 id="31">3.1 概念表述</h4>
<ul>
<li>spring aop采用动态代理机制和字节码生成技术，都是在运行期间为目标对象生成一个代理对象，而将横切逻辑织入到这个代理对象中，系统最终使用的是织入了横切逻辑的代理对象，而不是真正的目标对象。</li>
<li>spring采用的代理模式有静态代理、动态代理、CGLig代理。spring在发现目标对象实现了相应的Interface时，使用动态代理机制生成代理对象，如何无任何接口则使用Cglib动态字节码生成，但proxyTargetClass属性和optimize属性强制ProxyFactory采用基于类的代理</li>
<li>Spring aop使用ProxyFactory作为织入器，使用AnnotationAwareAspectJAutoProxyCreator来配置信息，搜集IOC容器中注册的Aspect,并应用到Pointcut定义的各个目标对象上。如果@Pointcut包含不支持的标志符的Pointcut表达式，将抛出IllegalArgumentException而拒绝，Spring aop只是借用一下AspectJ的Pointcut表述语言，而底层的Joinpoint类型匹配却依然是Spring原核心。</li>
</ul>
<h4 id="32">3.2 关键点实现</h4>
<ul>
<li>Joinpoint:在spring仅提供<strong>方法执行类型</strong>的连接点，对于属性，构造方法级别的Joinpoint,会破坏对象的封装，也是极特殊的应求需求</li>
<li>pointCut接口有两个重要的方法ClassFilter(目标类以及它们的实例)和methodFilte(方法拦截),分别用于匹配被执行织入操作的对象以及相应的方法</li>
</ul>
<pre><code>     public interface MethodMathcer{
          boolean mathces(Method method,Class target)
          boolean isRuntime();
          boolean mathces(Method method,Class target,Object[] args)
          MethodMathcer TRUE = TrueMethodMatcher.INSTANCE;
     }
</code></pre>
<blockquote>
<p>关于重载拦截：在对对象具体方法拦截的时候，可以忽略每次方法执行的时候调用者传入的参数，也可以每次都检查这些方法调用的参数。在isRuntion()返回false时，表示不会考虑具体jointPoint参数，这种methodFilter称之为StaticMethodMather,框架内部缓存匹配结果以提高性能。如果isRuntiome()返回true时，表明该MethodMatcher将会每次都对方法调用的参数进行匹配检查，无法对匹配结果进行缓存，称之为DynamicMethodMatcher。</p>
</blockquote>
<pre><code>+Pointcut
   -MethodMatcher
        -StaticMethodMatcher
             -StaticMethodMatherPointcut
                  -NameMatchMethodPointcut #简单实现，对一组方法名称及*匹配拦截，不对重载方法名进行匹配
                  -AbstractRegexpMethodPointcut
                       -JdkRegexpMethodPointcut：jdk正则表达式
                       -Perl5RegexpMethodPointcut：Perl5匹配
        -DynamicMethodMatcher
             -DynamicMethodMathcerPointcut
        -AnnotationMatchingPointCut:类级别的注解
        -ComposablePointcut:提供可以进行逻辑运算的pointcut实现，并与交的运算
        -ControlFlowPontcut:匹配程序的调用流程，不对方法执行的JoinPoint处的单一特征匹配,需要在运行期间检查程序的调用栈
</code></pre>
<ul>
<li>
<p>advice</p>
<ul>
<li>per-class类型的Advice:该类型的Advice的实例可以在目标对象类的所有实例之间共享。通常只是提供方法拦截的功能，不会为目标对象类保存任何状态或者添加新的特性。
<ul>
<li>before advice:实现的横切逻辑奖在相应的Joinpoint之前执行，通常不会打断程序的执行流程。</li>
<li>throwsAdvice:通常用于对系统中特定的异常情况进行监控，以统一的方式对所发生的异常进行处理。</li>
<li>afterReturningAdvice:可以访问当前Joinpoint的方法返回值，方法，方法参数以及所在的目标对象。只有方法正常返回的情况下，afterReturningAdvice才会执行。</li>
<li>around advice:spring中没有直接定义对应around advice的实现接口，而是直接使用methodInterceptor来完成around advice功能。其proceed()方法，可以让程序执行继续沿着调用链传播。在process()方法前后添加横切逻辑。</li>
</ul>
</li>
<li>per-instance类型的advice:per-instance类型的advice不会在目标类所有对象实例之间共享，而是会为不同的实例对象保存它们各自的状态以及相关逻辑。
<ul>
<li>Introduction可以在不改动目标类定义的情况下，为目标类添加新的属性以及行为。在spring中，为目标对象添加新的属性和行为必须声明相应的接口以及相应的实现，这样，再通过特定的拦截器将新的接口定义以及实现类中的逻辑附加到目标对象之上。之后，目标对象就拥有了新的状态和行为。</li>
<li>DelegatingIntroductionInterceptor:此类会使用它所持有的同一个“delegate&quot;接口实例 ，供同一目标类的所有实例共享使用。</li>
<li>DelegatePerTargetObjectIntroductionInterceptor:此对象会在内部持有一个目标对象与相应Introduction逻辑实现类之间的映射关系。当每个目标对象上的新定义的接口方法被调用的时候，此类会拦截这些调用，然后以目标对象实例作为键，到它持有的那个映射关系中取得对应当前目标对象实例的Introduction实现类实例。</li>
</ul>
</li>
</ul>
</li>
<li>
<p>aspect<br>
Advisor代表Spring中的Aspect,但是，Advisor通常只持有一个Pointcut和一个Advice,而理论上可以定义多个Pointcut和多个advice.</p>
</li>
</ul>
<pre><code>-PointcutAdvisor#此类中真正的定义一个Pointcut和一个Advice的Advisor
    -AbstractPointcutAdvisor
        -AbstractGenericPointcutAdvisor
             -NameMatchmethodPointcutAdvisor#内部持有NameMathcMethodPointcut类型的Pointcut实例，来设置方法名拦截。
             -RegexpMethodPointcutAdvisor#只能通过正则表达式为其设置相应的Pointcut
             -DefaultPointcutAdvisor#通用的PointcutAdvisor实现，通过设置相应的Pointcut和Advice来使用。
        -DefaultBeanFactoryPointcutAdvisor#其自身绑定到BeanFactory，可以通过容器中的Advice注册的beanName来关联对应的Advice,只有当对应的Pointcut匹配成功后，才去实例化对应的Advice,减少了窗口启动初期Advisor和Advice之间的耦合性。对应Advice的配置属性名称为&quot;advicebeanName&quot;
    -IntroductionAdvisor#只能应用于类级别的拦截，只能使用Introduction型的Advice.
</code></pre>
<ul>
<li>
<p>Ordered<br>
当系统实现中存在多个Advisor，当其中的某些Advisor的Pointcut匹配了同一个Joinpoint的时候，就会在这同一个Joinpoint处执行多个Advice的横切逻辑。如果同一个Joinpoint处执行的Advice逻辑存在优先顺序依赖的话，实现了Ordered接口，直接配置顺序呈来指定执行顺序，Order值越低，优先级越高。</p>
</li>
<li>
<p>TargetSource</p>
<ul>
<li>作用：包装目标对象的容器，当每个针对目标对象的方法调用经历层层拦截而到达调用链的终点时候，就该调用目标对象上定义的方法。通过“插足于”调用链与实际目标对象之间的某个TargetSource来取得具体目标对象，然后再调用从TargetSource中取得的目标对象上的相应方法。</li>
<li>特性：每次的方法调用都会触发TargetSource的getTarget()方法，从相应的实现类中取得具体的目标对象。</li>
<li>SingletonTargetSource:内部只持有一个目标对象，当每次方法调用到时间，SingletonTargetSource都会返回这同一个目标。</li>
<li>PrototypeTargetSource:此类型targetSource都会返回一个新的目标对象实例调用</li>
<li>HotSwappableTargetSourcev:此类可以在应用程序运行的时候，根据某种特定条件，动态替换目标对象类的具体实现。比如base dao接口有多个数据源的实现类，在程序启动之后，默认的实现类出现了问题，可以根据条件切换到别一个实现上。典型应用场景：对双数据源互换的实现改进，使用ThrowsAdvice对数据库相关异常进行捕捉，在捕捉到必要的切换信息后，调用HotSwappableTargetSouce的swap方法使用新的数据源替换旧的数据源。</li>
<li>CommonsPoolTargetSource:使用现有的Jakarta commons pool提供对象池支池，返回有限数据上限的目标对象实例，好像获取连接池中的Connection一样</li>
<li>ThreadLocalTargetSource:为不同的线程调用提供不同的目标对象，保证各自线程上对各目标对象的调用。</li>
</ul>
</li>
</ul>
<h4 id="33">3.3 开启实践</h4>
<ul>
<li>基于annonation的spring aop拦截,使用@AspectJ标签
<ul>
<li>在配置文件中添加<code>&lt;aop:aspectj-autoproxy/&gt;</code>注解</li>
<li>创建一个Java文件，使用<code>@Aspect</code>注解修饰该类</li>
<li>创建一个方法，使用<code>@Before</code>、<code>@After</code>、<code>@Around</code>等进行修饰，在注解中写上切入点的表达式</li>
</ul>
</li>
<li>基于配置文件的配置
<ul>
<li>创建一个Java文件，并指定一个用于执行拦截的方法，该方法可以有0个或多个参数</li>
<li>在Spring配置文件中注册该Java类为一个Bean</li>
<li>使用<code>&lt;aop:config/&gt;</code>、<code>&lt;aop:aspect/&gt;</code>等标签进行配置</li>
</ul>
</li>
</ul>
</div>]]></content:encoded></item><item><title><![CDATA[设计模式Gof]]></title><description><![CDATA[<div class="kg-card-markdown"><h2 id>目录</h2>
<ol>
<li><a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#first">概念</a></li>
<li><a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#second">设计模式原则</a></li>
<li><a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#three">创建型模式</a></li>
<li><a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#four">结构型模式</a></li>
<li><a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#five">行为型模式</a></li>
<li><a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#six">J2ee模式</a></li>
</ol>
<hr>
<p>庞杂的软件模式中，分为设计模式，分析模式，组织和过程模式，本文来分析设计模式,着重理解，具体定义可参考<a href="http://www.runoob.com/design-pattern/design-pattern-tutorial.html">(菜鸟教程-设计模式)</a></p>
<h4 id="1ahrefnamefirsta">1.<a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#" name="first">概念 </a></h4>
<ul>
<li>定义：模式是表示特定的情景，动机，解决方案三个方面关系的规则，每个模式描述了一个在某种特定情景下不断重复发生的问题，以及该问题解决方案的核心所在。模式既是一个事物又是一个过程，不仅描述该事物本身，而且提出了通过怎样的过程来产生该事物</li>
<li>作用：简化并加快设计，方便开发人员的通信，降低风险，有助于转到面向对象技术</li>
<li>特性：巧妙，通用，实践证明，简单，可重用，面向对象</li>
<li>组成元素：模式名，问题，情景，动机，解决方案，示例，结果情景，基本原理，相关模式，已知应用</li>
</ul>
<p><strong>范围准则</strong></p></div>]]></description><link>http://blog.ycminglei.cn/she-ji-mo-shi-gof/</link><guid isPermaLink="false">59e18ef8732a5961f290e9bd</guid><category><![CDATA[软件设计]]></category><dc:creator><![CDATA[ray]]></dc:creator><pubDate>Sat, 14 Oct 2017 04:16:27 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><h2 id>目录</h2>
<ol>
<li><a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#first">概念</a></li>
<li><a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#second">设计模式原则</a></li>
<li><a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#three">创建型模式</a></li>
<li><a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#four">结构型模式</a></li>
<li><a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#five">行为型模式</a></li>
<li><a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#six">J2ee模式</a></li>
</ol>
<hr>
<p>庞杂的软件模式中，分为设计模式，分析模式，组织和过程模式，本文来分析设计模式,着重理解，具体定义可参考<a href="http://www.runoob.com/design-pattern/design-pattern-tutorial.html">(菜鸟教程-设计模式)</a></p>
<h4 id="1ahrefnamefirsta">1.<a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#" name="first">概念 </a></h4>
<ul>
<li>定义：模式是表示特定的情景，动机，解决方案三个方面关系的规则，每个模式描述了一个在某种特定情景下不断重复发生的问题，以及该问题解决方案的核心所在。模式既是一个事物又是一个过程，不仅描述该事物本身，而且提出了通过怎样的过程来产生该事物</li>
<li>作用：简化并加快设计，方便开发人员的通信，降低风险，有助于转到面向对象技术</li>
<li>特性：巧妙，通用，实践证明，简单，可重用，面向对象</li>
<li>组成元素：模式名，问题，情景，动机，解决方案，示例，结果情景，基本原理，相关模式，已知应用</li>
</ul>
<p><strong>范围准则</strong></p>
<blockquote>
<p>类模式处理类和子类之间的关系，这些关系通过继承建立，是静态的，在编译时刻便确定下来了，对象模式处理对象间的关系，这些关系在运行时刻是可以变化 ，更具动态性。创建型类模式将对象的部分创建工作延迟到子类，而创建型对象模式则将它延迟到另一个对象中。结构型类模式使用继承机制来组合类，而结构型对象模式则描述 了对象的组装方式。行为型类模式使用继承描述算法和控制流，而行为型对象模式则描述 一组对象怎样协作完成单个对象所无法完成的任务</p>
</blockquote>
<p><strong>模式关系图</strong><img src="http://owmdyd8zo.bkt.clouddn.com/designImage.png" alt="designImage"></p>
<h4 id="2ahrefnameseconda">2 <a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#" name="second">设计模式原则</a></h4>
<ul>
<li>
<p>开闭原则（Open Close Principle）<br>
开闭原则就是说对扩展开放，对修改关闭。在程序需要进行拓展的时候，不能去修改原有的代码，实现一个热插拔的效果。所以一句话概括就是：为了使程序的扩展性好，易于维护和升级。想要达到这样的效果，我们需要使用接口和抽象类，后面的具体设计中我们会提到这点。</p>
</li>
<li>
<p>里氏代换原则（Liskov Substitution Principle）<br>
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说，任何基类可以出现的地方，子类一定可以出现。 LSP是继承复用的基石，只有当衍生类可以替换掉基类，软件单位的功能不受到影响时，基类才能真正被复用，而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现，所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科</p>
</li>
<li>
<p>依赖倒转原则（Dependence Inversion Principle）<br>
这个是开闭原则的基础，具体内容：针对接口编程，依赖于抽象而不依赖于具体。</p>
</li>
<li>
<p>接口隔离原则（Interface Segregation Principle）<br>
这个原则的意思是：使用多个隔离的接口，比使用单个接口要好。还是一个降低类之间的耦合度的意思，从这儿我们看出，其实设计模式就是一个软件的设计思想，从大型软件架构出发，为了升级和维护方便。所以上文中多次出现：降低依赖，降低耦合。</p>
</li>
<li>
<p>迪米特法则（最少知道原则）（Demeter Principle）<br>
为什么叫最少知道原则，就是说：一个实体应当尽量少的与其他实体之间发生相互作用，使得系统功能模块相对独立。</p>
</li>
<li>
<p>合成复用原则（Composite Reuse Principle）<br>
原则是尽量使用合成/聚合的方式，而不是使用继承。</p>
</li>
</ul>
<h4 id="3ahrefnamethreea">3.<a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#" name="three">创建型模式</a></h4>
<p>针对接口编程，而不是针对实现编程，不将变量声明为某个特定的具体类的实例对象，而是让它遵从抽象类所定义的接口，当你不得不在系统的某个地方实例化具体的类（即指定一个特定的实现）时，创建型模式可以帮你。通过抽象对象的创建过程，这些模式提供不同方式以在实例化时建立接口和实现的透明连接。   创建型模式抽象了实例化过程，帮助系统独立于如何创建、组合和表示它的那些对象，随着系统演化得越来越依赖于对象的复合而不是类继承，创建型模式变得更为重要。重心从对一组固定行为的硬编码转移为定义一个较小的基本行为集，这些行为可以被组合成任意数目的更复杂的行为。</p>
<h4 id="31abstractfactory">3.1 抽象工厂模式 Abstract Factory</h4>
<p>抽象工厂模式是围绕一个超级工厂创建其他工厂,接口是负责创建一个相关对象的工厂，不需要显式指定它们的类,每个生成的工厂都能按照工厂模式提供对象。</p>
<ol>
<li>意图:提供一个创建一系列相关或相互依赖对象的接口，而无需指定它们具体的类</li>
</ol>
<ul>
<li>动机:客户仅与抽象类定义的接口交互，而不使用特定的具体类的接口</li>
<li>适用场景</li>
<li>系统独立于产品的创建，组合以及表示</li>
</ul>
<ol start="2">
<li>系统要由多个产品系列中的一个来配置时，当你要强调一系列相关的产品对象的设计以便进行联合使用时</li>
<li>相关产品对象系列是共同使用的，而且必须确保这一点</li>
<li>希望提供产品的类库，只开放其接口，而不是其实现</li>
</ol>
<blockquote>
<p>典型应用</p>
</blockquote>
<ol>
<li>QQ 换皮肤，一整套一起换。</li>
<li>生成不同操作系统的程序</li>
</ol>
<ul>
<li>优点:分离了具体类，一个工厂封装创建产品对象的责任和过程，它将客户与类的实现分离，产品的类名不出现在客户代码中。有利于产品一致性，一个系列中的产品对象被设计成一起工作时。<br>
易于交换产品系列，容易改变一个应用的具体工厂，仅需转换到相应的工厂对象并重新创建接口即可，客户代码不用调整。</li>
<li>缺点:在于难于扩展抽象工厂以生产新种类的产品，因为这涉及抽象工厂类及其所有子类的改变</li>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/abstractfactory_pattern_uml_diagram.jpg" alt="抽象工厂"></li>
</ul>
<h4 id="32builder">3.2 构建器模式 Builder</h4>
<p>使用多个简单的对象一步一步构建成一个复杂的对象，与工厂模式的区别是：建造者模式更加关注与零件装配的顺序</p>
<ol>
<li>意图:将复杂对象的构建与其表示相分离，这样相同的构造过程可以创建不同的对象</li>
</ol>
<ul>
<li>动机:主要解决在软件系统中，有时候面临着&quot;一个复杂对象&quot;的创建工作，其通常由各个部分的子对象用一定的算法构成；由于需求的变化，这个复杂对象的各个部分经常面临着剧烈的变化，但是将它们组合在一起的算法却相对稳定。</li>
<li>适用场景</li>
<li>创建繁杂对象的算法独立于组成对象的部分以及这些部分的集合方式</li>
<li>构造过程必须允许已构建对象有不同的表示</li>
<li>将变与不变分离开</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>
<p>去肯德基，汉堡、可乐、薯条、炸鸡翅等是不变的，而其组合是经常变化的，生成出所谓的&quot;套餐&quot;</p>
</li>
<li>
<p>JAVA 中的 StringBuilder</p>
</li>
<li>
<p>优点: 可以对产品的内部表示进行改变，将构造代码与表示代码相分离</p>
</li>
<li>
<p>缺点: 产品必须有共同点，范围有限制。 如内部变化复杂，会有很多的建造类。</p>
</li>
<li>
<p>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/builder_pattern_uml_diagram.jpg" alt="Builder"></p>
</li>
</ul>
<h4 id="33factorymethod">3.3 工厂方法模式 Factory Method</h4>
<p>在工厂模式中，在创建对象时不会对客户端暴露创建逻辑，并且是通过使用一个共同的接口来指向新创建的对象。凡是出现了大量的产品需要创建，并且具有共同的接口时，可以通过工厂方法模式进行创建。有三种具体模式中，第一种如果传入的字符串有误，不能正确创建对象，第三种相对于第二种，不需要实例化工厂类，所以，大多数情况下，我们会选用第三种——静态工厂方法模式</p>
<ol>
<li>意图:定义一个创建对象的接口，让其子类自己决定实例化哪一个工厂类，工厂模式使其创建过程延迟到子类进行</li>
</ol>
<ul>
<li>动机:要解决接口选择的问题</li>
<li>适用场景：</li>
<li>类不能预料它必须创建的对象的类</li>
<li>类希望其子类指定它要创建对象</li>
<li>类将责任转给某个帮助子类，而用户希望定位那个被授权的帮助子类</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>
<p>日志记录器：记录可能记录到本地硬盘、系统事件、远程服务器等，用户可以选择记录日志到什么地方。</p>
</li>
<li>
<p>数据库访问，当用户不知道最后系统采用哪一类数据库，以及数据库可能有变化时。</p>
</li>
<li>
<p>优点:没有了将应用程序类绑定到代码中的要求，代码只处理接口，允许子类提供对象的扩展版本</p>
</li>
<li>
<p>缺点:每次增加一个产品时，都需要增加一个具体类和对象实现工厂，使得系统中类的个数成倍增加，在一定程度上增加了系统的复杂度，同时也增加了系统具体类的依赖;类的创建依赖工厂类，如果想要拓展程序，必须对工厂类进行修改，这违背了闭包原则</p>
</li>
<li>
<p>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/factory_pattern_uml_diagram.jpg" alt="工厂方法"></p>
</li>
</ul>
<h4 id="34prototype">3.4 原型模式 Prototype</h4>
<p>实现了一个原型接口，该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时，则采用这种模式</p>
<ol>
<li>意图:该模式的思想就是将一个对象作为原型，对其进行复制、克隆，产生一个和原对象类似的新对象</li>
</ol>
<ul>
<li>动机:在运行期建立和删除原型</li>
<li>适用场景</li>
<li>当一个系统应该独立于它的产品创建，构成和表示时</li>
<li>在运行时，指定需要实例化的类，例如动态载入</li>
<li>避免构建与产品的类层次结构相似的工厂类层次结构</li>
<li>当类的实例是仅有的一些不同状态组合之一的时候</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>
<p>细胞分裂</p>
</li>
<li>
<p>JAVA 中的 Object clone() 方法</p>
</li>
<li>
<p>优点:可以在运行时添加或删除产品，通过改变值指定新对象，通过改变结构指定新对象，减少子类的生成和使用，可以用类动态配置应用程序</p>
</li>
<li>
<p>缺点:1、当一个类引用不支持串行化的间接对象，或者引用含有循环结构的时候。 2、必须实现 Cloneable 接口。 3、逃避构造函数的约束</p>
</li>
<li>
<p>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/prototype_pattern_uml_diagram.jpg" alt="Prototype"></p>
</li>
</ul>
<h4 id="35singleton">3.5 单例模式 Singleton</h4>
<p>一个单一的类，该类负责创建自己的对象，同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式，可以直接访问，不需要实例化该类的对象。</p>
<ol>
<li>意图:保证一个类仅有一个实例，并提供一个访问它的全局访问点</li>
</ol>
<ul>
<li>动机:一个全局使用的类频繁地创建与销毁</li>
<li>适用场景：当您想控制实例数目，节省系统资源的时候</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ol>
<li>要求生产唯一序列号。</li>
<li>WEB 中的计数器，不用每次刷新都在数据库里加一次，用单例先缓存起来。</li>
<li>创建的一个对象需要消耗的资源过多，比如 I/O 与数据库的连接等</li>
</ol>
<ul>
<li>优点:对单个实例的受控制访问，比类操作更灵活，避免对资源的多重占用</li>
<li>缺点:没有接口，不能继承，与单一职责原则冲突，一个类应该只关心内部逻辑，而不关心外面怎么样来实例化</li>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/singleton_pattern_uml_diagram.jpg" alt="Singleton"></li>
</ul>
<hr>
<h4 id="4ahrefnamefoura">4.<a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#" name="four">结构型模式</a></h4>
<p>结构性模式控制了应用程序较大部分之间的关系</p>
<h5 id="41adapter">4.1 适配器模式 Adapter</h5>
<p>两个不兼容的接口之间的桥梁</p>
<ol>
<li>意图:适配器模式将某个类的接口转换成客户端期望的另一个接口表示，目的是消除由于接口不匹配所造成的类的兼容性问题，主要分为三类：类的适配器模式、对象的适配器模式、接口的适配器模式</li>
</ol>
<blockquote></blockquote>
<ul>
<li>
<p>类的适配器模式：当希望将一个类转换成满足另一个新接口的类时，可以使用类的适配器模式，创建一个新类，继承原有的类，实现新的接口即可。</p>
</li>
<li>
<p>对象的适配器模式：当希望将一个对象转换成满足另一个新接口的对象时，可以创建一个Wrapper类，持有原类的一个实例，在Wrapper类的方法中，调用实例的方法就行。</p>
</li>
<li>
<p>接口的适配器模式：当不希望实现一个接口中所有的方法时，可以创建一个抽象类Wrapper，实现所有方法，我们写别的类的时候，继承抽象类即可</p>
</li>
<li>
<p>动机:将一些&quot;现存的对象&quot;放到新的环境中，而新环境要求的接口是现对象不能满足的</p>
</li>
<li>
<p>适用场景:</p>
<ul>
<li>系统需要使用现有的类，而此类的接口不符合系统的需要</li>
</ul>
<ol start="2">
<li>想要建立一个可以重复使用的类，用于与一些彼此之间没有太大关联的一些类，包括一些可能在将来引进的类一起工作，这些源类不一定有一致的接口。</li>
<li>通过接口转换，将一个类插入另一个类系中</li>
</ol>
</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ol>
<li>美国电器 110V，中国 220V，就要有一个适配器将 110V 转化为 220V</li>
<li>JAVA JDK 1.1 提供了 Enumeration 接口，而在 1.2 中提供了 Iterator 接口，想要使用 1.2 的 JDK，则要将以前系统的 Enumeration 接口转化为 Iterator 接口，这时就需要适配器模式</li>
<li>在 LINUX 上运行 WINDOWS 程序</li>
<li>JAVA 中的 jdbc</li>
</ol>
<ul>
<li>优点:1.允许两个或多个不兼容的对象进行交互和通信，2.提高已有功能的重复使用性</li>
<li>缺点:1、过多地使用适配器，会让系统非常零乱，不易整体进行把握，2、由于 JAVA 至多继承一个类，所以至多只能适配一个适配者类，而且目标类必须是抽象类</li>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/adapter_pattern_uml_diagram.jpg" alt="Adapter"></li>
</ul>
<h5 id="42decorator">4.2 装饰模式 Decorator</h5>
<p>可以在不修改对象外观和功能的情况下添加或者删除对象功能，可以使用一种对客户端来说是透明的方法来修改对象的功能，也就是使用初始类的子类实例对初始对象进行授权</p>
<ol>
<li>意图:动态地给一个对象添加一些额外的职责。就增加功能来说，装饰器模式相比生成子类更为灵活</li>
</ol>
<ul>
<li>动机:一般为了扩展一个类经常使用继承方式实现，由于继承为类引入静态特征，并且随着扩展功能的增多，子类会很膨胀</li>
<li>适用场景</li>
<li>在不想增加很多子类的情况下扩展类</li>
<li>透明动态的为一个对象增加功能，而且还能动态撤销。（继承不能做到这一点，继承的功能是静态的，不能动态增删。）</li>
<li>无法通过静态子类化实现扩展时</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>不论一幅画有没有画框都可以挂在墙上，但是通常都是有画框的，并且实际上是画框被挂在墙上。在挂在墙上之前，画可以被蒙上玻璃，装到框子里；这时画、玻璃和画框形成了一个物体</li>
<li>优点:比静态继承具有更大的灵活性，避免了特征装载的类处于层次结构的过高级别，装饰类和被装饰类可以独立发展，不会相互耦合，装饰模式是继承的一个替代模式，装饰模式可以动态扩展一个实现类的功能</li>
<li>缺点:多层装饰比较复杂，产生过多相似的对象，不易排错</li>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/decorator_pattern_uml_diagram.jpg" alt="Adapter"></li>
</ul>
<h5 id="43proxy">4.3 代理模式 Proxy</h5>
<p>一个类代表另一个类的功能，创建具有现有对象的对象，以便向外界提供功能接口。如果已有的方法在使用的时候需要对原有的方法进行改进，此时有两种办法：<br>
第一种：修改原有的方法来适应，但这样违反了“对扩展开放，对修改关闭”的原则<br>
第二种：采用一个代理类调用原有的方法，且对产生的结果进行控制。这种方法就是代理模式。</p>
<ol>
<li>意图:为其他对象提供一种代理以控制对这个对象的访问</li>
</ol>
<ul>
<li>动机:在直接访问对象时带来的问题，可以在访问此对象时加上一个对此对象的访问层</li>
<li>适用场景</li>
<li>想在访问一个类时做一些控制</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>Windows 里面的快捷方式</li>
<li>买火车票不一定在火车站买，也可以去代售点</li>
<li>优点:功能划分的更加清晰，有助于后期维护，远程以隐藏对象位于不同的地址空间的事实，虚拟代理可以执行优化操作</li>
<li>缺点:1、由于在客户端和真实主题之间增加了代理对象，因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作，有些代理模式的实现非常复杂</li>
<li><strong>关联性</strong></li>
</ul>
<ol>
<li>和适配器模式的区别：适配器模式主要改变所考虑对象的接口，而代理模式不能改变所代理类的接口。</li>
<li>和装饰器模式的区别：装饰器模式为了增强功能，而代理模式是为了加以控制。</li>
</ol>
<ul>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/proxy_pattern_uml_diagram.jpg" alt="Adapter"></li>
</ul>
<h5 id="44facade">4.4 外观模式 Facade</h5>
<p>外观模式是为了解决类与类之间的依赖关系的，像spring一样，可以将类和类之间的关系配置到配置文件中，而外观模式就是将他们的关系放在一个Facade类中，降低了类类之间的耦合度，该模式中没有涉及到接口</p>
<ol>
<li>意图:为子系统中的一组接口提供一个一致的界面，外观模式定义了一个高层接口，这个接口使得这一子系统更加容易使用</li>
</ol>
<ul>
<li>动机:降低访问复杂系统的内部子系统时的复杂度，简化客户端与之的接口</li>
<li>适用场景</li>
<li>为复杂的模块或子系统提供简单外界访问的模块</li>
<li>想要对子系统进行分层</li>
<li>在客户端和抽象的实现类中存在许多依赖关系统</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>
<p>去医院看病，可能要去挂号、门诊、划价、取药，让患者或患者家属觉得很复杂，如果有提供接待人员，只让接待人员来处理，就很方便。</p>
</li>
<li>
<p>JAVA 的三层开发模式</p>
</li>
<li>
<p>优点:减少系统相互依赖，提高子系统与其客户端之间的弱耦合度，对客户端屏蔽了子系统组件</p>
</li>
<li>
<p>缺点:不符合开闭原则，如果要改东西很麻烦，继承重写都不合适</p>
</li>
<li>
<p>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/facade_pattern_uml_diagram.jpg" alt="Facade"></p>
</li>
</ul>
<h5 id="45bridge">4.5 桥接模式 Bridge</h5>
<p>桥接模式就是把事物和其具体实现分开，使他们可以各自独立的变化。桥接的用意是：将抽象化与实现化解耦，使得二者可以独立变化，像我们常用的JDBC桥DriverManager一样，JDBC进行连接数据库的时候，在各个数据库之间进行切换，基本不需要动太多的代码，甚至丝毫不用动，原因就是JDBC提供统一接口，每个数据库提供各自的实现，用一个叫做数据库驱动的程序来桥接就行了。</p>
<ol>
<li>意图:将抽象部分与实现部分分离，使它们都可以独立的变化</li>
</ol>
<ul>
<li>动机:在有多种可能会变化的情况下，用继承会造成类爆炸问题，扩展起来不灵活</li>
<li>适用场景</li>
<li>实现系统可能有多个角度分类，每一种角度都可能变化</li>
<li>一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性，避免在两个层次之间建立静态的继承联系，通过桥接模式可以使它们在抽象层建立一个关联关系</li>
<li>抽象的实现被改动应该对客户端没有影响</li>
<li>对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统，桥接模式尤为适用</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>墙上的开关，可以看到的开关是抽象的，不用管里面具体怎么实现的</li>
<li>优点:1、抽象和实现的分离。 2、提高扩展能力。 3、对客户端隐藏了实现的细节</li>
<li>缺点:桥接模式的引入会增加系统的理解与设计难度，由于聚合关联关系建立在抽象层，要求开发者针对抽象进行设计与编程</li>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/bridge_pattern_uml_diagram.jpg" alt="Facade"></li>
</ul>
<h5 id="46composite">4.6 组合模式 Composite</h5>
<p>又叫部分整体模式，是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象，用来表示部分以及整体层次</p>
<p>使用场景：将多个对象组合在一起进行操作，常用于表示树形结构中，例如二叉树，数等。</p>
<ol>
<li>意图:将对象组合成树形结构以表示&quot;部分-整体&quot;的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性</li>
</ol>
<ul>
<li>动机:它在树型结构的问题中，模糊了简单元素和复杂元素的概念，客户程序可以向处理简单元素一样来处理复杂元素，从而使得客户程序与复杂元素的内部结构解耦</li>
<li>适用场景</li>
<li>您想表示对象的部分-整体层次结构（树形结构）</li>
<li>您希望用户忽略组合对象与单个对象的不同，用户将统一地使用组合结构中的所有对象</li>
<li>结构可以具有任何级别的复杂性，而且是动态的</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>您算术表达式包括操作数、操作符和另一个操作数，其中，另一个操作符也可以是操作树、操作符和另一个操作数。</li>
<li>在 JAVA AWT 和 SWING 中，对于 Button 和 Checkbox 是树叶，Container 是树枝。</li>
<li>优点:1、高层模块调用简单，定义了由主要对象和复合对象组成的类层次结构。 2、节点自由增加，提供了结构的灵活性和可管理的接口</li>
<li>缺点:在使用组合模式时，其叶子和树枝的声明都是实现类，而不是接口，违反了依赖倒置原则</li>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/composite_pattern_uml_diagram.jpg" alt="Facade"></li>
</ul>
<h5 id="47flyweight">4.7 享元模式 Flyweight</h5>
<p>可以通过共享对象减少系统中低等级的、详细的对象数目，以减少内存占用和提高性能。实现对象的共享，即共享池，当系统中对象多的时候可以减少内存的开销，通常与工厂模式一起使用。FlyWeightFactory负责创建和管理享元单元，当一个客户端请求时，工厂需要检查当前对象池中是否有符合条件的对象，如果有，就返回已经存在的对象，如果没有，则创建一个新对象，FlyWeight是超类</p>
<ol>
<li>意图:运用共享技术有效地支持大量细粒度的对象</li>
</ol>
<ul>
<li>动机:在有大量对象时，有可能会造成内存溢出，把其中共同的部分抽象出来，如果有相同的业务请求，直接返回在内存中已有的对象，避免重新创建</li>
<li>适用场景</li>
<li>系统中有大量对象</li>
<li>这些对象消耗大量内存</li>
<li>这些对象的状态大部分可以外部化</li>
<li>这些对象可以按照内蕴状态分为很多组，当把外蕴对象从对象中剔除出来时，每一组对象都可以用一个对象来代替。</li>
<li>系统不依赖于这些对象身份，这些对象是不可分辨的</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>
<p>JAVA 中的 String，如果有则返回，如果没有则创建一个字符串保存在字符串缓存池里面</p>
</li>
<li>
<p>数据库的数据池</p>
</li>
<li>
<p>优点:大大减少对象的创建，降低系统的内存和存储设备，使效率提高</p>
</li>
<li>
<p>缺点:提高了系统的复杂度，需要分离出外部状态和内部状态，而且外部状态具有固有化的性质，不应该随着内部状态的变化而变化，否则会造成系统的混乱</p>
</li>
<li>
<p>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/flyweight_pattern_uml_diagram-1.jpg" alt="Facade"></p>
</li>
</ul>
<hr>
<h4 id="5ahrefnamefivea">5.<a href="http://blog.ycminglei.cn/she-ji-mo-shi-gof/#" name="five">行为型模式</a></h4>
<p>行为性模式可以影响一个系统的状态和行为流，通过优化状态和行为流转换和修改的方式，可以简化，优化并且提高应用程序的可维护性</p>
<h5 id="51">5.1 父类与子类</h5>
<h6 id="511strategy">5.1.1 策略模式 strategy</h6>
<p>需要设计一个接口，为一系列实现类提供统一的方法，多个实现类实现该接口，设计一个抽象类（可有可无，属于辅助类），提供辅助函数。策略模式的决定权在用户，系统本身提供不同算法的实现，新增或者删除算法，对各种算法做封装。因此，策略模式多用在算法决策系统中，外部用户只需要决定用哪个算法即可</p>
<ol>
<li>意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换,且算法的变化不会影响到使用算法的客户</li>
</ol>
<ul>
<li>动机:在有多种算法相似的情况下，使用 if...else 所带来的复杂和难以维护</li>
<li>适用场景</li>
<li>一个系统有许多许多类，而区分它们的只是他们直接的行为</li>
<li>需要算法的不同变体</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>旅行的出游方式，选择骑自行车、坐汽车，每一种旅行方式都是一个策略</li>
<li>JAVA AWT 中的 LayoutManager</li>
<li>优点:1、算法可以自由切换。 2、避免使用多重条件判断。 3、更容易扩展模型</li>
<li>缺点:1、策略类会增多。 2、所有策略类都需要对外暴露</li>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/strategy_pattern_uml_diagram.jpg" alt="Facade"></li>
</ul>
<h6 id="512templatemethod">5.1.2 模板方法模式 Template Method</h6>
<p>一个抽象类中，有一个主方法，再定义1...n个方法，可以是抽象的，也可以是实际的方法，定义一个类，继承该抽象类，重写抽象方法，通过调用抽象类，实现对子类的调用，</p>
<ol>
<li>意图:定义一个操作中的算法的骨架，而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤</li>
</ol>
<ul>
<li>动机:一些方法通用，却在每一个子类都重新写了这一方法</li>
<li>适用场景</li>
<li>想要一次实现算法的不变部分，而使用子类实现算法的可变行为</li>
<li>当子类间的通用行为需要分解，定位到通用类的时候，避免代码的重复的问题</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>spring 中对 Hibernate 的支持，将一些已经定好的方法封装起来，比如开启事务、获取 Session、关闭 Session 等，程序员不重复写那些已经规范好的代码，直接丢一个实体就可以保存</li>
<li>优点:1、封装不变部分，扩展可变部分。 2、提取公共代码，便于维护。 3、行为由父类控制，子类实现。</li>
<li>缺点:每一个不同的实现都需要一个子类来实现，导致类的个数增加，使得系统更加庞大</li>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/template_pattern_uml_diagram.jpg" alt="Template"></li>
</ul>
<h5 id="52">5.2 两个类之间</h5>
<h6 id="521observer">5.2.1 观察者模式Observer</h6>
<ol>
<li>意图:定义对象间的一种一对多的依赖关系，当一个对象的状态发生改变时，所有依赖于它的对象都得到通知并被自动更新</li>
</ol>
<ul>
<li>动机:一个对象状态改变给其他对象通知的问题，而且要考虑到易用和低耦合，保证高度的协作</li>
<li>适用场景</li>
<li>对一个对象的修改涉及对其它对象的悠，而且不知道有多少对象需要进行相应修改</li>
<li>对象应该能够在不用假设对象标识的前提下通知其它对象</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>拍卖的时候，拍卖师观察最高标价，然后通知给其他竞价者竞价</li>
<li>优点:抽象了主体与observer之间的耦合关系，支持广播方式的通信</li>
<li>缺点:</li>
</ul>
<ol>
<li>如果一个被观察者对象有很多的直接和间接的观察者的话，将所有的观察者都通知到会花费很多时间。</li>
<li>如果在观察者和观察目标之间有循环依赖的话，观察目标会触发它们之间进行循环调用，可能导致系统崩溃。</li>
<li>观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的，而仅仅只是知道观察目标发生了变化</li>
</ol>
<ul>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/observer_pattern_uml_diagram.jpg" alt="Observer"></li>
</ul>
<h6 id="522iterator">5.2.2 迭代器模式 Iterator</h6>
<ol>
<li>意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示</li>
</ol>
<ul>
<li>动机:不同的方式来遍历整个整合对象</li>
<li>适用场景</li>
<li>访问一个聚合对象的内容而无须暴露它的内部表示</li>
<li>需要为聚合对象提供多种遍历方式</li>
<li>为遍历不同的聚合结构提供一个统一的接口</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>JAVA 中的 iterator</li>
<li>优点:支持集合的不同遍历，简化了集合的接口</li>
<li>缺点:由于迭代器模式将存储数据和遍历数据的职责分离，增加新的聚合类需要对应增加新的迭代器类</li>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/iterator_pattern_uml_diagram.jpg" alt="Iterator"></li>
</ul>
<h6 id="523chainofresponsibility">5.2.3 责任链模式 Chain of Responsibility</h6>
<p>有多个对象，每个对象持有对下一个对象的引用，这样就会形成一条链，请求在这条链上传递，直到某一对象决定处理该请求。但是发出者并不清楚到底最终那个对象会处理该请求，所以，责任链模式可以实现，在隐瞒客户端的情况下，对系统进行动态的调整</p>
<ol>
<li>意图:避免请求发送者与接收者耦合在一起，让多个对象都有可能接收请求，将这些对象连接成一条链，并且沿着这条链传递请求，直到有对象处理它为止</li>
</ol>
<ul>
<li>动机:职责链上的处理者负责处理请求，客户只需要将请求发送到职责链上即可，无须关心请求的处理细节和请求的传递，所以职责链将请求的发送者和请求的处理者解耦了</li>
<li>适用场景</li>
<li>有多个对象可以处理同一个请求，具体哪个对象处理该请求由运行时刻自动确定</li>
<li>在不明确指定接收者的情况下，向多个对象中的一个提交一个请求</li>
<li>可动态指定能够处理请求的对象集</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>JS 中的事件冒泡</li>
<li>jsp servlet 的 Filter</li>
<li>优点:降低耦合度,增加向对象指定责任的灵活性</li>
<li>缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响，而且在进行代码调试时不太方便，可能会造成循环调用。 3、可能不容易观察运行时的特征，有碍于除错</li>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/chain_pattern_uml_diagram.jpg" alt="Template"></li>
</ul>
<h6 id="524command">5.2.4 命令模式 Command</h6>
<p>命令模式很好理解，举个例子，司令员下令让士兵去干件事情，从整个事情的角度来考虑，司令员的作用是，发出口令，口令经过传递，传到了士兵耳朵里，士兵去执行。这个过程好在，三者相互解耦，任何一方都不用去依赖其他人，只需要做好自己的事儿就行，司令员要的是结果，不会去关注到底士兵是怎么实现的。</p>
<ol>
<li>意图:将一个请求封装成一个对象，从而使您可以用不同的请求对客户进行参数化</li>
</ol>
<ul>
<li>动机:在软件系统中，行为请求者与行为实现者通常是一种紧耦合的关系，但某些场合，比如需要对行为进行记录、撤销或重做、事务等处理时，这种无法抵御变化的紧耦合的设计就不太合适</li>
<li>适用场景</li>
<li>想要通过要执行的动作来参数化对象</li>
<li>要在不同的时间指定，排序以及执行请求</li>
<li>必须支持undo,日志记录或事务</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>struts 1 中的 action 核心控制器 ActionServlet 只有一个，相当于 Invoker，而模型层的类会随着不同的应用有不同的模型类，相当于具体的 Command</li>
<li>优点:1.将调用操作的对象与知道如何完成该操作的对象相分离，2.容易添加新的命令</li>
<li>缺点:使用命令模式可能会导致某些系统有过多的具体命令类</li>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/command_pattern_uml_diagram.jpg" alt="Command"></li>
</ul>
<h5 id="53">5.3 类的状态</h5>
<h6 id="531memento">5.3.1 备忘录模式 Memento</h6>
<p>主要目的是保存一个对象的某个状态，以便在适当的时候恢复对象</p>
<ol>
<li>意图:在不破坏封装性的前提下，捕获一个对象的内部状态，并在该对象之外保存这个状态</li>
</ol>
<ul>
<li>动机:将对象恢复到原先保存的状态</li>
<li>适用场景</li>
<li>必须保存对象状态的快照，方便以后恢复状态</li>
<li>使用直接接口来获得状态可能会公开对象的实现细节，从而破坏对象的封装性</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>Windows 里的 ctri + z</li>
<li>数据库的事务管理</li>
<li>优点:保持封装的完整，简化了返回到初始状态所需的操作</li>
<li>缺点:消耗资源</li>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/memento_pattern_uml_diagram.jpg" alt="Memento"></li>
</ul>
<h6 id="532state">5.3.2 状态模式 State</h6>
<ol>
<li>意图:允许对象在内部状态发生改变时改变它的行为，对象看起来好像修改了它的类</li>
</ol>
<ul>
<li>动机:对象的行为依赖于它的状态（属性），并且可以根据它的状态改变而改变它的相关行为</li>
<li>适用场景</li>
<li>行为随状态改变而改变的场景</li>
<li>条件、分支语句的代替者</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>1111</li>
<li>优点:定位指定状态的行为，并且针对不同状态来划分行为，使状态转换显示进行</li>
<li>缺点:状态模式的结构与实现都较为复杂，状态模式对&quot;开闭原则&quot;的支持并不太好，对于可以切换状态的状态模式，增加新的状态类需要修改那些负责状态转换的源代码，否则无法切换到新增状态，而且修改某个状态类的行为也需修改对应类的源代码</li>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/state_pattern_uml_diagram.jpg" alt="State"></li>
</ul>
<h5 id="54">5.4 通过中间类</h5>
<h6 id="541visitor">5.4.1 访问者模式 Visitor</h6>
<p>访问者模式把数据结构和作用于结构上的操作解耦合，使得操作集合可相对自由地演化。访问者模式适用于数据结构相对稳定算法又易变化的系统。因为访问者模式使得算法操作增加变得容易。若系统数据结构对象易于变化，经常有新的数据对象增加进来，则不适合使用访问者模式。访问者模式的优点是增加操作很容易，因为增加操作意味着增加新的访问者。访问者模式将有关行为集中到一个访问者对象中，其改变不影响系统数据结构。<br>
简单来说，访问者模式就是一种分离对象数据结构与行为的方法，通过这种分离，可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。简单关系图：</p>
<ol>
<li>意图:主要将数据结构与数据操作分离</li>
</ol>
<ul>
<li>动机:稳定的数据结构和易变的操作耦合问题</li>
<li>适用场景</li>
<li>对象结构包含许多不同接口的对象类，并且想要对之些依赖于具体类的对象进行操作</li>
<li>定义对象的结构的类很少被修改，但想要在些结构上定义新的操作</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>1111</li>
<li>优点:容易添加新操作，集口相关操作并且排除不相关的操作</li>
<li>缺点:增加新的数据结构很困难。</li>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/visitor_pattern_uml_diagram.jpg" alt="Visitor"></li>
</ul>
<h6 id="542mediator">5.4.2 中介者模式 Mediator</h6>
<p>中介者模式也是用来降低类类之间的耦合的，因为如果类类之间有依赖关系的话，不利于功能的拓展和维护，因为只要修改一个对象，其它关联的对象都得进行修改。如果使用中介者模式，只需关心和Mediator类的关系，具体类类之间的关系及调度交给Mediator就行，这有点像spring容器的作用。</p>
<ol>
<li>意图:用一个中介对象来封装一系列的对象交互，中介者使各对象不需要显式地相互引用，从而使其耦合松散，而且可以独立地改变它们之间的交互</li>
</ol>
<ul>
<li>动机:对象与对象之间存在大量的关联关系，这样势必会导致系统的结构变得很复杂，同时若一个对象发生改变，我们也需要跟踪与之相关联的对象，同时做出相应的处理</li>
<li>适用场景</li>
<li>系统中对象之间存在比较复杂的引用关系，导致它们之间的依赖关系结构混乱而且难以复用该对象</li>
<li>想通过一个中间类来封装多个类中的行为，而又不想生成太多的子类</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>MVC 框架，其中C（控制器）就是 M（模型）和 V（视图）的中介者</li>
<li>优点:去除对象间的影响，简化了对象间的协议，集口化了控制，由于不需要直接互传消息，单个组件简单，通用，容易处理</li>
<li>缺点:中介者会庞大，变得复杂难以维护</li>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/mediator_pattern_uml_diagram.jpg" alt="Mediator"></li>
</ul>
<h6 id="543interpreter">5.4.3 解释器模式 Interpreter</h6>
<ol>
<li>意图:给定一个语言，定义它的文法表示，并定义一个解释器，这个解释器使用该标识来解释语言中的句子</li>
</ol>
<ul>
<li>动机:对于一些固定文法构建一个解释句子的解释器</li>
<li>适用场景</li>
<li>语言的语法比较简单</li>
<li>效率并不是最主要的问题</li>
</ul>
<blockquote>
<p>典型应用：</p>
</blockquote>
<ul>
<li>编译器、运算表达式计算</li>
<li>优点:1、可扩展性比较好，灵活。 2、增加了新的解释表达式的方式。 3、易于实现简单文法</li>
<li>缺点:1、可利用场景比较少。 2、对于复杂的文法比较难维护。 3、解释器模式会引起类膨胀。 4、解释器模式采用递归调用方法</li>
<li>类图:<br>
<img src="http://www.runoob.com/wp-content/uploads/2014/08/interpreter_pattern_uml_diagram.jpg" alt="Interpreter"></li>
</ul>
<hr>
</div>]]></content:encoded></item><item><title><![CDATA[xml]]></title><description><![CDATA[<div class="kg-card-markdown"><blockquote>
<p>用于数据管理，显示和组织等方面</p>
</blockquote>
<p>目录</p>
<ol>
<li>xpath</li>
<li>dom</li>
<li>schema</li>
<li>dtd</li>
</ol>
<hr>
<h3 id="dtd">dtd</h3>
<h4 id>内部声明</h4>
<p>DTD 被包含在您的 XML 源文件中，它应当通过下面的语法包装在一个 DOCTYPE 声明中.文档实例如下：</p>
<pre><code>&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;!DOCTYPE note [
    &lt;!ELEMENT note (to,from,heading,body)&gt;
    &lt;!ELEMENT to (#PCDATA)&gt;
    &lt;!ELEMENT from (#PCDATA)&gt;
    &lt;!ELEMENT heading (#PCDATA)</code></pre></div>]]></description><link>http://blog.ycminglei.cn/xml/</link><guid isPermaLink="false">59e18f22732a5961f290e9be</guid><dc:creator><![CDATA[ray]]></dc:creator><pubDate>Sat, 14 Oct 2017 04:16:20 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><blockquote>
<p>用于数据管理，显示和组织等方面</p>
</blockquote>
<p>目录</p>
<ol>
<li>xpath</li>
<li>dom</li>
<li>schema</li>
<li>dtd</li>
</ol>
<hr>
<h3 id="dtd">dtd</h3>
<h4 id>内部声明</h4>
<p>DTD 被包含在您的 XML 源文件中，它应当通过下面的语法包装在一个 DOCTYPE 声明中.文档实例如下：</p>
<pre><code>&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;!DOCTYPE note [
    &lt;!ELEMENT note (to,from,heading,body)&gt;
    &lt;!ELEMENT to (#PCDATA)&gt;
    &lt;!ELEMENT from (#PCDATA)&gt;
    &lt;!ELEMENT heading (#PCDATA)&gt;
    &lt;!ELEMENT body (#PCDATA)&gt;
]&gt;
&lt;note&gt;
    &lt;to&gt;George&lt;/to&gt;
    &lt;from&gt;John&lt;/from&gt;
    &lt;heading&gt;Reminder&lt;/heading&gt;
    &lt;body&gt;Don't forget the meeting!&lt;/body&gt;
&lt;/note&gt;
</code></pre>
<p>以上 DTD 解释如下：</p>
<blockquote>
<ul>
<li>!DOCTYPE note (第二行)定义此文档是 note 类型的文档。</li>
</ul>
</blockquote>
<ul>
<li>!ELEMENT note (第三行)定义 note 元素有四个元素：&quot;to、from、heading,、body&quot;</li>
<li>!ELEMENT to (第四行)定义 to 元素为 &quot;#PCDATA&quot; 类型</li>
<li>!ELEMENT from (第五行)定义 frome 元素为 &quot;#PCDATA&quot; 类型</li>
<li>!ELEMENT heading (第六行)定义 heading 元素为 &quot;#PCDATA&quot; 类型</li>
<li>!ELEMENT body (第七行)定义 body 元素为 &quot;#PCDATA&quot; 类型</li>
</ul>
<h4 id>外部声明</h4>
<p>假如 DTD 位于 XML 源文件的外部，那么它应通过下面的语法被封装在一个 DOCTYPE 定义中：<code>&lt;!DOCTYPE 根元素 SYSTEM &quot;文件名&quot;&gt;</code><br>
这个 XML 文档和上面的 XML 文档相同，但是拥有一个外部的 DTD</p>
<h4 id>声明属性</h4>
<ul>
<li>ATTLIST</li>
<li>语法：<code>&lt;!ATTLIST 元素名称 属性名称 属性类型 默认值&gt;</code></li>
<li>DTD：<code>&lt;!ATTLIST payment type CDATA &quot;check&quot;&gt;</code></li>
<li>XML：<code>&lt;payment type=&quot;check&quot; /&gt;</code></li>
</ul>
<blockquote>
<p><strong>属性类型的选项</strong></p>
</blockquote>
<ul>
<li>CDATA 值为字符数据 (character data)</li>
<li>(en1|en2|..) 此值是枚举列表中的一个值</li>
<li>ID 值为唯一的 id</li>
<li>IDREF 值为另外一个元素的 id</li>
<li>IDREFS 值为其他 id 的列表</li>
<li>NMTOKEN 值为合法的 XML 名称</li>
<li>NMTOKENS 值为合法的 XML 名称的列表</li>
<li>ENTITY 值是一个实体</li>
<li>ENTITIES 值是一个实体列表</li>
<li>NOTATION 此值是符号的名称</li>
<li>xml: 值是一个预定义的 XML 值</li>
</ul>
<h4 id="xsddtd">xsd 与 dtd</h4>
<p>xsd的前景更好一些，dtd拥有的功能xsd全都有，xsd又比dtd丰富了一些功能。<br>
xsd跟dtd比较有如下优点：</p>
<ol>
<li>xsd基于xml编辑，遵守xml语法</li>
<li>xsd大大扩充了数据类型</li>
<li>xsd支持命名空间</li>
<li>xsd支持扩展</li>
<li>xsd支持元素的继承</li>
<li>xsd支持元素组、属性组</li>
</ol>
<hr>
<h3 id="schema">schema</h3>
<hr>
<h3 id="xpath">xpath</h3>
<p>XPath 使用路径表达式来选取 XML 文档中的节点或节点集。节点是通过沿着路径 (path) 或者步 (steps) 来选取的。</p>
<ol>
<li>路径表过式</li>
</ol>
<ul>
<li>
<p>nodename #选取此节点的所有子节点</p>
</li>
<li>
<p>/ #从根节点选取</p>
</li>
<li>
<p>// #从匹配选择的当前节点选择文档中的节点，而不考虑它们的位置</p>
</li>
<li>
<p>. #选取当前节点</p>
</li>
<li>
<p>.. #选取当前节点的父节点</p>
</li>
<li>
<p>@ #选取属性</p>
</li>
<li>
<p>谓语用来查找某个特定的节点或者包含某个指定的值的节点,谓语被嵌在方括号中.</p>
</li>
<li>
<p>/bookstore/book[1]<br>
选取属于 bookstore 子元素的第一个 book 元素。</p>
</li>
<li>
<p>/bookstore/book[last()]<br>
选取属于 bookstore 子元素的最后一个 book 元素。</p>
</li>
<li>
<p>/bookstore/book[last()-1]<br>
选取属于 bookstore 子元素的倒数第二个 book 元素。</p>
</li>
<li>
<p>/bookstore/book[position()&lt;3]<br>
选取最前面的两个属于 bookstore 元素的子元素的 book 元素。</p>
</li>
<li>
<p>//title[@lang]<br>
选取所有拥有名为 lang 的属性的 title 元素。</p>
</li>
<li>
<p>//title[@lang='eng']<br>
选取所有 title 元素，且这些元素拥有值为 eng 的 lang 属性。</p>
</li>
<li>
<p>/bookstore/book[price&gt;35.00]<br>
选取 bookstore 元素的所有 book 元素，且其中的 price 元素的值须大于 35.00。</p>
</li>
<li>
<p>/bookstore/book[price&gt;35.00]/title<br>
选取 bookstore 元素中的 book 元素的所有 title 元素，且其中的 price 元素的值须大于 35.00</p>
</li>
<li>
<p>选取未知节点,可用通配符来选取未知的 XML 元素</p>
</li>
<li>
<p>* #匹配任何元素节点。</p>
</li>
<li>
<p>@* #匹配任何属性节点。</p>
</li>
<li>
<p>node() 匹配任何类型的节点。</p>
</li>
<li>
<p>选取若干路径,通过在路径表达式中使用“|”运算符，您可以选取若干个路径</p>
</li>
<li>
<p>//title | //price #选取文档中的所有 title 和 price 元素。</p>
</li>
<li>
<p>/bookstore/book/title | //price #选取属于 bookstore 元素的 book 元素的所有 title 元素，以及文档中所有的 price 元素。</p>
</li>
</ul>
</div>]]></content:encoded></item><item><title><![CDATA[RUP(Rational unified Process)]]></title><description><![CDATA[<div class="kg-card-markdown"><h3 id="1">1 概念</h3>
<ol>
<li>RUP<br>
统一软件过程一种用例驱动的,以架构为中心的,采用迭代增量方式开发的软件工程过程。它汲取了面向对象软件工程领域多年来的优秀研究成果,应用统一建模语言(UML)进行可视化建模,为面向对象的软件系统的开发提供了方法论的指导。</li>
<li>Unified Modeling Language<br>
用图形方式描述一个系统的静态结构和动态行为的一种可视化的面向对象建模语言,从不同的角度为系统建模，形成了整个系统的不同视图,UML作为一种建模语言，要和具体的软件过程相结合。这就实现了UML与RUP相结合</li>
<li>软件过程<br>
UML是一种可应用于软件开发的非常优秀的建模语言，但是UML本身并没有告诉人们怎样使用它，为了有效地使用UML，需要有一种方法应用于它，这就是软件过程。软件过程是为了获得客户所需要的软件，所进行的一系列任务及各个任务的工作步骤。常见的软件过程模型有瀑布模型、原型模型、增量模型、喷泉模型、RUP（统一软件过程）、敏捷过程等。</li>
<li>RUP特征<br>
RUP的基本特征：1.迭代式增量开发，2.用例驱动，3.以软件体系结构为中</li>
</ol>
<h3 id="2rup">2 RUP生命周期</h3>
<ol>
<li>初启：发起人确定项目的主要目标和范围，并进行初步的可行性分析和经济效益分析，为了达到该目的必须识别所有与系统交互的外部实体在较高层次上定义交互的特性它包括识别所有用例和描述一些重要的用例。包括验收规范，</li></ol></div>]]></description><link>http://blog.ycminglei.cn/rup-rational-unified-process/</link><guid isPermaLink="false">59e18f33732a5961f290e9bf</guid><category><![CDATA[软件设计]]></category><dc:creator><![CDATA[ray]]></dc:creator><pubDate>Sat, 14 Oct 2017 04:16:12 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><h3 id="1">1 概念</h3>
<ol>
<li>RUP<br>
统一软件过程一种用例驱动的,以架构为中心的,采用迭代增量方式开发的软件工程过程。它汲取了面向对象软件工程领域多年来的优秀研究成果,应用统一建模语言(UML)进行可视化建模,为面向对象的软件系统的开发提供了方法论的指导。</li>
<li>Unified Modeling Language<br>
用图形方式描述一个系统的静态结构和动态行为的一种可视化的面向对象建模语言,从不同的角度为系统建模，形成了整个系统的不同视图,UML作为一种建模语言，要和具体的软件过程相结合。这就实现了UML与RUP相结合</li>
<li>软件过程<br>
UML是一种可应用于软件开发的非常优秀的建模语言，但是UML本身并没有告诉人们怎样使用它，为了有效地使用UML，需要有一种方法应用于它，这就是软件过程。软件过程是为了获得客户所需要的软件，所进行的一系列任务及各个任务的工作步骤。常见的软件过程模型有瀑布模型、原型模型、增量模型、喷泉模型、RUP（统一软件过程）、敏捷过程等。</li>
<li>RUP特征<br>
RUP的基本特征：1.迭代式增量开发，2.用例驱动，3.以软件体系结构为中</li>
</ol>
<h3 id="2rup">2 RUP生命周期</h3>
<ol>
<li>初启：发起人确定项目的主要目标和范围，并进行初步的可行性分析和经济效益分析，为了达到该目的必须识别所有与系统交互的外部实体在较高层次上定义交互的特性它包括识别所有用例和描述一些重要的用例。包括验收规范，风险评估，所需资源估计，体现主要里程碑日期的阶段计划。</li>
</ol>
<ul>
<li>
<p>本阶段的主要目标如下</p>
<ol>
<li>明确软件系统的范围和边界条件包括从功能角度的前景分析产品验收标准和哪些做,与哪些不做的相关决定</li>
<li>明确区分系统的关键user case和主要的功能场景.</li>
<li>展现或者演示至少一种符合主要场景要求的候选软件体系结构</li>
<li>对整个项目做最初的项目成本和日程估计更详细的估计将在随后的细化阶段中做出</li>
<li>估计出潜在的风险主要指各种不确定因素造成的潜在风险</li>
<li>准备好项目的支持环境</li>
</ol>
</li>
<li>
<p>初始阶段的产出是</p>
<ol>
<li>蓝图文档核心项目需求关键特色主要约束的总体蓝图</li>
<li>原始用例模型完成 10%-20%</li>
<li>原始项目术语表可能部分表达为业务模型</li>
<li>原始商业案例包括业务的上下文验收规范年度映射市场认可等等成本预计</li>
<li>原始的风险评估</li>
<li>一个或多个原型</li>
</ol>
</li>
<li>
<p>初始阶段的评审标准</p>
<ol>
<li>风险承担者就范围定义成本 ,日程估计达成共识</li>
<li>以客观的主要用例证实对需求的理解</li>
<li>成本,日程优先级风险和开发过程的可信度</li>
<li>被开发体系结构原型的深度和广度</li>
<li>实际开支与计划开支的比较</li>
</ol>
</li>
</ul>
<ol start="2">
<li>细化：细化阶段的目标是分析问题领域，建立健全的体系结构基础，编制项目计划，淘汰项目中最高风险的元素，标志点项目正式确立，完成以下工作</li>
</ol>
<ul>
<li>初步的需求分析,利用<strong>用例图</strong>表示参与者与用命例以及用例与用例之间的关系。采用<strong>类图</strong>表示目标软件系统所基于的应用领域中的概念和概念之间的关系。构成的领域模型一方面可以帮助软件项目组理解业务背景，与业务专家进行有效沟通，另一方面，软件结构的主要基础。如果领域中含有明显的流程处理成分，可以考虑利用uml的<strong>活动图</strong>来刻画领域中的工作流，并标识业务流程中的并发，同步等特征</li>
<li>初步的高层设计。利用<strong>包图</strong>刻画刻画这些包及其间的关系，形成初步的依据包来划分的用例，用例图，类，类图。</li>
<li>部分的详细设计。对于系统中某些重要或者风险比较高的用命，可以采用<strong>交互图</strong>进一步探讨其内部的实现过程。</li>
<li>部分的原形构造，针对复杂的用例构造可实际运行的原型，让用户帮助软件项目组确认用户需求是有效方法。</li>
</ul>
<ol start="3">
<li>构建：<br>
对用例进行迭代，迭代过程由针对用例的分析，设计，编码，测试和集成5个子阶段构成。<br>
在实际开始构造软件系统之前，有必要预先制定迭代计划，计划制定遵循两项原则：1.用户认为业务价值较大的用例应优先安排，2.开发风险较高的用例优先安排。</li>
</ol>
<ul>
<li>在构造过程中，需要使用UML的<strong>交互图</strong>来设计用例的实现方法，为了与设计得出的交互图协调一致，需要修改或精化在细化阶段绘制的作为领域模型的<strong>类图</strong>。</li>
<li>在迭代过程中，对细化阶段给出的<strong>包图</strong>进行修改或精化，以便包图切实反映目标软件最顶层的结构划分状况</li>
</ul>
<ol start="4">
<li>部署：<br>
将构造阶段获得的软件系统在用户实际工作环境中试运行。</li>
</ol>
<blockquote>
<p>核心工作流：6个核心过程工作流（Core Process Workflows）和3个核心支持工作流（Core Supporting Workflows）</p>
</blockquote>
<ol>
<li>业务建模工作流：通过对目标组织的商业活动的分析，建立业务模型，表达目标的业务过程，用UML活动图来描绘涉及到的角色和活动的序列、</li>
</ol>
<ul>
<li>需求工作流：通过对目标组织业务分析，确定客户的需求，描述系统应具备的功能需求和性能需求。使用用例图、活动图等。</li>
<li>分析和设计工作流：对问题域进行分析，找出其中的类及类之间的关系。产生软件系统的分析模型。分为架构设计和详细设计。</li>
<li>实现工作流：任务是把设计模型转化成相应的代码。使用类图、状态图、相关动态图描述类的实现过程。</li>
<li>部署工作流：目的是产生可交付的软件版本，并将其发布到客户的工作环境中。主要使用部署图和构件图来描述系统的硬件设备的分布及连接情况、软件构件在硬件上的安装情况。</li>
<li>测试工作流：目的是尽早发现系统中的错误与缺陷、识别并跟踪处理缺陷、验证需求是否满足</li>
</ul>
<h3 id="3uml">3. 基于UML的需求分析</h3>
<p>在初步的业务需求描述已经形成的前提下，基于UML的需求分析过程分为两个步骤</p>
<ol>
<li>利用用例及用例图表示需求。从业务需求描述出发获取执行者和场景；对场景进行汇总，分类，抽象，形成<strong>用例</strong>；确定执行者与用例，用例与用例图之间的关系生成用例图</li>
<li>利用包图和类图表示目标软件系统的总体框架结构。根据领域知识，业务需求描述和经验设计目标软件系统的<strong>顶层结构</strong>；从业务需求中提取关键概念，形成领域<strong>概念模型</strong>；从概念模型和用例出发，研究系统中主要的类与类之间的关系，生成<strong>类图</strong>。</li>
</ol>
<p>具体实现</p>
<ul>
<li>生成用例：<br>
从外部视角看，用例是执行者与目标软件系统之间的一次典型交互作用；从内部视角看，用例代表系统执行的一系列动作，动作执行的结果能够被外部的执行者所察觉。完整的用例描述：用例名称，参与执行者，前置条件，一个主事流程，零到多个辅事件流和后置条件。</li>
</ul>
<pre><code>用例名称：传感器监测
  参与持行者：各类传感器，警报器，报警电话和显示器
  前置条件：系统已开机
  主事件流：
    1传感器向目标软件系统上报其监测数据，系统差别监测数据是否正常
    2如果不正常，启动警报器，拨打报警电话
    3报警电话接通后，软件系统播出语音，报告异常事件发生的时间 ，地点和事件的性质
    4系统在控制面板的显示器上显示报警时间及当前状态
  辅事件流：
    1如果报警电话无人接听，则重复拨打延迟反复拨号，直至电话接通，再转入主事件流
    2如果重拨次达到系统设置最大值，电话仍无人接听，则跳过主事件流的步骤3，转入步骤4
  后置条件：如果已发现异常的监测数据，系统处于报警状态，否则，系统处于监测状态。
</code></pre>
<ul>
<li>建立顶层结构:为后续的分析和设计活动建立一种结构和分划，便于开发人员在不同的开发阶段，以及同一开发阶段的不同开发人员，能够聚集于系统的不同部分。</li>
<li>UML包图，包的划分是实现“分而治之”的重要技术手段</li>
<li>顶层架构设计,确定架构模式，如流程处理模式，客户/服务器模式</li>
</ul>
<blockquote>
<p>确立顶层架构的过程中综合考虑以下因素:架构中包的数量,架构中包之间的耦合度，系统的稳定性，系统的必然性，物理网络拓扑，软件元素的安全、保密级别，技术专长</p>
</blockquote>
<h3 id="4uml">4 其于UML的软件设计</h3>
<h4 id="41">4.1 设计用例实现方案</h4>
<ol>
<li>提取边界类，实体类和控制类；边界类用于描述目标软件系统与外部环境之间的交互，并负责界面控制，外部接口，环境隔离</li>
<li>构造交互图</li>
<li>根据交互图精化类图</li>
</ol>
<h4 id="42">4.2 设计技术支撑方案</h4>
<p>如持久存储服务，安全控制服务，分布式事务管理服务，并发与同步控制服务和可靠消息服务</p>
<h4 id="43">4.3 设计用户界面</h4>
<p>1.熟悉用户并对用户分类<br>
2.按用户类别分析用户的工作流程与习惯<br>
3.设计命令系统并进行优化，优化命令系统时首先考虑命令的顺序，常用命令居先，命令的顺序与用户工作习惯保持一致，其次，根据外部服务之间的聚合关系组织相应的命令；然后充分考虑人类记忆的局限性，最好组织为一棵两层的多叉树；最后，减少用户完成一个操作所需的动作，并为熟练用户提供操作的快捷方式。</p>
<h4 id="44">4.4 精细设计模型</h4>
<p>改进的活动分为精化和合并两种，根据需求和架构原则来划分不同的粗粒度组件，粗粒度组件来源于分析活动中的业务实体，把具有很强相关性业务实体组合起来，形成一个集合。如果可能，在粗粒度组件之间定义单向的关联，有效的减少组件之间的耦合。</p>
<h3 id="5">5 架构文档化</h3>
<p>软件架构用来处理软件高层次结构的设计和实施，软件架构={元素，形式，关系/约束}，软件架构涉及到抽象，分解和组合，风格和美学。可以用四类视图表示</p>
<ul>
<li>逻辑视图，设计的对象模型</li>
<li>过程视图，捕捉设计的并发和同步特征</li>
<li>物理视图，描述了软件到硬件的映射，反映了分布式特性</li>
<li>开发视图，描述了在开发环境中软件的静态组织结构</li>
</ul>
<p>结构类别</p>
<ul>
<li>逻辑结构<br>
主要支持功能性需求，系统分解为一系统的关键抽象，大多数来自于问题域，表现为对象或对象类的形式。类图用来显示一个类的集合和它们的逻辑关系，状态图可以辅助定义对象的内部形为。公共机制和服务可以在类功能中定义。对于数据驱动程序高的应用程序，可以使用其他形式的逻辑视图，如E-R图来代替面向对象的方法 。</li>
<li>进程架构<br>
考虑一些非功能性的需求，解决并发性，分布性，系统完整性，容错性的问题，以及逻辑视图的主要抽象如何与进程结构相配合在一起。</li>
<li>开发架构<br>
关注软件开发环境下实际模块的组织。</li>
</ul>
</div>]]></content:encoded></item></channel></rss>