<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:wfw="http://wellformedweb.org/CommentAPI/">
<channel>
<title>LuckyDu - 编程工具</title>
<link>https://blog.anlucky.cn/index.php/tag/%E7%BC%96%E7%A8%8B%E5%B7%A5%E5%85%B7/</link>
<atom:link href="https://blog.anlucky.cn/index.php/feed/tag/%E7%BC%96%E7%A8%8B%E5%B7%A5%E5%85%B7/" rel="self" type="application/rss+xml" />
<language>zh-CN</language>
<description></description>
<lastBuildDate>Mon, 01 Apr 2024 09:41:34 +0800</lastBuildDate>
<pubDate>Mon, 01 Apr 2024 09:41:34 +0800</pubDate>
<item>
<title>Java工具类-通用对象转换为Json字符</title>
<link>https://blog.anlucky.cn/index.php/programming/java/213</link>
<guid>https://blog.anlucky.cn/index.php/programming/java/213</guid>
<pubDate>Mon, 01 Apr 2024 09:41:34 +0800</pubDate>
<dc:creator>都依凡</dc:creator>
<description><![CDATA[Java工具类-通用对象转换为Json字符import com.alibaba.fastjson2.JSONObject;import java.lang.reflect.Field;impor...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<h1>Java工具类-通用对象转换为Json字符</h1><pre><code class="lang-java">import com.alibaba.fastjson2.JSONObject;

import java.lang.reflect.Field;
import java.text.SimpleDateFormat;

public class JsonObjectUtlis {

    public static &lt;U&gt; JSONObject processEntityToJson(Class&lt;U&gt; clazz, U cla) {
        //将传过来的对象进行赋值处理，
        //此时u可用来代表传过来的对象（本示意中是Users）,
        //此时可以用u调用传过来对象的方法
        U u = clazz.cast(cla);
        //以下是验证此示意中实体类可被操作了
        //getDeclaredFields()：获得某个类的所有声明的字段，即包括public、private和proteced，但是不包括父类的申明字段。
        //.getClass()是一个对象实例的方法，只有对象实例才有这个方法，具体的类是没有的
        JSONObject jsonObject = new JSONObject();
        for (Field field : u.getClass().getDeclaredFields()) {
            //允许获取实体类private的参数信息 field.setAccessible(true);
            field.setAccessible(true);
            try {
                String name = field.getType().getName();
                if (&quot;java.util.Date&quot;.equals(name) &amp;&amp; field.get(u) != null) {
                    String dateStr = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss&quot;).format(field.get(u));
                    if (dateStr.indexOf(&quot;00:00:00&quot;) &gt; 0){
                        dateStr = dateStr.substring(0,10) ;
                    }
                    jsonObject.put(field.getName(), dateStr);
                }else {
                    jsonObject.put(field.getName(), field.get(u));
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                jsonObject.put(&quot;convertJsonStatus&quot;,500);
                return jsonObject;
            }
        }
        return jsonObject;
    }
}</code></pre><h2>使用例子</h2><pre><code class="lang-java">        JSONObject jsonObject = JsonObjectUtlis.processEntityToJson(JudgeResultSynchronizationMatVO.class, synchronizationVO);
        // 第一个参数为对象的Clss类    第二个入参为实际对象</code></pre><h2>Json判断</h2><blockquote>判断当前字符串是不是Json字符串，是JSON返回True 否则返回false</blockquote><pre><code class="lang-java">public static boolean isjson(String str) {
        try {
            JSONObject jsonStr = JSONObject.parseObject(str);
            return  true;
        } catch (Exception e) {
            return false;
        }
}</code></pre>
]]></content:encoded>
<slash:comments>0</slash:comments>
<comments>https://blog.anlucky.cn/index.php/programming/java/213#comments</comments>
<wfw:commentRss>https://blog.anlucky.cn/index.php/feed/tag/%E7%BC%96%E7%A8%8B%E5%B7%A5%E5%85%B7/</wfw:commentRss>
</item>
<item>
<title>阿里云配置Docker加速</title>
<link>https://blog.anlucky.cn/index.php/programming/tools/164</link>
<guid>https://blog.anlucky.cn/index.php/programming/tools/164</guid>
<pubDate>Sat, 27 May 2023 20:18:05 +0800</pubDate>
<dc:creator>都依凡</dc:creator>
<description><![CDATA[1.材料准备准备一个阿里云的实名账号，自己直接注册就可以阿里云官网：https://www.aliyun.com/2. 配置步骤进入阿里云控制台在左上角的三条杠中点开找到容器这个就是配置地址3....]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<h2>1.材料准备</h2><p>准备一个阿里云的实名账号，自己直接注册就可以</p><p>阿里云官网：<a href="https://www.aliyun.com/">https://www.aliyun.com/</a></p><h2>2. 配置步骤</h2><ol><li>进入阿里云控制台</li></ol><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/4262392929.png" alt="159" title="159"></p><ol start="2"><li>在左上角的三条杠中点开找到容器</li></ol><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/3386926228.png" alt="162" title="162"></p><ol start="3"><li>这个就是配置地址</li></ol><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/969021852.png" alt="163" title="163"></p><h2>3. 使用配置镜像加速的命令去配置docker</h2>
]]></content:encoded>
<slash:comments>0</slash:comments>
<comments>https://blog.anlucky.cn/index.php/programming/tools/164#comments</comments>
<wfw:commentRss>https://blog.anlucky.cn/index.php/feed/tag/%E7%BC%96%E7%A8%8B%E5%B7%A5%E5%85%B7/</wfw:commentRss>
</item>
<item>
<title>Docker的基本命令</title>
<link>https://blog.anlucky.cn/index.php/programming/tools/153</link>
<guid>https://blog.anlucky.cn/index.php/programming/tools/153</guid>
<pubDate>Sat, 27 May 2023 09:15:00 +0800</pubDate>
<dc:creator>都依凡</dc:creator>
<description><![CDATA[Docker的基本命令1. 帮助启动相关命令功能命令行代码启动dockersystemctl start docker停止dockersystemctl stop docker重启dockers...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<h1>Docker的基本命令</h1><h2>1. 帮助启动相关</h2><table><thead><tr><th align="center">命令功能</th><th align="center">命令行代码</th></tr></thead><tbody><tr><td align="center">启动docker</td><td align="center">systemctl start docker</td></tr><tr><td align="center">停止docker</td><td align="center">systemctl stop docker</td></tr><tr><td align="center">重启docker</td><td align="center">systemctl restart docker</td></tr><tr><td align="center">查看docker状态</td><td align="center">systemctl status docker</td></tr><tr><td align="center">开机启动docker</td><td align="center">systemctl enable docker</td></tr><tr><td align="center">查看docker概要信息</td><td align="center">docker info</td></tr><tr><td align="center">查看docker总体帮助文档</td><td align="center">docker --help</td></tr><tr><td align="center">查看docker命令帮助文档</td><td align="center">docker save --help (查看save命令的帮助文档)</td></tr></tbody></table><h2>2. 镜像相关命令</h2><h3>1. docker images 查看本地的镜像</h3><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/845719043.png" alt="154" title="154"></p><p>其中</p><ul><li>repository 表示镜像名称</li><li>Tag  表示镜像的版本标签  同一个镜像可以有多个标签</li><li>Image Id  表示镜像的ID</li><li>Created 表示创建时间</li><li>Size 表示文件大小</li></ul><table><thead><tr><th align="center">命令功能</th><th align="center">命令行代码</th></tr></thead><tbody><tr><td align="center">docker images -a</td><td align="center">列出本地所有的镜像</td></tr><tr><td align="center">docker images -q</td><td align="center">只显示镜像的ID</td></tr></tbody></table><h3>2. docker search &lt;某个镜像的名称&gt; 搜索镜像</h3><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/195960876.png" alt="155" title="155"></p><p>其中</p><ul><li>Name 表示镜像的名称</li><li>Description 表示镜像的说明</li><li>stars 点赞的数量</li><li>Officaial 表示是否是官方的 若是官方的 下方有一个[OK] 表示</li><li>AutoMated 表示是否是自动构建的</li></ul><table><thead><tr><th>命令功能</th><th>命令行代码</th></tr></thead><tbody><tr><td>分页查询镜像 查询N个</td><td>docker search &lt;镜像名称&gt; --limit &lt; n &gt;</td></tr></tbody></table><blockquote><p>Docker 自动构建镜像和非自动构建镜像的主要区别在于构建方式和更新机制。</p><p>自动构建镜像是通过与代码托管平台（如 GitHub、GitLab、Bitbucket 等）集成，当代码仓库中的代码发生变化时，Docker Hub 会自动触发构建过程，生成新的镜像。自动构建镜像的好处是可以自动化构建和更新镜像，减少手动操作，提高效率。但是，由于构建过程是自动化的，可能会导致构建失败或者镜像质量不稳定的问题。</p><p>非自动构建镜像则需要手动构建和更新，通常是通过 Dockerfile 文件来定义构建过程。这种方式可以更加灵活地控制构建过程和镜像内容，但需要手动操作，相对较为繁琐。</p><p>总的来说，自动构建镜像适用于需要频繁更新的应用，非自动构建镜像适用于需要更加精细控制的应用。</p></blockquote><h3>3. docker pull &lt;某个镜像的名称&gt; 下载/拉去镜像</h3><table><thead><tr><th>命令功能</th><th>命令行代码</th></tr></thead><tbody><tr><td>拉去某个镜像的最新版本 latest</td><td>docker pull &lt; 镜像的名称 &gt;</td></tr><tr><td>拉取某个镜像的指定版本(不要忘了冒号)</td><td>docker pull &lt;镜像的名称&gt;:&lt; tag ID &gt;</td></tr></tbody></table><p>### 4. docker system df 查看镜像/容器/数据卷占用的空间</p><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/2207833078.png" alt="156" title="156"></p><h3>5. docker rmi &lt;某个镜像的名字&gt; 删除镜像</h3><table><thead><tr><th>命令功能</th><th>命令行代码</th></tr></thead><tbody><tr><td>强制删除镜像</td><td>docker rmi -f &lt;镜像的ID&gt;</td></tr><tr><td>删除多个镜像</td><td>docker rmi -f &lt;镜像的ID&gt;:&lt;镜像ID&gt;</td></tr><tr><td>删除全部镜像</td><td>docker rmi -f ${docker images -qa}</td></tr></tbody></table><p>其中删除全部中使用到的是Shell脚本的特性，支持了参数的续传，可以将docker images -qa查询到的所有的镜像的ID传输给docker rmi -f执行，即就可以删除全部</p><h2>* Docker面试题：谈谈docker虚旋镜像是什么？</h2><h3>1. 虚旋镜像长什么样子？</h3><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/1293427115.png" alt="157" title="157"></p><p>在查询所有镜像仓库的时候可以看到的他 <strong>repository</strong>和<strong>TAG</strong>是<strong>&lt; none &gt;</strong></p><p>## 3.容器相关的命令</p><h3>1. 启动一个容器实例</h3><table><thead><tr><th>命令功能</th><th>命令行代码</th></tr></thead><tbody><tr><td>启动一个容器，如启动一个nginx其中 -d表示后台运行这个容器，不占用前台的输入</td><td>docker run --name &lt; 自定义这个容器的名字 &gt; -d &lt;镜像名称&gt;</td></tr></tbody></table><p>如，要启动一个Nginx容器，并且取名为MyNginx，且让他后台运行，不占用前台命令行，就有如下命令行</p><pre><code class="lang-shell">docker run --name Mynginx -d nginx
## 
## 其中 --name 表示指定一个名称 后面是自定义的名称  
## -d 表示后台运行
## 最后是启动的这个镜像的名称</code></pre><h3>2. 查询当前正在运行的容器</h3><table><thead><tr><th>命令功能</th><th>命令行代码</th></tr></thead><tbody><tr><td>查询当前正在运行的容器</td><td>docker ps</td></tr><tr><td>查询罗列过的和运行过的所有容器</td><td>docker ps -a</td></tr><tr><td>查询最近创建的容器</td><td>docker ps -l</td></tr><tr><td>查询最近N个创建的容器</td><td>docker ps -n &lt;查询的数量&gt;</td></tr><tr><td>查询正在运行的容器的ID</td><td>docker ps -q</td></tr></tbody></table><p>其中</p><p>docker ps -q查询当前正在运行的容器的ID可以和其他命令参数进行组合使用，如我们要查询所有运行过的容器就可以有如下的代码</p><pre><code class="lang-shell">docker ps -aq</code></pre><h3>3. 退出当前容器</h3><table><thead><tr><th>命令功能</th><th>命令行代码</th></tr></thead><tbody><tr><td>退出当前容器</td><td>exit  “使用这个命令退出容器会停止当前容器的运行”</td></tr><tr><td>退出当前容器</td><td>ctrl + p + q "使用这个命令退出容器不会停止容器的运行"</td></tr></tbody></table><h3>4. 启动/停止/强制停止的容器</h3><table><thead><tr><th>命令功能</th><th>命令行代码</th></tr></thead><tbody><tr><td>启动容器</td><td>docker start &lt;容器的ID&gt;</td></tr><tr><td>停止容器</td><td>docker stop &lt;容器的ID&gt;</td></tr><tr><td>强制停止容器</td><td>docker kill &lt;容器的ID&gt;</td></tr></tbody></table><h3>5. 删除已经停止容器</h3><p>在上面的例子中已经学到了删除镜像使用的命令是 docker rmi &lt;镜像的ID&gt;</p><p>在删除容器的时候，命令就是</p><pre><code class="lang-shell">docker rm &lt;容器的ID&gt;

# 强制删除容器 慎用 可以删除正在运行的容器 
dcker rm -f &lt;容器的ID&gt;</code></pre><p><strong>注意: 容器保护机制让docker 只可以删除已经停止的镜像</strong></p><h3>6. 查看容器启动的日志</h3><table><thead><tr><th>命令功能</th><th>命令行代码</th></tr></thead><tbody><tr><td>查看容器日志</td><td>docker logs &lt;容器的ID&gt;</td></tr></tbody></table><h2>4. 交互式启动</h2><h3>1. 什么是交互式启动？</h3><p>交互式启动如字面意思，有交互的启动，具体理解起来可以有如下例子：</p><p>现在我们在docker 中创建了一个ubantu容器，因为Ubantu是Linux系统，这个Linux系统是有交互的，我们启动的时候可以使用如下命令<code>docker run -it -name linux1 ubuntu</code> 我们可以直接进入到这个系统中，并产生了一个交互式控制台输入，这个时候可以调用我们的linux命令进行操作这个容器，这就是交互</p><blockquote><p>思考？</p><p>若我们退出的时候，应该如何再次进入，并产生交互控制台呢？</p></blockquote><p>这个时候就使用到了交互式启动运行</p><table><thead><tr><th>命令功能</th><th>命令代码</th></tr></thead><tbody><tr><td>创建容器的时候并返回一个交互控制台</td><td>docker run -it --name&lt;自定义容器的名称&gt; &lt;要启动的镜像名称&gt;</td></tr><tr><td>退出容器</td><td>exit “如果是run -it启动时退出会关闭容器”</td></tr><tr><td>退出容器</td><td>ctrl + p + q "使用这个命令退出容器不会停止容器的运行"</td></tr><tr><td>重新进入容器，并产生交互控制台(退出不会停止容器)</td><td>docker exec -it &lt;容器的ID&gt; /bin/bash</td></tr><tr><td>重新进入重启，并产生控制台(退出会停止容器)</td><td>docker attach &lt;容器的ID&gt; /bin/bash</td></tr></tbody></table>
]]></content:encoded>
<slash:comments>0</slash:comments>
<comments>https://blog.anlucky.cn/index.php/programming/tools/153#comments</comments>
<wfw:commentRss>https://blog.anlucky.cn/index.php/feed/tag/%E7%BC%96%E7%A8%8B%E5%B7%A5%E5%85%B7/</wfw:commentRss>
</item>
<item>
<title>SpringSecurity安全框架基础入门（前后端分离）</title>
<link>https://blog.anlucky.cn/index.php/programming/java/148</link>
<guid>https://blog.anlucky.cn/index.php/programming/java/148</guid>
<pubDate>Tue, 16 May 2023 14:09:00 +0800</pubDate>
<dc:creator>都依凡</dc:creator>
<description><![CDATA[SpringSecurity安全框架基础入门（前后端分离）1. 环境配置SpringBoot 2.7.11Maven 3.6.12. 依赖包        &lt;dependency&gt; ...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<h1>SpringSecurity安全框架基础入门（前后端分离）</h1><h2>1. 环境配置</h2><ul><li>SpringBoot 2.7.11</li><li>Maven 3.6.1</li></ul><h2>2. 依赖包</h2><pre><code class="lang-xml">        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;


        &lt;dependency&gt;
            &lt;groupId&gt;com.baomidou&lt;/groupId&gt;
            &lt;artifactId&gt;mybatis-plus-boot-starter&lt;/artifactId&gt;
            &lt;version&gt;3.5.3.1&lt;/version&gt;
        &lt;/dependency&gt;


        &lt;dependency&gt;
            &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
            &lt;artifactId&gt;lombok&lt;/artifactId&gt;
        &lt;/dependency&gt;


        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-data-redis&lt;/artifactId&gt;
        &lt;/dependency&gt;


        &lt;dependency&gt;
            &lt;groupId&gt;io.jsonwebtoken&lt;/groupId&gt;
            &lt;artifactId&gt;jjwt&lt;/artifactId&gt;
            &lt;version&gt;0.9.1&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;mysql&lt;/groupId&gt;
            &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
            &lt;version&gt;5.1.49&lt;/version&gt;
        &lt;/dependency&gt;


        &lt;dependency&gt;
            &lt;groupId&gt;com.alibaba&lt;/groupId&gt;
            &lt;artifactId&gt;druid-spring-boot-starter&lt;/artifactId&gt;
            &lt;version&gt;1.2.16&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;
        &lt;/dependency&gt;</code></pre><h2>3. 数据库配置</h2><pre><code class="lang-sql">CREATE DATABASE /*!32312 IF NOT EXISTS*/`school_secrity` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;

USE `school_secrity`;

/*Table structure for table `user` */

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT &#039;用户ID&#039;,
  `username` varchar(20) NOT NULL COMMENT &#039;用户名&#039;,
  `password` varchar(512) NOT NULL COMMENT &#039;用户密码&#039;,
  `roler` char(2) NOT NULL DEFAULT &#039;0&#039; COMMENT &#039;0 普通用户&#039;,
  `isDelete` char(1) DEFAULT &#039;0&#039; COMMENT &#039;0 存在 1删除&#039;,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;</code></pre><h2>4. SpringBoot配置文件</h2><pre><code class="lang-yml">server:
  port: 8080
  servlet:
    context-path: /
# mybatis plus配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 日志输出
  global-config:
    db-config:
      logic-delete-field: isDelete # 逻辑删除 需要加一个注解 @TableLogic
      logic-not-delete-value: 0 # 未删除逻辑值
      logic-delete-value: 1 # 已经删除逻辑值
# spring 配置
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver # 数据库连接驱动
    url: jdbc:mysql://localhost:3306/school_secrity # 数据库Url
    username: root # 数据库账号
    password: 123456 # 数据库密码
  thymeleaf:
    cache: false # 不使用缓存

  redis:
    host: 192.168.244.128
    port: 6379</code></pre><h2>5. 认证流程</h2><h3>1. 使用之前的配置</h3><p>若不理解 JWT是什么可以先去了解下 JWT是什么再来看本篇文章</p><p>在使用之前，首先要关闭 spring security 默认的登录页面</p><p>因为是前后端分离，我们就可以不使用session域进行存储用户的登录态，这里使用的 JWT认证登录态</p><p>因为是前后端分离，所以我们就可以在登录成功之后使用 JWT给用户返回一个登录的令牌，来认证登录信息</p><p>然后我们要指定我们自己的登录接口，和使用spring security自带的一个密码加密对象所以就有了如下的配置文件</p><pre><code class="lang-java">@Configuration
public class SecrityConfig extends WebSecurityConfigurerAdapter {
    
    /**
     * 密码加密对象
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    
    /**
     * 前后端分离项目,配置不使用Session 和 关闭 csrf 和 指定登录接口
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable() // 关闭scrf
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)// 不使用Session
                .and()
                .authorizeRequests()
                .antMatchers(&quot;/user/login&quot;).anonymous() // 登录接口可以匿名访问
                .anyRequest().authenticated(); // 其他接口必须已经登录


        // 配置认证过滤器 在这里可以先忽略这个
        // http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
    }


    @Bean
    @Override  // 身份验证管理器
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}</code></pre><h3>2. 编写认证实体类</h3><p>在spring security中认证有专门的实体类封装数据，所以我们需要将实体类编写，这里需要实现接口<code>UserDetails</code>即有了如下实体类</p><pre><code class="lang-java">/**
 * 创建用户登录的对象
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser implements UserDetails {
    
    /**
     * 登录的用户对象
     * 将我们自己编写的表对象实体注入到里面
     */
    private User user;

    /**
     * 权限列表，这里认证流程可以忽略
     * @return
     */
    @Override
    public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {
        return null;
    }

    /**
     * 获取密码
     * @return
     */
    @Override
    public String getPassword() {
        return user.getPassword();
    }

    /**
     * 获取用户名
     * @return
     */
    @Override
    public String getUsername() {
        return user.getUsername();
    }

    /**
     * 账户是未过期的
     * @return
     */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    /**
     * 账户是未被锁定的
     * @return
     */
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /**
     * 凭据是未过期的
     * @return
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /**
     * 账户已启用的
     * @return
     */
    @Override
    public boolean isEnabled() {
        return true;
    }
}</code></pre><h3>3. 使用MybatisPlus生成Service和Mapper和表实体类</h3><p>这里忽略不讲，使用逆向工程生成，或使用插件生成，或自己编写都可以</p><h3>4.重写默认登录接口</h3><p>在SpriingSecurity中有一个默认的登录接口，使用的是他自己的用户名和密码，我们需要重写，编程查询数据库得到用户名和密码</p><p>所以我们的Service层或单独的一个登录接口中要实现接口<code>UserDetailsService</code></p><blockquote>这里使用的是MybatisPlus生成的Service,在生成的Service中又实现了接口<code>UserDetailsService</code> 可以根据自己的习惯进行更改</blockquote><p>所以就有了如下实现类</p><pre><code class="lang-java">@Service
public class UserServiceImpl extends ServiceImpl&lt;UserMapper, User&gt;
    implements UserService, UserDetailsService {
    /**
     * 重写登录接口
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println(&quot;执行重写登录接口&quot;);
        LambdaQueryWrapper&lt;User&gt; wrapper = new LambdaQueryWrapper&lt;&gt;();
        wrapper.eq(User::getUsername,username);
        // 数据库查询用户对象
        User user = baseMapper.selectOne(wrapper);

        if (Objects.isNull(user)){
            throw new RuntimeException(&quot;用户名或者密码错误&quot;);
        }
        
        return new LoginUser(user);
    }
}</code></pre><h3>5. 编写自己的Controller和Service业务层</h3><p>我们在<code>SecrityConfig</code>配置文件中声明了自己的Controller接口请求URL<code>/user/login</code> 所以我们在定义的时候也要写这样的URL，所以有了</p><p>如下的Controller类</p><pre><code class="lang-java">@RestController
@RequestMapping(&quot;/user&quot;)
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(&quot;/login&quot;)
    public Result login(@RequestBody User user){
        Result login = userService.login(user);
        return login;
    }
}</code></pre><p>Service层中添加代码</p><p>在接口中需要自己添加此方法的接口</p><pre><code class="lang-java">    @Override
    public Result login(User user) {
        System.out.println(&quot;进入Login&quot;);
        // 将前端传输过来的用户对象封装成Springsecurity可以使用的对象
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword());
        
        // 认证管理器，在配置文件中注释掉了，可以放开，并且添加
        Authentication authenticate = null;
        try {
            authenticate = authenticationManager.authenticate(authenticationToken);
        }catch (Exception e){
            String message = e.getMessage();
            System.out.println(&quot;message = &quot; + message);
        }


        System.out.println(&quot;authenticate = &quot; + authenticate);
        if (Objects.isNull(authenticate)){
            throw new RuntimeException(&quot;用户名或者密码错误&quot;);
        }
        // 通过DeBug看 authenticate.getPrincipal(); 本质就是我们重写之后的LoginUser类对象，所以可以强转
        System.out.println(&quot;认证通过&quot;);
        LoginUser principal = (LoginUser) authenticate.getPrincipal();
        Map&lt;String, Object&gt; map = new HashMap&lt;&gt;();
        map.put(&quot;id&quot;,principal.getUser().getId());
        // 生成用户的JWT token
        String jwtToken = JWTUtils.getJwtToken(map);
        map.clear();
        map.put(&quot;token&quot;,jwtToken);
        return new Result(200,&quot;成功&quot;,map);
    }</code></pre><h3>6.注意事项</h3><p>其中我们要注意我们的密码加密对象<code>passwordEncoder</code></p><p>在此框架中，我们只需要将此对象注入即可以实现在密码校验上的自动校验，但是在密码存储时，需要我们自己调用encoder方法进行加密后存储</p><h3>7. 认证过滤器</h3><p>我们的登录接口是被配置文件放行的，所以我们可以直接访问，直接进入登录接口进行登录，若我们想要使用JWT生成的令牌进行登录态验证，我们就需要配置一个认证的过滤器，这个过滤器是在每次调用service层前执行的，所以就可以对我们每次请求过来的令牌进行认证，用户是否登录</p><p>所以就有如下Java代码</p><pre><code class="lang-java">/**
 * JWT过滤器
 */
@Component
public class JwtFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
         // 从请求头中获取token
        String token = request.getHeader(&quot;token&quot;);
        if (Strings.hasText(token)){
            try {
                Jws&lt;Claims&gt; claimsJws = JWTUtils.parseJwtToken(token);
                Object id = claimsJws.getBody().get(&quot;id&quot;);
                System.out.println(&quot;用户的id = &quot; + id);
                // 通过用户ID去redis缓存中拿用户的信息

                // 若用户不存在，那么是未登录 存在则已经登录

                // 封装用户信息
                UsernamePasswordAuthenticationToken authenticationToken
                        = new UsernamePasswordAuthenticationToken(&quot;admin&quot;,&quot;123456&quot;,null);
                // 其中这里的admin 123456 是需要从redis中获取到的数据装进去的并不是固定的
                // 放入上下文对象
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);

            } catch (SignatureException e) {
                e.printStackTrace();
            }
        }
        // 放行
        filterChain.doFilter(request,response);
    }
}</code></pre><blockquote>OncePerRequestFilter 我们实现这个接口之后，这个过滤器是只被执行一次的，因为过滤器在特殊情况下，是可能执行多次的</blockquote><h2>6. 认证代码执行顺序</h2><ol><li>前端请求控制器，因有过滤器的缘故，会直接进入过滤器</li><li>进入过滤器，因我们是登录接口，是没有token信息的，那么我们在过滤器中判断没有取到token将直接放行请求去service层登录</li><li>接下来会正式进入controller中，执行我们的代码逻辑，从controller中执行到我们的service层</li><li>进入service层，会调用我们自己的login方法，然后方法中会将我们的前端传输过来的用户对象封装成spring security可以识别的用户对象</li><li>然后使用我们的认证管理器对象，进行认证管理，没有抛出异常证明认证没有错误，我们需要调用认真管理器对象中的方法获取到我们的认证对象的信息</li><li>接下来就是判断对象是否为null 然后对我们的对象进行简单的封装，使用 JWT生成令牌并且返回</li><li>在之后的每次请求中需要带入令牌进行请求</li></ol><h2>7. 用户授权</h2><p>首选需要在登录的基础上进行授权，授权目的是判断用户是否有权限进行操作这个接口，或是否有权限进行这个操作</p><p>在上面的基础上我们在SecurityConfig配置文件中的类上加上注解，开启注解方式的授权</p><pre><code class="lang-java">@EnableGlobalMethodSecurity(prePostEnabled = true)//开启注解形式的授权功能</code></pre><h3>1. 简单的实现授权</h3><p>在controller中新创建一个test的控制器，有如下代码</p><pre><code class="lang-java">    @RequestMapping(&quot;/test&quot;)
    @PreAuthorize(&quot;hasAnyAuthority(&#039;user&#039;)&quot;) // 判断当前是否有user权限
    public Object test(){
        return &quot;123123123&quot;;
    }</code></pre><p>我们去调用这个方法，并且带入我们的token会提示我们没有权限，那么我们就实现了一个简单的权限添加</p><p>接下来是如何给用户添加权限</p><h3>2. 给用户添加权限</h3><p>我们在编写用户登录的实体类中，我们有一个获取用户权限的方法<code>getAuthorities</code>，所以我们要使用这个方法，将用户的权限封装到这里，即就可以更改LoginUser对象，加入一个属性</p><pre><code class="lang-java">    /**
     * 用户的权限集合
     */
    private Set&lt;String&gt; set;
    //LoginUserDetails要被存储到redis 但redis要求使用的数据必须序列化 但SimpleGrantedAuthority类型不是序列化
    //需要忽略此属性
    @JSONField(serialize = false)
    private List&lt;SimpleGrantedAuthority&gt; authorities; // 用于封装
     // 构造方法需要写一个不带authorities 只有 set 和 user 两个对象的构造
    public LoginUser(User user, Set&lt;String&gt; set) {
        this.user = user;
        this.set = set;
    }</code></pre><h3>3. 封装权限获取方法</h3><pre><code class="lang-java">    /**
     * @return
     */
    @Override
    public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {
        if (list!=null){
            return list;
        }
        list = new ArrayList&lt;&gt;();
        for (String s : set) {
            list.add(new SimpleGrantedAuthority(s));
        }
        return list;
    }</code></pre><h3>4. 修改过滤器中代码</h3><p>我们将对象中添加的权限信息，那么在每次请求非登录接口时都需要带上我们的权限，让我们的权限认证系统去认证是否有权限继续执行</p><p>那么将在过滤器中修改如下代码</p><pre><code class="lang-java">    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        log.info(&quot;进入过滤器&quot;);
        // 从请求头中获取token
        String token = request.getHeader(&quot;token&quot;);
        if (Strings.hasText(token)){
            try {
                Jws&lt;Claims&gt; claimsJws = JWTUtils.parseJwtToken(token);
                Object id = claimsJws.getBody().get(&quot;id&quot;);
                System.out.println(&quot;用户的id = &quot; + id);
                // 通过用户ID去redis缓存中拿用户的信息

                // 若用户不存在，那么是未登录 存在则已经登录

                // 封装用户信息
                // 获取用户权限信息，这里需要查询数据库获取
                ArrayList&lt;SimpleGrantedAuthority&gt; list = new ArrayList&lt;&gt;();
                list.add(new SimpleGrantedAuthority(&quot;test&quot;));
                list.add(new SimpleGrantedAuthority(&quot;user&quot;));
                UsernamePasswordAuthenticationToken authenticationToken
                        = new UsernamePasswordAuthenticationToken(&quot;admin&quot;,&quot;123456&quot;,list);
                // 放入上下文对象
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);

            } catch (SignatureException e) {
                e.printStackTrace();
            }
        }
        // 放行
        filterChain.doFilter(request,response);
    }</code></pre><h2>8. 自定义异常处理</h2><h3>1. 认证失败处理器</h3><pre><code class="lang-java">/**
 * 认证失败异常处理
 */
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse
            response, AuthenticationException authException) throws IOException,
            ServletException {
        Result result= new Result(401, &quot;用户认证失败！&quot;, null);
        response.getWriter().println(result);
    }
}</code></pre><h3>2. 授权失败处理器</h3><pre><code class="lang-java">/**
 * 授权失败异常处理
 */
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        Result result = new Result(400, &quot;授权失败&quot;, null);
        response.getWriter().println(result);
    }
}</code></pre><h3>3. 更改配置类</h3><p>在上面两个异常处理器处理完成后我们需要将异常处理类的对象注入到security的配置文件中，然后就配置完成了</p><pre><code class="lang-java">@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Autowired
private AuthenticationEntryPointImpl authenticationEntryPointImpl;

