從spring源碼到springboot

這篇文章僅記錄自己對於springboot的一次學習以及感悟,以及描述了從0開始製作自己的jar至推送到maven central repo。

What is SpringBoot

都已經2020年了,還在說自己不懂springboot,似乎對於java工程師來說彷彿已經說不過去了。還是一句話,要弄懂一個新興事物之前,不妨去研究一下它的背景以及它的出現解決了什麼問題。

在沒有SpringBoot出現的時候,Springframework(簡稱spring)對於j2ee來說,已經是所有開發人員搭建企業級web應用的首選。它的IOC和Aop等新興概念,極大的提高了開發的效率,追根溯源還是java的東西,在源碼當中隨處可見各種極具藝術性的設計模式:工廠、代理、單例、觀察者、裝飾者等等,但是在平時的開發過程當中我們比較經常使用和理解的一般是以IOC爲主。將對象的創建與保存交與beanFactory(Spring頂層工廠容器)管理,在需要的地方通過setter、接口、構造器、字段等等方式注入,實現應用的解耦。但是在最初的學習當中,你可能是這樣的打開方式:

 BeanFactory beanFactory=new ClassPathXmlApplicationContext("ApplicationContext.xml");
 User user=beanFactory.getBean(User.class);
 log.info("user:{}",user);

通過讀取xml配置文件的方式初始化spring工廠,再從工廠中獲取需要的bean,通過查看源碼的方式我們可以發現:
在這裏插入圖片描述
ClassPathXmlApplicationContext繼承了AbstractXmlApplicationContext抽象類,而通過查看整個spring的對象關係圖可以發現:
在這裏插入圖片描述
beanfactory爲spring當中頂層工廠接口,而且從它的命名也可以知道"bean的工廠"。在通過一階段的學習之後,又接觸到了springmvc–springframework的web層框架,以及mybatis–持久層框架等等結合在一起開發web應用,也是大家所熟知的SSM。(SSH就不描述了,hibernate對於之後的spring data jpa的學習還是挺重要的,struts就算了吧,資源開銷比較大,不支持併發量有要求的網站建設)。這個時候你的項目目錄可能是這樣子的:
在這裏插入圖片描述
或者是這樣子:
在這裏插入圖片描述
文件夾下盤根錯節,更可怕的是每創建一個新的項目,你不得不要重複之前的配置文件的創建,而且還極有可能因爲一些奇奇怪怪的問題導致項目啓動失敗。這確實也是傳統spring開發應用的一大弊病。

SpringBoot是怎麼開發的

在這裏插入圖片描述
以IDEA爲主,同樣是從0開始搭建一個spring的web應用,僅僅是需要在頁面中響應‘Hello World’,用Spring的傳統做法分爲以下幾步:

  • ApplicationContext.xml(springbean工廠配置文件)
  • Spring-mvc.xml(將springmvc的元素與spring集成)
  • mybatis等第三方持久層框架的配置文件
  • log4j等日誌配置文件開啓系統日誌輸出
  • 國際化、項目統一的配置文件
    Springboot只需要你創建一個maven工程,繼承它的parent.pom,引入spring-boot-starter-web jar,然後創建一個java class,啓動項目。 如果你的網絡還不錯的話,應該大概只需要幾分鐘的時間,就可以成功開啓你的spring web應用。 剛開始聽起來的時候,似乎很不可思議,但是確實springboot就是這樣的一個外表輕量級、內部卻是一個巨大怪獸的發明。

SpringBoot替代了SSM嗎?

首先,Pivotal團隊給出的解釋是:springboot並不是一個全新的框架,它只是爲開發者提供一種可插拔式的快速啓動spring應用的通道,本質還是spring。我在之前實習的時候,有一回領導要求我們幾個新人開發一個新的項目,在討論項目構建的時候,有一位兄弟面色犯難的回答:我只會springboot,SSM沒學過。 着實給我震住了……
學習springboot,至少要有spring的基礎,因爲我們是爲了改變傳統的開發模式,可是如果你都沒有經歷過,談何改變。 個人認爲,有些時候,絕對的便利,反而使人停止思考,如果底層都不重要,怎麼攀登更高的峯頂呢?

