<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
>
<channel>
<title><![CDATA[海博的前后端日常笔记]]></title> 
<atom:link href="http://www.haibohc.top/rss.php" rel="self" type="application/rss+xml" />
<description><![CDATA[知识点随想随记]]></description>
<link>http://www.haibohc.top/</link>
<language>zh-cn</language>

<item>
    <title>蓝宝石R7 260X风扇噪音问题终极解决方案</title>
    <link>http://www.haibohc.top/?post=14</link>
    <description><![CDATA[<h1>缓解蓝宝石 R7 260X 风扇吵、风扇突然启动、0rpm 无法修改、显卡 BIOS 写死的问题</h1>
<p>作为经典亮机卡，R7 260X 在 90 度以下风扇不想转，一转起来就是 3000+ rpm，非常吵。需要配下风扇转速到 2500~3000 这样。当然，直接这样调还不行，风扇大概率还是不转，需要 <strong>FanControl</strong> 再调一下。</p>
<p><img src="http://www.haibohc.top/content/uploadfile/202607/cbb81782986868.png" alt="" /></p>
<p>FanControl 识别出 GPU 风扇后，点击风扇右侧的省略号，有一个 <strong>强制应用</strong> 选项，勾上。<br />
先拉到 <strong>80</strong>，触发它的风扇狂转，然后拉低到 <strong>60</strong>，此时转速卡在 2800 左右。因为上面 AMD 设置了最小转速，FanControl 拉上来 2800 之后，转速就掉不回 0rpm，就能一直保持着转速，这样就不会让主机太烫。</p>
<p>然后再不断往下试探，调整 AMD 的 rpm 和 FanControl 的 rpm 即可，尽可能降低转速 rpm，减少噪音。</p>
<p><img src="http://www.haibohc.top/content/uploadfile/202607/cf391782986924.png" alt="" /></p>]]></description>
    <pubDate>Thu, 02 Jul 2026 18:03:50 +0800</pubDate>
    <dc:creator>haibo</dc:creator>
    <guid>http://www.haibohc.top/?post=14</guid>
</item>
<item>
    <title>若依部署必须用到的nginx配置</title>
    <link>http://www.haibohc.top/?post=13</link>
    <description><![CDATA[<p>以下是润色后的内容，使用 Markdown 进行了精致排版，仅补充了必要的细节和优化了可读性：</p>
<p>若依部署必须用到的 Nginx 配置：</p>
<pre><code class="language-nginx">location / {
    root   /www/wwwroot/dnr/dist;
    try_files $uri $uri/ /index.html;
    index  index.html index.htm;
}

location /prod-api/ {
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header REMOTE-HOST $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://localhost:8081/;
}</code></pre>
<p><strong>配置说明：</strong></p>
<ul>
<li>第一个 <code>location /</code> 块用于前端页面：指定了静态文件的根目录为 <code>/www/wwwroot/dnr/dist</code>，并通过 <code>try_files</code> 指令确保单页面应用（SPA）的路由能正确返回 <code>index.html</code>。</li>
<li>第二个 <code>location /prod-api/</code> 块用于后端 API 代理：将 <code>/prod-api/</code> 路径下的请求转发到本地后端服务 <code>http://localhost:8081/</code>，同时设置了必要的请求头（如 <code>Host</code>、<code>X-Real-IP</code>、<code>X-Forwarded-For</code>）以保证代理过程中的信息传递。</li>
</ul>]]></description>
    <pubDate>Fri, 08 May 2026 10:57:22 +0800</pubDate>
    <dc:creator>haibo</dc:creator>
    <guid>http://www.haibohc.top/?post=13</guid>
