从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

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