SpringBoot的自動裝配,你真的瞭解嗎?
相信在面試過程當中,現在幾乎大多數面試官都必不可免的會問:你會SpringBoot嗎? 答:會。 那它有什麼特性? 答:自動裝配、避免繁雜的配置文件。 它的自動裝配原理是什麼? 答:額,就是導入一些starter包,然後就可以直接在對象中注入了。 好的,回去等消息吧。
一段近乎真實的對話,我之前親身經歷過的一次面試經歷。在回去的路上想:又是造飛機大炮,難道去了讓我設計框架嗎? 在後來的工作和學習中,通過對於springboot的進一步學習和應用,我漸漸明白,學習原理並不是說立馬可以付諸實踐、獲得創收, 而是可以讓你同大牛們直接學習,直接對話,因爲並不是所有的東西都是@autowired,總會有你需要創造的時候,難道自己造一個不香嗎?
帶着疑問,開始自己的源碼學習之路。通過對spring5和springboot的代碼閱讀,對於自動裝配的瞭解比剛開始的時候知道的清楚了一些,正好在前後端分離驅使下swagger等優秀的後端api文檔生成插件的誕生,雖然springboot對於swagger、knife4j已經有了可支持的配置,但是似乎沒有一個更爲簡單一鍵式導入方式,所以我就想着自己造一個starter。下面先簡單講一下springboot自動裝配的一個大概過程:
首先先看一下springboot的入口類:
在這裏插入圖片描述
首先是我們熟悉的main方法入口類,通過run方法傳入我們的主配置類Class。@SpringBootApplication註解點擊去看下。
在這裏插入圖片描述
發現它是一個組合註解。點進去看一下@SpringBootConfiguration。
在這裏插入圖片描述
@configuration註解是spring配置類標識註解,相等於xml文件中的。 再看一下@EnableAutoConfiguration。
在這裏插入圖片描述
@EnableAutoConfiguration是開啓自動裝配功能的關鍵點。 再點進去@AutoConfigurationPackage。
在這裏插入圖片描述
@Import註解通過一個Class類來實現spring動態註冊bean,在Spring5.0,這個註解一般支持三種類型的Class:

  • 普通Java類
  • 實現了ImportBeanDefinitionRegistrar接口的類。
  • 實現了ImportSelector接口的類。
    符合以上三種情況之一的Class,首先自己會被掃描進Spring工廠,並且它本身也會具有創建bean的能力,可以通過spring的實例化-變量賦值-初始化-銷燬等生命週期中使用spring的各種豐富的擴展接口來實現動態註冊bean。
    我們再看看Register.class.在這裏插入圖片描述

啓動項目,將斷點打在方法實現處:
在這裏插入圖片描述
查看是否獲取到掃描路徑:
在這裏插入圖片描述
可以看到正是我的工程包名,再來看一下metadata是什麼:
在這裏插入圖片描述
總的意思應該就是獲取啓動類所在類以及子包以下所有子孫類,開啓掃描註冊功能。
再來看一下第三個註解:@Import(AutoConfigurationImportSelector.class)
在這裏插入圖片描述
同樣開啓斷點調試:
在這裏插入圖片描述
可以看到這就是springboot啓動的時候加載的所有第三方starter組件包配置類
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
最後一張圖可以看出,SpringBoot從所有starter包的META/spring.factories文件讀取EnableAutoConfiguration自動裝配類,這些自動裝配類會自己工作將他們的組件交由spring工廠管理,這也就是爲什麼你的@autowired可以直接注入你的組件。可以看一下mybatis-plus和web的starter包下的spring.factories文件:
在這裏插入圖片描述

