关于SpringBoot框架,看这篇就够了。

导读:本篇文章讲解 关于SpringBoot框架,看这篇就够了。,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

目录

是什么

有什么优点、解决了哪些问题

创建第一个以springboot项目

starter

核心配置文件application.yml或properties

application中的配置项

springboot的启动流程

自定义banner

整合日志打印

整合druid数据源

处理异常

常用的注解

Configuration

Import

conditional

ConfigruationProperties

基于springboot的SSM框架

基于springboot的SSMibatisPlus框架

springboot项目的打包及运行


是什么

        Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。—–摘自百度百科

小编的理解:

springboot是开发人员的福音,是所有通用框架的集大成者,这里举个生活中的例子说明一下:

用电器与插板的关系:

        我理解springboot就像是一个大插板,能插各种类型的用电器(用电器就是各种框架与中间件等),如果我们不为每个用电器进行个性化定制,springboot为每个插口都进行了通用性设置(比如插口默认是220V、默认能插几孔的插头),让开发人员在开发项目的时候以最少的配置、最少的时间去构建项目。

        此外,springcloud项目是基于springBoot扩展出来的概念,可以说没有springboot就没有springcloud,关于springboot的相关教程,后续小编会进行更新。

有什么优点、解决了哪些问题

我们来看官网的描述:

关于SpringBoot框架,看这篇就够了。

官网给出的特征:

        1. 直接嵌入Tomcat、Jetty或Undertow(无需部署WAR文件)

        2. 提供有见解的“starter”依赖项以简化构建配置

        3. 尽可能自动配置Spring和第三方库

        4. 提供生产就绪功能,如指标、运行状况检查和外部化配置

        5. 绝对没有代码生成,也不需要XML配置

小编的体验:

        1. 简化开发流程,可以无xml配置快速搭建项目。

        2. 简化开发流程,可以以starter快速导入项目所需要的依赖。

        3. 简化打包与运维流程,内部嵌套服务器,可以直接打包运行,无需依赖外部的应用服务器。

        4. 提供了配置类,可以与任何市面上流行的框架结合进行开发。

创建第一个以springboot项目

有两种方式可以搭建springboot项目:

        1. 可以通过官网快速的搭建你的第一个springboot项目。

        2. 可以通过idea快速搭建springboot项目,这里推荐idea创建的方式。

通过idea创建springboot项目:

1. 点击new-project,选择spring initializr,填写信息。

关于SpringBoot框架,看这篇就够了。

 2. 选择springboot的版本和依赖项,点击finish完成。

关于SpringBoot框架,看这篇就够了。

3. 修改maven的地址,改为本地安装地址,file-settings-输入maven

关于SpringBoot框架,看这篇就够了。4. 创建controller、service层以及代码,开始helloworld之旅。

 关于SpringBoot框架,看这篇就够了。

controller代码:

@RestController
public class HelloController {
    @Autowired
    HelloService helloService;

    @RequestMapping("hello")
    public String hello(){
        return helloService.hello();
    }
}

 service代码:

public interface HelloService {
    String hello();
}

service实现类代码:

@Service
public class HelloServiceImpl implements HelloService {
    @Override
    public String hello() {
        return "Hello World!";
    }
}

 5. 运行项目,在浏览器中访问接口。

关于SpringBoot框架,看这篇就够了。6. 访问接口,这里我在application.properties中指定了项目的端口为8099

关于SpringBoot框架,看这篇就够了。

7. 访问接口信息如下:

关于SpringBoot框架,看这篇就够了。

starter

        在创建springboot项目的时候,我们勾选了依赖的项目,下面我们来看一下pom文件吧:

关于SpringBoot框架,看这篇就够了。

这个starter是什么呢?我们可以ctrl+鼠标左键点进去看看….. 

关于SpringBoot框架,看这篇就够了。        可以看到,springboot的web-starter 依赖中直接引入了四个依赖,这可以大大节省我们去寻找各种框架的依赖和版本的时间,进行快速构建项目,而springboot提供的starter不仅仅只有web的,还有各种通用框架的,应有尽有,想想是不是就觉得很爽呢?

关于SpringBoot框架,看这篇就够了。

核心配置文件application.yml或properties

        在springboot中,为我们提供了一个核心配置文件,就是application,你可以把它命名为application.properties,也可以吧它命名为application.yml或者application.yaml三种命名方式都代表它是springboot的核心配置文件。

关于SpringBoot框架,看这篇就够了。

application中的配置项

        application中有一些常用的配置项,例如配置数据库信息的:

spring.datasource.driver-class-name=
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=

        例如配置项目访问根路径的:

server.servlet.context-path=

         例如配置项目的端口、访问路径:

server.port=8099
#配置完毕后,需要访问时加上此项 例如:原来是http://localhost:8080
#现在是http://localhost:8080/springboot01
server.servlet.context-path=/springboot01

 更多配置内容可以访问地址获取(需要什么就ctrl+f搜索即可):

Common Application Properties

springboot的启动流程

这里推荐博主文章:9千字长文带你了解SpringBoot启动过程–史上最详细 SpringBoot启动流程-图文并茂_Fly丶X的博客-CSDN博客_springboot启动流程

启动时输出的banner可以进行自定义:

关于SpringBoot框架,看这篇就够了。

在resources下新增banner.txt文件,输入文件内容,文件内容可以通过网站生成后粘贴到文件中: https://www.bootschool.net/ascii

关于SpringBoot框架,看这篇就够了。

整合日志打印

直接在resources目录下增加文件logback.xml

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="${catalina.base}/logs/" />
    <!-- 控制台输出 -->
    <appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 日志输出格式 -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            </pattern>
        </layout>
    </appender>
    <!-- 按照每天生成日志文件 -->
    <appender name="RollingFile"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/server.%d{yyyy-MM-dd}.log</FileNamePattern>
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            </pattern>
        </layout>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>
    <!-- 日志输出级别和日志的输出方式 -->
    <root level="info">
        <appender-ref ref="Stdout" />
       <!-- <appender-ref ref="RollingFile" />-->
    </root>

    <logger name="com.msb.mapper" level="DEBUG"></logger>
    <!--日志异步到数据库 -->
    <!--<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
            日志异步到数据库
            <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
               连接池
               <dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource">
                  <driverClass>com.mysql.jdbc.Driver</driverClass>
                  <url>jdbc:mysql://127.0.0.1:3306/databaseName</url>
                  <user>root</user>
                  <password>root</password>
                </dataSource>
            </connectionSource>
      </appender> -->
</configuration>

整合druid数据源

导入druid的依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>

 在application.yml中新增配置信息:

  spring: 
    datasource: 
      # 使用阿里的Druid连接池 
      type: com.alibaba.druid.pool.DruidDataSource 
      driver-class-name: com.mysql.cj.jdbc.Driver 
      # 填写你数据库的url、登录名、密码和数据库名 
      url: jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai 
      username: root 
      password: root 
    druid: 
      # 连接池的配置信息 
      # 初始化大小,最小,最大 
      initial-size: 5 
      min-idle: 5 
      maxActive: 20 
      # 配置获取连接等待超时的时间 
      maxWait: 60000 
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 
      timeBetweenEvictionRunsMillis: 60000 
      # 配置一个连接在池中最小生存的时间,单位是毫秒 
      minEvictableIdleTimeMillis: 300000 
      validationQuery: SELECT 1 
      testWhileIdle: true 
      testOnBorrow: false 
      testOnReturn: false 
      # 打开PSCache,并且指定每个连接上PSCache的大小 
      poolPreparedStatements: true 
      maxPoolPreparedStatementPerConnectionSize: 20 
      # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,&apos;wall&apos;用于防火墙 
      filters: stat,wall,slf4j 
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录 
      connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 
      # 配置DruidStatFilter 
      web-stat-filter: 
        enabled: true 
        url-pattern: "/*" 
        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" 
      # 配置DruidStatViewServlet 
      stat-view-servlet: 
        url-pattern: "/druid/*" 
        # IP白名单(没有配置或者为空,则允许所有访问) 
        allow: 127.0.0.1,192.168.8.109 
        # IP黑名单 (存在共同时,deny优先于allow) 
        deny: 192.168.1.188 
        #  禁用HTML页面上的“Reset All”功能 
        reset-enable: false 
        # 登录名 
        login-username: admin 
        # 登录密码 
        login-password: 123456

处理异常

        后台接口发生异常时,会出现空白页面的提示,非常不友好,这时可以使用springboot的异常处理来配置错误信息页面。

关于SpringBoot框架,看这篇就够了。使用方式如下: 

关于SpringBoot框架,看这篇就够了。

可以自定义异常处理类,指定哪些异常跳转到哪些页面

1. 新增Springboot的拦截器 :

@Component 
public class DemoInterceptor implements HandlerInterceptor { 
    @Override 
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 
        System.out.println("执行拦截器"); 
        return true; 
    } 
}

2. 配置拦截器

注意:类上有注解@Configuration。此类相当于SpringMVC配置文件。

