zoukankan      html  css  js  c++  java
  • 微服务之路(二)-springboot初体验

    前言

    基本概念

    应用主要分为两个方面:功能性和非功能性。

    功能性:系统所设计的业务范畴。

    非功能性:安全、性能、监控、数据指标(CPU利用率、网卡使用率)

    SpringBoot规约大于配置,意思就是大多数组件不需要自行配置,而是自动组装,目的就是为了简化开发。

    SpringBoot技术体系思维导图

    主要议题

    创建方式

    Spring Web Flux

    构建多模块应用

    构建可执行jar或者war

    主体内容

    一、项目创建方式

    1.图形化方式(这里不做介绍)

    2.命令行方式

    (1)配置好Maven环境变量后,cmd打开命令行,到要创建的目录下输入以下命令(DinteractiveMode=false为静默安装):

    mvn archetype:generate -DgroupId=com.gupao -DartifactId=my-first-springboot -Dversion=1.0.0-SNAPSHOT -DinteractiveMode=false
    

    然后IDE直接导入即可。

    (2)接着可以导入springboot依赖,去http://projects.spring.io/spring-boot/选择需要的版本导入相关依赖。

    (3)修改启动类App.class

    @SpringBootApplication
    public class App 
    {
        public static void main( String[] args ) {
            SpringApplication springApplication = new SpringApplication(App.class);
            springApplication.run(args);
        }
    }
    

    二、Spring Web Flux

    1.接下来,我们在上述基础上加入webflux依赖包。

    <!--spring-webFlux目前不能和spring-webmvc同时使用-->
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
    

    2.创建一个用户模型User.java

    /**
     * 用户模型
     */
    public class User {
        private Long id;
    
        private String name;
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    3.用户仓储UserRespository.java,为什么采用ConcurrentMap,由于默认的单例模式,可能会导致成员变量在多线程下不安全。

    /**
     * 用户的仓储
     */
    @Repository
    public class UserRepository {
    
        private final ConcurrentMap<Long, User> repository = new ConcurrentHashMap<>();
    
        private final AtomicLong idGenerator = new AtomicLong();
    
        public Boolean save(User user){
            //ID从1开始
            long id = idGenerator.incrementAndGet();
            user.setId(id);
            return repository.put(id,user) == null;
        }
    
        public Collection<User> findAll(){
            return repository.values();
        }
    }
    

    4.创建controller包,下UserController.java

    import com.gupao.domain.User;
    import com.gupao.repository.UserRepository;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * 用户控制器
     */
    @RestController
    public class UserController {
    
        private final UserRepository userRepository;
    
        public UserController(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    
        //保存使用Spring Web MVC
        @PostMapping("/user/save")
        public User user(String name){
            User user = new User();
            user.setName(name);
            userRepository.save(user);
            return user;
        }
    }
    

    5.然后创建一个config包,包下是WebFluxConfig.java

    import com.gupao.domain.User;
    import com.gupao.repository.UserRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.reactive.function.server.RequestPredicates;
    import org.springframework.web.reactive.function.server.RouterFunction;
    import org.springframework.web.reactive.function.server.RouterFunctions;
    import org.springframework.web.reactive.function.server.ServerResponse;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    import java.util.Collection;
    
    /**
     * Web Flux参数
     */
     @Configuration
    public class WebFluxConfig {
        @Bean
        @Autowired//@Autowired可加可不加,加上是便于理解
        public RouterFunction<ServerResponse> responseRouterFunctionUsers(UserRepository userRepository){
            Collection<User> users = userRepository.findAll();
            Flux<User> userFlux = Flux.fromIterable(users);
            //Mono<Collection<User>> mono = Mono.just(users);//二者选一
            RouterFunction<ServerResponse> route = RouterFunctions.route(RequestPredicates.path("/responseRouterFunctionUsers"),
                    request->ServerResponse.ok().body(userFlux,User.class));
            return route;
        }
    }
    

    6.这里插入讲解一下Mono和Flux

    Mono:0-1元素,类似于Java8中的Optional<>

    Flux:0-N个元素,类似于Iterable或者Collection

    7.启动项目,注意看控制台,用Postman分别测试mvc接口和flux接口。

    (1)先访问3次mvc接口,如图展示最后一次结果:

    (2)然后访问1次flux接口,结果如下:

    三、构建多模块应用

    1.修改主工程类型jar->pom

    2.新建web工程,将遗留代码移动到web java目录下。

    3.再从web工程,独立出model工程。

    4.将web工程依赖model工程。

    5.重复步骤3,独立出persistence。

    6.再从persistence添加model依赖。

    7.最终依赖关系:web->persistence->model

    四、构建可执行jar或者war

    web-1.0.0-SNAPSHOT.jar中没有注清单属性?

    需要一个springboot的插件,那好我们先把他放到主pom中。

    <!-- Package as an executable jar -->
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    			</plugin>
    		</plugins>
    	</build>
    

    jar规范里面,有一个MANIFEST.MF,里面有一个Main-Class的属性,API:

    java.uti.jar#Manifest#getAttributes
    

    cmd打开命令行:输入以下maven打包命令并跳过测试;

    E:WorkplacesMavenWorkplacemy-first-springboot>mvn -Dmaven.test.skip -U clean package
    

    结果发生了下面的错误:

    Execution repackage of goal org.springframework.boot:spring-boot-maven-plugin:2.2.7.RELEASE:repackage failed: Unable to find main class
    

    那么为啥呢?是因为插件懵逼了,到底执行哪个Main-Class呢?于是在插件基础上加上以下内容,整体如下:

    <!-- Package as an executable jar -->
      <build>
        <plugins>
          <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
              <mainClass>com.gupao.App</mainClass>
            </configuration>
          </plugin>
        </plugins>
      </build>
    

    然后再次打包,又出现以下问题:

    [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project persistence: Compilation failure: Compilation failure:
    [ERROR] /E:/Workplaces/MavenWorkplace/my-first-springboot/persistence/src/main/java/com/gupao/repository/UserRepository.java:[3,24] 程序包com.gupao.domain不存在
    [ERROR] /E:/Workplaces/MavenWorkplace/my-first-springboot/persistence/src/main/java/com/gupao/repository/UserRepository.java:[16,39] 找不到符号
    [ERROR]   符号:   类 User
    [ERROR]   位置: 类 com.gupao.repository.UserRepository
    [ERROR] /E:/Workplaces/MavenWorkplace/my-first-springboot/persistence/src/main/java/com/gupao/repository/UserRepository.java:[20,25] 找不到符号
    [ERROR]   符号:   类 User
    [ERROR]   位置: 类 com.gupao.repository.UserRepository
    [ERROR] /E:/Workplaces/MavenWorkplace/my-first-springboot/persistence/src/main/java/com/gupao/repository/UserRepository.java:[27,23] 找不到符号
    [ERROR]   符号:   类 User
    [ERROR]   位置: 类 com.gupao.repository.UserRepository
    [ERROR] -> [Help 1]
    [ERROR]
    [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
    [ERROR] Re-run Maven using the -X switch to enable full debug logging.
    [ERROR]
    [ERROR] For more information about the errors and possible solutions, please read the following articles:
    [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
    [ERROR]
    [ERROR] After correcting the problems, you can resume the build with the command
    [ERROR]   mvn <goals> -rf :persistence
    

    这次又为啥?其实我们要把该插件放到web模块的pom中,这次就OK了。

    {INFO] Reactor Summary for my-first-springboot 1.0.0-SNAPSHOT:
    [INFO]
    [INFO] my-first-springboot ................................ SUCCESS [  0.222 s]
    [INFO] model .............................................. SUCCESS [  1.781 s]
    [INFO] persistence ........................................ SUCCESS [  0.431 s]
    [INFO] web ................................................ SUCCESS [  1.072 s]
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  3.892 s
    [INFO] Finished at: 2020-05-11T23:03:36+08:00
    [INFO] ------------------------------------------------------------------------
    
    E:WorkplacesMavenWorkplacemy-first-springboot>
    

    接下来进入target目录

    E:WorkplacesMavenWorkplacemy-first-springboot>cd web/target
    

    列表发现有以下文件

    E:WorkplacesMavenWorkplacemy-first-springbootweb	arget>dir
     驱动器 E 中的卷是 documents
     卷的序列号是 90A4-C3EA
    
     E:WorkplacesMavenWorkplacemy-first-springbootweb	arget 的目录
    
    2020/05/11  23:03    <DIR>          .
    2020/05/11  23:03    <DIR>          ..
    2020/05/11  23:03    <DIR>          classes
    2020/05/11  23:03    <DIR>          generated-sources
    2020/05/11  23:03    <DIR>          maven-archiver
    2020/05/11  23:03    <DIR>          maven-status
    2020/05/11  23:03        19,691,419 web-1.0.0-SNAPSHOT.jar
    2020/05/11  23:03             4,552 web-1.0.0-SNAPSHOT.jar.original
                   2 个文件     19,695,971 字节
                   6 个目录 109,520,785,408 可用字节
    

    资源库中显示文件

    E:WorkplacesMavenWorkplacemy-first-springbootweb	arget>explorer .
    

    点进去web-1.0.0-SNAPSHOT.jar看看目录。

    注意

    • 有个叫做BOO-INF的目录它是springboot1.4才有的。
    • 同时当使用依赖或插件时,如果版本是Milestone时,主pom需要增加:
     <pluginRepositories>
       <pluginRepository>
         <id>spring-snapshots</id>
         <url>http://repo.spring.io/snapshot</url>
       </pluginRepository>
       <pluginRepository>
         <id>spring-milestones</id>
         <url>http://repo.spring.io/milestone</url>
       </pluginRepository>
     </pluginRepositories>
    
      <repositories>
        <repository>
          <id>spring-milestones</id>
          <name>Spring Milestones</name>
          <url>https://repo.spring.io/libs-milestone</url>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </repository>
      </repositories>
    
    • META-INF/MANIFEST.MF里面有指定两个属性Main-Class、Start-Class

    ​ 举例

    ​ Main-Class: org.springframework.boot.loader.JarLauncher

    ​ Start-Class: com.gupao.App

    • 除了jar或者war启动的方式,还有目录启动的方式:目录启动方式可以帮助解决过期的jar不支持Spring Boot的新方式。也可以这样,copy Main-Class,照样可以启动,6到爆炸:
    E:WorkplacesMavenWorkplacemy-first-springbootweb	argetweb-1.0.0-SNAPSHOT>java org.springframework.boot.loader.JarLauncher
    
      .   ____          _            __ _ _
     /\ / ___'_ __ _ _(_)_ __  __ _    
    ( ( )\___ | '_ | '_| | '_ / _` |    
     \/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.2.7.RELEASE)
    
    2020-05-11 23:30:12.272  INFO 6484 --- [           main] com.gupao.App                            : Starting App on MSI with PID 6484 (E:WorkplacesMavenWorkplacemy-first-springbootweb	argetweb-1.0.0-SNAPSHOTBOOT-INFclasses started by 66477 in E:WorkplacesMavenWorkplacemy-first-springbootweb	argetweb-1.0.0-SNAPSHOT)
    2020-05-11 23:30:12.276  INFO 6484 --- [           main] com.gupao.App                            : No active profile set, falling back to default profiles: default
    2020-05-11 23:30:15.324  INFO 6484 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
    2020-05-11 23:30:15.326  INFO 6484 --- [           main] com.gupao.App                            : Started App in 3.433 seconds (JVM running for 3.85)
    

    这里是jar包,如果是war包,启动的就是java org.springframework.boot.loader.WarLauncher。

    如果要打包成war包

    1.修改成war。

    2.创建“webapp/WEB-INF”目录(相对于src/main)。

    3.新建一个空的web.xml[步骤2和步骤3是为了绕过插件的限制

    或者使用在启动类的那个模块pom中

    <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <configuration>
        	<failOnMissingWebXml>false</failOnMissingWebXml>
        </configuration>
    </plugin>
    

    然后启动war包:

    E:WorkplacesMavenWorkplacemy-first-springbootweb	arget>java -jar web-1.0.0-SNAPSHOT.war
    

    Postman测试一波就ok了。

    这里插入一个小知识点,那就是war包启动我可以启动多个相同war包,只是在语句后面加上 --server.port=0即可分配一个随机可用的端口。

    Maven方式执行jar包

    (1)首先,将父包install。

    E:WorkplacesMavenWorkplacemy-first-springboot>mvn -Dmaven.test.skip -U clean install
    

    (2)然后,cd到web模块,执行以下命令:

    E:WorkplacesMavenWorkplacemy-first-springboot>cd web
    E:WorkplacesMavenWorkplacemy-first-springbootweb>mvn spring-boot:run
    

    五、总结

    问题1:WebFluxConfiguration里面的映射路径和controller里面的路径有什么区别吗?

    解答:基本上没有区别,但是注意,不能重复定义,或者URL语义有重复!

    问题2:webFlux不是不能跟mvc不能一起吗,怎么一起启动了?

    解答:spring-boot-starter-webmvc和spring-boot-starter-webflux可以放在同一个应用,课时webflux不会工作,默认使用webmvc,webflux不会被采用。其实webflux是兼容Annotation驱动,比如@RequestMapping。

    问题3:webFlux可以定义restFul吗?

    解答:可以,支持restFul。

    问题4:spring的就项目迁移到springboot,如何操作?

    解答:旧的xml方式采用@ImportResource导入。

    问题5:嵌入式Tomcat如何调优?

    解答:第一种通过application.properties文件调整配置参数。

    ​ 第二种通过接口回调:

    • TomcatConnectirCustomizer
    • TomcatContextCustomizer
  • 相关阅读:
    Python range() 函数用法
    substr
    disable enable 所有其他表关联的外键
    sql with multiply where
    小米开源文件管理器MiCodeFileExplorer-源码研究(7)-Favorite收藏管理和SQLite数据库CRUD
    Centos安装FastDFS+Nginx(一天时间搞定)
    Centos安装FastDFS+Nginx(一天时间搞定)
    小米开源文件管理器MiCodeFileExplorer-源码研究(6)-媒体文件MediaFile和文件类型MimeUtils
    小米开源文件管理器MiCodeFileExplorer-源码研究(6)-媒体文件MediaFile和文件类型MimeUtils
    小米开源文件管理器MiCodeFileExplorer-源码研究(5)-AsyncTask异步任务
  • 原文地址:https://www.cnblogs.com/xusp/p/12879215.html
Copyright © 2011-2022 走看看