在這裏插入圖片描述
可以查看Mybatis-plus的starter配置類來了解一下這個mybatis的增強版工具是如何工作的:
在這裏插入圖片描述
在這裏插入圖片描述
spring5通過一系列的條件註解來動態的判斷組件內部情況,根據實際情況選擇配置是否生效,在上圖你可以發現,誒,這不就是我在使用mybatis-plus的時候application.yml配置文件裏面寫的配置嗎, 原來它是在這邊讀取的,是不是覺得之前的困惑有一些明朗的地方了?

根據自動裝配原理,開發swagger的starter

具體細節不再過多描述,我歸結爲以下幾點:

  • 初始化新的springboot工程,引入autoconfig包,建立spring.factories文件,指明你的配置類。
  • 在配置類中通過動態讀取方式,選擇性注入你的bean。
  • 開發結束後,需要發佈到maven中央倉庫。

這是我最終的項目目錄:
在這裏插入圖片描述

swagger我選擇使用knife4j的增強版,AutoConfiguartion是開啓注入swagger的主配置類。

package com.gitee.sophis.autoconfig;

import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import com.gitee.sophis.swagger.Swagger2Config;
import com.gitee.sophis.swagger.SwaggerBootstrapUiDemoApplication;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import javax.annotation.PostConstruct;

/**
 * @program: springcloud-project
 * @description: 自動裝配類
 * @author: xjr
 * @create: 2020-04-12 13:52
 **/
@EnableConfigurationProperties({Swagger2Config.class})
@ConditionalOnProperty(prefix = "my.swagger",name = "active",havingValue = "open")
@Configuration
@Slf4j
@Import(SwaggerBootstrapUiDemoApplication.class)
@EnableSwagger2
@EnableSwaggerBootstrapUI
public class AutoConfiguartion{

    @PostConstruct
    public void init(){
      log.info("xjr:start Initializing:{}"+this.getClass().getName());
    }

}

可以看到我這邊提供了兩種方式開啓swgger:1.自定義註解 2.配置文件。當選擇配置文件方式開啓,需要在你的springboot工程的application.yml配置文件中my.swagger.active=true來使swagger生效。
Swagger實體配置類:

package com.gitee.sophis.swagger;

import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.List;

/**
 * TODO
 *
 * @author xjr
 * @version 1.0
 * @date 2020/2/4 15:59
 */
@ConfigurationProperties(prefix = "my.swagger",ignoreUnknownFields = true,ignoreInvalidFields = true)
@Slf4j
@Configuration("swagger2Coofig")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Swagger2Config {
    private String title;
    private String description;
    @Value("${eureka.instance.hostname:127.0.0.1}")
    private String ip;
    @Value("${server.port:8080}")
    private String port;
    private String concat;
    private String version;
    private String basePackage;



    @Bean("swaggerDocket")
    public Docket createRestApi() {
        log.info("knife4j scan basepackage:{}",this.basePackage);
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage(this.basePackage))
                .paths(PathSelectors.any())
                .build();
    }

    public ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title(title)
                .description(description)
                .termsOfServiceUrl(ip+":"+port+"/")
                .contact(concat)
                .version(version)
                .build();
    }

    private ApiKey apiKey() {
        return new ApiKey("BearerToken", "Authorization", "header");
    }
    private ApiKey apiKey1() {
        return new ApiKey("BearerToken1", "Authorization-x", "header");
    }

    private SecurityContext securityContext() {
        return SecurityContext.builder()
                .securityReferences(defaultAuth())
                .forPaths(PathSelectors.regex("/.*"))
                .build();
    }
    private SecurityContext securityContext1() {
        return SecurityContext.builder()
                .securityReferences(defaultAuth1())
                .forPaths(PathSelectors.regex("/.*"))
                .build();
    }

    List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        return Lists.newArrayList(new SecurityReference("BearerToken", authorizationScopes));
    }
    List<SecurityReference> defaultAuth1() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        return Lists.newArrayList(new SecurityReference("BearerToken1", authorizationScopes));
    }

}


我的自定義註解@openApi:

package com.gitee.sophis.annotations;

import com.gitee.sophis.autoconfig.AnnotationDriverManager;
import com.gitee.sophis.swagger.Swagger2Config;
import com.gitee.sophis.swagger.SwaggerBootstrapUiDemoApplication;
import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import com.github.xiaoymin.swaggerbootstrapui.configuration.MarkdownFileConfiguration;
import com.github.xiaoymin.swaggerbootstrapui.configuration.SecurityConfiguration;
import com.github.xiaoymin.swaggerbootstrapui.configuration.SwaggerBootstrapUIConfiguration;
import org.springframework.context.annotation.Import;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration;

import javax.xml.bind.Element;
import java.lang.annotation.*;

/**
 * @program: OSSRH-56661
 * @description: swagger註解驅動
 * @author: xjr
 * @create: 2020-04-18 11:37
 **/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({Swagger2Config.class,AnnotationDriverManager.class})
@Inherited
@Documented
@EnableSwagger2
@EnableSwaggerBootstrapUI
public @interface openApi {
    boolean active() default true;

    String  title() default "knife4j在線api文檔";

    String  ipAddress() default "127.0.0.1";

    String  port() default "8080";

    String description() default "一鍵註解驅動swagger";

    String basePackages() default "";
}

註解驅動主配置類:

package com.gitee.sophis.autoconfig;

import com.gitee.sophis.annotations.openApi;
import com.gitee.sophis.swagger.Swagger2Config;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;

import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.util.StringUtils;

import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;

import springfox.documentation.spring.web.plugins.Docket;

import java.net.Inet4Address;
import java.net.UnknownHostException;


/**
 * @program: OSSRH-56661
 * @description: swagger註解驅動
 * @author: xjr
 * @create: 2020-04-18 11:43
 **/
@Slf4j
public class AnnotationDriverManager implements ImportBeanDefinitionRegistrar, BeanFactoryAware, ResourceLoaderAware, EnvironmentAware,ApplicationContextAware {

      volatile Swagger2Config swagger2Config=null;

      volatile BeanFactory beanFactory;

      volatile Environment environment;

      volatile ResourceLoader resourceLoader;

      volatile  ApplicationContext applicationContext;

      volatile  Docket docket=null;

    @SneakyThrows
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        StandardAnnotationMetadata standardAnnotationMetadata= (StandardAnnotationMetadata) annotationMetadata;
        AnnotationAttributes annotationMap= AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(openApi.class.getName()));
        if (!annotationMap.getBoolean("active")){
            log.info("當前swagger功能已關閉");
            beanDefinitionRegistry.removeBeanDefinition("swagger2Coofig");
        }else{
            registryBean(standardAnnotationMetadata,annotationMap,beanDefinitionRegistry);
        }


    }

    public Swagger2Config buildConfig(Swagger2Config config,AnnotationAttributes annotationAttributes) throws UnknownHostException {
       swagger2Config=Swagger2Config.builder().basePackage(annotationAttributes.getString("basePackages")).description(annotationAttributes.getString("description")).
               title(annotationAttributes.getString("title")).version("1.0").
               ip(Inet4Address.getLocalHost().getHostAddress())
               .port(this.environment.getProperty("server.port")).build();
       return swagger2Config;

    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory=beanFactory;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment=environment;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader=resourceLoader;
    }

    public void registryBean(StandardAnnotationMetadata standardAnnotationMetadata ,AnnotationAttributes annotationMap,BeanDefinitionRegistry beanDefinitionRegistry ) throws UnknownHostException {
        Class targetClass=standardAnnotationMetadata.getIntrospectedClass();
        buildConfig(this.swagger2Config,annotationMap);
        Docket docket=beanFactory.getBean(Docket.class);
        docket=docket.apiInfo(this.swagger2Config.apiInfo()).select()
                .apis(RequestHandlerSelectors.basePackage(StringUtils.isEmpty(swagger2Config.getBasePackage())?targetClass.getPackage().getName():swagger2Config.getBasePackage()))
                .paths(PathSelectors.any())
                .build();


    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
}