</item>
<item>
    <title>MyBatis与MyBatisPlus核心区别详解</title>
    <link>http://www.haibohc.top/?post=12</link>
    <description><![CDATA[<h1>MyBatis 与 MyBatis-Plus 的详细区别</h1>
<p>MyBatis 和 MyBatis-Plus 都是 Java 生态中广泛使用的持久层框架，旨在简化数据库操作。MyBatis-Plus 是在 MyBatis 基础上构建的增强工具，两者在功能、开发效率和使用体验上存在显著差异。以下从多个维度进行详细对比。</p>
<hr />
<h2>1. 功能定位与扩展性</h2>
<ul>
<li>
<p><strong>MyBatis</strong></p>
<ul>
<li><strong>核心定位</strong>：一个半自动化的 ORM 框架，专注于 SQL 与 Java 对象的映射。</li>
<li><strong>功能特点</strong>：提供了强大而灵活的方式操作数据库，支持编写自定义 SQL、调用存储过程以及实现复杂的高级结果集映射。它赋予开发者对 SQL 的完全控制权，非常适合复杂、定制化的查询场景。</li>
</ul>
</li>
<li>
<p><strong>MyBatis-Plus</strong></p>
<ul>
<li><strong>核心定位</strong>：MyBatis 的增强工具包，旨在简化开发、提高效率。</li>
<li><strong>功能特点</strong>：在保留 MyBatis 所有特性的基础上，内置了大量开箱即用的功能，如通用 CRUD、多种条件构造器、分页插件、代码生成器等，极大地减少了开发者的模板代码编写量。</li>
</ul>
</li>
</ul>
<hr />
<h2>2. CRUD 操作的实现方式</h2>
<ul>
<li>
<p><strong>MyBatis</strong></p>
<ul>
<li>需要开发者手动编写每一个增删改查操作的 SQL 语句（在 XML 文件或注解中），并配置对应的参数映射和结果映射。</li>
<li><strong>优点</strong>：灵活、精准，能应对所有 SQL 场景。</li>
<li><strong>缺点</strong>：对于简单的单表操作，会产生大量重复性代码。</li>
</ul>
</li>
<li>
<p><strong>MyBatis-Plus</strong></p>
<ul>
<li>提供了通用的 <code>BaseMapper</code> 接口，只需让您的 Mapper 继承该接口，即可直接获得一系列内置方法（如 <code>insert</code>, <code>selectById</code>, <code>update</code>, <code>deleteById</code> 等），无需编写任何 SQL 即可完成基本操作。</li>
<li>配套提供强大的 <strong>代码生成器</strong>，可一键生成实体类、Mapper 接口、Service、Controller 以及基础的 XML 文件，进一步提升开发速度。</li>
</ul>
</li>
</ul>
<hr />
<h2>3. 查询条件构建</h2>
<ul>
<li>
<p><strong>MyBatis</strong></p>
<ul>
<li>构建动态查询条件通常需要在 XML 中使用 <code>&lt;if&gt;</code>, <code>&lt;choose&gt;</code>, <code>&lt;foreach&gt;</code> 等标签拼接 SQL 片段，或者在 Java 代码中拼接 SQL 字符串，过程较为繁琐且不易维护。</li>
</ul>
</li>
<li>
<p><strong>MyBatis-Plus</strong></p>
<ul>
<li>提供了非常直观的 <strong>条件构造器（Wrapper）</strong>，例如 <code>QueryWrapper</code>、<code>LambdaQueryWrapper</code>、<code>UpdateWrapper</code>。</li>
<li>可以通过链式调用的方式，以面向对象的形式构建查询条件，代码可读性高，且能有效防止 SQL 注入。</li>
<li>
<p><strong>示例</strong>：</p>
<pre><code class="language-java">// 查询年龄大于18且姓“张”的用户
QueryWrapper&lt;User&gt; wrapper = new QueryWrapper&lt;&gt;();
wrapper.gt("age", 18).likeRight("name", "张");
List&lt;User&gt; userList = userMapper.selectList(wrapper);

// 使用Lambda表达式，更安全（避免字段名拼写错误）
LambdaQueryWrapper&lt;User&gt; lambdaWrapper = new LambdaQueryWrapper&lt;&gt;();
lambdaWrapper.gt(User::getAge, 18).likeRight(User::getName, "张");</code></pre>
</li>
</ul>
</li>
</ul>
<hr />
<h2>4. 分页功能</h2>
<ul>
<li>
<p><strong>MyBatis</strong></p>
<ul>
<li>原生支持有限，通常需要开发者自己编写 <code>LIMIT</code> 语句或使用第三方分页插件（如 PageHelper）来实现物理分页。</li>
</ul>
</li>
<li>
<p><strong>MyBatis-Plus</strong></p>
<ul>
<li>内置了成熟、易用的 <strong>分页插件（PaginationInterceptor）</strong>。</li>
<li>配置后，只需传入一个 <code>Page</code> 对象作为参数，即可自动完成分页查询和总记录数统计，支持多种数据库方言。</li>
<li><strong>示例</strong>：
<pre><code class="language-java">Page&lt;User&gt; page = new Page&lt;&gt;(1, 10); // 查询第1页，每页10条
IPage&lt;User&gt; userPage = userMapper.selectPage(page, queryWrapper);
List&lt;User&gt; records = userPage.getRecords(); // 当前页数据
long total = userPage.getTotal(); // 总记录数</code></pre></li>
</ul>
</li>
</ul>
<hr />
<h2>5. 性能与扩展插件</h2>
<ul>
<li>
<p><strong>MyBatis</strong></p>
<ul>
<li>拥有一套插件机制（Interceptor），可以用于实现自定义功能，如 SQL 日志打印、执行时间监控、数据脱敏等。社区也有丰富的第三方插件。</li>
</ul>
</li>
<li>
<p><strong>MyBatis-Plus</strong></p>
<ul>
<li>在 MyBatis 插件机制的基础上，提供了更多内置的、针对性的优化插件：
<ul>
<li><strong>性能分析插件</strong>：输出 SQL 语句及其执行时间，帮助定位慢查询。</li>
<li><strong>乐观锁插件</strong>：通过 <code>@Version</code> 注解轻松实现乐观锁，解决并发更新问题。</li>
<li><strong>动态表名插件</strong>：支持在运行时动态切换表名，适用于分表场景。</li>
<li><strong>SQL 注入器</strong>：允许自定义全局通用方法，如“逻辑删除”、“自动填充”等。</li>
</ul></li>
</ul>
</li>
</ul>
<hr />
<h2>6. 使用场景与总结</h2>
<table>
<thead>
<tr>
<th style="text-align: left;">特性</th>
<th style="text-align: left;">MyBatis</th>
<th style="text-align: left;">MyBatis-Plus</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;"><strong>核心优势</strong></td>
<td style="text-align: left;"><strong>灵活、控制力强</strong>，适合复杂 SQL 和高度定制化的场景。</td>
<td style="text-align: left;"><strong>高效、开发快</strong>，提供了大量自动化工具和便捷 API。</td>
</tr>
<tr>
<td style="text-align: left;"><strong>适用项目</strong></td>
<td style="text-align: left;">遗留系统、对 SQL 优化有极高要求、业务逻辑极其复杂的项目。</td>
<td style="text-align: left;">新项目、快速原型开发、以单表操作为主或需要快速迭代的中后台系统。</td>
</tr>
<tr>
<td style="text-align: left;"><strong>学习曲线</strong></td>
<td style="text-align: left;">相对平缓，核心是 SQL 和映射配置。</td>
<td style="text-align: left;">需先掌握 MyBatis 基础，再学习其增强特性，初期有一定学习成本。</td>
</tr>
<tr>
<td style="text-align: left;"><strong>代码量</strong></td>
<td style="text-align: left;">较多，尤其是简单的 CRUD 也需要编写 SQL。</td>
<td style="text-align: left;"><strong>显著减少</strong>，大部分简单操作无需写 SQL。</td>
</tr>
</tbody>
</table>
<p><strong>结论</strong>：<br />
MyBatis-Plus 并非要替代 MyBatis，而是作为其强大的“增强包”。如果你的项目追求极致的开发效率，且业务以常规的 CRUD 为主，<strong>MyBatis-Plus 是更优的选择</strong>。如果你的项目涉及大量复杂的存储过程、动态 SQL 或对 SQL 性能有极致要求，需要完全掌控每一行 SQL 代码，那么<strong>原生 MyBatis 可能更适合</strong>。两者可以结合使用，在 MyBatis-Plus 项目中，你依然可以完全使用原生 MyBatis 的方式编写复杂 SQL，兼顾效率与灵活性。</p>]]></description>
    <pubDate>Thu, 26 Mar 2026 11:52:20 +0800</pubDate>
    <dc:creator>haibo</dc:creator>
    <guid>http://www.haibohc.top/?post=12</guid>