addPathPattern(): 拦截哪些URL。 /** 拦截全部

excludePathPatterns(): 不拦截哪些URL。当和addPathPattern()冲突时,excludePathPatterns()生效。

 @Configuration 
 public class MyConfig implements WebMvcConfigurer { 
     @Autowired 
     private DemoInterceptor demoInterceptor; 
     //配置拦截器的映射 
     @Override 
     public void addInterceptors(InterceptorRegistry registry) { 
         registry.addInterceptor(demoInterceptor).addPathPatterns("/**").excludePathPatterns("/login"); 
     } 
 }

常用的注解

@SrpingBootApplication:

启动类上的核心注解,它包含三个注解:

  1. SpringBootConfiguration 跟configuration等同
  2. EnableAutoConfiguration 自动装配
  3. ComponentScan 包扫描

Configuration

@Configuration

       指定当前的类为springboot的配置类,可以在配置类中定义@Bean注解,注入bean对象,将bean对象交给springboot去管理。

关于SpringBoot框架,看这篇就够了。Configuration(proxyBeanMethods=true)

proxyBeanMethods=true: 代表通过代理对象,依赖spring容器控制Bean的单例

proxyBeanMethods=false: 代表不会通过代理对象控制Bean的单例

Import

@Import: 加在类上,通过此注解可以引入其他类。

关于SpringBoot框架,看这篇就够了。

conditional

@Conditional:满足了conditional中的条件则进行组件注入

关于SpringBoot框架,看这篇就够了。关于SpringBoot框架,看这篇就够了。 

@ImportResource: 允许我们定义xml配置文件,在文件中配置bean,用法如下:

关于SpringBoot框架,看这篇就够了。

 

ConfigruationProperties

读取配置文件中的内容,以对象的形式注入特定的类中

@ ConfigruationProperties

关于SpringBoot框架,看这篇就够了。

关于SpringBoot框架,看这篇就够了。

基于springboot的SSM框架

1. 创建项目,可参考章节:创建第一个以springboot项目

2. 导入依赖

<!--日志包-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.6.1</version>
</dependency>
<!--代码生成工具-->
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.2</version>
</dependency>
<!-- 分页插件 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.1</version>
</dependency>

3. 配置application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
thymeleaf:
  prefix: classpath:/templates/html/
  suffix: .html
  cache: false
  model: HTML5
  encoding: UTF-8
mybatis:
  type-aliases-package: com.ssm.mapper
  mapper-locations: classpath:com/ssm/mapper/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
server:
  port: 8099

 4. 新增文件generatorConfig.xml,需要修改文件中的路径。

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE generatorConfiguration PUBLIC
        "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <classPathEntry
            location="D:\maven\repository\mysql\mysql-connector-java\8.0.22\mysql-connector-java-8.0.22.jar"/>
    <context id="testTables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <!--<jdbcConnection driverClass="${jdbc.driver}"--> <!--connectionURL="${jdbc.url}"--> <!--userId="${jdbc.username}"--> <!--password="${jdbc.password}">--> <!--</jdbcConnection>-->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3306/test?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai&amp;allowPublicKeyRetrieval=true"
                        userId="root" password="">
        </jdbcConnection>
        <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
        <!-- targetProject:生成PO类的位置 -->
        <javaModelGenerator targetPackage="com"
                            targetProject="F:\code">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false"/>
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <!-- targetProject:mapper映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="com"
                         targetProject="F:\code">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>
        <!-- targetPackage:mapper接口生成的位置 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com"
                             targetProject="F:\code">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false"/>
        </javaClientGenerator>
        <!-- 指定数据库表 -->
        <table tableName="gameuser" domainObjectName="Gameuser"
               enableCountByExample="false"
               enableUpdateByExample="false"
               enableDeleteByExample="false"
               enableSelectByExample="false"
               selectByExampleQueryId="false">
        </table>
        <!-- 有些表的字段需要指定java类型
        <table tableName="area" domainObjectName="Area"
               enableCountByExample="false"
               enableUpdateByExample="false"
               enableDeleteByExample="false"
               enableSelectByExample="false"
               selectByExampleQueryId="false">
        </table>
        -->
    </context>
</generatorConfiguration>

5. 新增log4j.properties

log4j.rootLogger=debug,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout

log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=d:/lc.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d(yyyy-MM-dd HH:mm:ss)%1 %F %p %m%n

6. 在test包下新增java类:

 

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class GeneratorCode {
    public void generator() throws Exception {
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("F:\\springbootall\\sp03\\ssm01\\target\\classes\\generatorConfig.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
                callback,warnings);
        myBatisGenerator.generate(null);
    }

    public static void main(String[] args) {
        GeneratorCode generatorSqlmap = new GeneratorCode();
        try {
            generatorSqlmap.generator();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

7. 运行java类就可以生成代码.

8. Controller类的代码如下:

 

@RequestMapping(value = "getAllGameUserList",method = RequestMethod.GET)
public Map<String,Object> getAllGameUsers(
        @RequestParam(value = "currentPage",required = false) int currentPage,
        @RequestParam(value = "pageSize",required = false) int pageSize,
        @RequestParam(value = "recordCount",required = false) int recordCount,
        @RequestParam(value = "usrLoginNm",required = false) String usrLoginNm,
        @RequestParam(value = "usrNm",required = false) String usrNm,
        @RequestParam(value = "usrCardNum",required = false) String usrCardNum,
        @RequestParam(value = "usrAddr",required = false) String usrAddr,
        @RequestParam(value = "usrBirDate",required = false) Date usrBirDate,
        @RequestParam(value = "usrHobby",required = false) String usrHobby,
        @RequestParam(value = "usrDetail",required = false) String usrDetail){
    Gameuser gameuser=new Gameuser();
    gameuser.setUsrLoginNm(usrLoginNm);
    gameuser.setUsrNm(usrNm);
    gameuser.setUsrCardNum(usrCardNum);
    gameuser.setUsrAddr(usrAddr);
    gameuser.setUsrBirDate(usrBirDate);
    gameuser.setUsrHobby(usrHobby);
    gameuser.setUsrDetail(usrDetail);
    logger.info("gameuser: "+gameuser.toString());
    PageInfo<Gameuser> gameuserPage = gameUserService.getAllGameUsers(currentPage, pageSize, recordCount, gameuser);
    Map<String,Object> objectMap=new HashMap<>();
    objectMap.put("recordList",gameuserPage.getList());
    objectMap.put("totalCount",gameuserPage.getTotal());
    objectMap.put("currentPage",gameuserPage.getPageNum());
    objectMap.put("pageSize",gameuserPage.getPageSize());
    return objectMap;
}

9. ServiceImpl代码如下:

@Override
public PageInfo<Gameuser> getAllGameUsers(int currentPage, int pageSize, int recordCount, Gameuser gameuser) {
    PageHelper.startPage(currentPage, pageSize);
    List<Gameuser> allGameUserLists = gameuserMapper.getAllGameUserLists(gameuser);
    PageInfo<Gameuser> gameuserPageInfo = new PageInfo<>(allGameUserLists);
    return gameuserPageInfo;
}

10. Mapperxml代码如下:

<select id="getAllGameUserLists" parameterType="com.ssm.bean.Gameuser" resultType="com.ssm.bean.Gameuser">
  SELECT * FROM gameuser
  <where>
    <if test="usrLoginNm != null and usrLoginNm != ''" >
      <bind name="usrLoginNmPattern" value="'%'+usrLoginNm+'%'"/>
      and usrLoginNm like #{usrLoginNmPattern}
    </if>
    <if test="usrNm != null and usrNm != ''" >
      <bind name="usrNmPattern" value="'%'+usrNm+'%'"/>
      and usrNm like #{usrNmPattern}
    </if>
    <if test="usrCardNum != null and usrCardNum != ''" >
      and usrCardNum = #{usrCardNum}
    </if>
    <if test="usrAddr != null and usrAddr != ''" >
      <bind name="usrAddrPattern" value="'%'+usrAddr+'%'"/>
      and usrAddr like #{usrAddrPattern}
    </if>
    <if test="usrBirDate != null and usrBirDate != ''" >
      and usrAddr = #{usrBirDate}
    </if>
    <if test="usrHobby != null and usrHobby != ''" >
      and usrHobby = #{usrHobby}
    </if>
    <if test="usrDetail != null and usrDetail != ''" >
      <bind name="usrDetailPattern" value="'%'+usrDetail+'%'"/>
      and usrDetail like #{usrDetailPattern}
    </if>
  </where>
</select>

基于springboot的SSMibatisPlus框架

详见我的另一篇博客:MybatisPlus详细教程,看后不会你打我…………_只为code醉的博客-CSDN博客

springboot项目的打包及运行

通过maven-install命令,对项目进行打包操作,打包后的jar包存放在target目录下:

关于SpringBoot框架,看这篇就够了。

直接放置在装有JDK的机器上,执行命令:

        java -jar jar包名称

即可运行项目。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/116523.html

(0)
seven_的头像seven_bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!