@openApi 也有一個active的布爾值控制swagger開啓,默認爲true,但是某些工程可能本地開發需要swagger,生產環境不需要,這個時候可以通過配置文件或者註解的active設置爲false,或者乾脆直接不引入,都可以達到想要的效果。
注:ImportBeanDefinitionRegistrar, BeanFactoryAware, ResourceLoaderAware, EnvironmentAware,ApplicationContextAware等都是spring5提供的擴展性接口,aware接口可以獲取spring工廠和環境等等對象,這些都在spring生命週期中初始化之前,所以可以在註冊bean時獲得其他bean的信息。

開發完了,要讓自己的成果被其他人使用,總得傳到倉庫裏,這邊我選擇使用國外的maven中央倉庫,中間一系列過程碰到了很多問題,最終在不懈努力下還是完成了,概述一下大概的過程:

  • 登錄maven的jira(jira是啥不懂的話就理解成禪道之類的)
  • 創建一個issue(工單),填寫你要發佈的插件的信息,等待系統審覈(審覈過程當中會需要反覆確認,這邊最好還是用英文描述一下你的問題比較好)
  • 審覈通過工單狀態爲變成resolved,這個時候可以在本地配置你的發佈信息,需要改動maven的settings文件,用戶名密碼默認爲你jira的賬號密碼。
  • 下載gpg祕鑰工具,生成祕鑰,上傳。
  • mvn clean deploy上傳,然後通知issue審覈,一般過個幾小時就可以在各大倉庫看到你的信息了。

在這裏插入圖片描述

登錄,沒有賬號的需要創建。因爲網絡的問題,所以這邊就沒有過多展示。

本地maven settins.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>

<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
-->

<!--
 | This is the configuration file for Maven. It can be specified at two levels:
 |
 |  1. User Level. This settings.xml file provides configuration for a single user,
 |                 and is normally provided in ${user.home}/.m2/settings.xml.
 |
 |                 NOTE: This location can be overridden with the CLI option:
 |
 |                 -s /path/to/user/settings.xml
 |
 |  2. Global Level. This settings.xml file provides configuration for all Maven
 |                 users on a machine (assuming they're all using the same Maven
 |                 installation). It's normally provided in
 |                 ${maven.conf}/settings.xml.
 |
 |                 NOTE: This location can be overridden with the CLI option:
 |
 |                 -gs /path/to/global/settings.xml
 |
 | The sections in this sample file are intended to give you a running start at
 | getting the most out of your Maven installation. Where appropriate, the default
 | values (values used when the setting is not specified) are provided.
 |
 |-->
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <!-- localRepository
   | The path to the local repository maven will use to store artifacts.
   |
   | Default: ${user.home}/.m2/repository
  
  -->