</item>
<item>
    <title>Vue3 组件</title>
    <link>http://www.haibohc.top/?post=11</link>
    <description><![CDATA[<h1>Vue3 组件</h1>
<p>每个 Vue 组件都是一个独立的 Vue 实例，具有自己的模板、数据、方法和生命周期钩子，使得组件可以自包含地定义和管理自己的功能和样式。</p>
<p>组件（Component）是 Vue.js 最强大的功能之一。</p>
<p>组件可以扩展 HTML 元素，封装可重用的代码，可以帮助你将用户界面拆分成独立和可复用的部分。</p>
<p>每个 Vue 组件都是一个独立的 Vue 实例，具有自己的模板、数据、方法和生命周期钩子，使得组件可以自包含地定义和管理自己的功能和样式。</p>
<h3>Vue 组件的特性和优势</h3>
<ul>
<li><strong>复用性</strong>: 组件可以在不同的地方多次使用，提高代码的复用性和可维护性。</li>
<li><strong>封装性</strong>: 每个组件都是独立的作用域，可以封装自己的状态和逻辑，避免全局污染。</li>
<li><strong>组合性</strong>: 可以通过组合多个小组件构建复杂的界面。</li>
<li><strong>响应式</strong>: 组件的数据响应式地绑定到视图，数据更新时自动更新视图。</li>
<li><strong>模块化</strong>: 支持模块化开发，可以使用现代前端工具链进行构建和管理。</li>
</ul>
<p>组件系统让我们可以用独立可复用的小组件来构建大型应用，几乎任意类型的应用的界面都可以抽象为一个组件树：</p>
<p><img src="//www.runoob.com/wp-content/uploads/2017/01/components.png" alt="components.png" /></p>
<p>每个 Vue 应用都是通过用 createApp 函数创建的，传递给 createApp 的选项用于配置根组件。当我们挂载应用时，该组件被用作渲染的起点。</p>
<p>一个应用需要被挂载到一个 DOM 元素中。</p>
<p>以下实例我们将 Vue 应用挂载到 <code>&lt;div id="app"&gt;&lt;/div&gt;</code>，应该传入 #app：</p>
<pre><code class="language-javascript">const RootComponent = { /* 选项 */ }
const app = Vue.createApp(RootComponent)
const vm = app.mount('#app')</code></pre>
<p>注册一个全局组件语法格式如下：</p>
<pre><code class="language-javascript">const app = Vue.createApp({...})

app.component('my-component-name', {
  /* ... */
})</code></pre>
<p><strong>my-component-name</strong> 为组件名，<strong>/<em> ... </em>/</strong> 部分为配置选项。注册后，我们可以使用以下方式来调用组件：</p>
<pre><code class="language-html">&lt;my-component-name&gt;&lt;/my-component-name&gt;</code></pre>
<p>一个简单的 Vue 组件的实例：</p>
<h2>全局组件实例</h2>
<p>注册一个简单的全局组件 runoob，并使用它：</p>
<pre><code class="language-html">&lt;div id="app"&gt;
    &lt;runoob&gt;&lt;/runoob&gt;
&lt;/div&gt;

&lt;script&gt;
// 创建一个Vue 应用
const app = Vue.createApp({})

// 定义一个名为 runoob的新全局组件
app.component('runoob', {
    template: '&lt;h1&gt;自定义组件!&lt;/h1&gt;'
})

app.mount('#app')
&lt;/script&gt;</code></pre>
<p>接下来我们再注册一个 button-counter 组件，在每次点击后，计数器会加 1:</p>
<h3>实例</h3>
<pre><code class="language-javascript">// 创建一个Vue 应用
const app = Vue.createApp({})

// 定义一个名为 button-counter 的新全局组件
app.component('button-counter', {
  data() {
    return {
      count: 0
    }
  },
  template: `
    &lt;button @click="count++"&gt;
      点了 {{ count }} 次！
    &lt;/button&gt;`
})
app.mount('#app')
&lt;/script&gt;</code></pre>
<p><strong>注意</strong>：template 中 ` 是反引号，不是单引号 '。</p>
<h3>组件的复用</h3>
<p>你可以将组件进行任意次数的复用：</p>
<h3>实例</h3>
<pre><code class="language-html">&lt;div id="components-demo"&gt;
  &lt;button-counter&gt;&lt;/button-counter&gt;
  &lt;button-counter&gt;&lt;/button-counter&gt;
  &lt;button-counter&gt;&lt;/button-counter&gt;
&lt;/div&gt;</code></pre>
<h3>全局组件</h3>
<p>以上的实例中我们的组件都只是通过 component 全局注册的。</p>
<p>全局注册的组件可以在随后创建的 app 实例模板中使用，也包括根实例组件树中的所有子组件的模板中。</p>
<h2>全局组件实例</h2>
<p>注册一个简单的全局组件 runoob，并使用它：</p>
<pre><code class="language-html">&lt;div id="app"&gt;
    &lt;runoob&gt;&lt;/runoob&gt;
&lt;/div&gt;

&lt;script&gt;
// 创建一个Vue 应用
const app = Vue.createApp({})

// 定义一个名为 runoob 的新全局组件
app.component('runoob', {
    template: '&lt;h1&gt;自定义组件!&lt;/h1&gt;'
})

app.mount('#app')
&lt;/script&gt;</code></pre>
<h3>局部组件</h3>
<p>全局注册往往是不够理想的。比如，如果你使用一个像 webpack 这样的构建系统，全局注册所有的组件意味着即便你已经不再使用一个组件了，它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。</p>
<p>在这些情况下，你可以通过一个普通的 JavaScript 对象来定义组件：</p>
<pre><code class="language-javascript">const ComponentA = {
  /* ... */
}
const ComponentB = {
  /* ... */
}
const ComponentC = {
  /* ... */
}</code></pre>
<p>然后在 components 选项中定义你想要使用的组件：</p>
<pre><code class="language-javascript">const app = Vue.createApp({
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})</code></pre>
<p>对于 components 对象中的每个属性来说，其属性名就是自定义元素的名字（component-a、component-b），其属性值就是这个组件的选项对象（ComponentA、ComponentB）。</p>
<p>我们也可以在实例选项中注册局部组件，这样组件只能在这个实例中使用：</p>
<h2>局部组件实例</h2>
<p>注册一个简单的局部组件 runoobA，并使用它：</p>
<pre><code class="language-html">&lt;div id="app"&gt;
    &lt;runoob-a&gt;&lt;/runoob-a&gt;
&lt;/div&gt;
&lt;script&gt;
var runoobA = {
  template: '&lt;h1&gt;自定义组件!&lt;/h1&gt;'
}

const app = Vue.createApp({
  components: {
    'runoob-a': runoobA
  }
})

app.mount('#app')
&lt;/script&gt;</code></pre>
<h3>单文件组件 (.vue 文件)</h3>
<p>使用单文件组件能够更好地组织和管理 Vue 组件，一个组件通常由三部分组成：模板、脚本和样式。</p>
<h3>实例</h3>
<pre><code class="language-vue">&lt;!-- MyComponent.vue --&gt;
&lt;template&gt;
  &lt;div&gt;
    &lt;p&gt;{{ message }}&lt;/p&gt;
    &lt;button @click="increment"&gt;Increment&lt;/button&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
export default {
  data() {
    return {
      message: 'Hello from MyComponent',
      count: 0
    };
  },
  methods: {
    increment() {
      this.count++;
    }
  }
};
&lt;/script&gt;

&lt;style scoped&gt;
/* 组件私有样式 */
p {
  color: blue;
}
&lt;/style&gt;</code></pre>
<h2>Prop</h2>
<p>prop 是子组件用来接受父组件传递过来的数据的一个自定义属性。</p>
<p>父组件的数据需要通过 props 把数据传给子组件，子组件需要显式地用 props 选项声明 &quot;prop&quot;：</p>
<h2>Prop 实例</h2>
<pre><code class="language-html">&lt;div id="app"&gt;
  &lt;site-name title="Google"&gt;&lt;/site-name&gt;
  &lt;site-name title="Runoob"&gt;&lt;/site-name&gt;
  &lt;site-name title="Taobao"&gt;&lt;/site-name&gt;
&lt;/div&gt;

&lt;script&gt;
const app = Vue.createApp({})

app.component('site-name', {
  props: ['title'],
  template: `&lt;h4&gt;{{ title }}&lt;/h4&gt;`
})

app.mount('#app')
&lt;/script&gt;</code></pre>
<p>一个组件默认可以拥有任意数量的 prop，任何值都可以传递给任何 prop。</p>
<h3>动态 Prop</h3>
<p>类似于用 v-bind 绑定 HTML 特性到一个表达式，也可以用 v-bind 动态绑定 props 的值到父组件的数据中。每当父组件的数据变化时，该变化也会传导给子组件：</p>
<h2>Prop 实例</h2>
<pre><code class="language-html">&lt;div id="app"&gt;
  &lt;site-info
    v-for="site in sites"
    :id="site.id"
    :title="site.title"
  &gt;&lt;/site-info&gt;
&lt;/div&gt;

&lt;script&gt;
const Site = {
  data() {
    return {
      sites: [
        { id: 1, title: 'Google' },
        { id: 2, title: 'Runoob' },
        { id: 3, title: 'Taobao' }
      ]
    }
  }
}

const app = Vue.createApp(Site)

app.component('site-info', {
  props: ['id','title'],
  template: `&lt;h4&gt;{{ id }} - {{ title }}&lt;/h4&gt;`
})

app.mount('#app')
&lt;/script&gt;</code></pre>
<h3>Prop 验证</h3>
<p>组件可以为 props 指定验证要求。</p>
<p>为了定制 prop 的验证方式，你可以为 props 中的值提供一个带有验证需求的对象，而不是一个字符串数组。例如：</p>
<pre><code class="language-javascript">Vue.component('my-component', {
  props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        // 这个值必须匹配下列字符串中的一个
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})</code></pre>
<p>当 prop 验证失败的时候，(开发环境构建版本的) Vue 将会产生一个控制台的警告。</p>
<p>type 可以是下面原生构造器：</p>
<ul>
<li><code>String</code></li>
<li><code>Number</code></li>
<li><code>Boolean</code></li>
<li><code>Array</code></li>
<li><code>Object</code></li>
<li><code>Date</code></li>
<li><code>Function</code></li>
<li><code>Symbol</code></li>
</ul>
<p>type 也可以是一个自定义构造器，使用 instanceof 检测。</p>]]></description>
    <pubDate>Tue, 17 Mar 2026 09:32:38 +0800</pubDate>
    <dc:creator>haibo</dc:creator>
    <guid>http://www.haibohc.top/?post=11</guid>
</item>
<item>
    <title>openclaw的适用范围</title>
    <link>http://www.haibohc.top/?post=10</link>
    <description><![CDATA[<h1>OpenClaw 的适用范围</h1>
<p>OpenClaw 是一款功能强大的开源工具，其设计旨在满足多样化的应用需求，主要适用于以下几个关键领域：</p>
<h2>1. 自动化测试与质量保证</h2>
<p>在软件开发周期中，OpenClaw 能够高效执行自动化测试任务。它支持多种测试框架和协议，适用于 Web 应用、移动应用及 API 接口的测试。通过模拟用户操作和系统交互，OpenClaw 帮助团队快速识别缺陷，提升软件质量，并缩短发布周期。</p>
<h2>2. 数据抓取与处理</h2>
<p>对于需要从网络或本地资源中提取数据的场景，OpenClaw 提供了灵活的抓取能力。它可以处理结构化与非结构化数据，适用于市场调研、竞品分析、内容聚合等任务。结合其数据处理模块，用户能够轻松清洗、转换并存储数据，为后续分析奠定基础。</p>
<h2>3. 系统监控与运维自动化</h2>
<p>在 IT 运维领域，OpenClaw 可用于监控系统性能、日志分析和故障响应。通过预设脚本和规则，它能自动执行日常维护操作，如备份、部署和警报处理，从而降低人工干预成本，提高系统稳定性和运维效率。</p>
<h2>4. 教育与研究</h2>
<p>OpenClaw 的开源特性使其成为教育和研究的理想工具。学生和研究人员可以利用其模块化架构学习自动化技术原理，或将其应用于实验数据收集、模拟仿真等学术项目中，促进技术创新与知识共享。</p>
<h2>5. 定制化业务集成</h2>
<p>凭借可扩展的插件体系，OpenClaw 能够适应特定行业的业务需求。无论是金融、电商还是制造业，用户均可通过定制开发，将其集成到现有工作流中，实现业务流程的自动化优化，提升整体运营效能。</p>
<h2>总结</h2>
<p>OpenClaw 以其多功能、易扩展的特点，广泛适用于测试自动化、数据处理、运维监控、教育研究及业务集成等多个领域。无论是个人开发者、企业团队还是学术机构，都能从中受益，推动效率提升与数字化转型。</p>]]></description>
    <pubDate>Mon, 09 Mar 2026 14:15:15 +0800</pubDate>
    <dc:creator>haibo</dc:creator>
    <guid>http://www.haibohc.top/?post=10</guid>
</item>
<item>
    <title>若依创建定时任务时遇到的一些问题</title>
    <link>http://www.haibohc.top/?post=9</link>
    <description><![CDATA[<h1>若依创建定时任务时遇到的一些问题</h1>
<p>写之前参考了其他大佬的方法，说class要加什么@Component（“bean名称”）啥的比较麻烦，自己捋了一下，整理成文</p>
<p><a href="https://doc.ruoyi.vip/ruoyi/document/htsc.html#%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1">后台手册 | RuoYi</a></p>
<p>首先是👆官方教程链接，接下来我想把自己写的实现类中的某个方法也加进去</p>
<p><img src="http://www.haibohc.top/content/uploadfile/202603/f3801772701398.png" alt="" /></p>
<p>👆一个若依代码生成的实现类</p>
<p><img src="http://www.haibohc.top/content/uploadfile/202603/bc2e1772701454.png" alt="" /></p>
<p>👆实现类中一个普通的方法，里面需要调用mapper查询数据库表。可以看到我没有直接用系统生成的qcNewRiverBoatmapper</p>
<p>若依定时任务调用有两种方式：</p>
<ol>
<li>Spring Bean 方式（目标字符串不包含多个.）- 通过 SpringUtils.getBean() 获取</li>
<li>普通类方式（目标字符串包含多个.）- 通过反射创建新实例<br />
当定时任务配置的目标方法使用的是类全路径名（如 com.ruoyi.system.service.impl.QcNewRiverBoatServiceImpl.updateRiverBoatListStatusOnline）时，会通过 Class.forName().newInstance() 创建新实例，导致 @Autowired 注入的 qcNewRiverBoatMapper 为 null。</li>
</ol>
<p><strong>需要修改 Service 实现类，使用 Spring 上下文工具类手动获取 Mapper，也就是👆第一张图的getMapper（）</strong></p>
<p>有个地方需要加白名单，这里官方教程没有说，但是是要加上的，复制一下内容使用全局搜索搜出这个变量，加上实现类的路径，如下图所示：</p>
<pre><code>public static final String[] JOB_WHITELIST_STR = {"com.ruoyi.quartz.task",</code></pre>
<p><img src="http://www.haibohc.top/content/uploadfile/202603/7cd11772701499.png" alt="" /></p>
<p>这里是我配置的定时任务，每天零点执行。这里把cron表达式也复制在这里</p>
<p>0 0 0 <em> </em> ?</p>
<p><img src="http://www.haibohc.top/content/uploadfile/202603/14d51772701538.png" alt="" /></p>]]></description>
    <pubDate>Thu, 05 Mar 2026 16:56:41 +0800</pubDate>
    <dc:creator>haibo</dc:creator>
    <guid>http://www.haibohc.top/?post=9</guid>
</item>
<item>
    <title>java面向对象特性</title>
    <link>http://www.haibohc.top/?post=8</link>
    <description><![CDATA[<h1>Java面向对象特性</h1>
<p>面向对象编程（OOP）是Java的核心思想，它通过模拟现实世界中的对象来构建程序，使代码更易于理解、维护和扩展。Java的面向对象特性主要包括以下四个方面：</p>
<h2>1. 封装（Encapsulation）</h2>
<p>封装是将数据（属性）和操作数据的方法（行为）捆绑在一起的过程，同时对外隐藏对象的内部实现细节。通过使用<code>private</code>访问修饰符限制对属性的直接访问，并提供公共的<code>getter</code>和<code>setter</code>方法来控制访问和修改，封装提高了代码的安全性和可维护性。</p>
<pre><code class="language-java">public class Person {
    private String name; // 私有属性
    private int age;

    // 公共方法访问私有属性
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}</code></pre>
<h2>2. 继承（Inheritance）</h2>
<p>继承允许一个类（子类）基于另一个类（父类）来构建，从而获得父类的属性和方法。这促进了代码的重用，并建立了类之间的层次关系。Java使用<code>extends</code>关键字实现单继承。</p>
<pre><code class="language-java">// 父类
class Animal {
    void eat() {
        System.out.println("动物在吃东西");
    }
}

// 子类继承父类
class Dog extends Animal {
    void bark() {
        System.out.println("狗在叫");
    }
}</code></pre>
<h2>3. 多态（Polymorphism）</h2>
<p>多态意味着“多种形态”，它允许使用统一的接口处理不同类型的对象。Java通过方法重载（编译时多态）和方法重写（运行时多态）实现多态。父类引用可以指向子类对象，调用方法时会根据实际对象类型执行相应的方法。</p>
<pre><code class="language-java">class Shape {
    void draw() {
        System.out.println("绘制形状");
    }
}

class Circle extends Shape {
    @Override
    void draw() {
        System.out.println("绘制圆形");
    }
}

// 多态示例
Shape shape = new Circle();
shape.draw(); // 输出：绘制圆形</code></pre>
<h2>4. 抽象（Abstraction）</h2>
<p>抽象是通过抽象类和接口来隐藏复杂性的过程，只展示必要的特征。抽象类用<code>abstract</code>关键字声明，可以包含抽象方法和具体方法；接口用<code>interface</code>声明，定义了一组方法规范，由类来实现。</p>
<pre><code class="language-java">// 抽象类
abstract class Vehicle {
    abstract void run(); // 抽象方法
}

// 接口
interface Flyable {
    void fly();
}

class Airplane extends Vehicle implements Flyable {
    void run() {
        System.out.println("飞机在跑道上滑行");
    }

    public void fly() {
        System.out.println("飞机在空中飞行");
    }
}</code></pre>
<h2>总结</h2>
<p>Java的面向对象特性使程序结构更加清晰，提高了代码的可重用性、灵活性和可扩展性。掌握封装、继承、多态和抽象是学习Java编程的基础，也是构建复杂应用程序的关键。</p>]]></description>
    <pubDate>Thu, 05 Mar 2026 11:58:37 +0800</pubDate>
    <dc:creator>haibo</dc:creator>
    <guid>http://www.haibohc.top/?post=8</guid>
</item>
<item>
    <title>Java Servlet</title>
    <link>http://www.haibohc.top/?post=7</link>
    <description><![CDATA[<h1>Servlet 简介</h1>
<h2>Servlet 是什么？</h2>
<p>Java Servlet 是运行在 Web 服务器或应用服务器上的程序，它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。</p>
<p>使用 Servlet，您可以收集来自网页表单的用户输入，呈现来自数据库或者其他源的记录，还可以动态创建网页。</p>
<p>Java Servlet 通常情况下与使用 CGI（Common Gateway Interface，公共网关接口）实现的程序可以达到异曲同工的效果。但是相比于 CGI，Servlet 有以下几点优势：</p>
<ul>
<li>性能明显更好。</li>
<li>Servlet 在 Web 服务器的地址空间内执行。这样它就没有必要再创建一个单独的进程来处理每个客户端请求。</li>
<li>Servlet 是独立于平台的，因为它们是用 Java 编写的。</li>
<li>服务器上的 Java 安全管理器执行了一系列限制，以保护服务器计算机上的资源。因此，Servlet 是可信的。</li>
<li>Java 类库的全部功能对 Servlet 来说都是可用的。它可以通过 sockets 和 RMI 机制与 applets、数据库或其他软件进行交互。</li>
</ul>
<h2>Servlet 架构</h2>
<p>下图显示了 Servlet 在 Web 应用程序中的位置。</p>
<p><img src="http://www.haibohc.top/content/uploadfile/202603/9f0f1772616712.jpg" alt="" /></p>
<h2>Servlet 任务</h2>
<p>Servlet 执行以下主要任务：</p>
<ul>
<li>读取客户端（浏览器）发送的显式的数据。这包括网页上的 HTML 表单，或者也可以是来自 applet 或自定义的 HTTP 客户端程序的表单。</li>
<li>读取客户端（浏览器）发送的隐式的 HTTP 请求数据。这包括 cookies、媒体类型和浏览器能理解的压缩格式等等。</li>
<li>处理数据并生成结果。这个过程可能需要访问数据库，执行 RMI 或 CORBA 调用，调用 Web 服务，或者直接计算得出对应的响应。</li>
<li>发送显式的数据（即文档）到客户端（浏览器）。该文档的格式可以是多种多样的，包括文本文件（HTML 或 XML）、二进制文件（GIF 图像）、Excel 等。</li>
<li>发送隐式的 HTTP 响应到客户端（浏览器）。这包括告诉浏览器或其他客户端被返回的文档类型（例如 HTML），设置 cookies 和缓存参数，以及其他类似的任务。</li>
</ul>
<h2>Servlet 包</h2>
<p>Java Servlet 是运行在带有支持 Java Servlet 规范的解释器的 web 服务器上的 Java 类。</p>
<p>Servlet 可以使用 <strong>javax.servlet</strong> 和 <strong>javax.servlet.http</strong> 包创建，它是 Java 企业版的标准组成部分，Java 企业版是支持大型开发项目的 Java 类库的扩展版本。</p>
<p>这些类实现 Java Servlet 和 JSP 规范。在写本教程的时候，二者相应的版本分别是 Java Servlet 2.5 和 JSP 2.1。</p>
<p>Java Servlet 就像任何其他的 Java 类一样已经被创建和编译。在您安装 Servlet 包并把它们添加到您的计算机上的 Classpath 类路径中之后，您就可以通过 JDK 的 Java 编译器或任何其他编译器来编译 Servlet。</p>]]></description>
    <pubDate>Tue, 03 Mar 2026 17:22:21 +0800</pubDate>
    <dc:creator>haibo</dc:creator>
    <guid>http://www.haibohc.top/?post=7</guid>
</item>
<item>
    <title>idea切换、删除本地分支</title>
    <link>http://www.haibohc.top/?post=6</link>
    <description><![CDATA[<p>​<br />
（为什么要写？因为有人不懂什么是签出checkout，却又手痒新建了分支，发现右键删不掉）</p>
<p>目标：删除haibo分支，切换回master分支<br />
<img src="http://www.haibohc.top/content/uploadfile/202603/55191772500152.png" alt="" /></p>
<p>点击签出</p>
<p><img src="http://www.haibohc.top/content/uploadfile/202603/ad621772500256.png" alt="" /></p>
<p>签出后即可删除haibo分支</p>
<p><img src="http://www.haibohc.top/content/uploadfile/202603/370a1772500265.png" alt="" /></p>
<p>操作完成！</p>
<p>在 Git 中，签出分支是一个常用的操作，它允许开发者在不同的分支之间切换，从而在不同的开发线上工作。签出（checkout）一般指的是切换到某个分支，或者将文件恢复到某个特定的版本。</p>
<p>签出一个已存在的分支可以使用 git checkout 命令。例如，如果要切换到名为 feature 的分支，可以执行以下命令：</p>
<p>git checkout feature</p>
<p>如果要创建一个新的分支并立即切换到该分支，可以使用 -b 参数：</p>
<p>git checkout -b new_feature</p>
<p>​</p>]]></description>
    <pubDate>Tue, 03 Mar 2026 09:05:00 +0800</pubDate>
    <dc:creator>haibo</dc:creator>
    <guid>http://www.haibohc.top/?post=6</guid>
</item>
<item>
    <title>Java单线程和多线程举例</title>
    <link>http://www.haibohc.top/?post=5</link>
    <description><![CDATA[<p>你想要了解 Java 中单线程和多线程的具体区别，并通过实际的代码例子来直观理解它们的执行方式，对吧？<br />
下面我会分别提供单线程和多线程的完整代码示例，并详细解释它们的执行逻辑和差异，让你能清晰地看到两者的核心区别。<br />
一、单线程示例<br />
单线程程序中，代码会按照顺序从头到尾依次执行，只有前一个任务完成，后一个任务才会开始。<br />
java<br />
运行</p>
<pre><code class="language-java">/**
 * 单线程示例：所有任务按顺序执行
 */
public class SingleThreadExample {
    public static void main(String[] args) {
        // 记录程序开始时间
        long startTime = System.currentTimeMillis();

        // 任务1：模拟耗时操作（比如计算）
        System.out.println("开始执行任务1...");
        for (int i = 0; i &lt; 1000000000; i++) {
            // 空循环模拟耗时计算
        }
        System.out.println("任务1执行完成！");

        // 任务2：模拟另一个耗时操作
        System.out.println("开始执行任务2...");
        for (int i = 0; i &lt; 1000000000; i++) {
            // 空循环模拟耗时计算
        }
        System.out.println("任务2执行完成！");

        // 计算总耗时
        long endTime = System.currentTimeMillis();
        System.out.println("单线程总耗时：" + (endTime - startTime) + " 毫秒");
    }
}</code></pre>
<p>执行结果（示例）：<br />
plaintext<br />
开始执行任务1...<br />
任务1执行完成！<br />
开始执行任务2...<br />
任务2执行完成！<br />
单线程总耗时：800 毫秒<br />
核心解释：<br />
主线程（main 线程）是唯一的执行线程，任务 1 必须完全执行完，任务 2 才会开始。<br />
总耗时约等于任务 1 耗时 + 任务 2 耗时。<br />
二、多线程示例<br />
多线程程序中，多个任务可以并发（宏观上同时）执行，多个线程各自独立运行，互不阻塞。<br />
java<br />
运行</p>
<pre><code class="language-java">/**
 * 多线程示例：多个任务并发执行
 */
public class MultiThreadExample {
    // 定义一个耗时任务的线程类
    static class TaskThread extends Thread {
        private String taskName;

        public TaskThread(String taskName) {
            this.taskName = taskName;
        }

        @Override
        public void run() {
            // 线程的核心执行逻辑
            System.out.println("开始执行" + taskName + "...");
            long startTime = System.currentTimeMillis();

            // 模拟耗时操作
            for (int i = 0; i &lt; 1000000000; i++) {
                // 空循环模拟耗时计算
            }

            long endTime = System.currentTimeMillis();
            System.out.println(taskName + "执行完成！耗时：" + (endTime - startTime) + " 毫秒");
        }
    }

    public static void main(String[] args) {
        // 记录程序开始时间
        long mainStartTime = System.currentTimeMillis();

        // 创建两个线程
        Thread thread1 = new TaskThread("任务1");
        Thread thread2 = new TaskThread("任务2");

        // 启动线程（注意：是start()不是run()，start()才会创建新线程）
        thread1.start();
        thread2.start();

        // 等待两个线程都执行完成（避免主线程先结束）
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 计算总耗时
        long mainEndTime = System.currentTimeMillis();
        System.out.println("多线程总耗时：" + (mainEndTime - mainStartTime) + " 毫秒");
    }
}</code></pre>
<p>执行结果（示例）：<br />
plaintext<br />
开始执行任务1...<br />
开始执行任务2...<br />
任务2执行完成！耗时：400 毫秒<br />
任务1执行完成！耗时：405 毫秒<br />
多线程总耗时：410 毫秒<br />
核心解释：<br />
start() 方法会创建新的线程，线程的执行逻辑在 run() 方法中。<br />
任务 1 和任务 2 会并发执行（宏观上同时跑），总耗时约等于耗时最长的那个任务的时间，而不是两者相加。<br />
join() 方法的作用是让主线程等待子线程执行完毕，否则主线程可能会先输出 “总耗时”，导致结果不准确。<br />
三、关键补充：线程的另一种创建方式（Runnable 接口）<br />
除了继承 Thread 类，更推荐实现 Runnable 接口（因为 Java 单继承的限制），效果完全一样：<br />
java<br />
运行</p>
<pre><code class="language-java">/**
 * 实现Runnable接口的多线程示例（推荐方式）
 */
public class RunnableExample {
    static class Task implements Runnable {
        private String taskName;

        public Task(String taskName) {
            this.taskName = taskName;
        }

        @Override
        public void run() {
            System.out.println("开始执行" + taskName + "...");
            // 模拟耗时操作
            for (int i = 0; i &lt; 1000000000; i++) {}
            System.out.println(taskName + "执行完成！");
        }
    }

    public static void main(String[] args) {
        // 创建Runnable任务
        Runnable task1 = new Task("任务1");
        Runnable task2 = new Task("任务2");

        // 包装成Thread并启动
        new Thread(task1).start();
        new Thread(task2).start();
    }
}</code></pre>
<p>总结<br />
单线程：代码串行执行，任务按顺序完成，总耗时 = 各任务耗时之和，适用于简单、无耗时操作的场景。<br />
多线程：任务并发执行，总耗时≈最长任务耗时，适用于有耗时操作（如 IO、计算、网络请求）的场景，能充分利用 CPU 资源。<br />
核心方法：启动线程用 start()（创建新线程），不要直接调用 run()（只是普通方法调用，不会创建新线程）；join() 可让主线程等待子线程完成。</p>]]></description>
    <pubDate>Mon, 02 Mar 2026 16:12:14 +0800</pubDate>
    <dc:creator>haibo</dc:creator>
    <guid>http://www.haibohc.top/?post=5</guid>
</item>
</channel>
</rss>