//配置异常处理器 这段代码在configure方法中定义
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPointImpl)//认证
.accessDeniedHandler(accessDeniedHandlerImpl);//授权
</code></pre><h2>9. 配置跨域设置</h2><p>配置类</p><pre><code class="lang-java">@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
            //设置允许跨域的路径
        registry.addMapping(&quot;/**&quot;)
                .allowedOriginPatterns(&quot;*&quot;)//允许跨域的域名
                .allowCredentials(true)//允许携带cookie
                .allowedMethods(&quot;GET&quot;,&quot;DELETE&quot;,&quot;PUT&quot;,&quot;POST&quot;)//跨域的请求方式
                .allowedHeaders(&quot;*&quot;)//允许的header属性
                .maxAge(3600);//跨域时间
    }
}</code></pre><p>然后在security中开启跨域，只需要在configure方法中添加如下代码</p><pre><code class="lang-java">//springsecrity开启允许跨域
    http.cors();</code></pre>
]]></content:encoded>
<slash:comments>0</slash:comments>
<comments>https://blog.anlucky.cn/index.php/programming/java/148#comments</comments>
<wfw:commentRss>https://blog.anlucky.cn/index.php/feed/tag/%E7%BC%96%E7%A8%8B%E5%B7%A5%E5%85%B7/</wfw:commentRss>
</item>
<item>
<title>SpringBoot基础入门</title>
<link>https://blog.anlucky.cn/index.php/programming/java/141</link>
<guid>https://blog.anlucky.cn/index.php/programming/java/141</guid>
<pubDate>Sat, 13 May 2023 16:19:00 +0800</pubDate>
<dc:creator>都依凡</dc:creator>
<description><![CDATA[SpringBoot基础入门Spring Boot是由Pivotal团队提供的一套开源框架，可以简化spring应用的创建及部署。它提供了丰富的Spring模块化支持，可以帮助开发者更轻松快捷地...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<h1>SpringBoot基础入门</h1><p>Spring Boot是由Pivotal团队提供的一套开源框架，可以简化spring应用的创建及部署。它提供了丰富的Spring模块化支持，可以帮助开发者更轻松快捷地构建出企业级应用。Spring Boot通过自动配置功能，降低了复杂性，同时支持基于JVM的多种开源框架，可以缩短开发时间，使开发更加简单和高效。（SpringBoot简化开发，可以快速部署SSM项目，整合了很多个配置，使用的时候只需要加上 Jar 包和对应的配置注解即可实现配置，Springboot内置了Tomcat）</p><h2>1. 构建SpringBoot项目</h2><blockquote>这里使用的IDEA编辑器，每个编辑器的构建方式可能有些不同，但是大致相同，使用别的编辑器的同学可以拿这篇文章作为参考</blockquote><h3>方式一：使用官网构建</h3><p>使用官网页面，直接下载springboot项目，使用流程如下</p><p>官网： <a href="https://start.spring.io/">https://start.spring.io/</a></p><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/4174152799.png" alt="137" title="137"></p><h3>方式二：使用IDEA构建</h3><blockquote>在IDEA中点击new project 创建新项目，然后根据需求构建项目</blockquote><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/3871765991.png" alt="138" title="138"></p><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/327866801.png" alt="139" title="139"></p><blockquote><p>注意：</p><p>​    使用此方法构建的SpringBoot和方式一构建的方式不同点在于一个是下载了一个压缩包文件，一个是直接在IDEA开发工具中下载，区别不大</p></blockquote><h3>方式三：自己配置</h3><blockquote><p>自己配置SpringBoot对于新手来说不太友好，要求对Jar包的熟悉程度比较高，个人不太推荐，大佬忽略，这里没有具体的配置方式</p><p>配置方式：</p><p>​    使用IDEA工具构建一个新的Maven工程，然后在POM文件中添加SpringBoot的相关依赖，自己创建Application启动类和配置文件</p></blockquote><pre><code>注意：
    Mybatis-Plus不可以直接勾选Jar包进行依赖引入，需要我们自己去官网选择版本引入</code></pre><h3>目录结构</h3><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/1552494730.png" alt="140" title="140"></p><h2>2. SpringBoot核心配置</h2><p>SpringBoot有两种配置文件结尾，<code>application.properties</code> <code>application.yml</code></p><p>yml 是一种 yaml 格式的配置文件，主要采用一定的空格、换行等格式排版进行配置。</p><p>yaml 是一种直观的能够被计算机识别的的数据序列化格式，容易被人类阅读，yaml 类似于 xml，但是语法比 xml 简洁很多，<strong>值与前面的冒号配置项必须要有一个空格</strong>， yml 后缀也可以使用 yaml 后缀。</p><blockquote><p>注意：</p><p>​    SpringBoot项目的配置文件必须使用<code>application</code>名称，否则不识别</p></blockquote><h3>1. SpringBoot基本配置</h3><pre><code class="lang-yml"># SpringBoot的核心配置文件
server:
  port: 8080  # 指定项目运行的端口
  servlet:
    context-path: /  # 指定项目的运行路径
    encoding:
      charset: utf-8 # 指定项目的编码集
spring:
  profiles:
    active: dev # 多环境下使用的环境是哪个，不写就默认 application</code></pre><h3>2. 多环境配置</h3><p>在实际开发的过程中，我们的项目会经历很多的阶段（开发-&gt;测试-&gt;上线），每个阶段的配置也会不 同，例如：端口、上下文根、数据库等，那么这个时候为了方便在不同的环境之间切换，SpringBoot 提供了多环境配置</p><blockquote><p>为每个环境创建一个配置文件，命名必须以 application-环境标识.properties|yml</p><p>例如：</p><ol><li><code>application-dev.yml</code></li><li><code>application-test.yml</code></li></ol></blockquote><h3>3. 自定义配置</h3><p>SpringBoot 的核心配置文件中，除了使用内置的配置项之外，我们还可以在自定义配置，然后采用注解去读取配置的属性值</p><p>如：</p><p>​    在<code>application.yml配置文件中</code>有如下配置</p><pre><code class="lang-yml">school:
  name: 清华大学
  address: 北京</code></pre><p>那么我们就可以在项目中使用</p><pre><code class="lang-java">    @Value(&quot;${school.name}&quot;) // 注入属性
    private String schoolName; </code></pre><h2>3. SpringBoot事务管理</h2><p>Spring Boot 使用事务非常简单，底层依然采用的是 Spring 本身提供的事务管理</p><p>在入口类中使用注解 @EnableTransactionManagement 开启事务支持</p><p>在访问数据库的 Service 方法上添加注解 @Transactional 即可</p><h2>4. SpringBoot整合mybatisPlus、thymeleaf、druid、redis、Dubbo配置</h2><pre><code class="lang-yml"># SpringBoot的核心配置文件
server:
  port: 8080  # 指定项目运行的端口
  servlet:
    context-path: /  # 指定项目的运行路径
    encoding:
      charset: UTF-8 # 指定项目的编码集
      enabled: true # 开启为 true
      force: true # 强制使用

# Spring 配置
spring:
  application:
    name: demo # spring项目名称
  profiles:
    active: dev # 使用的环境是哪个
  thymeleaf:
    cache: false # thymeleaf 不使用缓存
  datasource: # 数据库连接配置
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/ssm
    type: com.alibaba.druid.pool.DruidDataSource # 使用的数据库连接池需要导入 jar 包 druid-spring-boot-starter
    druid: # 可以不配置，有默认值
      initial-size: 5 # 初始化连接数
      min-idle: 5 # 最小连接数
      max-active: 20 # 最大连接数
      max-wait: 60000 # 获取数据库连接最大等待时间 单位毫秒
      time-between-eviction-runs-millis: 60000 # 配置多久进行一次检测，检测需要关闭的空闲连接 单位毫秒
  redis:  # redis 配置
    host: localhost
    port: 6379
#    password:  # redis密码，没有注释掉

# mybatis-plus 配置
mybatis-plus:
  mapper-locations: classpath*:/mapper/**/*.xml # 默认值，扫描mapper.xml文件
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 日志输出
  global-config:
    db-config:
      logic-delete-field: isDelete # 逻辑删除 需要加一个注解 @TableLogic
      logic-not-delete-value: 0 # 未删除逻辑值
      logic-delete-value: 1 # 已经删除逻辑值

# Dubbo服务提供者
#dubbo:
#  application:
#    name: provide
#  registry:
#    address: zookeeper://localhost:2181
#  scan:
#    base-packages: com.dyf # 这里配置Dubbo的注解所在包,扫描的是接口实现类


# Dubbo消费者
#dubbo:
#  application:
#    name: cosumer
#  registry:
#    address: zookeeper://localhost:2181
#  scan:
#    base-packages: com.dyf # 这里配置Dubbo的注解所在包,扫描的是controller</code></pre><h2>5. SpringBoot整合MybatisPlus分页插件配置类</h2><pre><code class="lang-java">@Configuration
public class MyBatisPlusConfig {
    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 这里需要指定数据库类型 这里使用的Mysql
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}</code></pre><h2>6. 整合所需要的 jar 包</h2><p>导入SpringBoot 父类项目</p><pre><code class="lang-xml">    &lt;parent&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
        &lt;version&gt;2.7.11&lt;/version&gt;
        &lt;relativePath/&gt; &lt;!-- lookup parent from repository --&gt;
    &lt;/parent&gt;</code></pre><p>导入所需要的 Jar 包</p><pre><code class="lang-xml">    &lt;dependencies&gt;
        &lt;!--redis--&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-data-redis&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;!--springboot Web包--&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;!--springBoot 测试包--&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;!--Druid 数据库连接池包--&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.alibaba&lt;/groupId&gt;
            &lt;artifactId&gt;druid-spring-boot-starter&lt;/artifactId&gt;
            &lt;version&gt;1.2.16&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;!--mybatisPlus包--&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.baomidou&lt;/groupId&gt;
            &lt;artifactId&gt;mybatis-plus-boot-starter&lt;/artifactId&gt;
            &lt;version&gt;3.5.3.1&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;!--redis Jar包--&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-data-redis&lt;/artifactId&gt;
        &lt;/dependency&gt;

        &lt;!--Dubbo Jar包--&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.apache.dubbo&lt;/groupId&gt;
            &lt;artifactId&gt;dubbo-spring-boot-starter&lt;/artifactId&gt;
            &lt;version&gt;2.7.8&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;!--zookeep包，注册中心，整合Dubbo时候使用--&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.apache.dubbo&lt;/groupId&gt;
            &lt;artifactId&gt;dubbo-dependencies-zookeeper&lt;/artifactId&gt;
            &lt;version&gt;2.7.8&lt;/version&gt;
            &lt;type&gt;pom&lt;/type&gt;
            &lt;!--取消引入这两个日志框架，若引入控制台会报错，Jar重复引入--&gt;
            &lt;exclusions&gt;
                &lt;exclusion&gt;
                    &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
                    &lt;artifactId&gt;slf4j-reload4j&lt;/artifactId&gt;
                &lt;/exclusion&gt;
                &lt;exclusion&gt;
                    &lt;groupId&gt;log4j&lt;/groupId&gt;
                    &lt;artifactId&gt;log4j&lt;/artifactId&gt;
                &lt;/exclusion&gt;
            &lt;/exclusions&gt;
        &lt;/dependency&gt;

    &lt;/dependencies&gt;

    &lt;build&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
                &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
                &lt;configuration&gt;
                    &lt;excludes&gt;
                        &lt;exclude&gt;
                            &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
                            &lt;artifactId&gt;lombok&lt;/artifactId&gt;
                        &lt;/exclude&gt;
                    &lt;/excludes&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;</code></pre>
]]></content:encoded>
<slash:comments>0</slash:comments>
<comments>https://blog.anlucky.cn/index.php/programming/java/141#comments</comments>
<wfw:commentRss>https://blog.anlucky.cn/index.php/feed/tag/%E7%BC%96%E7%A8%8B%E5%B7%A5%E5%85%B7/</wfw:commentRss>
</item>
<item>
<title>IDEA中如何将方法的注释一并重写</title>
<link>https://blog.anlucky.cn/index.php/programming/java/133</link>
<guid>https://blog.anlucky.cn/index.php/programming/java/133</guid>
<pubDate>Thu, 11 May 2023 21:37:54 +0800</pubDate>
<dc:creator>都依凡</dc:creator>
<description><![CDATA[1. 需求分析在我们设计接口的时候，通常情况下会将注释一并加上去，但是我们的接口中的注释往往不会跟着我们的接口实现而重写过来，每次都需要拷贝，非常麻烦，接下来就是一个如何设置IDEA将注释也一并...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<h2>1. 需求分析</h2><ol><li>在我们设计接口的时候，通常情况下会将注释一并加上去，但是我们的接口中的注释往往不会跟着我们的接口实现而重写过来，每次都需要拷贝，非常麻烦，接下来就是一个如何设置IDEA将注释也一并重写过来</li></ol><h2>2. 设置方式</h2><h3>1. 现有接口</h3><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/4191379170.png" alt="128" title="128"></p><h3>2. 在实现类中重写</h3><p>只需要勾选重写窗口中的 <code>Copy JavaDoc</code> 即可</p><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/2766119068.png" alt="129" title="129"></p><blockquote><p>注意：</p><p>​        单行注释和多行注释都不支持被重写，只支持文档注释，下面会介绍Java中的注释</p></blockquote><h2>3. Java中的注释</h2><h3>1. 普通注释</h3><p>普通注释的注释方法 <code>//</code></p><p>如：</p><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/529472937.png" alt="130" title="130"></p><blockquote>普通注释只可以注释单行内容</blockquote><h3>2. 多行注释</h3><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/363565862.png" alt="131" title="131"></p><blockquote>多行注释可以注释多行内容，需要在注释的范围内，不可以超越范围</blockquote><h3>3. 文档注释</h3><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/1888091903.png" alt="132" title="132"></p><blockquote><p>文档注释也可以多行，文档注释加在方法上面可以生成一些方法上面的内容，如参数 和返回值，并且支持被重写复制到实现类上</p><p>如何快捷生成：</p><p>在IDEA中（其他工具方法可能不同），在方法上面输入 <code>/**</code> 然后按一下<code>回车</code>就可以生成了</p></blockquote>
]]></content:encoded>
<slash:comments>0</slash:comments>
<comments>https://blog.anlucky.cn/index.php/programming/java/133#comments</comments>
<wfw:commentRss>https://blog.anlucky.cn/index.php/feed/tag/%E7%BC%96%E7%A8%8B%E5%B7%A5%E5%85%B7/</wfw:commentRss>
</item>
<item>
<title>Mysql56个民族数据库</title>
<link>https://blog.anlucky.cn/index.php/programming/database/127</link>
<guid>https://blog.anlucky.cn/index.php/programming/database/127</guid>
<pubDate>Sat, 06 May 2023 17:02:00 +0800</pubDate>
<dc:creator>都依凡</dc:creator>
<description><![CDATA[Mysql 56个民族数据库表1. 创建数据库-- 创建数据库create database [数据库名称] charset=utf8;-- 使用数据库use database [数据库名称];...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<h1>Mysql 56个民族数据库表</h1><h2>1. 创建数据库</h2><pre><code class="lang-sql">-- 创建数据库
create database [数据库名称] charset=utf8;
-- 使用数据库
use database [数据库名称];</code></pre><h2>2. 创建民族表</h2><pre><code class="lang-sql">create table [表名称](
    `id` varchar(32) primary key comment &#039;民族id&#039;,
    `nation` varchar(64) comment &#039;民族名称&#039;
);</code></pre><h2>3. 添加民族数据</h2><pre><code class="lang-sql">INSERT INTO `[表名称]` VALUES (&#039;1&#039;, &#039;汉族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;2&#039;, &#039;蒙古族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;3&#039;, &#039;回族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;4&#039;, &#039;藏族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;5&#039;, &#039;维吾尔族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;6&#039;, &#039;苗族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;7&#039;, &#039;彝族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;8&#039;, &#039;壮族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;9&#039;, &#039;布依族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;10&#039;, &#039;朝鲜族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;11&#039;, &#039;满族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;12&#039;, &#039;侗族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;13&#039;, &#039;瑶族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;14&#039;, &#039;白族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;15&#039;, &#039;土家族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;16&#039;, &#039;哈尼族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;17&#039;, &#039;哈萨克族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;18&#039;, &#039;傣族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;19&#039;, &#039;黎族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;20&#039;, &#039;傈僳族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;21&#039;, &#039;佤族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;22&#039;, &#039;畲族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;23&#039;, &#039;高山族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;24&#039;, &#039;拉祜族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;25&#039;, &#039;水族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;26&#039;, &#039;东乡族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;27&#039;, &#039;纳西族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;28&#039;, &#039;景颇族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;29&#039;, &#039;柯尔克孜族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;30&#039;, &#039;土族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;31&#039;, &#039;达翰尔族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;32&#039;, &#039;么佬族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;33&#039;, &#039;羌族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;34&#039;, &#039;布朗族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;35&#039;, &#039;撒拉族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;36&#039;, &#039;毛南族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;37&#039;, &#039;仡佬族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;38&#039;, &#039;锡伯族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;39&#039;, &#039;阿昌族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;40&#039;, &#039;普米族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;41&#039;, &#039;塔吉克族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;42&#039;, &#039;怒族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;43&#039;, &#039;乌孜别克族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;44&#039;, &#039;俄罗斯族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;45&#039;, &#039;鄂温克族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;46&#039;, &#039;德昂族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;47&#039;, &#039;保安族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;48&#039;, &#039;裕固族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;49&#039;, &#039;京族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;50&#039;, &#039;塔塔尔族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;51&#039;, &#039;独龙族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;52&#039;, &#039;鄂伦春族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;53&#039;, &#039;赫哲族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;54&#039;, &#039;门巴族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;55&#039;, &#039;珞巴族&#039;);
INSERT INTO `[表名称]` VALUES (&#039;56&#039;, &#039;基诺族&#039;);</code></pre><h2>4. 使用技巧</h2><p>将添加的数据的语句复制到记事本中，使用记事本中的替换功能进行替换</p><p>快捷键：<code>Ctrl + h</code></p><p><img src="http://blog.anlucky.cn/usr/uploads/2023/05/3028700854.png" alt="126" title="126"></p><p>然后在数据库中执行SQL语句即可完成</p>
]]></content:encoded>
<slash:comments>0</slash:comments>
<comments>https://blog.anlucky.cn/index.php/programming/database/127#comments</comments>
<wfw:commentRss>https://blog.anlucky.cn/index.php/feed/tag/%E7%BC%96%E7%A8%8B%E5%B7%A5%E5%85%B7/</wfw:commentRss>
</item>
<item>
<title>Java工具类-发送邮件(支持HTM)</title>
<link>https://blog.anlucky.cn/index.php/programming/java/124</link>
<guid>https://blog.anlucky.cn/index.php/programming/java/124</guid>
<pubDate>Fri, 28 Apr 2023 16:17:28 +0800</pubDate>
<dc:creator>都依凡</dc:creator>
<description><![CDATA[Java工具类-发送邮件支持HTM1. 导入Maven坐标  &lt;dependency&gt;        &lt;groupId&gt;javax.mail&lt;/groupId&gt...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<h1>Java工具类-发送邮件支持HTM</h1><h2>1. 导入Maven坐标</h2><pre><code class="lang-xml">  &lt;dependency&gt;
        &lt;groupId&gt;javax.mail&lt;/groupId&gt;
        &lt;artifactId&gt;mail&lt;/artifactId&gt;
        &lt;version&gt;1.4.7&lt;/version&gt;
    &lt;/dependency&gt;</code></pre><h2>2. 创建Java发送邮件工具类</h2><pre><code class="lang-java">/**
 * 发送邮件工具类
 */
public class SendEmailUtils {
    
    private String user; // 发件人称号，同邮箱地址
    private String password; // 如果是qq邮箱可以使户端授权码，或者登录密码
    private String host;    // 指定发送邮件的主机QQ为 smtp.qq.com

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    /**
     * 创建邮件发送的对象
     *
     * @param USER     发件人称号，同邮箱地址
     * @param PASSWORD 如果是qq邮箱可以使户端授权码，或者登录密码
     * @param HOST     指定发送邮件的主机QQ为 smtp.qq.com
     */
    public SendEmailUtils(String USER, String PASSWORD, String HOST) {
        this.user = USER;
        this.password = PASSWORD;
        this.host = HOST;
    }

    /**
     * 发送邮件 支持HTML标签
     * @param toEmail 邮件接收方的邮箱
     * @param headText 邮件的头部主题信息
     * @param bodyText 邮件的主体部分实质内容
     * @return 成功返回true 失败返回false
     */
    public boolean send(String toEmail, String headText, String bodyText) {
        // 获取系统属性
        Properties properties = System.getProperties();
        // 设置邮件服务器
        properties.setProperty(&quot;mail.smtp.host&quot;, getHost());
        properties.put(&quot;mail.smtp.auth&quot;, &quot;true&quot;);
        // 获取默认session对象
        Session session = Session.getDefaultInstance(properties, new Authenticator() {
            public PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(getUser(), getPassword()); //发件人邮件用户名、授权码
            }
        });
        boolean success = true;
        try {
            // 创建默认的 MimeMessage 对象
            MimeMessage message = new MimeMessage(session);

            // Set From: 头部头字段
            message.setFrom(new InternetAddress(user));

            // Set To: 头部头字段
            message.addRecipient(Message.RecipientType.TO,
                    new InternetAddress(toEmail));

            // Set Subject: 头部头字段
            message.setSubject(headText);

            // 设置邮件的内容体
            message.setContent(bodyText, &quot;text/html;charset=UTF-8&quot;);

            // 发送消息
            Transport.send(message);

        } catch (MessagingException mex) {
            success =false;
            mex.printStackTrace();
        }

        return success;

    }
}</code></pre><h2>3. 使用Java工具类</h2><ol><li>QQ邮箱授权码需要在QQ邮箱中获取</li><li>别的邮箱的授权码可以百度搜索在哪里获取，一般情况下QQ使用的比较多</li></ol><pre><code class="lang-java">    public static void main(String[] args) {
        String user = &quot;xxx@qq.com&quot;; // 发件人称号，同邮箱地址
        String password = &quot;邮箱授权码&quot;; // 如果是qq邮箱可以使户端授权码，或者登录密码
        String host = &quot;smtp.qq.com&quot;;    // 指定发送邮件的主机QQ为 smtp.qq.com
        SendEmailUtils sendEmailUtils = new SendEmailUtils(user,password,host);
        String toEmail=&quot;xxx@qq.com&quot;;
        String head=&quot;头信息&quot;;
        String body=&quot;&lt;h1&gt;主题信息&lt;/h1&gt;&quot;;
        boolean send = sendEmailUtils.send(toEmail, head, body);
        System.out.println(&quot;send = &quot; + send);
    }</code></pre>
]]></content:encoded>
<slash:comments>0</slash:comments>
<comments>https://blog.anlucky.cn/index.php/programming/java/124#comments</comments>
<wfw:commentRss>https://blog.anlucky.cn/index.php/feed/tag/%E7%BC%96%E7%A8%8B%E5%B7%A5%E5%85%B7/</wfw:commentRss>
</item>
<item>
<title>Java工具类-图片验证码</title>
<link>https://blog.anlucky.cn/index.php/programming/java/123</link>
<guid>https://blog.anlucky.cn/index.php/programming/java/123</guid>
<pubDate>Fri, 28 Apr 2023 14:28:00 +0800</pubDate>
<dc:creator>都依凡</dc:creator>
<description><![CDATA[1. 创建接收实体类这个实体类是可以更变的，具体的实体类可以根据自己的业务需求去编写，这里使用的是一个比较通用一点的实体类来接收，也可以不使用实体类接收，直接使用，不过在日常开发中，不建议这样去...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<h2>1. 创建接收实体类</h2><blockquote>这个实体类是可以更变的，具体的实体类可以根据自己的业务需求去编写，这里使用的是一个比较通用一点的实体类来接收，也可以不使用实体类接收，直接使用，不过在日常开发中，不建议这样去做</blockquote><ul><li>创建实体，并生成他的 Get 和 Set 方法，有参数构造和无参数构造</li></ul><pre><code class="lang-java">public class VerifyCode {
    private String code;   // 验证码文字信息
    private BufferedImage imgs; // 图片验证码信息
}</code></pre><h2>2. 验证码工具类</h2><pre><code class="lang-java">import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;

/**
 * 获取验证码
 */
public class VerifyCodeUtils {

    private int height = 30;  // 图片宽

    private int width = 80;   // 图片高

    private int codeNumber = 4; // 生成的验证码个数

    // 定义有哪些字符验证码
    private String codeString = &quot;23456789abcdefghjkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ&quot;;
    // 定义背景颜色
    private Color backgroundColor = new Color(255, 255, 255);

    // 定义字体
    private String[] fontNames = { &quot;宋体&quot;, &quot;华文楷体&quot;, &quot;黑体&quot;, &quot;微软雅黑&quot;, &quot;楷体_GB2312&quot;};

    // 获取一个验证码，用来生成图片
    public String getCodeText(){
        StringBuffer codeText = new StringBuffer();
        for (int i = 0; i &lt; codeNumber; i++) {
            int randomNumber = getRandomNumber(1,codeString.length());
            codeText.append(codeString.charAt(randomNumber));
        }
        return  codeText.toString();
    }
    // 获取一个随机数字，用来取得codeString中的随机一个字母
    public int getRandomNumber(int start,int end){
        int randomNumber = (int) (Math.random() * (end-start)+start);
        return randomNumber;
    }

    // 生成一个随机颜色
    public Color getRandomColor(){
        int r = getRandomNumber(1,255);
        int g = getRandomNumber(1,255);
        int b = getRandomNumber(1,255);
        Color color = new Color(r,g,b);
        return color;
    }
    // 生成一个随机字体
    public Font getRandomFont(){
        // 字体 如黑体
        String fontStyle = fontNames[getRandomNumber(0,fontNames.length)];
        //  字体加粗斜体 Font.BOLD = 1     Font.ITALIC = 2     Font.BOLD + Font.ITALIC = 3
        int fontBlod = getRandomNumber(1,4);
        // 定义字体大小
        int size = getRandomNumber(24,30);
//        System.out.println(&quot;字体：&quot; + fontStyle + &quot;   加粗：&quot; + fontBlod + &quot;   字体大小：&quot; + size);
        Font font = new Font(fontStyle,fontBlod,size);
        return font;
    }

    // 获取图片验证码
    public VerifyCode getCodeImge(){
        // 创建一个验证码的实体
        VerifyCode verifyCode = new VerifyCode();
        // 创建画板  不带透明色的画板
        BufferedImage bufferedImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        // 获取此画板的画笔
        Graphics graphics = bufferedImage.getGraphics();
        // 设置画笔的颜色
        graphics.setColor(backgroundColor);
        // 填充画板背景颜色
        graphics.fillRect(0,0,width,height);
        // 获取随机验证码文字
        String codeText = getCodeText();
        // 在画板上画出验证码
        for (int i = 0; i &lt; codeNumber; i++) {
            // 给画笔设置一个随机颜色
            graphics.setColor(getRandomColor());
            // 设置一个随机颜色
            graphics.setFont(getRandomFont());
            // 设置验证码的X坐标 当前是第几个字符转为float 乘以画板大小，除以验证码个数等于平均每个验证码的位置
            float x = i*1.0f*width/codeNumber;
            int y = height - getRandomNumber(8,height-(8*2));
            // 将验证码逐个写到图片上
            graphics.drawString(codeText.charAt(i)+&quot;&quot;, (int) x,y);
        }
        // 在实体中添加验证码的信息
        verifyCode.setCode(codeText);
        verifyCode.setImgs(bufferedImage);
        return verifyCode;
    }
}
</code></pre><h2>3. 使用验证码工具类</h2><ul><li>这个代码是JavaServlet中的一个请求，是最基础的web请求和响应方式，在使用中可以根据自己的实际使用场景去使用，这里只做了一个大概的演示</li></ul><pre><code class="lang-java">/**
 * 获取验证码
 */
// 注解servlet注册
@WebServlet(&quot;/getVerify&quot;)
public class VerifyCodeServlet extends ServletUtils {
    // 这里的servlet Utils和继承HttpServlet中的doGet和doPost方法一致
    public void getVerify(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 创建工具类对象
        VerifyCodeUtils verifyCodeUtils = new VerifyCodeUtils();
        // 获取验证码，返回实体对象
        VerifyCode codeImge = verifyCodeUtils.getCodeImge();
        // 查询验证码是什么
        System.out.println(&quot;验证码：&quot; + codeImge.getCode());
        // 在session域中放入验证码
        req.getSession().setAttribute(&quot;registerVerifyCode&quot;,codeImge.getCode());
        // 将图片写到页面上
        ImageIO.write(codeImge.getImgs(),&quot;png&quot;,resp.getOutputStream());
    }
}</code></pre>
]]></content:encoded>
<slash:comments>0</slash:comments>
<comments>https://blog.anlucky.cn/index.php/programming/java/123#comments</comments>
<wfw:commentRss>https://blog.anlucky.cn/index.php/feed/tag/%E7%BC%96%E7%A8%8B%E5%B7%A5%E5%85%B7/</wfw:commentRss>
</item>
<item>
<title>Git版本控制工具入门</title>
<link>https://blog.anlucky.cn/index.php/programming/tools/53</link>
<guid>https://blog.anlucky.cn/index.php/programming/tools/53</guid>
<pubDate>Sun, 16 Apr 2023 19:01:00 +0800</pubDate>
<dc:creator>都依凡</dc:creator>
<description><![CDATA[一 、Git 版本控制工具1. 软件安装官网： Git - 安装 Git (git-scm.com)下载地址： https://git-scm.com/download/win2.设置用户名设置...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<h2>一 、Git 版本控制工具</h2><h3>1. 软件安装</h3><p>官网： <a href="https://git-scm.com/book/zh/v2/起步-安装-Git">Git - 安装 Git (git-scm.com)</a></p><p>下载地址： <a href="https://git-scm.com/download/win">https://git-scm.com/download/win</a></p><p><img src="http://blog.anlucky.cn/usr/uploads/2023/04/1736430454.png" alt="45" title="45"></p><h3>2.设置用户名</h3><p>设置用户名</p><pre><code>git config --global user.name &quot;XXX&quot; # 设置用户名
git config --global user.email &quot;xxx&quot; # 设置邮箱</code></pre><p>查看设置的用户名</p><pre><code>git config --global user.name
git config --global user.email</code></pre><h3>3. 设置常用命令的别名</h3><ol><li>打开用户目录，创建.bashrc文件，可以使用命令窗口创建，命令如下：</li></ol><pre><code>touch ~/.bashrc</code></pre><ol start="2"><li>编辑文件内容</li></ol><pre><code># 用于输出git提交日志
alias git-log=&#039;git log --pretty=oneline --all --graph --abbrev-commit&#039;
#用于输出当前目录所有文件及基本信息
alias ll=&#039;ls -al&#039;</code></pre><ol start="3"><li>使用配置文件，让配置文件生效</li></ol><pre><code>source ~/.bashrc</code></pre><h2>二、基本使用</h2><p>要使用Git对我们的代码进行版本控制，首先需要获得本地仓库</p><h3>创建本地仓库</h3><p>本地仓库可以是任何一个文件夹，都可以是一个本地仓库，可以对这个仓库的文件进行版本控制和内容管理</p><h2>1. 初始化</h2><p>直接在文件夹中打开Git命令管理时此时文件夹并不是一个本地仓库，需要对此文件夹进行初始化，在初始化之后，这个文件夹就变成了本地仓库，就可以使用命令进行管理和控制文件了</p><pre><code># 仓库初始化
git init</code></pre><h2>2. 基础操作命令</h2><p>Git在工作目录下，对于文件的修改会存在下面几个状态，这些状态会随着Git的命令而发生变化</p><h3>2.0 Git管理的仓库文件状态分类</h3><ul><li>当我们在仓库中创建一个文件的时候，此时文件为：<strong>未跟踪状态，这个状态下的文件不在本地仓库，不在Git所管理的范围中</strong></li><li>当我们调用 <code>git add</code> (需要选择哪些文件 <code>.</code> 表示所有修改的文件)这个命令之后，这些文件会被放入到Git的暂存区</li><li>当我们调用<code>git commit</code>这个命令之后，被放入暂存区的文件会被提交到本地仓库，此时这些文件就会被git所管理，就可以进行版本控制了</li></ul><h3>2.1 查看当前仓库文件状态</h3><pre><code># 这个命令可以查询到当前仓库的文件状态（暂存区 / 工作区 / 已提交）
git status</code></pre><h3>2.2 将文件从工作区添加到暂存区</h3><pre><code class="lang-git"> # 这个命令会将指定文件名称的文件放入到暂存区
 git add &lt;文件名称&gt;</code></pre><pre><code class="lang-git"># 这个命令会将所有的已修改的文件放入到暂存区
git add .</code></pre><h3>2.3 将文件从暂存区放入到本地仓库管理</h3><pre><code class="lang-git"># 这个命令会将在暂存区存储的文件放入到本地仓库
git commit -m &quot;XXXX(这个地方是修改的日志)&quot;</code></pre><h3>2.4 查看提交日志</h3><ul><li>若配置了别名，可以使用 git -log 就包含了下面这些参数</li></ul><pre><code class="lang-git">git log -all # 显示所有的分支
git log -pretty=oneline # 将提交的信息显示到一行
git log -abbrev-commit # 使得输出的commitId更简短
git log graph # 以图像形式显示</code></pre><h3>2.5 文件版本回退</h3><pre><code># 这个命令会将仓库中的文件切换到指定的版本时候的样子
# CommitId可以使用log查看
git reset --hard &lt;CommitId&gt;</code></pre><h2>三、Git分支</h2><p>分支可以有效的把当前模块的开发分离出来进行重大的BUG修改，或开发新的功能，以不影响开发的主线</p><h3>1. 查看本地分支</h3><pre><code># 查看当前本地仓库中的分支，每个仓库中的分支是独立的，互不影响
git branch</code></pre><h3>2. 创建本地分支</h3><pre><code class="lang-git"># 在当前本地仓库创建一个分支
git branch &lt;分支的名称&gt;</code></pre><h3>3. 切换分支</h3><p>这个命令可以切换到指定的分子下，切换之后本地仓库文件会显示为当前分支的文件状态</p><pre><code class="lang-git">git checkout &lt;分支名称&gt;</code></pre><p><strong>切换分支高级版：分支不存在创建 + 切换</strong></p><pre><code>git checkout -b &lt;分支名称&gt;</code></pre><h3>4. 合并分支</h3><p>将指定分支的文件内容合并到当前分支</p><pre><code class="lang-git">git merge &lt;分支名称&gt;</code></pre><h3>5. 删除分支</h3><p>删除分支的时候不可以删除当前所在的分支，只可以删除别的分支</p><p>如：当前所在分支master，有另外分支dev1 那么我们可以在master分支删除dev1分支，若在dev1分支中是不可以删除dev1分支的</p><pre><code>git branch -d &lt;分支名称&gt; # 删除分支时做检查</code></pre><p><strong>删除分支不做检查，直接删除</strong></p><pre><code>git branch -D &lt;分支名称&gt;</code></pre><h2>四、冲突解决</h2><h3>1. 冲突的发生</h3><p>当两个分支上对同一个文件，的同一行进行修改，这时候就需要手动去解决冲突</p><h3>2. 如何解决冲突</h3><ol><li><p>处理文件中冲突的地方</p><ol><li>选择要保留的内容，或要删除的内容</li></ol></li><li>将解决完冲突的文件加入到暂存区</li><li>提交到仓库</li></ol><h3>3. 开发中分支使用原则与流程</h3><p>几乎所有的版本控制系统都以某种形式支持分支。 </p><p>使用分支意味着你可以把你的工作从开发主线上分离开来进行重大的Bug修改、开发新的功能，以免影响开发主线。 </p><p>在开发中，一般有如下分支使用原则与流程：</p><ul><li>master （生产） 分支：线上分支，主分支，中小规模项目作为线上运行的应用对应的分支；</li><li>develop（开发）分支：是从master创建的分支，一般作为开发部门的主要开发分支，如果没有其 他并行开发不同期上线要求，都可以在此版本进行开发，阶段开发完成后，需要是合并到master 分支，准备上线。</li><li>feature/xxxx分支：从develop创建的分支，一般是同期并行开发，但不同期上线时创建的分支， 分支上的研发任务完成后合并到develop分支，之后该分支可以删除。</li><li>hotfix/xxxx分支：从master派生的分支，一般作为线上bug修复使用，修复完成后需要合并到 master、test、develop分支。 还有一些其他分支，在此不再详述，例如test分支（用于代码测试）、pre分支（预上线分支）等。</li></ul><h2>五、Git远程仓库</h2><ol><li>GitHub（ 地址：<a href="https://github.com/">https://github.com/</a> ）是一个面向开源及私有软件项目的托管平台，因为只支持 Git 作为唯一的版本库格式进行托管，故名GitHub</li><li>码云（地址： <a href="https://gitee.com/">https://gitee.com/</a> ）是国内的一个代码托管平台，由于服务器在国内，所以相比于 GitHub，码云速度会更快</li><li>GitLab （地址： <a href="https://about.gitlab.com/">https://about.gitlab.com/</a> ）是一个用于仓库管理系统的开源项目，使用Git作为 代码管理工具，并在此基础上搭建起来的web服务，一般用于在企业、学校等内部网络搭建git私 服。</li></ol><h2>以码云为例子</h2><p>码云（地址： <a href="https://gitee.com/">https://gitee.com/</a> ）</p><h3>1. 创建远程仓库</h3><p><img src="http://blog.anlucky.cn/usr/uploads/2023/04/1017034519.png" alt="46" title="46"></p><h3>2. 远程仓库地址</h3><p><img src="http://blog.anlucky.cn/usr/uploads/2023/04/2878041136.png" alt="47" title="47"></p><h3>3. 配置 / 生成本地公钥</h3><p>在Git命令窗口中执行下面代码</p><pre><code>ssh-keygen -t ed25519 -C &quot;xxxxx@xxxxx.com&quot; </code></pre><p><strong>其中<a href="mailto:xxxxx@xxxxx.com">xxxxx@xxxxx.com</a></strong>只是生成的 sshkey 的名称，并不约束或要求具体命名为某个邮箱。</p><p>现网的大部分教程均讲解的使用邮箱生成，其一开始的初衷仅仅是为了便于辨识所以使用了邮箱</p><p>按照提示完成三次回车，即可生成 ssh key。通过查看 <code>~/.ssh/id_ed25519.pub</code> 文件内容，获取到你的 public key</p><pre><code>cat ~/.ssh/id_ed25519.pub</code></pre><h3>4. 验证配置的公钥是否成功</h3><pre><code>ssh -T git@gitee.com</code></pre><p><img src="http://blog.anlucky.cn/usr/uploads/2023/04/4214284285.png" alt="48" title="48"></p><p>会提示输入Yes，直接输入yes,输出显示Git中配置的用户名即可算配置成功</p><h2>六、远程仓库命令</h2><h3>0. 初始化远程仓库</h3><p>要想连接到远程仓库，在远程仓库之前需要执行以下</p><ul><li>git init 将当前文件夹变为一个git仓库</li></ul><pre><code>git remote add &lt;远程仓库名称一般是origin&gt; &lt;仓库地址SSH地址&gt;</code></pre><h3>1. 查看远程仓库</h3><pre><code># 查看当前远程仓库，如果返回一个 origin 那么绑定成功
git remote</code></pre><h3>2. 从远程仓库克隆项目</h3><p>将远程仓库的项目克隆到本地，若不指定本地目录那么将会生成一个远程仓库一样的文件夹</p><pre><code class="lang-git">git clone &lt;仓库地址&gt; [本地目录]</code></pre><h3>3. 拉取远程仓库</h3><pre><code class="lang-git">git fetch [远程仓库名称一般为origin] [分支名称不指定为所有分支]</code></pre><p><strong>上面这个命令可以简化为下面的一个命令</strong></p><p>这个命令可以本地自动拉取和更新</p><pre><code>git pull [远程仓库名称一般为origin] [分支名称不指定为所有分支]</code></pre><h3>4. 推送到远程仓库</h3><p>将本地仓库的文件推送到远程仓库 master分支下</p><pre><code class="lang-git">git push [origin] [master]</code></pre><h2>七、IDEA中使用Git</h2><h3>1. 配置Git</h3><pre><code>在IDEA中的设置，搜索Git，找到本机电脑上所装的Git.exe文件，即设置成功</code></pre><p><img src="http://blog.anlucky.cn/usr/uploads/2023/04/3727164576.png" alt="49" title="49"></p><p><strong>出现对应的版本号就是配置成功了</strong></p><h3>2. 配置远程仓库</h3><p><img src="http://blog.anlucky.cn/usr/uploads/2023/04/3181376286.png" alt="50" title="50"></p><p><img src="http://blog.anlucky.cn/usr/uploads/2023/04/1744833953.png" alt="51" title="51"></p><h3>3. 配置本地仓库配置</h3><p>有两个地方可以commit到本地仓库</p><p>在上面Git选择中commit或者点击这个 <code>对号</code></p><p><img src="http://blog.anlucky.cn/usr/uploads/2023/04/437968519.png" alt="52" title="52"></p>
]]></content:encoded>
<slash:comments>0</slash:comments>
<comments>https://blog.anlucky.cn/index.php/programming/tools/53#comments</comments>
<wfw:commentRss>https://blog.anlucky.cn/index.php/feed/tag/%E7%BC%96%E7%A8%8B%E5%B7%A5%E5%85%B7/</wfw:commentRss>
</item>
</channel>
</rss>