<localRepository>D:\my_repository</localRepository>
  <!-- interactiveMode
   | This will determine whether maven prompts you when it needs input. If set to false,
   | maven will use a sensible default value, perhaps based on some other setting, for
   | the parameter in question.
   |
   | Default: true
  <interactiveMode>true</interactiveMode>
  -->

  <!-- offline
   | Determines whether maven should attempt to connect to the network when executing a build.
   | This will have an effect on artifact downloads, artifact deployment, and others.
   |
   | Default: false
  <offline>false</offline>
  -->

  <!-- pluginGroups
   | This is a list of additional group identifiers that will be searched when resolving plugins by their prefix, i.e.
   | when invoking a command line like "mvn prefix:goal". Maven will automatically add the group identifiers
   | "org.apache.maven.plugins" and "org.codehaus.mojo" if these are not already contained in the list.
   |-->
  <pluginGroups>
    <!-- pluginGroup
     | Specifies a further group identifier to use for plugin lookup.
    <pluginGroup>com.your.plugins</pluginGroup>
    -->
  </pluginGroups>

  <!-- proxies
   | This is a list of proxies which can be used on this machine to connect to the network.
   | Unless otherwise specified (by system property or command-line switch), the first proxy
   | specification in this list marked as active will be used.
   |-->
  <proxies>
    <!-- proxy
     | Specification for one proxy, to be used in connecting to the network.
     |
    <proxy>
      <id>optional</id>
      <active>true</active>
      <protocol>http</protocol>
      <username>proxyuser</username>
      <password>proxypass</password>
      <host>proxy.host.net</host>
      <port>80</port>
      <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
    </proxy>
    -->
  </proxies>

  <!-- servers
   | This is a list of authentication profiles, keyed by the server-id used within the system.
   | Authentication profiles can be used whenever maven must make a connection to a remote server.
   |-->
  <servers>
   <server>
        <id>oss</id>
        <username>你的賬號</username>
        <password>你的密碼</password>
      </server>
    <!-- server
     | Specifies the authentication information to use when connecting to a particular server, identified by
     | a unique name within the system (referred to by the 'id' attribute below).
     |
     | NOTE: You should either specify username/password OR privateKey/passphrase, since these pairings are
     |       used together.
     |
    <server>
      <id>deploymentRepo</id>
      <username>repouser</username>
      <password>repopwd</password>
    </server>
    -->

    <!-- Another sample, using keys to authenticate.
    <server>
      <id>siteServer</id>
      <privateKey>/path/to/private/key</privateKey>
      <passphrase>optional; leave empty if not used.</passphrase>
    </server>
    -->
  </servers>

  <!-- mirrors
   | This is a list of mirrors to be used in downloading artifacts from remote repositories.
   |
   | It works like this: a POM may declare a repository to use in resolving certain artifacts.
   | However, this repository may have problems with heavy traffic at times, so people have mirrored
   | it to several places.
   |
   | That repository definition will have a unique id, so we can create a mirror reference for that
   | repository, to be used as an alternate download site. The mirror site will be the preferred
   | server for that repository.
   |-->
  <mirrors>
    <!-- mirror
     | Specifies a repository mirror site to use instead of a given repository. The repository that
     | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used
     | for inheritance and direct lookup purposes, and must be unique across the set of mirrors.
     |
    <mirror>
      <id>mirrorId</id>
      <mirrorOf>repositoryId</mirrorOf>
      <name>Human Readable Name for this Mirror.</name>
      <url>http://my.repository.com/repo/path</url>
    </mirror>
     -->
	 <mirror>
<id>nexus-aliyun</id>
<mirrorOf>*</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>

  </mirrors>

  <!-- profiles
   | This is a list of profiles which can be activated in a variety of ways, and which can modify
   | the build process. Profiles provided in the settings.xml are intended to provide local machine-
   | specific paths and repository locations which allow the build to work in the local environment.
   |
   | For example, if you have an integration testing plugin - like cactus - that needs to know where
   | your Tomcat instance is installed, you can provide a variable here such that the variable is
   | dereferenced during the build process to configure the cactus plugin.
   |
   | As noted above, profiles can be activated in a variety of ways. One way - the activeProfiles
   | section of this document (settings.xml) - will be discussed later. Another way essentially
   | relies on the detection of a system property, either matching a particular value for the property,
   | or merely testing its existence. Profiles can also be activated by JDK version prefix, where a
   | value of '1.4' might activate a profile when the build is executed on a JDK version of '1.4.2_07'.
   | Finally, the list of active profiles can be specified directly from the command line.
   |
   | NOTE: For profiles defined in the settings.xml, you are restricted to specifying only artifact
   |       repositories, plugin repositories, and free-form properties to be used as configuration
   |       variables for plugins in the POM.
   |
   |-->
  <profiles>
  	  
    <!-- profile
     | Specifies a set of introductions to the build process, to be activated using one or more of the
     | mechanisms described above. For inheritance purposes, and to activate profiles via <activatedProfiles/>
     | or the command line, profiles have to have an ID that is unique.
     |
     | An encouraged best practice for profile identification is to use a consistent naming convention
     | for profiles, such as 'env-dev', 'env-test', 'env-production', 'user-jdcasey', 'user-brett', etc.
     | This will make it more intuitive to understand what the set of introduced profiles is attempting
     | to accomplish, particularly when you only have a list of profile id's for debug.
     |
     | This profile example uses the JDK version to trigger activation, and provides a JDK-specific repo.
    <profile>
      <id>jdk-1.4</id>

      <activation>
        <jdk>1.4</jdk>
      </activation>

      <repositories>
        <repository>
          <id>jdk14</id>
          <name>Repository for JDK 1.4 builds</name>
          <url>http://www.myhost.com/maven/jdk14</url>
          <layout>default</layout>
          <snapshotPolicy>always</snapshotPolicy>
        </repository>
      </repositories>
    </profile>
    -->

    <!--
     | Here is another profile, activated by the system property 'target-env' with a value of 'dev',
     | which provides a specific path to the Tomcat instance. To use this, your plugin configuration
     | might hypothetically look like:
     |
     | ...
     | <plugin>
     |   <groupId>org.myco.myplugins</groupId>
     |   <artifactId>myplugin</artifactId>
     |
     |   <configuration>
     |     <tomcatLocation>${tomcatPath}</tomcatLocation>
     |   </configuration>
     | </plugin>
     | ...
     |
     | NOTE: If you just wanted to inject this configuration whenever someone set 'target-env' to
     |       anything, you could just leave off the <value/> inside the activation-property.
     |
    <profile>
      <id>env-dev</id>

      <activation>
        <property>
          <name>target-env</name>
          <value>dev</value>
        </property>
      </activation>

      <properties>
        <tomcatPath>/path/to/tomcat/instance</tomcatPath>
      </properties>
    </profile>
    -->
    <profile>
        <id>oss</id>
        <activation>
          <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
          <gpg.executable>gpg</gpg.executable>
          <gpg.passphrase>生成祕鑰配置的密碼</gpg.passphrase>
        </properties>
    </profile>
  </profiles>

  <!-- activeProfiles
   | List of profiles that are active for all builds.
   |
  <activeProfiles>
    <activeProfile>alwaysActiveProfile</activeProfile>
    <activeProfile>anotherAlwaysActiveProfile</activeProfile>
  </activeProfiles>
  -->
</settings>

pom.xml

```java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.2.RELEASE</version>
    </parent>
    <groupId>com.gitee.sophis</groupId>
    <artifactId>spring-boot-knif4j-starter</artifactId>
    <version>1.0.2.RELEASE</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <maven.deploy.skip>true</maven.deploy.skip>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <distributionManagement>
        <snapshotRepository>
            <id>oss</id>
            <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
        </snapshotRepository>
        <repository>
            <id>oss</id>
            <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
        </repository>
    </distributionManagement>
    <dependencies>
        <!-- swagger-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>swagger-bootstrap-ui</artifactId>
        <version>1.9.6</version>
        </dependency>
        <!-- 自動裝配-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- Spring Boot End -->
        <!--阿里巴巴json-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.38</version>
            <optional>true</optional>
        </dependency>
        <!-- 代碼自動生成-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- Spring Boot Begin -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <profiles>
        <profile>
            <id>release</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-source-plugin</artifactId>
                        <version>2.2.1</version>
                        <executions>
                            <execution>
                                <id>attach-sources</id>
                                <goals>
                                    <goal>jar-no-fork</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-javadoc-plugin</artifactId>
                        <version>2.9.1</version>
                        <configuration>
                            <additionalparam>-Xdoclint:none</additionalparam>
                        </configuration>
                        <executions>
                            <execution>
                                <id>attach-javadocs</id>
                                <goals>
                                    <goal>jar</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-gpg-plugin</artifactId>
                        <version>1.5</version>
                        <executions>
                            <execution>
                                <id>sign-artifacts</id>
                                <phase>verify</phase>
                                <goals>
                                    <goal>sign</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
    <licenses>
        <license>
            <name>The Apache Software License, Version 2.0</name>
            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
            <distribution>repo</distribution>
        </license>
    </licenses>

    <scm>
        <url>https://gitee.com/sophis/OSSRH-56661.git</url>
        <connection>https://gitee.com/sophis/OSSRH-56661.git</connection>
        <developerConnection>http://www.sophi.store</developerConnection>
    </scm>

    <developers>
        <developer>
            <name>xiejiarong</name>
            <email>937890254@qq.com</email>
            <url>https://gitee.com/sophis/OSSRH-56661</url>
        </developer>
    </developers>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <!-- sonatype提供了自動release的插件,這意味着運行mvn clean deploy後不用手動去close-> release了,此插件會自動release我們的項目到Maven中央倉庫。 -->
            <plugin>
                <groupId>org.sonatype.plugins</groupId>
                <artifactId>nexus-staging-maven-plugin</artifactId>
                <version>1.6.7</version>
                <extensions>true</extensions>
                <configuration>
                    <serverId>oss</serverId>
                    <nexusUrl>https://oss.sonatype.org/</nexusUrl>
                    <autoReleaseAfterClose>true</autoReleaseAfterClose>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

最後執行mvn clean deploy -P release.推送到遠程倉庫,這個時候需要跟jira工單反饋一下你的進度,大概在幾個小時之後,maven倉庫就會同步你的jar。
這邊提供幾個倉庫地址:sonatype
nexus倉庫地址
默認maven中央倉庫地址

最後結果:
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述
上傳成功。 這個時候可以可以隨便重新建一個springboot工程,maven的鏡像地址可以使用阿里雲也可以直接使用中央倉庫,導入我這個新的starter包

 <dependency>
        <groupId>com.gitee.sophis</groupId>
        <artifactId>spring-boot-knif4j-starter</artifactId>
        <version>1.0.2.RELEASE</version>
 </dependency>

然後在啓動類加上@openApi註解,

package com.xjr.knife4j.demo;

import com.gitee.sophis.annotations.openApi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@openApi(title = "knife4j的springboot start",description = "自己封裝的knife4j的springboot starter版本demo")
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

新建一個web類,提供一個返回helloworld的get接口,使用swagger註解修飾

package com.xjr.knife4j.demo.web;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @program: demo
 * @description:
 * @author: xjr
 * @create: 2020-05-03 00:12
 **/
@RestController
@Api("測試接口")
@ApiResponses({
        @ApiResponse(code = 200,message = "請求成功"),
        @ApiResponse(code = 403,message = "無權限"),
        @ApiResponse(code = 500,message = "服務器內部異常"),
        @ApiResponse(code = 413,message = "客戶端崩潰")
})
public class HelloController {

    @GetMapping("/hello")
    @ApiOperation("返回helloworld的測試接口")
    public String hello(){
        return "hello world";
    }
}

啓動項目,查看打印日誌:
在這裏插入圖片描述

可以看到打印出了我們的starter配置類的掃描語句.這時訪問 localhost:8080/doc.html,
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述
也可以直接在可視化界面進行調試,可以看到成功返回了helloworld。可以看一下如果把註解active設置爲false
在這裏插入圖片描述
重新啓動:
在這裏插入圖片描述
重新訪問:
在這裏插入圖片描述
swagger功能就此關閉。當然也可以使用配置文件的方式進行配置,根據個人喜好。

個人小結: Spring作爲java開發的經典框架,閱讀它的源碼對於自身的提高無非有莫大的幫助,程序員本身是上帝式的職業,每天都在創造新的東西,學習是恆久的事情,瞭解事物的原理並非是一個苛刻而無用的事情,曾經我畢業進第一家公司在剛開始學習的時候,公司文檔有一句話感觸頗深:知其然而知其所以然。 So,the more you know,the better you will be.bye

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章