-
一、简介 mybatis框架本身其实只有一个配置文件,及mybatis-config.xml,只是这个配置文件中有一个属性mappers(映射器),可能会包含大量的SQL映射文件,所以通常会单独扩展,形成一个个单独的xxxMapper.xml映射文件。 二、Mybatis的配置层次: -configuration(根标签,配置) -properties(属性) -settings(设置集) -setting(定义全局性设置,例如开启二级缓存) -typeAiases(类型别名集) -typeAlias(为一些类定义别名) -typeHandlers(类型处理器:定义Java类型与数据库中数据类型之间的转换关系) -objectFactory(对象工厂) -plugins(插件集) -plugin(mybatis插件或自定义插件) -environments(配置mybatis的环境) -environment(环境变量:支持多套环境变量,如开发环境,生产环境) -transactionManager(事务管理器,默认JDBC) -DataSource(数据源,一般使用连接池) -databaseIdProvider(数据库厂商标识) -mappers(指定映射文件或映射类) 三、xml核心配置 3.1 properties(属性标签) 属性标签用来提供动态的属性配置,可采用以下几种方式进行配置 1. 直接配置 例如数据源信息配置,设置好的属性值在整个配置文件中可以进行动态引用,使用${}可以引用已配置的属性值。 <properties> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/myDB"/> <property name="username" value="root"/> <property name="password" value="root"/> </properties> 2. 属性文件配置 使用java中的属性配置文件,把属性配置元素具体到配置文件中,然后在properties标签中使用resource属性引入,利用配置文件中的key名称作为占位符,就可以取到实际值 <!-- 引入属性配置文件 --> <properties resource="jdbc.properties"></properties> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> 3. url引入 类似于属性文件,只是文件是使用 url 指定远程的配置文件。 注意:properties 元素允许配置 resource 属性或 url 属性,只能二选一,要么使用 resource 指定本地的配置文件,要么使用 url 指定远程的配置文件,因为 Mybatis 在加载配置时,如果发现 url 与 resource 同时存在,会抛出异常禁止 <!-- 配置url--> <properties url="http://xxxx"></properties> 4. Java代码 在代码中构建java.util.Properties 属性对象并传递到 SqlSessionFactoryBuilder.build() 方法中 // 构建属性对象 Properties props = new Properties(); props.setProperty("driver","com.mysql.jdbc.Driver"); props.setProperty("url","jdbc:mysql://127.0.0.1:3306/myDB"); props.setProperty("username","user1"); props.setProperty("password","123456"); // 传递属性构建 SqlSessionFactory SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props); 5. 优先级 除了属性文件引入和url引入不可重复外,其它的方式都允许配置,优先级别如下: 在代码中构建java.util.Properties 属性对象 属性文件引入和url引入 直接在properties内部子标签元素property中设置的属性 3.2 settings(设置标签) settings标签用于动态改变mybatis的运行状态,mybatis提供了大量的设备属性,总结了一下如下所示 <settings> <!-- 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存 支持 true | false 默认 true --> <setting name="cacheEnabled" value="true"/> <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。 支持 true | false 默认 false --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。 支持 true | false 默认 false (在 3.4.1 及之前的版本中默认为 true) --> <setting name="aggressiveLazyLoading" value="true"/> <!-- 是否允许单个语句返回多结果集(需要数据库驱动支持)。 支持 true | false 默认 true --> <setting name="multipleResultSetsEnabled" value="true"/> <!-- 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。 支持 true | false 默认 true --> <setting name="useColumnLabel" value="true"/> <!-- s 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性, 但仍可正常工作(如 Derby)。 支持 true | false 默认 false --> <setting name="useGeneratedKeys" value="false"/> <!-- 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 支持 NONE, PARTIAL, FULL 默认 PARTIAL --> <setting name="autoMappingBehavior" value="PARTIAL"/> <!-- 指定发现自动映射目标未知列(或未知属性类型)的行为。NONE:不做任何反应 WARNING: 输出警告日志( org.apache.ibatis.session.AutoMappingUnknownColumnBehavior 的日志等级必须设置为 WARN) FAILING: 映射失败 (抛出 SqlSessionException) 支持 NONE, WARNING, FAILING 默认 NONE --> <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/> <!-- 置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。 支持 SIMPLE REUSE BATCH 默认 SIMPLE --> <setting name="defaultExecutorType" value="SIMPLE"/> <!-- 设置超时时间,它决定数据库驱动等待数据库响应的秒数。 支持任意正整数默认未设置 (null) --> <setting name="defaultStatementTimeout" value="25"/> <!-- 设置结果集获取数量,此参数可以在查询设置中被覆盖。支持任意正整数默认未设置 (null) --> <setting name="defaultFetchSize" value="100"/> <!-- 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 支持 true | false 默认 false --> <setting name="safeRowBoundsEnabled" value="false"/> <!-- 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 支持 true | false 默认 false --> <setting name="mapUnderscoreToCamelCase" value="false"/> <!-- MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 支持 SESSION | STATEMENT 默认 SESSION --> <setting name="localCacheScope" value="SESSION"/> <!-- 当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可, 比如 NULL、VARCHAR 或 OTHER。 JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。 默认 OTHER --> <setting name="jdbcTypeForNull" value="OTHER"/> <!-- 指定对象的哪些方法触发一次延迟加载。 支持 用逗号分隔的方法列表。 默认 equals,clone,hashCode,toString --> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/> <!-- 指定动态 SQL 生成使用的默认脚本语言。 支持 一个类型别名或全限定类名。 默认 org.apache.ibatis.scripting.xmltags.XMLLanguageDriver --> <setting name="defaultScriptingLanguage" value="org.apache.ibatis.scripting.xmltags.XMLLanguageDriver"/> <!-- 指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5) 支持 一个类型别名或全限定类名。 默认 org.apache.ibatis.type.EnumTypeHandler --> <setting name="defaultEnumTypeHandler" value="org.apache.ibatis.type.EnumTypeHandler"/> <!-- 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。 注意基本类型(int、boolean 等)是不能设置成 null 的。 支持 true | false 默认 false --> <setting name="callSettersOnNulls" value="false"/> <!-- 当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。 (新增于 3.4.2) 支持 true | false 默认 false --> <setting name="returnInstanceForEmptyRow" value="false"/> <!-- 指定 MyBatis 增加到日志名称的前缀。 支持 任何字符串 默认 未设置 --> <setting name="logPrefix" /> <!-- 定 MyBatis 所用日志的具体实现,未指定时将自动查找。 支持 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 默认 未设置 --> <setting name="logImpl" /> <!-- 指定 Mybatis 创建可延迟加载对象所用到的代理工具。 支持 CGLIB | JAVASSIST 默认 JAVASSIST --> <setting name="proxyFactory" value="JAVASSIST"/> </settings> 3.3 typeAliases(类型别名) 用于给Java类型设置一个简称,仅用于XML配置,是为了降低冗余的全限定类名书写。类型别名在mybatis中分为系统内置和用户自定义两类,在解析配置文件时会把typeAliases实例存储进configuration对象中。使用时可直接获取。 3.3.1 自定义别名 自定义别名可以直接设置,如下,在使用com.it.learn.Author的地方可以直接使用别名Author <typeAliases> <typeAlias alias="Author" type="com.it.learn.Author"/> </typeAliases> 但当某个项目中特别多java类需要设置别名时,可以直接指定包名扫描,如下,每个在com.it.learn包中的Java类,都可以使用首字母小写的名称来作为别名。但如果Java类上存在@Alias注解,则使用注解value值作为别名 <typeAliases> <package name="com.it.learn"/> </typeAliases> 3.3.2 系统内置别名 mybatis已经针对大部分常见的java类型内建了相应的类型别名,不区分大小写,基本上都是类名首字母小写,同时也支持数组类型,只需要加上'[]'即可。但需要注意的是,Java中的基本类型和包装类不区分大小写的情况下是同名的,因此基本类型的别名前缀都有下划线'_',而包装类没有。mybatis的注册信息源码在org.apache.ibatis.type.TypeAliasRegistry#TypeAliasRegistry方法中,有兴趣的朋友可以看看。 3.4 typeHandlers(类型处理器) mybatis在赋值预处理SQL语句(PreparedStatement)中所需要的参数或从结果集ResultSet中获取对象时,都会使用类型处理器将获取到的值转换为Java类型。也即处理Java类型和JDBC类型之间的映射关系,默认的一些类型处理器如下所示: 更多的类型转换器可以在mybatis的org.apache.ibatis.type.TypeHandlerRegistry#TypeHandlerRegistry() 方法中查看。 当默认的处理器无法满足业务需求时,也可以自定义类型处理器,只需要继承org.apache.ibatis.type.BaseTypeHandler 类即可,该类实现了 org.apache.ibatis.type.TypeHandler 接口,该接口主要定义了两种方法:setParameter 和 getResult,在预编译时设置参数(增删改查传入参数)与查询结果集后转换为 Java 类型时发挥作用。自定义类型处理器配置流程: 3.4.1 首先定义类MyExampleTypeHandler,利用注解与泛型设置映射类型 @MappedJdbcTypes(JdbcType.VARCHAR) public class MyExampleTypeHandler extends BaseTypeHandler<String> { @Override public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, parameter); } @Override public String getNullableResult(ResultSet rs, String columnName) throws SQLException { return rs.getString(columnName); } @Override public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return rs.getString(columnIndex); } @Override public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return cs.getString(columnIndex); } } 3.4.2 在配置文件中设置类型处理器 <!-- mybatis-config.xml --> <typeHandlers> <typeHandler handler="org.mybatis.example.MyExampleTypeHandler"/> <!-- 或者使用包扫描处理器 --> <package name="org.mybatis.example"/> </typeHandlers> 3.4.3 一个类型处理器就已经配置完成,但如果想要处理特殊情况,例如只希望对某个Java类中的属性生效,则可以先将@MappedJdbcTypes注解去除,直接在映射文件中编写,则自定义类型处理器,只会对 birthday 字段产生影响,其余字段均不受影响。 <resultMap id="MyResultMap" type="com.panshenlian.pojo.User"> <!-- id为int类型,但是没指定自定义类型处理器,不受影响--> <id column="id" property="id" /> <!-- username为String类型,但是没指定自定义类型处理器,不受影响--> <id column="username" property="username" /> <!-- password为String类型,但是没指定自定义类型处理器,不受影响--> <id column="password" property="password" /> <!-- birthday为String类型,指定自定义类型处理器,受影响!--> <id column="birthday" property="birthday" typeHandler="com.mybatis.typeHandler.MyStringHandler"/> </resultMap> 3.5 objectFactory(对象工厂) 创建结果对象的新实例时使用的对象工厂,利用该工厂的实例来完成结果对象的实例化。默认的对象工厂只会实例化目标类,也即调用无参构造或者通过存在的参数映射调用有参构造,如果进行扩展,可以创建自己的对象工厂来实,只需要继承DefaultObjectFactory类即可,一般情况下不会使用到。 ———————————————— 原文链接:https://blog.csdn.net/weixin_42189550/article/details/130326404
-
1、全局配置文件SqlMapConfig 1.1 全局配置文件SqlMapConfig配置项 在使用MyBatis框架时,最重要的一步就是配置项目工程的全局参数信息,以及数据库连接信息,这些配置信息均在全局配置文件SqlMapConfig.xml中。除此之外,该配置文件中还包含了SQL应神剑的加载路径、全局参数以及类型别名等一系列MyBatis的核心配置信息。SqlMapConfig中的配置信息必须严格按照格式标准进行配置。 全局配置文件SqlMapConfig.xml中各个配置项的名称、含义及描述如下表: 配置名称 配置含义 配置简介 configuration 包裹所有配置标签 整个配置文件的顶级标签 properties 属性 该标签可以引入外部配置的属性,也可以自己配置。该配置标签所在的同一个配置文件的其他配置均可以引用此配置中的属性 setting 全局配置参数 用来配置一些改变运行时行为的信息,例如是否使用缓存机制,是否使用延迟加载,是否使用错误处理机制等。并且可以设置最大并发请求数量、最大并发事务数量,以及是否启用命名空间等 typeAliases 类型别名 用来设置一些别名来代替Java的长类型声明(如java.lang.int变为int),减少配置编码的冗余 typeHandlers 类型处理器 将SQL中返回的数据库类型转换为相应的Java类型的处理器配置 objectFactory 对象工厂 实例化目标类的工厂类配置 plugins 插件 可以通过插件修改MyBatis的核心行为,例如对语句执行的某一点进行拦截调用 environments 环境集合属性对象 数据库环境信息的集合。在一个配置文件中,可以有多种数据库环境集合,这样可以使MyBatis将SQL同时映射至多个数据库 environment 环境子属性对象 数据库环境配置的详细配置 transactionManager 事务管理 指定MyBatis的事务管理器 dataSource 数据源 使用其中的type指定数据源的连接类型,在标签对中可以使用property属性指定数据库连接池的其他信息 mappers 映射器 配置SQL映射文件的位置,告知MyBatis去哪里加载SQL映射文件 按照MyBatis的全局配置文件中的配置顺序,下面给出一个配置了全部参数的样例配置文件,配置文件仅供参考,具体示例配置如下: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--1、properties属性引入外部配置文件:注册DB连接四要素的属性文件--> <properties resource="jdbc.properties" /> <!--2、全局参数设置--> <settings> <!-- 配置LOG信息 --> <setting name="logImpl" value="LOG4J" /> <!-- 延迟加载总开关 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 侵入式延迟加载开关 --> <setting name="aggressiveLazyLoading" value="true"/> <!-- 关闭二级查询缓存 --> <!--<setting name="cacheEnabled" value="false" />--> </settings> <!--3、配置别名--> <typeAliases> <package name="com.ccff.mybatis.model"/> </typeAliases> <!--4、配置类型转换器--> <typeHandlers> <!--一个简单类型转换器--> <typeHandler handler="org.mybatis.example.ExampleTypeHandler" /> </typeHandlers> <!--5、对象工厂--> <objectFactory type="org.mybatis.example.ExampleObjectFactory"> <!--对象工厂注入的参数--> <property name="someProperty" value="100" /> </objectFactory> <!--6、插件--> <plugins> <plugin interceptor="org.mybatis.example.ExamplePlugin"> <property name="someProperty" value="100" /> </plugin> </plugins> <!-- 7、environments数据库环境配置:和spring整合后 environments配置将废除--> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理--> <transactionManager type="JDBC" /> <!-- 数据库连接池--> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <!--8、配置SQL映射文件的位置--> <mappers> <mapper resource="sqlmap/UserMapper.xml"/> <mapper resource="sqlmap/StudentMapper.xml"/> <mapper resource="sqlmap/BasketballPlayerMapper.xml"/> <mapper resource="sqlmap/FinacialMapper.xml"/> <mapper resource="sqlmap/NewsLabelMapper.xml" /> <mapper resource="sqlmap/LazyLoadingMapper.xml" /> <mapper resource="sqlmap/BookMapper.xml" /> <mapper resource="sqlmap/GoodsMapper.xml" /> </mappers> </configuration> 1.2 properties配置分析 在SqlMapConfig配置文件中,properties标签中的数据可以供整个配置文件中的其他配置使用。properties标签可以引入一个可动态变换的外部配置,如一个传统的Java配置文件,亦或是一个properties参数配置文件。当然,在properties标签内部也可以放置property标签,来配置子元素信息。对于配置文件中的其他配置,可以通过property子标签的name属性来取得相应的value值。 一个properties标签配置样例如下(引入了一个jdbc.properties文件) <!-- 注册DB连接四要素的属性文件 --> <properties resource="jdbc.properties"> <property name="username" value="root" /> <property name="password" value="root" /> </properties> 可以看到,其中引入了一个jdbc.properties属性文件,该配置文件样例如下: jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8 jdbc.username=root jdbc.password=root jdbc.properties配置文件中配置了数据库的详细连接信息,properties标签这样引入它避免了数据库信息的“硬编码”。当需要连接其他数据库时,只需要更改要连接的数据库的配置文件路径即可。 当然properties标签也可以包含property子标签,该子标签中的值也可以被配置文件中的其他配置使用,属于一种全局参数。可以看到,这里虽引入了jdbc.properties文件,但是其中的username和password还可以重新在property子标签中配置,这个时候取的就是property子标签中的value值。这样配置是因为有时候有些模块的数据库连接的用户可能需要以不同的角色登录,这样可以在property子标签中动态分配数据库连接的用户名username和密码password。 在properties标签中引入的配置文件信息以及property子标签中配置的信息,在其他标签中可以使用“$ {}”占位符的方式来获取,在“$ {}”中填写引入的配置文件中参数的name或者property子标签的name,样例配置中的数据源信息配置其实就是获取properties标签中的配置信息。 <!-- 数据库连接池--> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> 另外,在MyBatis 3.4.2之后,还可以在“$ {}”占位符中设置一个默认值。当然,首先要在properties标签中设置一个启用占位符默认值的配置,该配置如下: <!-- 注册DB连接四要素的属性文件 --> <properties resource="jdbc.properties"> <property name="org.apache.ibatis.parsing.PropertyParser.enable-defaut-value" value="true" /> </properties> 然后在其他属性引用properties标签中的参数时,可以这样设置一个默认值 <!-- 数据库连接池--> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username:root}"/> <property name="password" value="${jdbc.password}"/> </dataSource> 即在所需引入的属性名的后面添加“:”引号,然后紧跟着填写当jdbc.username不存在或为空时的默认值。 1.3 setting配置分析 setting配置也是MyBatis全局配置文件中比较重要的配置,它影响MyBatis框架在运行时的一些行为。setting配置缓存、延迟加载、结果集控制、执行器、分页设置、命名规则等一系列控制性参数,与MyBatis的运行性能息息相关。所有的setting配置都被包裹在settings标签对中,setting可配置的参数如下表所示。 ———————————————— 原文链接:https://blog.csdn.net/weixin_36378917/article/details/85246242
-
MyBatis核心配置文件(mybatis-config.xml) 主要用于配置数据连接和MyBatis运行时所需的各种特性:包含了影响MyBatis行为甚深的设置(settings)和属性(properties) mybatis-config.xml是Mybatis的全局配置文件,名称可以是任意,但是一般命名都为(mybatis-config) <configuration>元素是整个XML配置文件的根节点,其角色就相当于是MyBatis的总管,所有的配置信息都会存放在它的里面 MyBatis还提供了设置这些配置信息的方法。configuration可从配置文件里获取属性值,也可以通过程序直接设置,configuration可供配置内容 核心配置文件常用元素 configuration为配置文件的根元素节点,下面是configuration元素子节点。 (1)properties 通过resource属性从外部指定属性文件(db.properties),该属性文件描述数据库连接的相关配置(数据库驱动、连接数据库的url、数据库用户名、数据库密码),位置也是在/resources目录下 (2)settings 设置MyBatis运行中的一些行为,比如此处设置MyBatis的log日志实现为LOG4J,即使用log4j实现日志功能 (3)environments 表示配置MyBatis的多套运行环境,将SQL映射到多个不同的数据库上,该元素节点不可以配置多个environments子元素节点,但是必须指定其中一个默认运行环境(通过default指定) ①environment 配置MyBatis的一套运行环境,需指定运行环境ID、事务管理、数据源配置等相关信息 environment子元素 描述 transactionManager 事务管理器 dataSource 数据源 (4)mappers 告诉MyBatis去哪里找到SQL映射文件(开发者定义的映射SQL语句),整个项目中可以有1个或多个SQL映射文件。 mappers子元素 描述 mapper 指定SQL映射文件的路径,其中resource属性的值表述了SQL映射文件的路径(类资源路径) (5)typeAliases 为Java类型命名一个别名(简称) (6)typeHandlers 类型处理器 (7)plugins 插件 注意:mybatis-config.xml文件的元素节点是有一定顺序的,节点位置若不按顺序排位,那么XML文件会报错 配置文件(mybatis-config.xml)详细 1.properties(数据库连接) 通过该标签来读取Java配置信息 数据源信息写在db.properties文件中,可以通过properties标签来加载该文件 db.properties: db.driver=com.mysql.jdbc.Driver db.url=jdbc:mysql://localhost:3306/my db.username=root db.password=root mybatis-config.xml使用properties标签: <configuration> <!-- 通过properties标签,读取java配置文件的内容 --> <properties resource="db.properties" /> <!-- 配置mybatis的环境信息 --> <environments default="development"> <environment id="development"> <!-- 配置JDBC事务控制,由mtybatis进行管理 --> <transactionManager type="JDBC"></transactionManager> <!-- 配置数据源,采用dbcp连接池 --> <dataSource type="POOLED"> <property name="driver" value="${db.driver}" /> <property name="url" value="${db.url}" /> <property name="username" value="${db.username}" /> <property name="password" value="${db.password}" /> </dataSource> </environment> </environments> </configuration> 注意: <properties resource="db.properties"> <property name="db.username" value="1234" /> 优先① </properties> (1)先加载properties中property标签声明的属性 因此在property中的name属性的值和value比properties中的resource属性先加载;后加载的db.properties会覆盖于property加载属性和值 (2)再加载properties标签引入的Java配置文件中的属性 (3)最后再读取映射文件中parameterType的值会和properties的属性值发生冲突。因此,在properties文件里的内容命名最好加上db.代表是跟数据源相关的属性,这样不容易跟后面的属性发生冲突 2.settings(运行) 设置一些非常重要的设置选项,用于设置和改变MyBatis运行中的行为 settings元素支持的属性。 设置项 描述 允许值 默认 cacheEnabled 该配置影响的所有映射器中配置的缓存的全局开关 true/false true lazyLoadingEnabled 全局性设置懒加载。false则所有相关联的都会被初始化加载 true/false true autoMappingBehavior MyBatis对于resultMap自动映射的匹配级别 NONE PARTIAL aggressiveLazyLoading 当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载 true,false multipleResultSetsEnabled 是否允许单一语句返回多结果集(需要兼容驱动) true,false true useColumnLabel 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果 true,false true useGeneratedKeys 允许JDBC支持自动生成主键,需要驱动兼容。如果设置为true则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如Derby) true,false false defaultExecutorType 配置默认的执行器。SIMPLE就是普通的执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 SIMPLER,EUSE,BATCH SIMPLE defaultStatementTimeout 设置超时时间,它决定驱动等待数据库响应的秒数 Any positive integer;任意正整数 Not Set (null) safeRowBoundsEnabled 允许在嵌套语句中使用分页(RowBounds) true,false false mapUnderscoreToCamelCase 是否开启自动驼峰命名规则(camel case)映射。即从经典数据库列名A_COLUMN到经典Java属性名aColumn的类似映射 true, false false localCacheScope MyBatis利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为SESSION,这种情况下会缓存一个会话中执行的所有查询。若设置值为STATEMENT,本地会话仅用在语句执行上,对相同SqlSession 的不同调用将不会共享数据 SESSION,STATEMENT SESSION jdbcTypeForNull 当没有为参数提供特定的JDBC类型时。为空值指定JDBC类型。某些驱动需要指定列的JDBC类型,多数情况直接用一般类型即可,比如NULL、VARCHAR或OTHER JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER OTHER lazyLoadTriggerMethods 指定哪个对象的方法触发一次延迟加载 用逗号分隔的方法名列表 equals,clone,hashCode,toString defaultScriptingLanguage 指定动态SQL生成的默认语言 类型别名或完全限定类名 org.apache.ibatis.scripting.xmltags.XMLDynamicLanguageDriver callSettersOnNulls 指定当结果集中值为null的时候,是否调用映射对象的setter(map对象时为put)方法,这对于有 Map.keySet()依赖或null值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成 null的 true,false false logPrefix 指定MyBatis增加到日志名称的前缀 指定MyBatis所用日志的具体实现,未指定时将自动查找 SLF4J,LOG4J,LOG4J2,JDK_LOGGING,COMMONS_LOGGING,STDOUT_LOGGING,NO_LOGGING proxyFactory 指定Mybatis创建具有延迟加载能力的对象所用到的代理工具 CGLIB,JAVASSIST CGLIB 3.environments(环境) MyBatis可以配置成适应多种环境,这种机制有助于将SQL映射应用于多种数据库之中 例如:开发、测试和生产环境需要有不同的配置;或者共享相同Schema的多个生产数据库,像使用相同的SQL映射。 注意:尽管可以配置多个环境,每个SqlSessionFactory实例只能选择其一 所以,如果想连接两个数据库,就需要创建两个SqlSessionFactory实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推。 指定创建哪种环境,只要将它作为可选的参数传递给SqlSessionFactoryBuilder即可。 SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment); SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment,properties); 如果忽略了环境参数,那么默认环境将会被加载 SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader); SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader,properties); 使用environments 环境元素定义了如何配置环境。 <environments default="development"> <!-- default="development"默认的环境ID,id="development"自定义的环境ID --> <environment id="development"> <!-- type事务管理器配置 --> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <!-- type数据源的配置 --> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> transactionManager(事务管理器) 在MyBatis中有两种类型的事务管理器(type=“[JDBC|MANAGED]”) JDBC 直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务范围 MANAGED 几乎没做什么,它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如:JEE应用服务器的上下文ServeltContext) 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需将closeConnection属性设置为false来阻止它默认的关闭行为。 <transactionManager type="MANAGED"> <property name="closeConnection" value="false"/> </transactionManager> 如果正在使用Spring + MyBatis,则没有必要配置事务管理器,因为Spring模块会使用自带的管理器来覆盖前面的配置 这两种事务管理器类型都不需要任何属性。它们不过是类型别名,换句话说,可以使用 TransactionFactory接口的实现类的完全限定名或类型别名代替它们 public interface TransactionFactory { void setProperties(Properties props); Transaction newTransaction(Connection conn); Transaction newTransaction(DataSource dataSource, TrarnsactionIsolationLevel level, boolean autoCommit); } 任何在XML中配置的属性在实例化之后将会被传递给setProperties()方法。也需要创建一个Transaction接口的实现类,这个接口也很简单: public interface Transaction { Connection getConnection() throws SQLException; void commit() throws SQLException; void rollback() throws SQLException; void close() throws SQLException; } 使用这两个接口,可以完全自定义MyBatis对事务的处理 dataSource(数据源) dataSource元素使用标准的JDBC数据源接口来配置JDBC连接对象的资源 许多MyBatis的应用程序将会按示例中的例子来配置数据源。然而它并不是必须的。要知道为了方便使用延迟加载,数据源才是必须的。 有三种内建的数据源类型(也就是type=“[UNPOOLED|POOLED|JNDI]”) (1)UNPOOLED 数据源的实现只是每次被请求时打开和关闭连接。虽然一点慢,它对在及时可用连接方面没有性能要求的简单应用程序是一个很好的选择(某些数据库对连接池不重要,这个配置是理想的) ———————————————— 原文链接:https://blog.csdn.net/qq_45305209/article/details/130347108
-
一、mybatis-config.xml 的作用 mybatis-config.xml是MyBatis核心配置文件,该文件配置了MyBatis的一些全局信息,包含数据库连接信息和Mybatis运行时所需的各种特性,以及设置和影响Mybatis行为的一些属性 二、Mybatis核心配置文件结构 configuration配置 properties 可以配置在Java属性配置文件中 settings 修改Mybatis在运行时的行为方式 typeAliases 为Java类型命名一个别名(简称) typeHandlers 类型处理器 objectFactory 对象工厂 plugins 插件 environments 环境 environment 环境变量 transactionManager 事务管理器 dataSource 数据源 mappers 映射器 三、配置文件讲解 configuration configuration元素是整个XML配置文件的根节点,相当于MyBatis的总管 MyBatis所有的配置信息都会存放在这里面。Mybatis提供了设置这些配置信息的方法。 Configuration可从配置文件里获取属性值,也可以通过程序直接设置 properties元素 properties元素描述的都是外部化、可替代的属性 通过外部指定的方式(通过在典型Java属性配置文件 如database.properties) database.properties driver=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/smbms user=root password=root mybatis-config.xml 引入database.properties !--引入 database.properties 文件--> <properties resource="database.properties"/> …… <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${user}"/> <property name="password" value="${password}"/> </dataSource> 上述代码中,driver url username password属性将会用database.properties文件中的值来替换 直接配置为xml,并使用这些属性对配置项实现动态配置 mybatis-config.xml !-- properties元素中直接配置property属性--> <properties> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/smbms"/> <property name="user" value="root"/> <property name="password" value="root"/></properties> …… <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${user}"/> <property name="password" value="${password}"/> </dataSource> 在上述代码中,driver url username password将会由properties元素中设置的值来替换 两种方式优先级 resource属性值的优先级高于property子节点配置的值 settings元素 settings元素的作用是设置一些非常重要的选项,用于设置和改变Mybatis运行中的行为 settings元素支持的属性 cacheEnabled : 对在此配置文件下的所有cache进行全局性开/关设置 - true|false lazyLoadingEnabled : 全局性设置懒加载。如果设为false,则所有相关联的设置都会被初始化加载 -true|false autoMappingBehavior : Mybatis对于resultMap自动映射的匹配级别 -PAPTLAL|NONE|FULL type Aliases元素 配置类型别名,通过与MyBatis的SQL映射文件相关联,减少输入多余的完整类名,以简化操作 1.基础配置 <typeAliases> <!--这里给实体类取别名,方便在mapper配置文件中使用--> <typeAlias alias="user" type="cn.smbms.pojo.User"/> <typeAlias alias="provider" type="cn.smbms.pojo.Provider"/> …… </typeAliases> 2.简便配置 第一种方法弊端在于:如果一个项目中有多个POJO,需要一一进行配置。所以在第二种方法中,我们通过name属性直接指定包名,让Mybatis自动去扫描指定包下的JavaBean,并设置一个别名 <typeAliases> <package name="cn.smbms.pojo"/> </typeAliases> <mapper namespace="cn.smbms.dao.user.User Mapper"> <!--查询用户表记录数--> <select id="count" result Type="int">select count(1) as count from smbms_user </select> <!--查询用户列表--> <select id="get User List" result Type="User">select * from smbms_user/select> </mapper> 另外,Mybatis已经为许多常见的Java基础数据类型内建了相应的类型别名。一般都与其映射类型一致,并且都是大小写不敏感的,比如映射的类型int Boolean String Integer等,它们的别名都是int Boolean String Integer environments元素 配置不同的运行环境 但是不管增加几套运行环境,都必须要明确选择出当前唯一的一个运行环境。这是因为每个数据库都对应一个Sql Session Factory实例,需要指明哪个运行环境被创建,并把运行环境中设置的参数传递个Sql Session Factory Builder 配置如下 <environments default="development"> <!--开发环境--> <environment id="development"> <transaction Manager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${user}"/> <property name="password" value="${password}"/> </dataSource> </environment> <!--测试环境--> <environment id="test"> …… </environment> </environments> 上述代码中,需要注意以下几个关键点: (1)默认的运行环境ID:通过default属性来指定当前的运行环境ID为development,对于环境ID的命名要确保唯一。 (2)transactionManager事务管理器:设置其类型为JDBC(My Batis有两种事务管理类型,即JDBC和MANAGED),直接使用JDBC的提交和回滚功能,依赖于从数据源获得连接来管理事务的生命周期。 (3)dataSource元素:使用标准的JDBC数据源接口来配置JDBC连接对象的资源。My Batis提供了三种数据源类型UNPOOLED、POOLED、JNDI),这里使用POOLED数据源类型。该类型利用“池”的概念将JDBC连接对象组织起来,减少了创建新的连接实例时所必需的初始化和认证时间,是My Batis实现的简单的数据库连接池类型,它使数据库连接可被复用,不必在每次请求时都去创建一个物理连接。对于高并发的Web应用,这是一种流行的处理方式,有利于快速响应请求。 mappers 用来定义SQL的映射语句 1.使用类资源路径获取资源 <mappers> <mapper resource="cn/smbms/dao/user/User Mapper.xml"/> <mapper resource="cn/smbms/dao/provider/Provider Mapper.xml"/></mappers> 2.使用URL获取资源 <mappers> <mapper url="file:///E:/sqlmappers/User Mapper.xml"/> <mapper url="file:///E:/sqlmappers/Provider Mapper.xml"/> </mappers> ———————————————— 原文链接:https://blog.csdn.net/qq_43795348/article/details/109553684
-
大部分时候,我们都是在Spring 里面去集成MyBatis。因为Spring 对MyBatis 的一些操作进行的封装,我们不能直接看到它的本质,所以先看下不使用容器的时候,也就是编程的方式,MyBatis 怎么使用。先引入mybatis jar 包。 首先我们要创建一个全局配置文件,这里面是对MyBatis 的核心行为的控制,比如mybatis-config.xml。 第二个就是我们的映射器文件,Mapper.xml,通常来说一张表对应一个,我们会在这个里面配置我们增删改查的SQL 语句,以及参数和返回的结果集的映射关系。跟JDBC 的代码一样,我们要执行对数据库的操作,必须创建一个会话,这个在MyBatis 里面就是SqlSession。SqlSession 又是工厂类根据全局配置文件创建的。所以整个的流程就是这样的(如下代码)。最后我们通过SqlSession 接口上的方法,传入我们的Statement ID 来执行SQL。这是第一种方式。 这种方式有一个明显的缺点,就是会对Statement ID 硬编码,而且不能在编译时进行类型检查,所以通常我们会使用第二种方式,就是定义一个Mapper 接口的方式。这个接口全路径必须跟Mapper.xml 里面的namespace 对应起来,方法也要跟StatementID 一一对应。 public void testMapper() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); try { BlogMapper mapper = session.getMapper(BlogMapper.class); Blog blog = mapper.selectBlogById(1); System.out.println(blog); } finally { session.close(); } } 这个就是我们单独使用MyBatis 的基本流程。 核心对象的生命周期 在编程式使用的这个demo 里面,我们看到了MyBatis 里面的几个核心对象:SqlSessionFactoryBuiler、SqlSessionFactory、SqlSession 和Mapper 对象。这几个核心对象在MyBatis 的整个工作流程里面的不同环节发挥作用。如果说我们不用容器,自己去管理这些对象的话,我们必须思考一个问题:什么时候创建和销毁这些对象?在一些分布式的应用里面,多线程高并发的场景中,如果要写出高效的代码,必须了解这四个对象的生命周期。这四个对象的声明周期的描述在官网上面也可以找到。我们从每个对象的作用的角度来理解一下,只有理解了它们是干什么的,才知道什么时候应该创建,什么时候应该销毁。 SqlSessionFactoryBuilder 这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但是最好还是不要让其一直存在,以保证所有的 XML 解析资源可以被释放给更重要的事情。 SqlSessionFactory SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏味道(bad smell)”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。 SqlSession 每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的作用域中。 换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。 这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。 下面的示例就是一个确保 SqlSession 关闭的标准模式: SqlSession session = sqlSessionFactory.openSession(); try { // 你的应用逻辑代码 } finally { session.close(); } 在你的所有的代码中一致地使用这种模式来保证所有数据库资源都能被正确地关闭。 映射器实例(Mapper) 映射器是一些由你创建的、绑定你映射的语句的接口。映射器接口的实例是从 SqlSession 中获得的。因此从技术层面讲,任何映射器实例的最大作用域是和请求它们的 SqlSession 相同的。尽管如此,映射器实例的最佳作用域是方法作用域。 也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可丢弃。 并不需要显式地关闭映射器实例,尽管在整个请求作用域保持映射器实例也不会有什么问题,但是你很快会发现,像 SqlSession 一样,在这个作用域上管理太多的资源的话会难于控制。 为了避免这种复杂性,最好把映射器放在方法作用域内。下面的示例就展示了这个实践: SqlSession session = sqlSessionFactory.openSession(); try { BlogMapper mapper = session.getMapper(BlogMapper.class); // 你的应用逻辑代码 } finally { session.close(); } 这个就是我们在编程式的使用里面看到的四个对象的生命周期的总结。 核心配置解读 第一个是config 文件。大部分时候我们只需要很少的配置就可以让MyBatis 运行起来。其实MyBatis 里面提供的配置项非常多,我们没有配置的时候使用的是系统的默认值。 目前最新的版本是3.5.1,大家可以从官方上下载到最新的源码。中文地址:http://www.mybatis.org/mybatis-3/zh/index.html <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <properties resource="db.properties"></properties> <settings> <!-- 打印查询语句 --> <setting name="logImpl" value="STDOUT_LOGGING" /> <!-- 控制全局缓存(二级缓存)--> <setting name="cacheEnabled" value="true"/> <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖--> <setting name="aggressiveLazyLoading" value="false"/> <!-- Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST --> <!--<setting name="proxyFactory" value="CGLIB" />--> <!-- STATEMENT级别的缓存,使一级缓存,只针对当前执行的这一statement有效 --> <!-- <setting name="localCacheScope" value="STATEMENT"/> --> <setting name="localCacheScope" value="SESSION"/> </settings> <typeAliases> <typeAlias alias="blog" type="com.wuzz.domain.Blog" /> </typeAliases> <!-- <typeHandlers> <typeHandler handler="com.wuzz.type.MyTypeHandler"></typeHandler> </typeHandlers>--> <!-- 对象工厂 --> <!-- <objectFactory type="com.wuzz.objectfactory.GPObjectFactory"> <property name="wuzz" value="666"/> </objectFactory>--> <!-- <plugins> <plugin interceptor="com.wuzz.interceptor.SQLInterceptor"> <property name="wuzz" value="betterme" /> </plugin> <plugin interceptor="com.wuzz.interceptor.MyPageInterceptor"> </plugin> </plugins>--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 --> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="BlogMapper.xml"/> <mapper resource="BlogMapperExt.xml"/> </mappers> </configuration> configuration configuration 是整个配置文件的根标签,实际上也对应着MyBatis 里面最重要的配置类Configuration。它贯穿MyBatis 执行流程的每一个环节。这里面有很多的属性,跟其他的子标签也能对应上。 注意:MyBatis 全局配置文件顺序是固定的,否则启动的时候会报错。 properties 第一个是properties 标签,用来配置参数信息,比如最常见的数据库连接信息。为了避免直接把参数写死在xml 配置文件中,我们可以把这些参数单独放在properties 文件中,用properties 标签引入进来,然后在xml 配置文件中用${}引用就可以了。可以用resource 引用应用里面的相对路径,也可以用url 指定本地服务器或者网络的绝对路径。 我们为什么要把这些配置独立出来?有什么好处?或者说,公司的项目在打包的时候,有没有把properties 文件打包进去? 提取,利于多处引用,维护简单; 把配置文件放在外部,避免修改后重新编译打包,只需要重启应用; 程序和配置分离,提升数据的安全性,比如生产环境的密码只有运维人员掌握。 settings 这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项的意图、默认值等 ———————————————— 原文链接:https://blog.csdn.net/MaNong125/article/details/122455518
-
前提提要 本系列Mybatis笔记基于mybatis3.5.2 , https://github.com/mybatis/mybatis-3/releases/tag/mybatis-3.5.2中有详细的文档说明,可以进行下载阅读,本文只是对Mybatis的一些常用操作进行汇总总结。 推荐视频资料:https://www.bilibili.com/video/BV1VP4y1c7j7?p=1 所需前提:对maven有些了解。(maven v3.6.1)如果对maven不是很熟悉推荐博主文章。Maven常用知识梳理总结 MyBatis系列文章索引页✋ IDE: IntelliJ IDEA 2021.3 构建工具: apache-maven-3.6.3 MySQL版本:MySQL 5.7.35 MyBatis版本:MyBatis 3.5.7 文章目录 前提提要 一.全局配置文件 1. properties: 通过配置文件动态绑定值 2. setting :对全局设置相关属性值 3. typeAliases( alias,package) 设置类型的别名 4. environments:设置数据库环境 4.1 transactionManager:设置事务管理方式 5. mappers:引入映射文件 6.总结 一.全局配置文件 全局配置文件,见名知意就是对当前MyBatis的一些全局属性进行设置。也就是对各种数据操作进行统一规定。 全局配置文件包含了全局设置(setting)和properties两个大的部分,通过这两个大的部分动态的影响MyBatis的行为。下面我从几个方面去介绍: 大致来看看最简单的全局配置文件: <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> ... <typeAliases> <environments default="development"> <mappers> ... </configuration> 但是,上面各个标签的顺序,我绝对不是随便给的。MyBatis对各个标签的顺序有规定。 如果标签的顺序不正确,会出现错误,如下图: 核心配置文件中的标签必须按照固定的顺序(有的标签可以不写,但顺序一定不能乱): properties、settings、typeAliases、typeHandlers、objectFactory、objectWrapperFactory、reflectorFactory、plugins、environments、databaseIdProvider、mappers 那我就按照上述的顺序来将一些常见的进行介绍: 1. properties: 通过配置文件动态绑定值 <properties>表名的是可外部化,可替换的一些属性。在典型的JAVA属性中配置他们文件的实例。比如jdbc.properties文件的导入。 <!--引入配置文件--> <properties resource="jdbc.properties"/> [jdbc.properties] jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3307/mybatis jdbc.username=root jdbc.password=admin 然后,我们就可以在数据源中动态的设置这些值。 <environment id="test"> <!--事务管理器通过jdbc来进行管理--> <transactionManager type="JDBC"/> <!--数据源,从数据连接池中进行取用 --> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> 2. setting :对全局设置相关属性值 <setting>是在mybatis运行过程中修改运行行为一个非常重要的方式。其中规定了很多属性的值,如是否开启全局缓存,懒加载是否开启等等. 我只截取其中的一部分,其他详细的请查阅相关文档。 3. typeAliases( alias,package) 设置类型的别名 设置别名:为一些比较长的类名起一个比较短的代称。 里面有的属性有 type: 需要设置别名的全类型名。 alias: 设置此类型的别名,并且不进行区分大小写 ,如果这项不进行设定的话,框架给一个默认的别名——以类名作为别名。 <typeAliases> <typeAlias type="com.fangshaolei.mybatis.Account" alias="account" /> <typeAlias type="com.fangshaolei.mybatis.User" alias="uu" /> </typeAliases> 但是,对于开发中,这种操作一般比较繁琐。一般使用扫描包的方式来做。通过以包为单位,设置和修改包下的所有类都拥有默认的别名(即是类名),并且所有的别名都不区分大小写。 <package name="com.fangshaolei.mybatis.bean"/> 系统中默认配置的别名: 4. environments:设置数据库环境 <environments>用于设置多个连接数据库的环境。比如说一些其他数据库,Oracle,MySQL等等,在需要的使用达到动态切换的目的。 而对于<environments>要使用哪个环境,是通过default属性来设置对应environment的id来实现的。 一个environments有很多的environment,但是只能有一个作为项目的数据库环境。 <environments default="development"> <!--开发环境 environment: 用来配置某个具体的环境 属性: id: 表示连接数据库环境的唯一标识,不能重复 属性: type: = "JDBC | MANAGED" JDBC: 表示当前环境中,执行SQL时,使用的是JDBC中原生的事务管理方式, 事务的提交和回滚需要手动来进行处理 MANAGED: 被管理,例如spring --> <environment id="development"> <!--事务管理器通过jdbc来进行管理--> <transactionManager type="JDBC"/> <!--数据源,从数据连接池中进行取用 datasource: 配置数据源 属性: type: 设置数据源类型 type = "POOLED | UNPOOLED | JNDI" POOLED: 表示使用数据库连接池缓存数据库连接 UNPOOLED: 表示不使用数据库中的连接词 JNDI: 表示使用上下文中的数据源连接池 --> <dataSource type="POOLED"> <!--设施连接数据库的驱动--> <property name="driver" value="com.mysql.jdbc.Driver"/> <!--设置连接地址--> <property name="url" value="jdbc:mysql://localhost:3307/mybatis"/> <!--用户名--> <property name="username" value="root"/> <!--密码--> <property name="password" value="admin"/> </dataSource> </environment> <!--测试--> <environment id="test"> </environment> </environments> 4.1 transactionManager:设置事务管理方式 属性:type=“JDBC/MANAGED” JDBC: 在当前环境中,执行sql时,使用的是jdbc原生的事务管理方式,需要手动的提交和回滚事务 MANAGED:被管理,例如Spring 5. mappers:引入映射文件 mappers引入映射文件有三种方式(严格来说是四种)。而这里博主只介绍一种常用的方式:🍒以包围单位引入配置文件 但是,以包为单位引入配置文件,有两个要求: mapper接口所在的包要和映射文件所在的包要一致。 mapper接口要和映射文件的名称一致。 满足了上述两个要求之后,就可以在<mappers>标签内利用<package>标签实现批量引入映射文件。 <mappers> <package name="com.fangshaolei.mybatis.mapper"/> </mappers> Notice:还有一个需要注意的点,在resources中,如果一次性创建多个层级目录,不能使用如图所示,使用.来创建。而要使用com/fangshaolei/mybatis/mapper才能创建出和类包一样的目录结构。 否则会报:BindingException: Type interface com.atguigu.mybatis.mapper.UserMapper is not known to the MapperRegistry. 6.总结 本节内容,没有什么特殊的tips,对于还有一些其他的配置信息,可以在MyBatis官方文档中有详细说明,博主不在班门弄斧的解释。 一份完整的mybatis-config.xml配置示例: <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--引入配置文件--> <properties resource="jdbc.properties"/> <!-- mybatis配置文件中一定要按照顺序来进行配置 properties?,settings?,typeAliases?,typeHandlers?,objectFactory?, objectWrapperFactory?,reflectorFactory?,plugins?,environments?, databaseIdProvider?,mappers?)". --> <typeAliases> <!--进行书写别名 类型别名他不进行区分大小写 ,如果alias不进行设置就会拥有一个默认的别名,默认的就是当前的类名 typealias: 设置某个类型的别名 属性: type: 设置需要设置别名的类型 alias: 设置某个类型的别名,若不设置该属性,就会拥有一个默认的别名,默认的别名就是当前的类名。 --> <!--<typeAlias type="com.fangshaolei.mybatis.pojo.User" alias="user"/>--> <package name="com.fangshaolei.mybatis.pojo"/> <!--通过以包为单位来进行设置--> </typeAliases> <!-- environments: 配置多个连接数据的环境 属性: default: 设置默认使用的环境id --> <environments default="development"> <!--开发环境 environment: 用来配置某个具体的环境 属性: id: 表示连接数据库环境的唯一标识,不能重复 属性: type: = "JDBC | MANAGED" JDBC: 表示当前环境中,执行SQL时,使用的是JDBC中原生的事务管理方式, 事务的提交和回滚需要手动来进行处理 MANAGED: 被管理,例如spring --> <environment id="development"> <!--事务管理器通过jdbc来进行管理--> <transactionManager type="JDBC"/> <!--数据源,从数据连接池中进行取用 datasource: 配置数据源 属性: type: 设置数据源类型 type = "POOLED | UNPOOLED | JNDI" POOLED: 表示使用数据库连接池缓存数据库连接 UNPOOLED: 表示不使用数据库中的连接词 JNDI: 表示使用上下文中的数据源连接池 --> <dataSource type="POOLED"> <!--设施连接数据库的驱动--> <property name="driver" value="com.mysql.jdbc.Driver"/> <!--设置连接地址--> <property name="url" value="jdbc:mysql://localhost:3307/mybatis"/> <!--用户名--> <property name="username" value="root"/> <!--密码--> <property name="password" value="admin"/> </dataSource> </environment> <!--测试--> <environment id="test"> <!--事务管理器通过jdbc来进行管理--> <transactionManager type="JDBC"/> <!--数据源,从数据连接池中进行取用 --> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <!--映入配置文件--> <mappers> <!-- <mapper resource="mappers/UserMapper.xml"/>--> <!-- 以包围单位引入映射文件 要求: 1.mapper接口所在的包要和映射文件所在的包一致 2. mapper接口要和映射文件的名字是一致的 --> <package name="com.fangshaolei.mybatis.mapper"/> </mappers> </configuration> ———————————————— 原文链接:https://blog.csdn.net/qq_41860497/article/details/123450860
-
一、概述开发中的遇到一个有关于统计需求,而这个需求需要根据两个数据库才能实现,而如果直接通过两个模块之间的交互也可以实现,但是这样一来,如果加入了搜索条件,而这搜索条件又分散在不同的库中,这样依赖数据处理起来就很复杂,而且还没有对数据进行有效的分页处理,考虑目前数据库都放再一台服务器中,还没有不同数据库放在不同服务器的需求,在sql中引入了库名。虽然引入了库名,对应的业务能跑起来了,但是也遇到过一个比较奇特的问题,也就是数据库中存在了同名的库,因为之前的sql中引入了库名,在不改变代码的情况下,直接修改配置文件和数据库的库名会导致引入了库名的sql无法使用,为此,引入了.xml文件中动态修改数据库库名的功能,具体的话通过mybatis的全局变量实现。二、实现方案经过摸索,大致的得到了三种实现方案2.1 mybaits自带全局变量定义在springboot中yml文件中加入如下全局变量配置mybatis: configuration: variables: myConfigName: "`base1`"然后再sql中 <select id="selectTest" resultType="java.util.Map"> select * from ${myConfigName}.table1 </select>需要注意的是反引号 " ` ",如果不再库名上加上反引号,那么sql就无法顺利的跑起来,此外占位符要使用$,因为$才获取到全局变量这样,就可以动态的配置库名,但是这样的话,有一个缺点,无法设置默认值,如果myConfigName没有进行配置,那么这个sql就无法执行2.2 使用@value和mybatis进行全局变量的定义使用@value为java的一个成员变量复制,并且实则hi默认值,这样我们通过yml文件配置对应库名了 /** * 数据库名称 */ public static String dataBaseName; //注意引号 @Value("${data-conf.dataBaseName:`test`}") private void setUserDataBaseName(String name){ dataBaseName=name; }mapper定义和对应的sql调用List<Object> getInfoFromtaba(@Param("dataBaseName") String dataBaseName); List<Object> result=xxxmapper.getInfoFromtaba(xxx.dataBaseName).xml写法<!-- 注意,这里可以使用#占位符 ---> <select id="selectTest" resultType="java.util.Map"> select * from #{dataBaseName}.table1 </select>这种方式解决了默认数据库名的问题,但是也存在一个问题,就是mapper的接口中多了数据库名作为参数,这种形参的设置看着很别扭。2.3 使用@value和mybatis进行全局变量的定义这种方式类似于上述2.2 的方案,只是他能减少mapper接口的不必要的数据库名形参,只要不去细看sql,一般情况,开发人员感受不到库名的动态配置 /** * 数据库名称 */ public static String dataBaseName; //注意引号 @Value("${data-conf.dataBaseName:`test`}") private void setUserDataBaseName(String name){ dataBaseName=name; }<!-- 注意,这里可以使用#占位符 ---> <select id="selectTest" resultType="java.util.Map"> select * from <choose> <when test="null != @com.project.mapper.config.TestClass@dataBaseName"> ${@com.project.mapper.config.TestClass@dataBaseName}.table1 AS tu </when> <otherwise> `pre-base`.table1 AS tu </otherwise> </choose> </select>注:这里@com.project.mapper.config.TestClass@dataBaseName 的第一个@com.project.mapper.config.TestClass是类的路径,后面的@dataBaseName是库名。注意再test中boolean表达式子中不需要$占位符,而后续具体数据的位置需要展示位符号$,使用<chose>的原因一是为了保险,这样就是对应的变量的默认值丢失了,也能使用默认库,二是为了避免${}报变量过长的错原文链接:https://huaweicloud.csdn.net/64eea6cea3cccc782cc579d6.html
-
Spring Boot定义全局变量在Spring Boot应用程序中,我们经常需要定义一些全局变量,这些变量可以在应用的任何地方访问。Spring Boot提供了多种方式来定义全局变量,本文将介绍其中的几种方法,并提供相应的代码示例。1. 使用配置文件Spring Boot支持使用属性配置文件来定义全局变量。我们可以将全局变量的值配置在application.properties或application.yml文件中,然后通过@Value注解将其注入到代码中。首先,我们在application.properties文件中定义一个全局变量:myapp: globalVar: Hello World然后,在使用该全局变量的类中使用@Value注解进行注入:@Component public class MyBean { @Value("${myapp.globalVar}") private String globalVar; // ... }这样,globalVar变量的值就会被注入为Hello World。2. 使用静态变量另一种定义全局变量的方法是使用静态变量。我们可以在一个类中定义一个静态变量,并通过@Component注解将该类交由Spring管理。@Component public class GlobalVariables { public static final String GLOBAL_VAR = "Hello World"; // ... }然后,在代码的任何地方,我们都可以通过GlobalVariables.GLOBAL_VAR访问到该全局变量。3. 使用单例模式我们还可以使用单例模式来定义全局变量。通过将变量定义在一个单例类中,可以确保全局变量只有一个实例,并且可以在应用的任何地方访问到。首先,定义一个单例类,并在其中定义全局变量:@Component public class GlobalVariablesSingleton { private static GlobalVariablesSingleton instance; private String globalVar; private GlobalVariablesSingleton() { // 初始化全局变量 globalVar = "Hello World"; } public static GlobalVariablesSingleton getInstance() { if (instance == null) { instance = new GlobalVariablesSingleton(); } return instance; } public String getGlobalVar() { return globalVar; } // ... }然后,在代码的任何地方,我们都可以通过GlobalVariablesSingleton.getInstance().getGlobalVar()访问到该全局变量。总结本文介绍了几种在Spring Boot应用程序中定义全局变量的方法。使用配置文件、静态变量或单例模式都可以实现全局变量的定义。选择合适的方法取决于具体的需求和场景。希望本文对你在Spring Boot中定义全局变量有所帮助。通过合理的使用全局变量,我们可以在应用程序中方便地共享数据和配置信息。注意:本文示例代码基于Spring Boot 2.x版本。原文链接:https://blog.csdn.net/weixin_45131680/article/details/133810260
-
网上有很多springboot的入门教程,自己也因为项目要使用springboot,所以利用业余时间自学了下springboot和springcloud,使用下来发现springboot还是挺简单的,体现了极简的编程风格,大部分通用都是通过注解就可以完成,下面就来详细讲解下如何使用springboot来开发一个简单的restful api网关功能,可以提供给H5或者android、ios进行接口开发,还是很方便的。1. 使用spring initialization创建SpringBoot项目有很多方法可以快速创建Springboot项目,可以通过idea的springboot initialization来创建,也可以通过手工新建一个maven工程,然后引入springboot的dependency来完成sprignboot的工程导入,还可以通过spring官网的来创建springboot项目,因为有些同学可能没装idea,这里就通过官网的工程初始化指引来创建一个springboot空工程。首先输入网址 https://start.spring.io,打开后可以看到下图:在serch for dependency输入web,即可完成基本的restful接口网关的功能,如果要JPA或者oauth安全相关的组件,可以增加rest repository、spring security等相关组件依赖库,spring提供的配套组件还是很多的,基本涵盖了所有应用场合。加入web组件后,点击下方的绿色按钮Generate Project即可创建一个springboot工程,并且自动下载到本地,接下来直接在idea或者eclipse打开该工程就可以了,在创建的时候可以选择Maven工程或者Gradle工程,这里我们使用了大家比较熟悉的Maven工程。2. 工程结构下面我们在ide中打开工程,这里使用的ide是idea,工程的目录结构为:可以看到工程中有maven的pom文件,也自动创建了SpringbootStartApplication.java该类为springboot的启动类,待会儿我们一起看下这个类,先看下maven的pom文件有哪些。这里主要是依赖了springboot的1.4.7版本,目前最新已经更新到1.5.6了,这里没有用最新版本,还是不当小白鼠了,在dependency中依赖了spring-boot-starter-web还有个test测试的组件,如果不写测试代码,可以不使用该test组件,最后还加入了支持springboot的maven plugin组件。 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>刚看完了pom文件,在导入工程的时候,ide一般会自动导入依赖库,在国内访问maven的依赖库速度感人,建议使用阿里云的maven镜像服务器,或者使用公司的maven私服,如果公司没有私服或者自己学习可以直接使用阿里云的镜像速度还是不错的,maven setting.xml中需要添加mirror地址,具体如何配置这里就不详细描述了,可以自行百度,这里也顺便附上阿里云maven地址:<mirror> <id>nexus-aliyun</id> <mirrorOf>*</mirrorOf> <name>Nexus aliyun</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url> </mirror>配置好了pom后,我们一起看下自动生成的Application.java这个类相当于我们程序的main函数入口,这里再顺便介绍下因为springboot集成了Tomcat和Jetty,默认使用Tomcat作为应用容器,开发者只需要将工程打成jar包直接丢到服务器上就可以执行了,不需要再单独部署到was、jboss、tomcat这些应用服务器上。SpringBootStartApplication.java@SpringBootApplication public class SpringbootStartApplication { public static void main(String[] args) { SpringApplication.run(SpringbootStartApplication.class, args); } }所有的springboot application启动类都需要在类级别上加上@SpringBootApplication注解,其他参数不用任何调整,后续可以把一些初始化的动作放到该类中进行,目前本例中就不加其他的启动加载项了。这样一个api网关的架子就搭好了,是不是很简单!下面我们就可以将主要精力聚焦在业务逻辑代码上了,这里为了简化程序,不会将项目进行深入的分层设计,在实际项目中,一般都会对项目进行分层设计,如果是api网关,没有view层但是起码也会有对外接入decontroller层、处理业务逻辑的service层、处理数据持久化的dao层,同时也会有一些POJO业务实体类,这里就不详细展开了,后续也会对互联网架构设计进行详细讲述,这里我们只创建了一个UserController类,里面只有获取用户信息的方法,分别根据参数和请求方式的不同用三种方法进行了重写,下面就来一一道来。先来讲述下最简单的使用get请求用户信息的实现方式,代码如下,写好后直接在Application类点击右键有个RunAs,点击后会自动运行,运行成功后可以使用http发包工具进行测试,这里推荐使用chrome的postman或者使用firefox的httprequester插件,都是比较简单的发包工具,get请求的上送为http://localhost:8081/springboot/getUserByGet?userName=feiweiwei//@RestController注解能够使项目支持Rest @RestController @SpringBootApplication //表示该controller类下所有的方法都公用的一级上下文根 @RequestMapping(value = "/springboot") public class UserController { //这里使用@RequestMapping注解表示该方法对应的二级上下文路径 @RequestMapping(value = "/getUserByGet", method = RequestMethod.GET) String getUserByGet(@RequestParam(value = "userName") String userName){ return "Hello " + userName; } }这里用到的注解主要有@RequestMapping表示请求的URL上下文路径,该路径不能重复,为了保证与团队其他同事写的不重复,一般会在每个controller前面加一个一级上下文目录,具体路径参数放在value后面,在每个方法前加一个二级目录,这样可以有效的避免路径冲突。还有注解是@RequestParam,该注解可以通过value指定入参,这里return的返回值就是实际的接口返回。下面介绍下POST的请求方式,可以通过在@RequestMapping注解中设置method为POST来表示该请求为POST请求,除了get、post还有put、delete等请求方式,都可以通过该参数设置。 //通过RequestMethod.POST表示请求需要时POST方式 @RequestMapping(value = "/getUserByPost", method = RequestMethod.POST) String getUserByPost(@RequestParam(value = "userName") String userName){ return "Hello " + userName; }下面介绍下请求参数为JSON格式的请求方法的写法,这里需要注意下如果请求参数是像上面那样通过url form形式提交的请求参数,那么必须使用@RequestParam注解来标示参数,如果使用的请求报文是POST形势的JSON串,那么这里在入参的注解一定要使用@RequestBody,否则会报json解析错误。 //在入参设置@RequestBody注解表示接收整个报文体,这里主要用在接收整个POST请求中的json报文体, //目前主流的请求报文也都是JSON格式了,使用该注解就能够获取整个JSON报文体作为入参,使用JSON解析工具解析后获取具体参数 @RequestMapping(value = "/getUserByJson",method = RequestMethod.POST) String getUserByJson(@RequestBody String data){ return "Json is " + data; }3. 小结到此一个简单的restful风格的api网关就完成了,对于移动开发人员可以自己写简单的服务端进行全栈开发了,原来做spring的同学也可以很快上手springboot,springboot总体上来说还是简化了原先复杂的配置,让大家更容易快速上手和搭建服务端,代码的git地址在下方,欢迎大家下载,谢谢。git代码地址:https://github.com/feiweiwei/springboot-start.git作者:monkey01链接:https://www.jianshu.com/p/af3d5800f763
-
SpringBoot基础学习目标:能够理解Spring的优缺点能够理解SpringBoot的特点能够理解SpringBoot的核心功能能够搭建SpringBoot的环境能够完成application.properties配置文件的配置能够完成application.yml配置文件的配置能够使用SpringBoot集成Mybatis能够使用SpringBoot集成Junit能够使用SpringBoot集成SpringData JPA一、SpringBoot简介1.1 原有Spring优缺点分析1.1.1 Spring的优点分析Spring是Java企业版(Java Enterprise Edition,JEE,也称J2EE)的轻量级代替品。无需开发重量级的Enterprise JavaBean(EJB),Spring为企业级Java开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单的Java对象(Plain Old Java Object,POJO)实现了EJB的功能。1.1.2 Spring的缺点分析虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring用XML配置,而且是很多XML配置。Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式XML配置。Spring 3.0引入了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML。所有这些配置都代表了开发时的损耗。因为在思考Spring特性配置和解决业务问题之间需要进行思维切换,所以编写配置挤占了编写应用程序逻辑的时间。和所有框架一样,Spring实用,但与此同时它要求的回报也不少。除此之外,项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。1.2 SpringBoot的概述1.2.1 SpringBoot解决上述Spring的缺点SpringBoot对上述Spring的缺点进行的改善和优化,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。1.2.2 SpringBoot的特点为基于Spring的开发提供更快的入门体验开箱即用,没有代码生成,也无需XML配置。同时也可以修改默认值来满足特定的需求提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标,健康检测、外部配置等SpringBoot不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式1.2.3 SpringBoot的核心功能起步依赖起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。自动配置Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。 注意:起步依赖和自动配置的原理剖析会在第三章《SpringBoot原理分析》进行详细讲解二、SpringBoot快速入门2.1 代码实现2.1.1 创建Maven工程使用idea工具创建一个maven工程,该工程为普通的java工程即可2.1.2 添加SpringBoot的起步依赖SpringBoot要求,项目要继承SpringBoot的起步依赖spring-boot-starter-parentorg.springframework.bootspring-boot-starter-parent2.0.1.RELEASESpringBoot要集成SpringMVC进行Controller的开发,所以项目要导入web的启动依赖org.springframework.bootspring-boot-starter-web2.1.3 编写SpringBoot引导类要通过SpringBoot提供的引导类起步SpringBoot才可以进行访问package com.itheima;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class MySpringBootApplication {public static void main(String[] args) {//运行SpringBoot的引导类 参数就是SpringBoot的引导类的字节码对象SpringApplication.run(MySpringBootApplication.class);}}2.1.4 编写Controller在引导类MySpringBootApplication同级包或者子级包中创建QuickStartController2.1.5 测试执行SpringBoot起步类的主方法,控制台打印日志如下:通过日志发现tomcat已经起步,端口监听8080,web应用的虚拟工程名称为空打开浏览器访问url地址为:http://localhost:8080/quick2.2 快速入门解析2.2.2 SpringBoot代码解析@SpringBootApplication:标注SpringBoot的启动类,该注解具备多种功能(后面详细剖析)SpringApplication.run(MySpringBootApplication.class) 代表运行SpringBoot的启动类,参数为SpringBoot启动类的字节码对象2.2.3 SpringBoot工程热部署我们在开发中反复修改类、页面等资源,每次修改后都是需要重新启动才生效,这样每次启动都很麻烦,浪费了大量的时间,我们可以在修改代码后不重启就能生效,在 pom.xml 中添加如下配置就可以实现这样的功能,我们称之为热部署。注意:IDEA进行SpringBoot热部署失败原因出现这种情况,并不是热部署配置问题,其根本原因是因为Intellij IEDA默认情况下不会自动编译,需要对IDEA进行自动编译的设置,如下:然后 Shift+Ctrl+Alt+/,选择Registry2.2.4 使用idea快速创建SpringBoot项目通过idea快速创建的SpringBoot项目的pom.xml中已经导入了我们选择的web的起步依赖的坐标可以使用快速入门的方式创建Controller进行访问,此处不再赘述三、SpringBoot原理分析3.1 起步依赖原理分析3.1.1 分析spring-boot-starter-parent按住Ctrl点击pom.xml中的spring-boot-starter-parent,跳转到了spring-boot-starter-parent的pom.xml,xml配置如下(只摘抄了部分重点配置):org.springframework.bootspring-boot-dependencies2.0.1.RELEASE../../spring-boot-dependencies按住Ctrl点击pom.xml中的spring-boot-starter-dependencies,跳转到了spring-boot-starter-dependencies的pom.xml,xml配置如下(只摘抄了部分重点配置):从上面的spring-boot-starter-dependencies的pom.xml中我们可以发现,一部分坐标的版本、依赖管理、插件管理已经定义好,所以我们的SpringBoot工程继承spring-boot-starter-parent后已经具备版本锁定等配置了。所以起步依赖的作用就是进行依赖的传递。3.1.2 分析spring-boot-starter-web按住Ctrl点击pom.xml中的spring-boot-starter-web,跳转到了spring-boot-starter-web的pom.xml,xml配置如下(只摘抄了部分重点配置):从上面的spring-boot-starter-web的pom.xml中我们可以发现,spring-boot-starter-web就是将web开发要使用的spring-web、spring-webmvc等坐标进行了“打包”,这样我们的工程只要引入spring-boot-starter-web起步依赖的坐标就可以进行web开发了,同样体现了依赖传递的作用。3.2 自动配置原理解析按住Ctrl点击查看启动类MySpringBootApplication上的注解@SpringBootApplication————————————————原文链接:https://blog.csdn.net/qq_36554778/article/details/109559506
-
1,SpringBoot简介 SpringBoot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化 Spring 应用的初始搭建以及开发过程。 使用了 Spring 框架后已经简化了我们的开发。而 SpringBoot 又是对 Spring 开发进行简化的,可想而知 SpringBoot 使用的简单及广泛性。既然 SpringBoot 是用来简化 Spring 开发的,那我们就先回顾一下,以 SpringMVC 开发为例: 创建工程,并在 pom.xml 配置文件中配置所依赖的坐标 编写 web3.0 的配置类 作为 web 程序,web3.0 的配置类不能缺少,而这个配置类还是比较麻烦的,代码如下 编写 SpringMVC 的配置类 做到这只是将工程的架子搭起来。要想被外界访问,最起码还需要提供一个 Controller 类,在该类中提供一个方法。 编写 Controller 类 从上面的 SpringMVC 程序开发可以看到,前三步都是在搭建环境,而且这三步基本都是固定的。SpringBoot 就是对这三步进行简化了。接下来我们通过一个入门案例来体现 SpingBoot 简化 Spring 开发。 1.1 SpringBoot快速入门 1.1.1 开发步骤 SpringBoot 开发起来特别简单,分为如下几步: 创建新模块,选择Spring初始化,并配置模块相关基础信息 选择当前模块需要使用的技术集 开发控制器类 运行自动生成的Application类 知道了 SpringBoot 的开发步骤后,接下来我们进行具体的操作 1.1.1.1 创建新模块 点击 + 选择 New Module 创建新模块 选择 Spring Initializr ,用来创建 SpringBoot 工程 以前我们选择的是 Maven ,今天选择 Spring Initializr 来快速构建 SpringBoot 工程。而在 Module SDK 这一项选择我们安装的 JDK 版本。 对 SpringBoot 工程进行相关的设置 我们使用这种方式构建的 SpringBoot 工程其实也是 Maven 工程,而该方式只是一种快速构建的方式而已。 注意:打包方式这里需要设置为 Jar 选中 Web,然后勾选 Spring Web 由于我们需要开发一个 web 程序,使用到了 SpringMVC 技术,所以按照下图红框进行勾选 注意,SpringBoot版本选择3以下的。然后Download那一串取消勾选。 完成 SpringBoot 工程的构建 经过以上步骤后就创建了如下结构的模块,它会帮我们自动生成一个 Application 类,而该类一会再启动服务器时会用到 注意: 在创建好的工程中不需要创建配置类 创建好的项目会自动生成其他的一些文件,而这些文件目前对我们来说没有任何作用,所以可以将这些文件删除。 可以删除的目录和文件如下: .mvn .gitignore HELP.md mvnw mvnw.cmd 1.1.1.2 创建 Controller 在 com.suki.controller 包下创建 BookController ,代码如下: @RestController @RequestMapping("/books") public class BookController { @GetMapping("/{id}") public String getById(@PathVariable Integer id){ System.out.println("id ==> "+id); return "hello , spring boot!"; } } 1.1.1.3 启动服务器 运行 SpringBoot 工程不需要使用本地的 Tomcat 和 插件,只运行项目 com.itheima 包下的 Application 类,我们就可以在控制台看出如下信息 1.1.1.4 进行测试 使用 Postman 工具来测试我们的程序 通过上面的入门案例我们可以看到使用 SpringBoot 进行开发,使整个开发变得很简单,那它是如何做到的呢? 要研究这个问题,我们需要看看 Application 类和 pom.xml 都书写了什么。先看看 Applicaion 类,该类内容如下: @SpringBootApplication public class Springboot01QuickstartApplication { public static void main(String[] args) { SpringApplication.run(Springboot01QuickstartApplication.class, args); } } 这个类中的东西很简单,就在类上添加了一个 @SpringBootApplication 注解,而在主方法中就一行代码。我们在启动服务器时就是执行的该类中的主方法。 再看看 pom.xml 配置文件中的内容 <?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 https://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.7.10</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.suki</groupId> <artifactId>springboot_01_quickstart</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springboot_01_quickstart</name> <description>springboot_01_quickstart</description> <!--JDK的版本--> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!--该依赖就是我们在创建SpringBoot工程勾选的那个Spring Web产生的--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <!--这个是单元测试的依赖--> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <!--这个插件是在打包时需要的,而这里暂时还没有用到--> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 我们代码之所以能简化,就是因为指定的父工程和 Spring Web 依赖实现的。具体的我们后面在聊。 1.1.2 对比 做完 SpringBoot 的入门案例后,接下来对比一下 Spring 程序和 SpringBoot 程序。如下图 坐标 Spring 程序中的坐标需要自己编写,而且坐标非常多 SpringBoot 程序中的坐标是我们在创建工程时进行勾选自动生成的 web3.0配置类 Spring 程序需要自己编写这个配置类。这个配置类大家之前编写过,肯定感觉很复杂 SpringBoot 程序不需要我们自己书写 配置类 Spring/SpringMVC 程序的配置类需要自己书写。而 SpringBoot 程序则不需要书写。 注意:基于Idea的 Spring Initializr 快速构建 SpringBoot 工程时需要联网。 1.1.3 官网构建工程 在入门案例中之所以能快速构建 SpringBoot 工程,是因为 Idea 使用了官网提供了快速构建 SpringBoot 工程的组件实现的。那如何在官网进行工程构建呢?通过如下步骤构建 1.1.3.1 进入SpringBoot官网 官网地址如下: https://spring.io/projects/spring-boot 进入到 SpringBoot 官网后拖到最下方就可以看到如下内容 然后点击 Spring Initializr 超链接就会跳转到如下页面 这个页面内容是不是感觉很眼熟的,这和我们使用 Idea 快速构建 SpringBoot 工程的界面基本相同。在上面页面输入对应的信息 1.1.3.2 选择依赖 选择 Spring Web 可以点击上图右上角的 ADD DEPENDENCIES... CTRL + B 按钮,就会出现如下界面 1.1.3.3 生成工程 以上步骤完成后就可以生成 SpringBoot 工程了。在页面的最下方点击 GENERATE CTRL + 回车 按钮生成工程并下载到本地,如下图所示 打开下载好的压缩包可以看到工程结构和使用 Idea 生成的一模一样,如下图 而打开 pom.xml 文件,里面也包含了父工程和 Spring Web 的依赖。 通过上面官网的操作,我们知道 Idea 中快速构建 SpringBoot 工程其实就是使用的官网的快速构建组件,那以后即使没有 Idea 也可以使用官网的方式构建 SpringBoot 工程。 1.1.4 SpringBoot工程快速启动 1.1.4.1 问题导入 以后我们和前端开发人员协同开发,而前端开发人员需要测试前端程序就需要后端开启服务器,这就受制于后端开发人员。为了摆脱这个受制,前端开发人员尝试着在自己电脑上安装 Tomcat 和 Idea ,在自己电脑上启动后端程序,这显然不现实。 我们后端可以将 SpringBoot 工程打成 jar 包,该 jar 包运行不依赖于 Tomcat 和 Idea 这些工具也可以正常运行,只是这个 jar 包在运行过程中连接和我们自己程序相同的 Mysql 数据库即可。这样就可以解决这个问题,如下图 那现在问题是如何打包呢? 1.1.4.2 打包 由于我们在构建 SpringBoot 工程时已经在 pom.xml 中配置了如下插件 <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> 所以我们只需要使用 Maven 的 package 指令打包就会在 target 目录下生成对应的 Jar 包。 注意:该插件必须配置,不然打好的 jar 包也是有问题的。 1.1.4.3 启动 进入 jar 包所在位置,在 命令提示符 中输入如下命令 java -jar springboot_01_quickstart-0.0.1-SNAPSHOT.jar 执行上述命令就可以看到 SpringBoot 运行的日志信息 1.2 SpringBoot概述 SpringBoot 是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程。 大家已经感受了 SpringBoot 程序,回过头看看 SpringBoot 主要作用是什么,就是简化 Spring 的搭建过程和开发过程。 原始 Spring 环境搭建和开发存在以下问题: 配置繁琐 依赖设置繁琐 SpringBoot 程序优点恰巧就是针对 Spring 的缺点 自动配置。这个是用来解决 Spring 程序配置繁琐的问题 起步依赖。这个是用来解决 Spring 程序依赖设置繁琐的问题 辅助功能(内置服务器,…)。我们在启动 SpringBoot 程序时既没有使用本地的 tomcat 也没有使用 tomcat 插件,而是使用 SpringBoot 内置的服务器。 接下来我们来说一下 SpringBoot 的起步依赖 1.2.1 起步依赖 我们使用 Spring Initializr 方式创建的 Maven 工程的的 pom.xml 配置文件中自动生成了很多包含 starter 的依赖,如下图 这些依赖就是启动依赖,接下来我们探究一下他是如何实现的。 1.2.1.1 探索父工程 从上面的文件中可以看到指定了一个父工程,我们进入到父工程,发现父工程中又指定了一个父工程,如下图所示 再进入到该父工程中,在该工程中我们可以看到配置内容结构如下图所示 上图中的 properties 标签中定义了各个技术软件依赖的版本,避免了我们在使用不同软件技术时考虑版本的兼容问题。在 properties 中我们找 servlet 和 mysql 的版本如下图 dependencyManagement 标签是进行依赖版本锁定,但是并没有导入对应的依赖;如果我们工程需要那个依赖只需要引入依赖的 groupid 和 artifactId 不需要定义 version。 而 build 标签中也对插件的版本进行了锁定,如下图 看完了父工程中 pom.xml 的配置后不难理解我们工程的的依赖为什么都没有配置 version。 1.2.1.2 探索依赖 在我们创建的工程中的 pom.xml 中配置了如下依赖 进入到该依赖,查看 pom.xml 的依赖会发现它引入了如下的依赖 里面的引入了 spring-web 和 spring-webmvc 的依赖,这就是为什么我们的工程中没有依赖这两个包还能正常使用 springMVC 中的注解的原因。 而依赖 spring-boot-starter-tomcat ,从名字基本能确认内部依赖了 tomcat,所以我们的工程才能正常启动。 结论:以后需要使用技术,只需要引入该技术对应的起步依赖即可 1.2.1.3 小结 starter SpringBoot 中常见项目名称,定义了当前项目使用的所有项目坐标,以达到减少依赖配置的目的 parent 所有 SpringBoot 项目要继承的项目,定义了若干个坐标版本号(依赖管理,而非依赖),以达到减少依赖冲突的目的 spring-boot-starter-parent(2.5.0)与 spring-boot-starter-parent(2.4.6)共计57处坐标版本不同 实际开发 使用任意坐标时,仅书写GAV中的G和A,V由SpringBoot提供 G:groupid A:artifactId V:version 如发生坐标错误,再指定version(要小心版本冲突) 1.2.2 程序启动 创建的每一个 SpringBoot 程序时都包含一个类似于下面的类,我们将这个类称作引导类 @SpringBootApplication public class Springboot01QuickstartApplication { public static void main(String[] args) { SpringApplication.run(Springboot01QuickstartApplication.class, args); } } 注意: SpringBoot 在创建项目时,采用jar的打包方式 SpringBoot 的引导类是项目的入口,运行 main 方法就可以启动项目 因为我们在 pom.xml 中配置了 spring-boot-starter-web 依赖,而该依赖通过前面的学习知道它依赖 tomcat ,所以运行 main 方法就可以使用 tomcat 启动咱们的工程。 1.2.3 切换web服务器 现在我们启动工程使用的是 tomcat 服务器,那能不能不使用 tomcat 而使用 jetty 服务器,jetty 在我们 maven 高级时讲 maven 私服使用的服务器。而要切换 web 服务器就需要将默认的 tomcat 服务器给排除掉,怎么排除呢?使用 exclusion 标签 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <artifactId>spring-boot-starter-tomcat</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency> 现在我们运行引导类可以吗?运行一下试试,打印的日志信息如下 程序直接停止了,为什么呢?那是因为排除了 tomcat 服务器,程序中就没有服务器了。所以此时不光要排除 tomcat 服务器,还要引入 jetty 服务器。在 pom.xml 中因为 jetty 的起步依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency> 接下来再次运行引导类,在日志信息中就可以看到使用的是 jetty 服务器 小结: 通过切换服务器,我们不难发现在使用 SpringBoot 换技术时只需要导入该技术的起步依赖即可。 ———————————————— 原文链接:https://blog.csdn.net/Eumenides_Suki/article/details/129998706
-
Spring Boot 中集成Redis 1. Redis 介绍 Redis 是一种非关系型数据库(NoSQL),NoSQL 是以 key-value 的形式存储的,和传统的关系型数据库不一样,不一定遵循传统数据库的一些基本要求,比如说 SQL 标准,ACID 属性,表结构等等,这类数据库主要有以下特点:非关系型的、分布式的、开源的、水平可扩展的。 NoSQL 使用场景有:对数据高并发读写、对海量数据的高效率存储和访问、对数据的高可扩展性和高可用性等等。 Redis 的 key 可以是字符串、哈希、链表、集合和有序集合。value 类型很多,包括 String、list、set、zset。这些数据类型都支持 push/pop、add/remove、取交集和并集以及更多更丰富的操作,Redis 也支持各种不同方式的排序。为了保证效率,数据都是在缓存在内存中,它也可以周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件中。 有了 redis 有哪些好处呢?举个比较简单的例子,看下图: Redis 集群和 Mysql 是同步的,首先会从 redis 中获取数据,如果 redis 挂了,再从 mysql 中获取数据,这样网站就不会挂掉。更多关于 redis 的介绍以及使用场景,可以谷歌和百度,在这就不赘述了。 2. Redis 安装 本课程是在 vmvare 虚拟机中来安装的 redis (centos 7),学习的时候如果有自己的阿里云服务器,也可以在阿里云中来安装 redis,都可以。只要能 ping 的通云主机或者虚拟机的 ip,然后在虚拟机或者云主机中放行对应的端口(或者关掉防火墙)即可访问 redis。下面来介绍一下 redis 的安装过程: 安装 gcc 编译 因为后面安装redis的时候需要编译,所以事先得先安装gcc编译。阿里云主机已经默认安装了 gcc,如果是自己安装的虚拟机,那么需要先安装一下 gcc: yum install gcc-c++ 下载 redis 有两种方式下载安装包,一种是去官网上下载(https://redis.io),然后将安装包考到 centos 中,另种方法是直接使用 wget 来下载: wget http://download.redis.io/releases/redis-3.2.8.tar.gz 1 如果没有安装过 wget,可以通过如下命令安装: yum install wget 解压安装 解压安装包: tar –vzxf redis-3.2.8.tar.gz 然后将解压的文件夹 redis-3.2.8 放到 /usr/local/ 下,一般安装软件都放在 /usr/local 下。然后进入 /usr/local/redis-3.2.8/ 文件夹下,执行 make 命令即可完成安装。 【注】如果 make 失败,可以尝试如下命令: make MALLOC=libc make install 修改配置文件 安装成功之后,需要修改一下配置文件,包括允许接入的 ip,允许后台执行,设置密码等等。 打开 redis 配置文件:vi redis.conf 在命令模式下输入 /bind 来查找 bind 配置,按 n 来查找下一个,找到配置后,将 bind 配置成 0.0.0.0,允许任意服务器来访问 redis,即: bind 0.0.0.0 使用同样的方法,将 daemonize 改成 yes (默认为 no),允许 redis 在后台执行。 将 requirepass 注释打开,并设置密码为 123456(密码自己设置)。 启动 redis 在 redis-3.2.8 目录下,指定刚刚修改好的配置文件 redis.conf 来启动 redis: redis-server ./redis.conf 再启动 redis 客户端: redis-cli 由于我们设置了密码,在启动客户端之后,输入 auth 123456 即可登录进入客户端。 然后我们来测试一下,往 redis 中插入一个数据: set name 然后来获取 name get name 如果正常获取到 CSDN,则说明没有问题。 3. Spring Boot 集成 Redis 3.1 依赖导入 Spring Boot 集成 redis 很方便,只需要导入一个 redis 的 starter 依赖即可。如下: 这里也导入阿里巴巴的 fastjson 是为了在后面我们要存一个实体,为了方便把实体转换成 json 字符串存进去。 3.2 Redis 配置 导入了依赖之后,我们在 application.yml 文件里配置 redis: server: port: 8080 spring: #redis相关配置 redis: database: 5 # 配置redis的主机地址,需要修改成自己的 host: 192.168.48.190 port: 6379 password: 123456 timeout: 5000 jedis: pool: # 连接池中的最大空闲连接,默认值也是8。 max-idle: 500 # 连接池中的最小空闲连接,默认值也是0。 min-idle: 50 # 如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽) max-active: 1000 # 等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException max-wait: 2000 3.3 常用 api 介绍 Spring Boot 对 redis 的支持已经非常完善了,丰富的 api 已经足够我们日常的开发,这里我介绍几个最常用的供大家学习,其他 api 希望大家自己多学习,多研究。用到会去查即可。 有两个 redis 模板:RedisTemplate 和 StringRedisTemplate。我们不使用 RedisTemplate,RedisTemplate 提供给我们操作对象,操作对象的时候,我们通常是以 json 格式存储,但在存储的时候,会使用 Redis 默认的内部序列化器;导致我们存进里面的是乱码之类的东西。当然了,我们可以自己定义序列化,但是比较麻烦,所以使用 StringRedisTemplate 模板。StringRedisTemplate 主要给我们提供字符串操作,我们可以将实体类等转成 json 字符串即可,在取出来后,也可以转成相应的对象,这就是上面我导入了阿里 fastjson 的原因。 3.3.1 redis:string 类型 新建一个 RedisService,注入 StringRedisTemplate,使用 stringRedisTemplate.opsForValue() 可以获取 ValueOperations 对象,通过该对象即可读写 redis 数据库了。如下: 该对象操作的是 string,我们也可以存实体类,只需要将实体类转换成 json 字符串即可。下面来测试一下: 先启动 redis,然后运行这个测试用例,观察控制台打印的日志如下: 我的微信公众号为:程序员私房菜 用户信息:{"password":"123456","username":"CSDN"} 3.3.2 redis:hash 类型 hash 类型其实原理和 string 一样的,但是有两个 key,使用 stringRedisTemplate.opsForHash() 可以获取 HashOperations 对象。比如我们要存储订单信息,所有订单信息都放在 order 下,针对不同用户的订单实体,可以通过用户的 id 来区分,这就相当于两个 key 了。 可以看出,hash 和 string 没啥两样,只不过多了个参数,Spring Boot 中操作 redis 非常简单方便。来测试一下: 3.3.3 redis:list 类型 使用 stringRedisTemplate.opsForList() 可以获取 ListOperations listOperations redis 列表对象,该列表是个简单的字符串列表,可以支持从左侧添加,也可以支持从右侧添加,一个列表最多包含 2 ^ 32 -1 个元素。 可以看出,这些 api 都是一样的形式,方便记忆也方便使用。具体的 api 细节我就不展开了,大家可以自己看 api 文档。其实,这些 api 根据参数和返回值也能知道它们是做什么用的。来测试一下: 4. 总结 本节主要介绍了 redis 的使用场景、安装过程,以及 Spring Boot 中集成 redis 的详细步骤。在实际项目中,通常都用 redis 作为缓存,在查询数据库的时候,会先从 redis 中查找,如果有信息,则从 redis 中取;如果没有,则从数据库中查,并且同步到 redis 中,下次 redis 中就有了。更新和删除也是如此,都需要同步到 redis。redis 在高并发场景下运用的很多。 Spring Boot中集成ActiveMQ 1. JMS 和 ActiveMQ 介绍 1.1 JMS 是啥 百度百科的解释: JMS 即 Java 消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的 API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java 消息服务是一个与具体平台无关的 API,绝大多数 MOM 提供商都对 JMS 提供支持。 JMS 只是接口,不同的提供商或者开源组织对其有不同的实现,ActiveMQ 就是其中之一,它支持JMS,是 Apache 推出的。JMS 中有几个对象模型: 连接工厂:ConnectionFactory JMS连接:Connection JMS会话:Session JMS目的:Destination JMS生产者:Producer JMS消费者:Consumer JMS消息两种类型:点对点和发布/订阅。 可以看出 JMS 实际上和 JDBC 有点类似,JDBC 是可以用来访问许多不同关系数据库的 API,而 JMS 则提供同样与厂商无关的访问方法,以访问消息收发服务。本文主要使用 ActiveMQ。 1.2 ActiveMQ ActiveMQ 是 Apache 的一个能力强劲的开源消息总线。ActiveMQ 完全支持JMS1.1和J2EE 1.4规范,尽管 JMS 规范出台已经是很久的事情了,但是 JMS 在当今的 Java EE 应用中间仍然扮演着特殊的地位。ActiveMQ 用在异步消息的处理上,所谓异步消息即消息发送者无需等待消息接收者的处理以及返回,甚至无需关心消息是否发送成功。 异步消息主要有两种目的地形式,队列(queue)和主题(topic),队列用于点对点形式的消息通信,主题用于发布/订阅式的消息通信。本章节主要来学习一下在 Spring Boot 中如何使用这两种形式的消息。 2. ActiveMQ安装 使用 ActiveMQ 首先需要去官网下载,官网地址为:http://activemq.apache.org/ 本课程使用的版本是 apache-activemq-5.15.3,下载后解压缩会有一个名为 apache-activemq-5.15.3 的文件夹,没错,这就安装好了,非常简单,开箱即用。打开文件夹会看到里面有个 activemq-all-5.15.3.jar,这个 jar 我们是可以加进工程里的,但是使用 maven 的话,这个 jar 我们不需要。 在使用 ActiveMQ 之前,首先得先启动,刚才解压后的目录中有个 bin 目录,里面有 win32 和 win64 两个目录,根据自己电脑选择其中一个打开运行里面的 activemq.bat 即可启动 ActiveMQ。 消息生产者生产消息发布到queue中,然后消息消费者从queue中取出,并且消费消息。这里需要注意:消息被消费者消费以后,queue中不再有存储,所以消息消费者不可消费到已经被消费的消息。Queue支持存在多个消息消费者,但是对一个消息而言,只会有一个消费者可以消费 启动完成后,在浏览器中输入 http://127.0.0.1:8161/admin/ 来访问 ActiveMQ 的服务器,用户名和密码是 admin/admin。如下: 我们可以看到有 Queues 和 Topics 这两个选项,这两个选项分别是点对点消息和发布/订阅消息的查看窗口。何为点对点消息和发布/订阅消息呢? 点对点消息:消息生产者生产消息发布到 queue 中,然后消息消费者从 queue 中取出,并且消费消息。这里需要注意:消息被消费者消费以后,queue 中不再有存储,所以消息消费者不可消费到已经被消费的消息。Queue 支持存在多个消息消费者,但是对一个消息而言,只会有一个消费者可以消费。 发布/订阅消息:消息生产者(发布)将消息发布到 topic 中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到 topic 的消息会被所有订阅者消费。下面分析具体的实现方式。 3. ActiveMQ集成 3.1 依赖导入和配置 在 Spring Boot 中集成 ActiveMQ 需要导入如下 starter 依赖: 然后在 application.yml 配置文件中,对 activemq 做一下配置: # 如果此处设置为true,需要添加activemq-pool的依赖包,否则会自动配置失败,无法注入JmsMessagingTemplate enabled: false 3.2 Queue 和 Topic 的创建 首先我们需要创建两种消息 Queue 和 Topic,这两种消息的创建,我们放到 ActiveMqConfig 中来创建,如下: 可以看出创建 Queue 和 Topic 两种消息,分别使用 new ActiveMQQueue 和 new ActiveMQTopic 来创建,分别跟上对应消息的名称即可。这样在其他地方就可以直接将这两种消息作为组件注入进来了。 3.3 消息的发送接口 在 Spring Boot 中,我们只要注入 JmsMessagingTemplate 模板即可快速发送消息,如下: convertAndSend 方法中第一个参数是消息发送的目的地,第二个参数是具体的消息内容。 3.4 点对点消息生产与消费 3.4.1 点对点消息的生产 消息的生产,我们放到 Controller 中来做,由于上面已经生成了 Queue 消息的组件,所以在 Controller 中我们直接注入进来即可。然后调用上文的消息发送方法 sendMessage 即可成功生产一条消息。 3.4.2 点对点消息的消费 点对点消息的消费很简单,只要我们指定目的地即可,jms 监听器一直在监听是否有消息过来,如果有,则消费。 可以看出,使用 @JmsListener 注解来指定要监听的目的地,在消息接收方法内部,我们可以根据具体的业务需求做相应的逻辑处理即可。 3.4.3 测试一下 启动项目,在浏览器中输入:http://localhost:8081/activemq/send/queue,观察控制台的输出日志,出现下面的日志说明消息发送和消费成功。 收到的消息为:Queue: hello activemq! 3.5 发布/订阅消息的生产和消费 3.5.1 发布/订阅消息的生产 和点对点消息一样,我们注入 topic 并调用 producer 的 sendMessage 方法即可发送订阅消息,如下,不再赘述: 3.5.2 发布/订阅消息的消费 发布/订阅消息的消费和点对点不同,订阅消息支持多个消费者一起消费。其次,Spring Boot 中默认的时点对点消息,所以在使用 topic 时,会不起作用,我们需要在配置文件 application.yml 中添加一个配置: 该配置是 false 的话,则为点对点消息,也是 Spring Boot 默认的。这样是可以解决问题,但是如果这样配置的话,上面提到的点对点消息又不能正常消费了。所以二者不可兼得,这并非一个好的解决办法。 比较好的解决办法是,我们定义一个工厂,@JmsListener 注解默认只接收 queue 消息,如果要接收 topic 消息,需要设置一下 containerFactory。我们还在上面的那个 ActiveMqConfig 配置类中添加: 经过这样的配置之后,我们在消费的时候,在 @JmsListener 注解中指定这个容器工厂即可消费 topic 消息。如下:指定 containerFactory 属性为上面我们自己配置的 topicListenerContainer 即可。由于 topic 消息可以多个消费,所以该消费的类可以拷贝几个一起测试一下,这里我就不贴代码了,可以参考我的源码测试。 3.5.3 测试一下 启动项目,在浏览器中输入:http://localhost:8081/activemq/send/topic,观察控制台的输出日志,出现下面的日志说明消息发送和消费成功。 收到的消息为:Topic: hello activemq! 收到的消息为:Topic: hello activemq! 4. 总结 本章主要介绍了 jms 和 activemq 的相关概念、activemq 的安装与启动。详细分析了 Spring Boot 中点对点消息和发布/订阅消息两种方式的配置、消息生产和消费方式。ActiveMQ 是能力强劲的开源消息总线,在异步消息的处理上很有用,希望大家好好消化一下。 Spring Boot中集成 Shiro Shiro 是一个强大、简单易用的 Java 安全框架,主要用来更便捷的认证,授权,加密,会话管等等,可为任何应用提供安全保障。本课程主要来介绍 Shiro 的认证和授权功能。 1. Shiro 三大核心组件 Shiro 有三大核心的组件:Subject、SecurityManager 和 Realm。先来看一下它们之间的关系。 Subject:认证主体。它包含两个信息:Principals 和 Credentials。看一下这两个信息具体是什么。 Principals:身份。可以是用户名,邮件,手机号码等等,用来标识一个登录主体身份; Credentials:凭证。常见有密码,数字证书等等。 说白了,就是需要认证的东西,最常见的就是用户名密码了,比如用户在登录的时候,Shiro 需要去进行身份认证,就需要 Subject 认证主体。 SecurityManager:安全管理员。这是 Shiro 架构的核心,它就像 Shiro 内部所有原件的保护伞一样。我们在项目中一般都会配置 SecurityManager,开发人员大部分精力主要是在 Subject 认证主体上面。我们在与 Subject 进行交互的时候,实际上是 SecurityManager 在背后做一些安全操作。 Realms:Realms 是一个域,它是连接 Shiro 和具体应用的桥梁,当需要与安全数据交互的时候,比如用户账户、访问控制等,Shiro 就会从一个或多个 Realms 中去查找。我们一般会自己定制 Realm,这在下文会详细说明。 1. Shiro 身份和权限认证 1.2 Shiro 身份认证 我们来分析一下 Shiro 身份认证的过程,看一下官方的一个认证图: Step1:应用程序代码在调用 Subject.login(token) 方法后,传入代表最终用户的身份和凭证的 AuthenticationToken 实例 token。 Step2:将 Subject 实例委托给应用程序的 SecurityManager(Shiro的安全管理)来开始实际的认证工作。这里开始真正的认证工作了。 Step3,4,5:然后 SecurityManager 就会根据具体的 realm 去进行安全认证了。 从图中可以看出,realm 可以自定义(Custom Realm)。 1.3 Shiro 权限认证 权限认证,也就是访问控制,即在应用中控制谁能访问哪些资源。在权限认证中,最核心的三个要素是:权限,角色和用户。 权限(permission):即操作资源的权利,比如访问某个页面,以及对某个模块的数据的添加,修改,删除,查看的权利; 角色(role):指的是用户担任的的角色,一个角色可以有多个权限; 用户(user):在 Shiro 中,代表访问系统的用户,即上面提到的 Subject 认证主体。 它们之间的的关系可以用下图来表示: 一个用户可以有多个角色,而不同的角色可以有不同的权限,也可由有相同的权限。比如说现在有三个角色,1是普通角色,2也是普通角色,3是管理员,角色1只能查看信息,角色2只能添加信息,管理员都可以,而且还可以删除信息,类似于这样。 2. Spring Boot 集成 Shiro 过程 2.1 依赖导入 Spring Boot 2.0.3 集成 Shiro 需要导入如下 starter 依赖: 2.2 数据库表数据初始化 这里主要涉及到三张表:用户表、角色表和权限表,其实在 demo 中,我们完全可以自己模拟一下,不用建表,但是为了更加接近实际情况,我们还是加入 mybatis,来操作数据库。下面是数据库表的脚本。 ———————————————— 原文链接:https://blog.csdn.net/cuiqwei/article/details/118188540
-
Spring Boot集成MyBatis 1. MyBatis 介绍 大家都知道,MyBatis 框架是一个持久层框架,是 Apache 下的顶级项目。Mybatis 可以让开发者的主要精力放在 sql 上,通过 Mybatis 提供的映射方式,自由灵活的生成满足需要的 sql 语句。使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs 映射成数据库中的记录,在国内可谓是占据了半壁江山。本节课程主要通过两种方式来对 Spring Boot 集成 MyBatis 做一讲解。重点讲解一下基于注解的方式。因为实际项目中使用注解的方式更多一点,更简洁一点,省去了很多 xml 配置(这不是绝对的,有些项目组中可能也在使用 xml 的方式)。 2. MyBatis 的配置 2.1 依赖导入 Spring Boot 集成 MyBatis,需要导入 mybatis-spring-boot-starter 和 mysql 的依赖,这里我们使用的版本时 1.3.2,如下: <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> 我们点开 mybatis-spring-boot-starter 依赖,可以看到我们之前使用 Spring 时候熟悉的依赖,就像我在课程的一开始介绍的那样,Spring Boot 致力于简化编码,使用 starter 系列将相关依赖集成在一起,开发者不需要关注繁琐的配置,非常方便。 <!-- 省去其他 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> </dependency> 2.2 properties.yml配置 我们再来看一下,集成 MyBatis 时需要在 properties.yml 配置文件中做哪些基本配置呢? # 服务端口号 server: port: 8080 # 数据库地址 datasource: url: localhost:3306/blog_test spring: datasource: # 数据库配置 driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://${datasource.url}?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&autoReconnect=true&failOverReadOnly=false&maxReconnects=10 username: root password: 123456 hikari: maximum-pool-size: 10 # 最大连接池数 max-lifetime: 1770000 mybatis: # 指定别名设置的包为所有entity type-aliases-package: com.itcodai.course10.entity configuration: map-underscore-to-camel-case: true # 驼峰命名规范 mapper-locations: # mapper映射文件位置 - classpath:mapper/*.xml 我们来简单介绍一下上面的这些配置:关于数据库的相关配置,我就不详细的解说了,这点相信大家已经非常熟练了,配置一下用户名、密码、数据库连接等等,这里使用的连接池是 Spring Boot 自带的 hikari,感兴趣的朋友可以去百度或者谷歌搜一搜,了解一下。 这里说明一下 map-underscore-to-camel-case: true, 用来开启驼峰命名规范,这个比较好用,比如数据库中字段名为:user_name, 那么在实体类中可以定义属性为 userName (甚至可以写成 username,也能映射上),会自动匹配到驼峰属性,如果不这样配置的话,针对字段名和属性名不同的情况,会映射不到。 3. 基于 xml 的整合 使用原始的 xml 方式,需要新建 UserMapper.xml 文件,在上面的 application.yml 配置文件中,我们已经定义了 xml 文件的路径:classpath:mapper/*.xml,所以我们在 resources 目录下新建一个 mapper 文件夹,然后创建一个 UserMapper.xml 文件。 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.itcodai.course10.dao.UserMapper"> <resultMap id="BaseResultMap" type="com.itcodai.course10.entity.User"> <id column="id" jdbcType="BIGINT" property="id" /> <result column="user_name" jdbcType="VARCHAR" property="username" /> <result column="password" jdbcType="VARCHAR" property="password" /> </resultMap> <select id="getUserByName" resultType="User" parameterType="String"> select * from user where user_name = #{username} </select> </mapper> 这和整合 Spring 一样的,namespace 中指定的是对应的 Mapper, 中指定对应的实体类,即 User。然后在内部指定表的字段和实体的属性相对应即可。这里我们写一个根据用户名查询用户的 sql。 实体类中有 id,username 和 password,我不在这贴代码,大家可以下载源码查看。UserMapper.java 文件中写一个接口即可: User getUserByName(String username); 中间省略 service 的代码,我们写一个 Controller 来测试一下: @RestController public class TestController { @Resource private UserService userService; @RequestMapping("/getUserByName/{name}") public User getUserByName(@PathVariable String name) { return userService.getUserByName(name); } } 启动项目,在浏览器中输入:http://localhost:8080/getUserByName/CSDN 即可查询到数据库表中用户名为 CSDN 的用户信息(事先搞两个数据进去即可): {"id":2,"username":"CSDN","password":"123456"} 这里需要注意一下:Spring Boot 如何知道这个 Mapper 呢?一种方法是在上面的 mapper 层对应的类上面添加 @Mapper 注解即可,但是这种方法有个弊端,当我们有很多个 mapper 时,那么每一个类上面都得添加 @Mapper 注解。另一种比较简便的方法是在 Spring Boot 启动类上添加@MaperScan 注解,来扫描一个包下的所有 mapper。如下: @SpringBootApplication @MapperScan("com.itcodai.course10.dao") public class Course10Application { public static void main(String[] args) { SpringApplication.run(Course10Application.class, args); } } 这样的话,com.itcodai.course10.dao 包下的所有 mapper 都会被扫描到了。 4. 基于注解的整合 基于注解的整合就不需要 xml 配置文件了,MyBatis 主要提供了 @Select, @Insert, @Update, Delete 四个注解。这四个注解是用的非常多的,也很简单,注解后面跟上对应的 sql 语句即可,我们举个例子: @Select("select * from user where id = #{id}") User getUser(Long id); 这跟 xml 文件中写 sql 语句是一样的,这样就不需要 xml 文件了,但是有个问题,有人可能会问,如果是两个参数呢?如果是两个参数,我们需要使用 @Param 注解来指定每一个参数的对应关系,如下: @Select("select * from user where id = #{id} and user_name=#{name}") User getUserByIdAndName(@Param("id") Long id, @Param("name") String username); 可以看出,@Param 指定的参数应该要和 sql 中 #{} 取的参数名相同,不同则取不到。可以在 controller 中自行测试一下,接口都在源码中,文章中我就不贴测试代码和结果了。 有个问题需要注意一下,一般我们在设计表字段后,都会根据自动生成工具生成实体类,这样的话,基本上实体类是能和表字段对应上的,最起码也是驼峰对应的,由于在上面配置文件中开启了驼峰的配置,所以字段都是能对的上的。但是,万一有对不上的呢?我们也有解决办法,使用 @Results 注解来解决。 @Select("select * from user where id = #{id}") @Results({ @Result(property = "username", column = "user_name"), @Result(property = "password", column = "password") }) User getUser(Long id); @Results 中的 @Result 注解是用来指定每一个属性和字段的对应关系,这样的话就可以解决上面说的这个问题了。 当然了,我们也可以 xml 和注解相结合使用,目前我们实际的项目中也是采用混用的方式,因为有时候 xml 方便,有时候注解方便,比如就上面这个问题来说,如果我们定义了上面的这个 UserMapper.xml,那么我们完全可以使用 @ResultMap 注解来替代 @Results 注解,如下: @Select("select * from user where id = #{id}") @ResultMap("BaseResultMap") User getUser(Long id); @ResultMap 注解中的值从哪来呢?对应的是 UserMapper.xml 文件中定义的 时对应的 id 值: <resultMap id="BaseResultMap" type="com.itcodai.course10.entity.User"> 这种 xml 和注解结合着使用的情况也很常见,而且也减少了大量的代码,因为 xml 文件可以使用自动生成工具去生成,也不需要人为手动敲,所以这种使用方式也很常见。 5. 总结 本节课主要系统的讲解了 Spring Boot 集成 MyBatis 的过程,分为基于 xml 形式和基于注解的形式来讲解,通过实际配置手把手讲解了 Spring Boot 中 MyBatis 的使用方式,并针对注解方式,讲解了常见的问题已经解决方式,有很强的实战意义。在实际项目中,建议根据实际情况来确定使用哪种方式,一般 xml 和注解都在用。 第11课:Spring Boot事务配置管理 1. 事务相关 场景:我们在开发企业应用时,由于数据操作在顺序执行的过程中,线上可能有各种无法预知的问题,任何一步操作都有可能发生异常,异常则会导致后续的操作无法完成。此时由于业务逻辑并未正确的完成,所以在之前操作过数据库的动作并不可靠,需要在这种情况下进行数据的回滚。 事务的作用就是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都必须成功执行,只要有发生异常就回退到事务开始未进行操作的状态。这很好理解,转账、购票等等,必须整个事件流程全部执行完才能人为该事件执行成功,不能转钱转到一半,系统死了,转账人钱没了,收款人钱还没到。 事务管理是 Spring Boot 框架中最为常用的功能之一,我们在实际应用开发时,基本上在 service 层处理业务逻辑的时候都要加上事务,当然了,有时候可能由于场景需要,也不用加事务(比如我们就要往一个表里插数据,相互没有影响,插多少是多少,不能因为某个数据挂了,把之前插的全部回滚)。 2. Spring Boot 事务配置 2.1 依赖导入 在 Spring Boot 中使用事务,需要导入 mysql 依赖: <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> 导入了 mysql 依赖后,Spring Boot 会自动注入 DataSourceTransactionManager,我们不需要任何其他的配置就可以用 @Transactional 注解进行事务的使用。关于 mybatis 的配置,在上一节课中已经说明了,这里还是使用上一节课中的 mybatis 配置即可。 2.2 事务的测试 我们首先在数据库表中插入一条数据: id user_name password 1 测试 123456 然后我们写一个插入的 mapper: public interface UserMapper { @Insert("insert into user (user_name, password) values (#{username}, #{password})") Integer insertUser(User user); } OK,接下来我们来测试一下 Spring Boot 中的事务处理,在 service 层,我们手动抛出个异常来模拟实际中出现的异常,然后观察一下事务有没有回滚,如果数据库中没有新的记录,则说明事务回滚成功。 @Service public class UserServiceImpl implements UserService { @Resource private UserMapper userMapper; @Override @Transactional public void isertUser(User user) { // 插入用户信息 userMapper.insertUser(user); // 手动抛出异常 throw new RuntimeException(); } } 我们来测试一下: @RestController public class TestController { @Resource private UserService userService; @PostMapping("/adduser") public String addUser(@RequestBody User user) throws Exception { if (null != user) { userService.isertUser(user); return "success"; } else { return "false"; } } } 我们使用 postman 调用一下该接口,因为在程序中抛出了个异常,会造成事务回滚,我们刷新一下数据库,并没有增加一条记录,说明事务生效了。事务很简单,我们平时在使用的时候,一般不会有多少问题,但是并不仅仅如此…… 3. 常见问题总结 从上面的内容中可以看出,Spring Boot 中使用事务非常简单,@Transactional 注解即可解决问题,说是这么说,但是在实际项目中,是有很多小坑在等着我们,这些小坑是我们在写代码的时候没有注意到,而且正常情况下不容易发现这些小坑,等项目写大了,某一天突然出问题了,排查问题非常困难,到时候肯定是抓瞎,需要费很大的精力去排查问题。 这一小节,我专门针对实际项目中经常出现的,和事务相关的细节做一下总结,希望读者在读完之后,能够落实到自己的项目中,能有所受益。 3.1 异常并没有被 ”捕获“ 到 首先要说的,就是异常并没有被 ”捕获“ 到,导致事务并没有回滚。我们在业务层代码中,也许已经考虑到了异常的存在,或者编辑器已经提示我们需要抛出异常,但是这里面有个需要注意的地方:并不是说我们把异常抛出来了,有异常了事务就会回滚,我们来看一个例子: @Service public class UserServiceImpl implements UserService { @Resource private UserMapper userMapper; @Override @Transactional public void isertUser2(User user) throws Exception { // 插入用户信息 userMapper.insertUser(user); // 手动抛出异常 throw new SQLException("数据库异常"); } } 我们看上面这个代码,其实并没有什么问题,手动抛出一个 SQLException 来模拟实际中操作数据库发生的异常,在这个方法中,既然抛出了异常,那么事务应该回滚,实际却不如此,读者可以使用我源码中 controller 的接口,通过 postman 测试一下,就会发现,仍然是可以插入一条用户数据的。 那么问题出在哪呢?因为 Spring Boot 默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。比如上面我们的例子中抛出的 RuntimeException 就没有问题,但是抛出 SQLException 就无法回滚了。针对非运行时异常,如果要进行事务回滚的话,可以在 @Transactional 注解中使用 rollbackFor 属性来指定异常,比如 @Transactional(rollbackFor = Exception.class),这样就没有问题了,所以在实际项目中,一定要指定异常。 3.2 异常被 ”吃“ 掉 这个标题很搞笑,异常怎么会被吃掉呢?还是回归到现实项目中去,我们在处理异常时,有两种方式,要么抛出去,让上一层来捕获处理;要么把异常 try catch 掉,在异常出现的地方给处理掉。就因为有这中 try…catch,所以导致异常被 ”吃“ 掉,事务无法回滚。我们还是看上面那个例子,只不过简单修改一下代码: @Service public class UserServiceImpl implements UserService { @Resource private UserMapper userMapper; @Override @Transactional(rollbackFor = Exception.class) public void isertUser3(User user) { try { // 插入用户信息 userMapper.insertUser(user); // 手动抛出异常 throw new SQLException("数据库异常"); } catch (Exception e) { // 异常处理逻辑 } } } 读者可以使用我源码中 controller 的接口,通过 postman 测试一下,就会发现,仍然是可以插入一条用户数据,说明事务并没有因为抛出异常而回滚。这个细节往往比上面那个坑更难以发现,因为我们的思维很容易导致 try…catch 代码的产生,一旦出现这种问题,往往排查起来比较费劲,所以我们平时在写代码时,一定要多思考,多注意这种细节,尽量避免给自己埋坑。 那这种怎么解决呢?直接往上抛,给上一层来处理即可,千万不要在事务中把异常自己 ”吃“ 掉。 3.3 事务的范围 事务范围这个东西比上面两个坑埋的更深!我之所以把这个也写上,是因为这是我之前在实际项目中遇到的,该场景在这个课程中我就不模拟了,我写一个 demo 让大家看一下,把这个坑记住即可,以后在写代码时,遇到并发问题,就会注意这个坑了,那么这节课也就有价值了。 我来写个 demo: @Service public class UserServiceImpl implements UserService { @Resource private UserMapper userMapper; @Override @Transactional(rollbackFor = Exception.class) public synchronized void isertUser4(User user) { // 实际中的具体业务…… userMapper.insertUser(user); } } 可以看到,因为要考虑并发问题,我在业务层代码的方法上加了个 synchronized 关键字。我举个实际的场景,比如一个数据库中,针对某个用户,只有一条记录,下一个插入动作过来,会先判断该数据库中有没有相同的用户,如果有就不插入,就更新,没有才插入,所以理论上,数据库中永远就一条同一用户信息,不会出现同一数据库中插入了两条相同用户的信息。 但是在压测时,就会出现上面的问题,数据库中确实有两条同一用户的信息,分析其原因,在于事务的范围和锁的范围问题。 从上面方法中可以看到,方法上是加了事务的,那么也就是说,在执行该方法开始时,事务启动,执行完了后,事务关闭。但是 synchronized 没有起作用,其实根本原因是因为事务的范围比锁的范围大。也就是说,在加锁的那部分代码执行完之后,锁释放掉了,但是事务还没结束,此时另一个线程进来了,事务没结束的话,第二个线程进来时,数据库的状态和第一个线程刚进来是一样的。即由于mysql Innodb引擎的默认隔离级别是可重复读(在同一个事务里,SELECT的结果是事务开始时时间点的状态),线程二事务开始的时候,线程一还没提交完成,导致读取的数据还没更新。第二个线程也做了插入动作,导致了脏数据。 这个问题可以避免,第一,把事务去掉即可(不推荐);第二,在调用该 service 的地方加锁,保证锁的范围比事务的范围大即可。 4. 总结 本章主要总结了 Spring Boot 中如何使用事务,只要使用 @Transactional 注解即可使用,非常简单方便。除此之外,重点总结了三个在实际项目中可能遇到的坑点,这非常有意义,因为事务这东西不出问题还好,出了问题比较难以排查,所以总结的这三点注意事项,希望能帮助到开发中的朋友。 第12课:Spring Boot中使用监听器 1. 监听器介绍 什么是 web 监听器?web 监听器是一种 Servlet 中特殊的类,它们能帮助开发者监听 web 中特定的事件,比如 ServletContext, HttpSession, ServletRequest 的创建和销毁;变量的创建、销毁和修改等。可以在某些动作前后增加处理,实现监控。 2. Spring Boot中监听器的使用 web 监听器的使用场景很多,比如监听 servlet 上下文用来初始化一些数据、监听 http session 用来获取当前在线的人数、监听客户端请求的 servlet request 对象来获取用户的访问信息等等。这一节中,我们主要通过这三个实际的使用场景来学习一下 Spring Boot 中监听器的使用。 2.1 监听Servlet上下文对象 监听 servlet 上下文对象可以用来初始化数据,用于缓存。什么意思呢?我举一个很常见的场景,比如用户在点击某个站点的首页时,一般都会展现出首页的一些信息,而这些信息基本上或者大部分时间都保持不变的,但是这些信息都是来自数据库。如果用户的每次点击,都要从数据库中去获取数据的话,用户量少还可以接受,如果用户量非常大的话,这对数据库也是一笔很大的开销。 针对这种首页数据,大部分都不常更新的话,我们完全可以把它们缓存起来,每次用户点击的时候,我们都直接从缓存中拿,这样既可以提高首页的访问速度,又可以降低服务器的压力。如果做的更加灵活一点,可以再加个定时器,定期的来更新这个首页缓存。就类似与 CSDN 个人博客首页中排名的变化一样。 下面我们针对这个功能,来写一个 demo,在实际中,读者可以完全套用该代码,来实现自己项目中的相关逻辑。首先写一个 Service,模拟一下从数据库查询数据: @Service public class UserService { /** * 获取用户信息 * @return */ public User getUser() { // 实际中会根据具体的业务场景,从数据库中查询对应的信息 return new User(1L, "测试", "123456"); } } 然后写一个监听器,实现 ApplicationListener 接口,重写 onApplicationEvent 方法,将 ContextRefreshedEvent 对象传进去。如果我们想在加载或刷新应用上下文时,也重新刷新下我们预加载的资源,就可以通过监听 ContextRefreshedEvent 来做这样的事情。如下: /** * 使用ApplicationListener来初始化一些数据到application域中的监听器 * @author shengni ni * @date 2018/07/05 */ @Component public class MyServletContextListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { // 先获取到application上下文 ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext(); // 获取对应的service UserService userService = applicationContext.getBean(UserService.class); User user = userService.getUser(); // 获取application域对象,将查到的信息放到application域中 ServletContext application = applicationContext.getBean(ServletContext.class); application.setAttribute("user", user); } } 正如注释中描述的一样,首先通过 contextRefreshedEvent 来获取 application 上下文,再通过 application 上下文来获取 UserService 这个 bean,项目中可以根据实际业务场景,也可以获取其他的 bean,然后再调用自己的业务代码获取相应的数据,最后存储到 application 域中,这样前端在请求相应数据的时候,我们就可以直接从 application 域中获取信息,减少数据库的压力。下面写一个 Controller 直接从 application 域中获取 user 信息来测试一下。 @RestController @RequestMapping("/listener") public class TestController { @GetMapping("/user") public User getUser(HttpServletRequest request) { ServletContext application = request.getServletContext(); return (User) application.getAttribute("user"); } } 启动项目,在浏览器中输入 http://localhost:8080/listener/user 测试一下即可,如果正常返回 user 信息,那么说明数据已经缓存成功。不过 application 这种是缓存在内存中,对内存会有消耗,后面的课程中我会讲到 redis,到时候再给大家介绍一下 redis 的缓存。 2.2 监听HTTP会话 Session对象 监听器还有一个比较常用的地方就是用来监听 session 对象,来获取在线用户数量,现在有很多开发者都有自己的网站,监听 session 来获取当前在下用户数量是个很常见的使用场景,下面来介绍一下如何来使用。 /** * 使用HttpSessionListener统计在线用户数的监听器 * @author shengwu ni * @date 2018/07/05 */ @Component public class MyHttpSessionListener implements HttpSessionListener { private static final Logger logger = LoggerFactory.getLogger(MyHttpSessionListener.class); /** * 记录在线的用户数量 */ public Integer count = 0; @Override public synchronized void sessionCreated(HttpSessionEvent httpSessionEvent) { logger.info("新用户上线了"); count++; httpSessionEvent.getSession().getServletContext().setAttribute("count", count); } @Override public synchronized void sessionDestroyed(HttpSessionEvent httpSessionEvent) { logger.info("用户下线了"); count--; httpSessionEvent.getSession().getServletContext().setAttribute("count", count); } } 可以看出,首先该监听器需要实现 HttpSessionListener 接口,然后重写 sessionCreated 和 sessionDestroyed 方法,在 sessionCreated 方法中传递一个 HttpSessionEvent 对象,然后将当前 session 中的用户数量加1,sessionDestroyed 方法刚好相反,不再赘述。然后我们写一个 Controller 来测试一下。 @RestController @RequestMapping("/listener") public class TestController { /** * 获取当前在线人数,该方法有bug * @param request * @return */ @GetMapping("/total") public String getTotalUser(HttpServletRequest request) { Integer count = (Integer) request.getSession().getServletContext().getAttribute("count"); return "当前在线人数:" + count; } } 该 Controller 中是直接获取当前 session 中的用户数量,启动服务器,在浏览器中输入 localhost:8080/listener/total 可以看到返回的结果是1,再打开一个浏览器,请求相同的地址可以看到 count 是 2 ,这没有问题。但是如果关闭一个浏览器再打开,理论上应该还是2,但是实际测试却是 3。原因是 session 销毁的方法没有执行(可以在后台控制台观察日志打印情况),当重新打开时,服务器找不到用户原来的 session,于是又重新创建了一个 session,那怎么解决该问题呢?我们可以将上面的 Controller 方法改造一下: @GetMapping("/total2") public String getTotalUser(HttpServletRequest request, HttpServletResponse response) { Cookie cookie; try { // 把sessionId记录在浏览器中 cookie = new Cookie("JSESSIONID", URLEncoder.encode(request.getSession().getId(), "utf-8")); cookie.setPath("/"); //设置cookie有效期为2天,设置长一点 cookie.setMaxAge( 48*60 * 60); response.addCookie(cookie); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } Integer count = (Integer) request.getSession().getServletContext().getAttribute("count"); return "当前在线人数:" + count; } 可以看出,该处理逻辑是让服务器记得原来那个 session,即把原来的 sessionId 记录在浏览器中,下次再打开时,把这个 sessionId 传过去,这样服务器就不会重新再创建了。重启一下服务器,在浏览器中再次测试一下,即可避免上面的问题。 2.3 监听客户端请求Servlet Request对象 使用监听器获取用户的访问信息比较简单,实现 ServletRequestListener 接口即可,然后通过 request 对象获取一些信息。如下: /** * 使用ServletRequestListener获取访问信息 * @author shengwu ni * @date 2018/07/05 */ @Component public class MyServletRequestListener implements ServletRequestListener { private static final Logger logger = LoggerFactory.getLogger(MyServletRequestListener.class); @Override public void requestInitialized(ServletRequestEvent servletRequestEvent) { HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); logger.info("session id为:{}", request.getRequestedSessionId()); logger.info("request url为:{}", request.getRequestURL()); request.setAttribute("name", "测试"); } @Override public void requestDestroyed(ServletRequestEvent servletRequestEvent) { logger.info("request end"); HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); logger.info("request域中保存的name值为:{}", request.getAttribute("name")); } } 这个比较简单,不再赘述,接下来写一个 Controller 测试一下即可。 @GetMapping("/request") public String getRequestInfo(HttpServletRequest request) { System.out.println("requestListener中的初始化的name数据:" + request.getAttribute("name")); return "success"; } 3. Spring Boot中自定义事件监听 在实际项目中,我们往往需要自定义一些事件和监听器来满足业务场景,比如在微服务中会有这样的场景:微服务 A 在处理完某个逻辑之后,需要通知微服务 B 去处理另一个逻辑,或者微服务 A 处理完某个逻辑之后,需要将数据同步到微服务 B,这种场景非常普遍,这个时候,我们可以自定义事件以及监听器来监听,一旦监听到微服务 A 中的某事件发生,就去通知微服务 B 处理对应的逻辑。 3.1 自定义事件 自定义事件需要继承 ApplicationEvent 对象,在事件中定义一个 User 对象来模拟数据,构造方法中将 User 对象传进来初始化。如下: /** * 自定义事件 * @author shengwu ni * @date 2018/07/05 */ public class MyEvent extends ApplicationEvent { private User user; public MyEvent(Object source, User user) { super(source); this.user = user; } // 省去get、set方法 } 3.2 自定义监听器 接下来,自定义一个监听器来监听上面定义的 MyEvent 事件,自定义监听器需要实现 ApplicationListener 接口即可。如下: /** * 自定义监听器,监听MyEvent事件 * @author shengwu ni * @date 2018/07/05 */ @Component public class MyEventListener implements ApplicationListener<MyEvent> { @Override public void onApplicationEvent(MyEvent myEvent) { // 把事件中的信息获取到 User user = myEvent.getUser(); // 处理事件,实际项目中可以通知别的微服务或者处理其他逻辑等等 System.out.println("用户名:" + user.getUsername()); System.out.println("密码:" + user.getPassword()); } } 然后重写 onApplicationEvent 方法,将自定义的 MyEvent 事件传进来,因为该事件中,我们定义了 User 对象(该对象在实际中就是需要处理的数据,在下文来模拟),然后就可以使用该对象的信息了。 OK,定义好了事件和监听器之后,需要手动发布事件,这样监听器才能监听到,这需要根据实际业务场景来触发,针对本文的例子,我写个触发逻辑,如下: /** * UserService * @author shengwu ni */ @Service public class UserService { @Resource private ApplicationContext applicationContext; /** * 发布事件 * @return */ public User getUser2() { User user = new User(1L, "测试", "123456"); // 发布事件 MyEvent event = new MyEvent(this, user); applicationContext.publishEvent(event); return user; } } 在 service 中注入 ApplicationContext,在业务代码处理完之后,通过 ApplicationContext 对象手动发布 MyEvent 事件,这样我们自定义的监听器就能监听到,然后处理监听器中写好的业务逻辑。 最后,在 Controller 中写一个接口来测试一下: @GetMapping("/request") public String getRequestInfo(HttpServletRequest request) { System.out.println("requestListener中的初始化的name数据:" + request.getAttribute("name")); return "success"; } 在浏览器中输入 http://localhost:8080/listener/publish,然后观察一下控制台打印的用户名和密码,即可说明自定义监听器已经生效。 4. 总结 本课系统的介绍了监听器原理,以及在 Spring Boot 中如何使用监听器,列举了监听器的三个常用的案例,有很好的实战意义。最后讲解了项目中如何自定义事件和监听器,并结合微服务中常见的场景,给出具体的代码模型,均能运用到实际项目中去,希望读者认真消化。 第13课:Spring Boot中使用拦截器 拦截器的原理很简单,是 AOP 的一种实现,专门拦截对动态资源的后台请求,即拦截对控制层的请求。使用场景比较多的是判断用户是否有权限请求后台,更拔高一层的使用场景也有,比如拦截器可以结合 websocket 一起使用,用来拦截 websocket 请求,然后做相应的处理等等。拦截器不会拦截静态资源,Spring Boot 的默认静态目录为 resources/static,该目录下的静态页面、js、css、图片等等,不会被拦截(也要看如何实现,有些情况也会拦截,我在下文会指出)。 1. 拦截器的快速使用 使用拦截器很简单,只需要两步即可:定义拦截器和配置拦截器。在配置拦截器中,Spring Boot 2.0 以后的版本和之前的版本有所不同,我会重点讲解一下这里可能出现的坑。 1.1 定义拦截器 定义拦截器,只需要实现 HandlerInterceptor 接口,HandlerInterceptor 接口是所有自定义拦截器或者 Spring Boot 提供的拦截器的鼻祖,所以,首先来了解下该接口。该接口中有三个方法: preHandle(……)、postHandle(……) 和 afterCompletion(……) 。 preHandle(……) 方法:该方法的执行时机是,当某个 url 已经匹配到对应的 Controller 中的某个方法,且在这个方法执行之前。所以 preHandle(……) 方法可以决定是否将请求放行,这是通过返回值来决定的,返回 true 则放行,返回 false 则不会向后执行。 postHandle(……) 方法:该方法的执行时机是,当某个 url 已经匹配到对应的 Controller 中的某个方法,且在执行完了该方法,但是在 DispatcherServlet 视图渲染之前。所以在这个方法中有个 ModelAndView 参数,可以在此做一些修改动作。 afterCompletion(……) 方法:顾名思义,该方法是在整个请求处理完成后(包括视图渲染)执行,这时做一些资源的清理工作,这个方法只有在 preHandle(……) 被成功执行后并且返回 true 才会被执行。 了解了该接口,接下来自定义一个拦截器。 /** * 自定义拦截器 * @author shengwu ni * @date 2018/08/03 */ public class MyInterceptor implements HandlerInterceptor { private static final Logger logger = LoggerFactory.getLogger(MyInterceptor.class); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); String methodName = method.getName(); logger.info("====拦截到了方法:{},在该方法执行之前执行====", methodName); // 返回true才会继续执行,返回false则取消当前请求 return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { logger.info("执行完方法之后进执行(Controller方法调用之后),但是此时还没进行视图渲染"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { logger.info("整个请求都处理完咯,DispatcherServlet也渲染了对应的视图咯,此时我可以做一些清理的工作了"); } } OK,到此为止,拦截器已经定义完成,接下来就是对该拦截器进行拦截配置。 1.2 配置拦截器 在 Spring Boot 2.0 之前,我们都是直接继承 WebMvcConfigurerAdapter 类,然后重写 addInterceptors 方法来实现拦截器的配置。但是在 Spring Boot 2.0 之后,该方法已经被废弃了(当然,也可以继续用),取而代之的是 WebMvcConfigurationSupport 方法,如下: @Configuration public class MyInterceptorConfig extends WebMvcConfigurationSupport { @Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**"); super.addInterceptors(registry); } } 在该配置中重写 addInterceptors 方法,将我们上面自定义的拦截器添加进去,addPathPatterns 方法是添加要拦截的请求,这里我们拦截所有的请求。这样就配置好拦截器了,接下来写一个 Controller 测试一下: @Controller @RequestMapping("/interceptor") public class InterceptorController { @RequestMapping("/test") public String test() { return "hello"; } } 让其跳转到 hello.html 页面,直接在 hello.html 中输出 hello interceptor 即可。启动项目,在浏览器中输入 localhost:8080/interceptor/test 看一下控制台的日志: ====拦截到了方法:test,在该方法执行之前执行==== 执行完方法之后进执行(Controller方法调用之后),但是此时还没进行视图渲染 整个请求都处理完咯,DispatcherServlet也渲染了对应的视图咯,此时我可以做一些清理的工作了 可以看出拦截器已经生效,并能看出其执行顺序。 1.3 解决静态资源被拦截问题 上文中已经介绍了拦截器的定义和配置,但是这样是否就没问题了呢?其实不然,如果使用上面这种配置的话,我们会发现一个缺陷,那就是静态资源被拦截了。可以在 resources/static/ 目录下放置一个图片资源或者 html 文件,然后启动项目直接访问,即可看到无法访问的现象。 也就是说,虽然 Spring Boot 2.0 废弃了WebMvcConfigurerAdapter,但是 WebMvcConfigurationSupport 又会导致默认的静态资源被拦截,这就需要我们手动将静态资源放开。 如何放开呢?除了在 MyInterceptorConfig 配置类中重写 addInterceptors 方法外,还需要再重写一个方法:addResourceHandlers,将静态资源放开: /** * 用来指定静态资源不被拦截,否则继承WebMvcConfigurationSupport这种方式会导致静态资源无法直接访问 * @param registry */ @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations("classpath:/static/"); super.addResourceHandlers(registry); } 这样配置好之后,重启项目,静态资源也可以正常访问了。如果你是个善于学习或者研究的人,那肯定不会止步于此,没错,上面这种方式的确能解决静态资源无法访问的问题,但是,还有更方便的方式来配置。 我们不继承 WebMvcConfigurationSupport 类,直接实现 WebMvcConfigurer 接口,然后重写 addInterceptors 方法,将自定义的拦截器添加进去即可,如下: @Configuration public class MyInterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // 实现WebMvcConfigurer不会导致静态资源被拦截 registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**"); } } 这样就非常方便了,实现 WebMvcConfigure 接口的话,不会拦截 Spring Boot 默认的静态资源。 这两种方式都可以,具体他们之间的细节,感兴趣的读者可以做进一步的研究,由于这两种方式的不同,继承 WebMvcConfigurationSupport 类的方式可以用在前后端分离的项目中,后台不需要访问静态资源(就不需要放开静态资源了);实现 WebMvcConfigure 接口的方式可以用在非前后端分离的项目中,因为需要读取一些图片、css、js文件等等。 2. 拦截器使用实例 2.1 判断用户有没有登录 一般用户登录功能我们可以这么做,要么往 session 中写一个 user,要么针对每个 user 生成一个 token,第二种要更好一点,那么针对第二种方式,如果用户登录成功了,每次请求的时候都会带上该用户的 token,如果未登录,则没有该 token,服务端可以检测这个 token 参数的有无来判断用户有没有登录,从而实现拦截功能。我们改造一下 preHandle 方法,如下: @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); String methodName = method.getName(); logger.info("====拦截到了方法:{},在该方法执行之前执行====", methodName); // 判断用户有没有登陆,一般登陆之后的用户都有一个对应的token String token = request.getParameter("token"); if (null == token || "".equals(token)) { logger.info("用户未登录,没有权限执行……请登录"); return false; } // 返回true才会继续执行,返回false则取消当前请求 return true; } 重启项目,在浏览器中输入 localhost:8080/interceptor/test 后查看控制台日志,发现被拦截,如果在浏览器中输入 localhost:8080/interceptor/test?token=123 即可正常往下走。 2.2 取消拦截操作 根据上文,如果我要拦截所有 /admin 开头的 url 请求的话,需要在拦截器配置中添加这个前缀,但是在实际项目中,可能会有这种场景出现:某个请求也是 /admin 开头的,但是不能拦截,比如 /admin/login 等等,这样的话又需要去配置。那么,可不可以做成一个类似于开关的东西,哪里不需要拦截,我就在哪里弄个开关上去,做成这种灵活的可插拔的效果呢? 是可以的,我们可以定义一个注解,该注解专门用来取消拦截操作,如果某个 Controller 中的方法我们不需要拦截掉,即可在该方法上加上我们自定义的注解即可,下面先定义一个注解: /** * 该注解用来指定某个方法不用拦截 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface UnInterception { } 然后在 Controller 中的某个方法上添加该注解,在拦截器处理方法中添加该注解取消拦截的逻辑,如下: @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); String methodName = method.getName(); logger.info("====拦截到了方法:{},在该方法执行之前执行====", methodName); // 通过方法,可以获取该方法上的自定义注解,然后通过注解来判断该方法是否要被拦截 // @UnInterception 是我们自定义的注解 UnInterception unInterception = method.getAnnotation(UnInterception.class); if (null != unInterception) { return true; } // 返回true才会继续执行,返回false则取消当前请求 return true; } Controller 中的方法代码可以参见源码,重启项目在浏览器中输入 http://localhost:8080/interceptor/test2?token=123 测试一下,可以看出,加了该注解的方法不会被拦截。 3. 总结 本节主要介绍了 Spring Boot 中拦截器的使用,从拦截器的创建、配置,到拦截器对静态资源的影响,都做了详细的分析。Spring Boot 2.0 之后拦截器的配置支持两种方式,可以根据实际情况选择不同的配置方式。最后结合实际中的使用,举了两个常用的场景,希望读者能够认真消化,掌握拦截器的使用。 ———————————————— 原文链接:https://blog.csdn.net/cuiqwei/article/details/118188540
-
Spring Boot中的MVC支持 Spring Boot 的 MVC 支持主要来介绍实际项目中最常用的几个注解,包括 @RestController、 @RequestMapping、@PathVariable、@RequestParam 以及 @RequestBody。主要介绍这几个注解常用的使用方式和特点。 1. @RestController @RestController 是 Spring Boot 新增的一个注解,我们看一下该注解都包含了哪些东西。 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Controller @ResponseBody public @interface RestController { String value() default ""; } 可以看出, @RestController 注解包含了原来的 @Controller 和 @ResponseBody 注解,使用过 Spring 的朋友对 @Controller 注解已经非常了解了,这里不再赘述, @ResponseBody 注解是将返回的数据结构转换为 Json 格式。所以 @RestController 可以看作是 @Controller 和 @ResponseBody 的结合体,相当于偷个懒,我们使用 @RestController 之后就不用再使用 @Controller 了。但是需要注意一个问题:如果是前后端分离,不用模板渲染的话,比如 Thymeleaf,这种情况下是可以直接使用@RestController 将数据以 json 格式传给前端,前端拿到之后解析;但如果不是前后端分离,需要使用模板来渲染的话,一般 Controller 中都会返回到具体的页面,那么此时就不能使用@RestController了,比如: public String getUser() { return "user"; } 其实是需要返回到 user.html 页面的,如果使用 @RestController 的话,会将 user 作为字符串返回的,所以这时候我们需要使用 @Controller 注解。这在下一节 Spring Boot 集成 Thymeleaf 模板引擎中会再说明。 2. @RequestMapping @RequestMapping 是一个用来处理请求地址映射的注解,它可以用于类上,也可以用于方法上。在类的级别上的注解会将一个特定请求或者请求模式映射到一个控制器之上,表示类中的所有响应请求的方法都是以该地址作为父路径;在方法的级别表示进一步指定到处理方法的映射关系。 该注解有6个属性,一般在项目中比较常用的有三个属性:value、method 和 produces。 value 属性:指定请求的实际地址,value 可以省略不写 method 属性:指定请求的类型,主要有 GET、PUT、POST、DELETE,默认为 GET produces属性:指定返回内容类型,如 produces = “application/json; charset=UTF-8” @RequestMapping 注解比较简单,举个例子: @RestController @RequestMapping(value = "/test", produces = "application/json; charset=UTF-8") public class TestController { @RequestMapping(value = "/get", method = RequestMethod.GET) public String testGet() { return "success"; } } 这个很简单,启动项目在浏览器中输入 localhost:8080/test/get 测试一下即可。 针对四种不同的请求方式,是有相应注解的,不用每次在 @RequestMapping 注解中加 method 属性来指定,上面的 GET 方式请求可以直接使用 @GetMapping("/get") 注解,效果一样。相应地,PUT 方式、POST 方式和 DELETE 方式对应的注解分别为 @PutMapping、@PostMapping 和 DeleteMapping。 3. @PathVariable @PathVariable 注解主要是用来获取 url 参数,Spring Boot 支持 restfull 风格的 url,比如一个 GET 请求携带一个参数 id 过来,我们将 id 作为参数接收,可以使用 @PathVariable 注解。如下: @GetMapping("/user/{id}") public String testPathVariable(@PathVariable Integer id) { System.out.println("获取到的id为:" + id); return "success"; } 这里需要注意一个问题,如果想要 url 中占位符中的 id 值直接赋值到参数 id 中,需要保证 url 中的参数和方法接收参数一致,否则就无法接收。如果不一致的话,其实也可以解决,需要用 @PathVariable 中的 value 属性来指定对应关系。如下: @RequestMapping("/user/{idd}") public String testPathVariable(@PathVariable(value = "idd") Integer id) { System.out.println("获取到的id为:" + id); return "success"; } 对于访问的 url,占位符的位置可以在任何位置,不一定非要在最后,比如这样也行:/xxx/{id}/user。另外,url 也支持多个占位符,方法参数使用同样数量的参数来接收,原理和一个参数是一样的,例如: @GetMapping("/user/{idd}/{name}") public String testPathVariable(@PathVariable(value = "idd") Integer id, @PathVariable String name) { System.out.println("获取到的id为:" + id); System.out.println("获取到的name为:" + name); return "success"; } 运行项目,在浏览器中请求 localhost:8080/test/user/2/zhangsan 可以看到控制台输出如下信息: 获取到的id为:2 获取到的name为:zhangsan 所以支持多个参数的接收。同样地,如果 url 中的参数和方法中的参数名称不同的话,也需要使用 value 属性来绑定两个参数。 4. @RequestParam @RequestParam 注解顾名思义,也是获取请求参数的,上面我们介绍了 @PathValiable 注解也是获取请求参数的,那么 @RequestParam 和 @PathVariable 有什么不同呢?主要区别在于: @PathValiable 是从 url 模板中获取参数值, 即这种风格的 url:http://localhost:8080/user/{id} ;而 @RequestParam 是从 request 里面获取参数值,即这种风格的 url:http://localhost:8080/user?id=1 。我们使用该 url 带上参数 id 来测试一下如下代码: @GetMapping("/user") public String testRequestParam(@RequestParam Integer id) { System.out.println("获取到的id为:" + id); return "success"; } 可以正常从控制台打印出 id 信息。同样地,url 上面的参数和方法的参数需要一致,如果不一致,也需要使用 value 属性来说明,比如 url 为:http://localhost:8080/user?idd=1 @RequestMapping("/user") public String testRequestParam(@RequestParam(value = "idd", required = false) Integer id) { System.out.println("获取到的id为:" + id); return "success"; } 除了 value 属性外,还有个两个属性比较常用: required 属性:true 表示该参数必须要传,否则就会报 404 错误,false 表示可有可无。 defaultValue 属性:默认值,表示如果请求中没有同名参数时的默认值。 从 url 中可以看出,@RequestParam 注解用于 GET 请求上时,接收拼接在 url 中的参数。除此之外,该注解还可以用于 POST 请求,接收前端表单提交的参数,假如前端通过表单提交 username 和 password 两个参数,那我们可以使用 @RequestParam 来接收,用法和上面一样。 @PostMapping("/form1") public String testForm(@RequestParam String username, @RequestParam String password) { System.out.println("获取到的username为:" + username); System.out.println("获取到的password为:" + password); return "success"; } 我们使用 postman 来模拟一下表单提交,测试一下接口: 那么问题来了,如果表单数据很多,我们不可能在后台方法中写上很多参数,每个参数还要 @RequestParam 注解。针对这种情况,我们需要封装一个实体类来接收这些参数,实体中的属性名和表单中的参数名一致即可。 public class User { private String username; private String password; // set get } 使用实体接收的话,我们不能在前面加 @RequestParam 注解了,直接使用即可。 @PostMapping("/form2") public String testForm(User user) { System.out.println("获取到的username为:" + user.getUsername()); System.out.println("获取到的password为:" + user.getPassword()); return "success"; } 使用 postman 再次测试一下表单提交,观察一下返回值和控制台打印出的日志即可。在实际项目中,一般都是封装一个实体类来接收表单数据,因为实际项目中表单数据一般都很多。 5. @RequestBody @RequestBody 注解用于接收前端传来的实体,接收参数也是对应的实体,比如前端通过 json 提交传来两个参数 username 和 password,此时我们需要在后端封装一个实体来接收。在传递的参数比较多的情况下,使用 @RequestBody 接收会非常方便。例如: public class User { private String username; private String password; // set get } @PostMapping("/user") public String testRequestBody(@RequestBody User user) { System.out.println("获取到的username为:" + user.getUsername()); System.out.println("获取到的password为:" + user.getPassword()); return "success"; } 我们使用 postman 工具来测试一下效果,打开 postman,然后输入请求地址和参数,参数我们用 json 来模拟,如下图所有,调用之后返回 success。 同时看一下后台控制台输出的日志: 获取到的username为:测试 获取到的password为:123456 可以看出,@RequestBody 注解用于 POST 请求上,接收 json 实体参数。它和上面我们介绍的表单提交有点类似,只不过参数的格式不同,一个是 json 实体,一个是表单提交。在实际项目中根据具体场景和需要使用对应的注解即可。 6. 总结 本节课主要讲解了 Spring Boot 中对 MVC 的支持,分析了 @RestController、 @RequestMapping、@PathVariable、 @RequestParam 和 @RequestBody 四个注解的使用方式,由于 @RestController 中集成了 @ResponseBody 所以对返回 json 的注解不再赘述。以上四个注解是使用频率很高的注解,在所有的实际项目中基本都会遇到,要熟练掌握。 Spring Boot集成 Swagger2 展现在线接口文档 1. Swagger 简介 1.1 解决的问题 随着互联网技术的发展,现在的网站架构基本都由原来的后端渲染,变成了前后端分离的形态,而且前端技术和后端技术在各自的道路上越走越远。前端和后端的唯一联系,变成了 API 接口,所以 API 文档变成了前后端开发人员联系的纽带,变得越来越重要。 那么问题来了,随着代码的不断更新,开发人员在开发新的接口或者更新旧的接口后,由于开发任务的繁重,往往文档很难持续跟着更新,Swagger 就是用来解决该问题的一款重要的工具,对使用接口的人来说,开发人员不需要给他们提供文档,只要告诉他们一个 Swagger 地址,即可展示在线的 API 接口文档,除此之外,调用接口的人员还可以在线测试接口数据,同样地,开发人员在开发接口时,同样也可以利用 Swagger 在线接口文档测试接口数据,这给开发人员提供了便利。 1.2 Swagger 官方 我们打开 Swagger 官网,官方对 Swagger 的定义为: The Best APIs are Built with Swagger Tools 翻译成中文是:“最好的 API 是使用 Swagger 工具构建的”。由此可见,Swagger 官方对其功能和所处的地位非常自信,由于其非常好用,所以官方对其定位也合情合理。本文主要讲解在 Spring Boot 中如何导入 Swagger2 工具来展现项目中的接口文档。本节课使用的 Swagger 版本为 2.2.2。下面开始进入 Swagger2 之旅。 2. Swagger2 的 maven 依赖 使用 Swagger2 工具,必须要导入 maven 依赖,当前官方最高版本是 2.8.0,我尝试了一下,个人感觉页面展示的效果不太好,而且不够紧凑,不利于操作。另外,最新版本并不一定是最稳定版本,当前我们实际项目中使用的是 2.2.2 版本,该版本稳定,界面友好,所以本节课主要围绕着 2.2.2 版本来展开,依赖如下: <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.2.2</version> </dependency> 3. Swagger2 的配置 使用 Swagger2 需要进行配置,Spring Boot 中对 Swagger2 的配置非常方便,新建一个配置类,Swagger2 的配置类上除了添加必要的 @Configuration 注解外,还需要添加 @EnableSwagger2 注解。 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.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; /** * @author shengwu ni */ @Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) // 指定构建api文档的详细信息的方法:apiInfo() .apiInfo(apiInfo()) .select() // 指定要生成api接口的包路径,这里把controller作为包路径,生成controller中的所有接口 .apis(RequestHandlerSelectors.basePackage("com.itcodai.course06.controller")) .paths(PathSelectors.any()) .build(); } /** * 构建api文档的详细信息 * @return */ private ApiInfo apiInfo() { return new ApiInfoBuilder() // 设置页面标题 .title("Spring Boot集成Swagger2接口总览") // 设置接口描述 .description("跟一起学Spring Boot第06课") // 设置联系方式 .contact("测试," + "www.baidu.com") // 设置版本 .version("1.0") // 构建 .build(); } } 在该配置类中,已经使用注释详细解释了每个方法的作用了,在此不再赘述。到此为止,我们已经配置好了 Swagger2 了。现在我们可以测试一下配置有没有生效,启动项目,在浏览器中输入 localhost:8080/swagger-ui.html,即可看到 swagger2 的接口页面,如下图所示,说明Swagger2 集成成功。 结合该图,对照上面的 Swagger2 配置文件中的配置,可以很明确的知道配置类中每个方法的作用。这样就很容易理解和掌握 Swagger2 中的配置了,也可以看出,其实 Swagger2 配置很简单。 【友情提示】可能有很多朋友在配置 Swagger 的时候会遇到下面的情况,而且还关不掉的,这是因为浏览器缓存引起的,清空一下浏览器缓存即可解决问题。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gBvZvwz7-1595163751524)(http://p99jlm9k5.bkt.clouddn.com/blog/images/1/error.png)] 4. Swagger2 的使用 上面我们已经配置好了 Swagger2,并且也启动测试了一下,功能正常,下面我们开始使用 Swagger2,主要来介绍 Swagger2 中的几个常用的注解,分别在实体类上、 Controller 类上以及 Controller 中的方法上,最后我们看一下 Swagger2 是如何在页面上呈现在线接口文档的,并且结合 Controller 中的方法在接口中测试一下数据。 4.1 实体类注解 本节我们建一个 User 实体类,主要介绍一下 Swagger2 中的 @ApiModel 和 @ApiModelProperty 注解,同时为后面的测试做准备。 import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @ApiModel(value = "用户实体类") public class User { @ApiModelProperty(value = "用户唯一标识") private Long id; @ApiModelProperty(value = "用户姓名") private String username; @ApiModelProperty(value = "用户密码") private String password; // 省略set和get方法 } 解释下 @ApiModel 和 @ApiModelProperty 注解: @ApiModel 注解用于实体类,表示对类进行说明,用于参数用实体类接收。 @ApiModelProperty 注解用于类中属性,表示对 model 属性的说明或者数据操作更改。 该注解在在线 API 文档中的具体效果在下文说明。 4.2 Controller 类中相关注解 我们写一个 TestController,再写几个接口,然后学习一下 Controller 中和 Swagger2 相关的注解。 import com.itcodai.course06.entiy.JsonResult; import com.itcodai.course06.entiy.User; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/swagger") @Api(value = "Swagger2 在线接口文档") public class TestController { @GetMapping("/get/{id}") @ApiOperation(value = "根据用户唯一标识获取用户信息") public JsonResult<User> getUserInfo(@PathVariable @ApiParam(value = "用户唯一标识") Long id) { // 模拟数据库中根据id获取User信息 User user = new User(id, "测试", "123456"); return new JsonResult(user); } } 我们来学习一下 @Api 、 @ApiOperation 和 @ApiParam 注解。 @Api 注解用于类上,表示标识这个类是 swagger 的资源。 @ApiOperation 注解用于方法,表示一个 http 请求的操作。 @ApiParam 注解用于参数上,用来标明参数信息。 这里返回的是 JsonResult,是第02课中学习返回 json 数据时封装的实体。以上是 Swagger 中最常用的 5 个注解,接下来运行一下项目工程,在浏览器中输入 localhost:8080/swagger-ui.html 看一下 Swagger 页面的接口状态。 可以看出,Swagger 页面对该接口的信息展示的非常全面,每个注解的作用以及展示的地方在上图中已经标明,通过页面即可知道该接口的所有信息,那么我们直接在线测试一下该接口返回的信息,输入id为1,看一下返回数据: 可以看出,直接在页面返回了 json 格式的数据,开发人员可以直接使用该在线接口来测试数据的正确与否,非常方便。上面是对于单个参数的输入,如果输入参数为某个对象这种情况,Swagger 是什么样子呢?我们再写一个接口。 @PostMapping("/insert") @ApiOperation(value = "添加用户信息") public JsonResult<Void> insertUser(@RequestBody @ApiParam(value = "用户信息") User user) { // 处理添加逻辑 return new JsonResult<>(); } 重启项目,在浏览器中输入 localhost:8080/swagger-ui.html 看一下效果: 5. 总结 OK,本节课详细分析了 Swagger 的优点,以及 Spring Boot 如何集成 Swagger2,包括配置,相关注解的讲解,涉及到了实体类和接口类,以及如何使用。最后通过页面测试,体验了 Swagger 的强大之处,基本上是每个项目组中必备的工具之一,所以要掌握该工具的使用,也不难。 第07课:Spring Boot集成Thymeleaf模板引擎 1. Thymeleaf 介绍 Thymeleaf 是适用于 Web 和独立环境的现代服务器端 Java 模板引擎。 Thymeleaf 的主要目标是为您的开发工作流程带来优雅的自然模板 - 可以在浏览器中正确显示的HTML,也可以用作静态原型,从而在开发团队中实现更强大的协作。 以上翻译自 Thymeleaf 官方网站。传统的 JSP+JSTL 组合是已经过去了,Thymeleaf 是现代服务端的模板引擎,与传统的 JSP 不同,Thymeleaf 可以使用浏览器直接打开,因为可以忽略掉拓展属性,相当于打开原生页面,给前端人员也带来一定的便利。 什么意思呢?就是说在本地环境或者有网络的环境下,Thymeleaf 均可运行。由于 thymeleaf 支持 html 原型,也支持在 html 标签里增加额外的属性来达到 “模板+数据” 的展示方式,所以美工可以直接在浏览器中查看页面效果,当服务启动后,也可以让后台开发人员查看带数据的动态页面效果。比如: <div class="ui right aligned basic segment"> <div class="ui orange basic label" th:text="${blog.flag}">静态原创信息</div> </div> <h2 class="ui center aligned header" th:text="${blog.title}">这是静态标题</h2> 类似与上面这样,在静态页面时,会展示静态信息,当服务启动后,动态获取数据库中的数据后,就可以展示动态数据,th:text 标签是用来动态替换文本的,这会在下文说明。该例子说明浏览器解释 html 时会忽略 html 中未定义的标签属性(比如 th:text),所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示数据。 2. 依赖导入 在 Spring Boot 中使用 thymeleaf 模板需要引入依赖,可以在创建项目工程时勾选 Thymeleaf,也可以创建之后再手动导入,如下: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> 另外,在 html 页面上如果要使用 thymeleaf 模板,需要在页面标签中引入: <html xmlns:th="http://www.thymeleaf.org"> 1 3. Thymeleaf相关配置 因为 Thymeleaf 中已经有默认的配置了,我们不需要再对其做过多的配置,有一个需要注意一下,Thymeleaf 默认是开启页面缓存的,所以在开发的时候,需要关闭这个页面缓存,配置如下。 spring: thymeleaf: cache: false #关闭缓存 否则会有缓存,导致页面没法及时看到更新后的效果。 比如你修改了一个文件,已经 update 到 tomcat 了,但刷新页面还是之前的页面,就是因为缓存引起的。 4. Thymeleaf 的使用 4.1 访问静态页面 这个和 Thymeleaf 没啥关系,应该说是通用的,我把它一并写到这里的原因是一般我们做网站的时候,都会做一个 404 页面和 500 页面,为了出错时给用户一个友好的展示,而不至于一堆异常信息抛出来。Spring Boot 中会自动识别模板目录(templates/)下的 404.html 和 500.html 文件。我们在 templates/ 目录下新建一个 error 文件夹,专门放置错误的 html 页面,然后分别打印些信息。以 404.html 为例: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> 这是404页面 </body> </html> 原文链接:https://blog.csdn.net/cuiqwei/article/details/118188540
-
1. Spring Boot是什么 我们知道,从 2002 年开始,Spring 一直在飞速的发展,如今已经成为了在Java EE(Java Enterprise Edition)开发中真正意义上的标准,但是随着技术的发展,Java EE使用 Spring 逐渐变得笨重起来,大量的 XML 文件存在于项目之中。繁琐的配置,整合第三方框架的配置问题,导致了开发和部署效率的降低。 2012 年 10 月,Mike Youngstrom 在 Spring jira 中创建了一个功能请求,要求在 Spring 框架中支持无容器 Web 应用程序体系结构。他谈到了在主容器引导 spring 容器内配置 Web 容器服务。这是 jira 请求的摘录: 我认为 Spring 的 Web 应用体系结构可以大大简化,如果它提供了从上到下利用 Spring 组件和配置模型的工具和参考体系结构。在简单的 main()方法引导的 Spring 容器内嵌入和统一这些常用Web 容器服务的配置。 这一要求促使了 2013 年初开始的 Spring Boot 项目的研发,到今天,Spring Boot 的版本已经到了 2.0.3 RELEASE。Spring Boot 并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。 它集成了大量常用的第三方库配置,Spring Boot应用中这些第三方库几乎可以是零配置的开箱即用(out-of-the-box),大部分的 Spring Boot 应用都只需要非常少量的配置代码(基于 Java 的配置),开发者能够更加专注于业务逻辑。 2. 为什么学习Spring Boot 2.1 从Spring官方来看 我们打开 Spring 的官方网站,可以看到下图: 我们可以看到图中官方对 Spring Boot 的定位:Build Anything, Build任何东西。Spring Boot旨在尽可能快地启动和运行,并且只需最少的 Spring 前期配置。 同时我们也来看一下官方对后面两个的定位: SpringCloud:Coordinate Anything,协调任何事情; SpringCloud Data Flow:Connect everything,连接任何东西。 仔细品味一下,Spring 官网对 Spring Boot、SpringCloud 和 SpringCloud Data Flow三者定位的措辞非常有味道,同时也可以看出,Spring 官方对这三个技术非常重视,是现在以及今后学习的重点(SpringCloud 相关达人课课程届时也会上线)。 2.2 从Spring Boot的优点来看 Spring Boot 有哪些优点?主要给我们解决了哪些问题呢?我们以下图来说明: 2.2.1 良好的基因 Spring Boot 是伴随着 Spring 4.0 诞生的,从字面理解,Boot是引导的意思,因此 Spring Boot 旨在帮助开发者快速搭建 Spring 框架。Spring Boot 继承了原有 Spring 框架的优秀基因,使 Spring 在使用中更加方便快捷。 2.2.2 简化编码 举个例子,比如我们要创建一个 web 项目,使用 Spring 的朋友都知道,在使用 Spring 的时候,需要在 pom 文件中添加多个依赖,而 Spring Boot 则会帮助开发着快速启动一个 web 容器,在 Spring Boot 中,我们只需要在 pom 文件中添加如下一个 starter-web 依赖即可。 我们点击进入该依赖后可以看到,Spring Boot 这个 starter-web 已经包含了多个依赖,包括之前在 Spring 工程中需要导入的依赖,我们看一下其中的一部分,如下: 由此可以看出,Spring Boot 大大简化了我们的编码,我们不用一个个导入依赖,直接一个依赖即可。 2.2.3 简化配置 Spring 虽然使Java EE轻量级框架,但由于其繁琐的配置,一度被人认为是“配置地狱”。各种XML、Annotation配置会让人眼花缭乱,而且配置多的话,如果出错了也很难找出原因。Spring Boot更多的是采用 Java Config 的方式,对 Spring 进行配置。举个例子: 我新建一个类,但是我不用 @Service注解,也就是说,它是个普通的类,那么我们如何使它也成为一个 Bean 让 Spring 去管理呢?只需要@Configuration 和@Bean两个注解即可,如下: @Configuration表示该类是个配置类,@Bean表示该方法返回一个 Bean。这样就把TestService作为 Bean 让 Spring 去管理了,在其他地方,我们如果需要使用该 Bean,和原来一样,直接使用@Resource注解注入进来即可使用,非常方便。 @Resource private TestService testService; 另外,部署配置方面,原来 Spring 有多个 xml 和 properties配置,在 Spring Boot 中只需要个 application.yml即可。 2.2.4 简化部署 在使用 Spring 时,项目部署时需要我们在服务器上部署 tomcat,然后把项目打成 war 包扔到 tomcat里,在使用 Spring Boot 后,我们不需要在服务器上去部署 tomcat,因为 Spring Boot 内嵌了 tomcat,我们只需要将项目打成 jar 包,使用 java -jar xxx.jar一键式启动项目。 另外,也降低对运行环境的基本要求,环境变量中有JDK即可。 2.2.5 简化监控 我们可以引入 spring-boot-start-actuator 依赖,直接使用 REST 方式来获取进程的运行期性能参数,从而达到监控的目的,比较方便。但是 Spring Boot 只是个微框架,没有提供相应的服务发现与注册的配套功能,没有外围监控集成方案,没有外围安全管理方案,所以在微服务架构中,还需要 Spring Cloud 来配合一起使用。 2.3 从未来发展的趋势来看 微服务是未来发展的趋势,项目会从传统架构慢慢转向微服务架构,因为微服务可以使不同的团队专注于更小范围的工作职责、使用独立的技术、更安全更频繁地部署。而 继承了 Spring 的优良特性,与 Spring 一脉相承,而且 支持各种REST API 的实现方式。Spring Boot 也是官方大力推荐的技术,可以看出,Spring Boot 是未来发展的一个大趋势。 3. 本课程能学到什么 本课程使用目前 Spring Boot 最新版本2.0.3 RELEASE,课程文章均为作者在实际项目中剥离出来的场景和demo,目标是带领学习者快速上手 Spring Boot,将 Spring Boot 相关技术点快速运用在微服务项目中。全篇分为两部分:基础篇和进阶篇。 基础篇(01—10课)主要介绍 Spring Boot 在项目中最常使用的一些功能点,旨在带领学习者快速掌握 Spring Boot 在开发时需要的知识点,能够把 Spring Boot 相关技术运用到实际项目架构中去。该部分以 Spring Boot 框架为主线,内容包括Json数据封装、日志记录、属性配置、MVC支持、在线文档、模板引擎、异常处理、AOP 处理、持久层集成等等。 进阶篇(11—17课)主要是介绍 Spring Boot 在项目中拔高一些的技术点,包括集成的一些组件,旨在带领学习者在项目中遇到具体的场景时能够快速集成,完成对应的功能。该部分以 Spring Boot 框架为主线,内容包括拦截器、监听器、缓存、安全认证、分词插件、消息队列等等。 认真读完该系列文章之后,学习者会快速了解并掌握 Spring Boot 在项目中最常用的技术点,作者课程的最后,会基于课程内容搭建一个 Spring Boot 项目的空架构,该架构也是从实际项目中剥离出来,学习者可以运用该架构于实际项目中,具备使用 Spring Boot 进行实际项目开发的能力。 5. 本课程开发环境和插件 第01课:Spring Boot开发环境搭建和项目启动 上一节对 SpringBoot 的特性做了一个介绍,本节主要对 jdk 的配置、Spring Boot工程的构建和项目的启动、Spring Boot 项目工程的结构做一下讲解和分析。 1. jdk 的配置 本课程是使用 IDEA 进行开发,在IDEA 中配置 jdk 的方式很简单,打开File->Project Structure 选择 SDKs 在 JDK home path 中选择本地 jdk 的安装目录 在 Name 中为 jdk 自定义名字 通过以上三步骤,即可导入本地安装的 jdk。如果是使用 STS 或者 eclipse 的朋友,可以通过两步骤添加: window->preference->java->Instralled JRES来添加本地 jdk。 window-->preference-->java-->Compiler选择 jre,和 jdk 保持一致。 2. Spring Boot 工程的构建 2.1 IDEA 快速构建 IDEA 中可以通过File->New->Project来快速构建 Spring Boot 工程。如下,选择 Spring Initializr,在 Project SDK 中选择刚刚我们导入的 jdk,点击 Next,到了项目的配置信息。 Group:填企业域名,本课程使用com.itcodai Artifact:填项目名称,本课程中每一课的工程名以course+课号命令,这里使用 course01 Dependencies:可以添加我们项目中所需要的依赖信息,根据实际情况来添加,本课程只需要选择 Web 即可。 2.2 官方构建 第二种方式可以通过官方构建,步骤如下: 访问 http://start.spring.io/。 在页面上输入相应的 Spring Boot 版本、Group 和 Artifact 信息以及项目依赖,然后创建项目。 解压后,使用 IDEA 导入该 maven 工程:File->New->Model from Existing Source,然后选择解压后的项目文件夹即可。如果是使用 eclipse 的朋友,可以通过Import->Existing Maven Projects->Next,然后选择解压后的项目文件夹即可。 2.3 maven配置 创建了 Spring Boot 项目之后,需要进行 maven 配置。打开File->settings,搜索 maven,配置一下本地的 maven 信息。如下: 在 Maven home directory 中选择本地 Maven 的安装路径;在 User settings file 中选择本地 Maven 的配置文件所在路径。在配置文件中,我们配置一下国内阿里的镜像,这样在下载 maven 依赖时,速度很快。 如果是使用 eclipse 的朋友,可以通过window-->preference-->Maven-->User Settings来配置,配置方式和上面一致。 2.4 编码配置 同样地,新建项目后,我们一般都需要配置编码,这点非常重要,很多初学者都会忘记这一步,所以要养成良好的习惯。 IDEA 中,仍然是打开File->settings,搜索 encoding,配置一下本地的编码信息。如下: 如果是使用 eclipse 的朋友,有两个地方需要设置一下编码: window–> perferences–>General–>Workspace,将Text file encoding改成utf-8 window–>perferences–>General–>content types,选中Text,将Default encoding填入utf-8 OK,编码设置完成即可启动项目工程了。 3. Spring Boot 项目工程结构 Spring Boot 项目总共有三个模块src/main/java路径:主要编写业务程序 src/main/resources路径:存放静态文件和配置文件 src/test/java路径:主要编写测试程序 默认情况下,如上图所示会创建一个启动类 Course01Application,该类上面有个@SpringBootApplication注解,该启动类中有个 main 方法,没错,Spring Boot 启动只要运行该 main 方法即可,非常方便。另外,Spring Boot 内部集成了 tomcat,不需要我们人为手动去配置 tomcat,开发者只需要关注具体的业务逻辑即可。 到此为止,Spring Boot 就启动成功了,为了比较清楚的看到效果,我们写一个 Controller 来测试一下,如下: 4. 总结 本节我们快速学习了如何在 IDEA 中导入 jdk,以及使用 IDEA 如何配置 maven 和编码,如何快速的创建和启动 Spring Boot 工程。IDEA 对 Spring Boot 的支持非常友好,建议大家使用 IDEA 进行 Spring Boot 的开发,从下一课开始,我们真正进入 Spring Boot 的学习中。 学习源码链接点击这里 第02课:Spring Boot返回Json数据及数据封装 在项目开发中,接口与接口之间,前后端之间数据的传输都使用 Json 格式,在 Spring Boot 中,接口返回 Json 格式的数据很简单,在 Controller 中使用@RestController注解即可返回 Json 格式的数据,@RestController也是 Spring Boot 新增的一个注解,我们点进去看一下该注解都包含了哪些东西。 可以看出, @RestController 注解包含了原来的 @Controller 和 @ResponseBody 注解,使用过 Spring 的朋友对 @Controller 注解已经非常了解了,这里不再赘述, @ResponseBody 注解是将返回的数据结构转换为 Json 格式。所以在默认情况下,使用了 @RestController 注解即可将返回的数据结构转换成 Json 格式,Spring Boot 中默认使用的 Json 解析技术框架是 jackson。我们点开 pom.xml 中的 spring-boot-starter-web 依赖,可以看到一个 spring-boot-starter-json 依赖: Spring Boot 中对依赖都做了很好的封装,可以看到很多 spring-boot-starter-xxx 系列的依赖,这是 Spring Boot 的特点之一,不需要人为去引入很多相关的依赖了,starter-xxx 系列直接都包含了所必要的依赖,所以我们再次点进去上面这个 spring-boot-starter-json 依赖,可以看到: 到此为止,我们知道了 Spring Boot 中默认使用的 json 解析框架是 jackson。下面我们看一下默认的 jackson 框架对常用数据类型的转 Json 处理。 1. Spring Boot 默认对Json的处理 在实际项目中,常用的数据结构无非有类对象、List对象、Map对象,我们看一下默认的 jackson 框架对这三个常用的数据结构转成 json 后的格式如何。 1.1 创建 User 实体类 为了测试,我们需要创建一个实体类,这里我们就用 User 来演示。 1.2 创建Controller类 然后我们创建一个 Controller,分别返回 User对象、List 和 Map。 1.3 测试不同数据类型返回的json OK,写好了接口,分别返回了一个 User 对象、一个 List 集合和一个 Map 集合,其中 Map 集合中的 value 存的是不同的数据类型。接下来我们依次来测试一下效果。 在浏览器中输入:localhost:8080/json/user 返回 json 如下: {"id":1,"username":"测试","password":"123456"} 在浏览器中输入:localhost:8080/json/list 返回 json 如下: [{"id":1,"username":"测试","password":"123456"},{"id":2,"username":"测试课","password":"123456"}] 在浏览器中输入:localhost:8080/json/map 返回 json 如下: {"作者信息":{"id":1,"username":"测试","password":"123456"},"CSDN地址":"http://blog.csdn.net/eson_15","测试数量":001,"博客地址":"http://blog.itcodai.com"} 可以看出,map 中不管是什么数据类型,都可以转成相应的 json 格式,这样就非常方便。 1.4 jackson 中对null的处理 在实际项目中,我们难免会遇到一些 null 值出现,我们转 json 时,是不希望有这些 null 出现的,比如我们期望所有的 null 在转 json 时都变成 “” 这种空字符串,那怎么做呢?在 Spring Boot 中,我们做一下配置即可,新建一个 jackson 的配置类: 然后我们修改一下上面返回 map 的接口,将几个值改成 null 测试一下: 重启项目,再次输入:localhost:8080/json/map,可以看到 jackson 已经将所有 null 字段转成了空字符串了。 {"作者信息":{"id":1,"username":"测试","password":""},"CSDN地址":"","测试数量":001,"博客地址":"http://blog.itcodai.com"} 2. 使用阿里巴巴FastJson的设置 2.1 jackson 和 fastJson 的对比 有很多朋友习惯于使用阿里巴巴的 fastJson 来做项目中 json 转换的相关工作,目前我们项目中使用的就是阿里的 fastJson,那么 jackson 和 fastJson 有哪些区别呢?根据网上公开的资料比较得到下表。 选项 fastJson jackson 上手难易程度 容易 中等 高级特性支持 中等 丰富 官方文档、Example支持 中文 英文 处理json速度 略快 快 关于 fastJson 和 jackson 的对比,网上有很多资料可以查看,主要是根据自己实际项目情况来选择合适的框架。从扩展上来看,fastJson 没有 jackson 灵活,从速度或者上手难度来看,fastJson 可以考虑,我们项目中目前使用的是阿里的 fastJson,挺方便的。 2.2 fastJson依赖导入 使用 fastJson 需要导入依赖,本课程使用 1.2.35 版本,依赖如下: 2.2 使用 fastJson 处理 null 使用 fastJson 时,对 null 的处理和 jackson 有些不同,需要继承 WebMvcConfigurationSupport 类,然后覆盖 configureMessageConverters 方法,在方法中,我们可以选择对要实现 null 转换的场景,配置好即可。如下: 3. 封装统一返回的数据结构 以上是 Spring Boot 返回 json 的几个代表的例子,但是在实际项目中,除了要封装数据之外,我们往往需要在返回的 json 中添加一些其他信息,比如返回一些状态码 code ,返回一些 msg 给调用者,这样调用者可以根据 code 或者 msg 做一些逻辑判断。所以在实际项目中,我们需要封装一个统一的 json 返回结构存储返回信息。 3.1 定义统一的 json 结构 由于封装的 json 数据的类型不确定,所以在定义统一的 json 结构时,我们需要用到泛型。统一的 json 结构中属性包括数据、状态码、提示信息即可,构造方法可以根据实际业务需求做相应的添加即可,一般来说,应该有默认的返回结构,也应该有用户指定的返回结构。如下: 3.2 修改 Controller 中的返回值类型及测试 由于 JsonResult 使用了泛型,所以所有的返回值类型都可以使用该统一结构,在具体的场景将泛型替换成具体的数据类型即可,非常方便,也便于维护。在实际项目中,还可以继续封装,比如状态码和提示信息可以定义一个枚举类型,以后我们只需要维护这个枚举类型中的数据即可(在本课程中就不展开了)。根据以上的 JsonResult,我们改写一下 Controller,如下: 我们重新在浏览器中输入:localhost:8080/jsonresult/user 返回 json 如下: {"code":"0","data":{"id":1,"password":"123456","username":"测试"},"msg":"操作成功!"} 输入:localhost:8080/jsonresult/list,返回 json 如下: {"code":"0","data":[{"id":1,"password":"123456","username":"测试"},{"id":2,"password":"123456","username":"达人课"}],"msg":"获取用户列表成功"} 输入:localhost:8080/jsonresult/map,返回 json 如下: {"code":"0","data":{"作者信息":{"id":1,"password":"","username":"测试"},"CSDN地址":null,"粉丝数量":4153,"博客地址":"http://blog.itcodai.com"},"msg":"操作成功!"} 通过封装,我们不但将数据通过 json 传给前端或者其他接口,还带上了状态码和提示信息,这在实际项目场景中应用非常广泛。 4. 总结 本节主要对 Spring Boot 中 json 数据的返回做了详细的分析,从 Spring Boot 默认的 jackson 框架到阿里巴巴的 fastJson 框架,分别对它们的配置做了相应的讲解。另外,结合实际项目情况,总结了实际项目中使用的 json 封装结构体,加入了状态码和提示信息,使得返回的 json 数据信息更加完整。 第03课:Spring Boot使用slf4j进行日志记录 在开发中,我们经常使用 System.out.println() 来打印一些信息,但是这样不好,因为大量的使用 System.out 会增加资源的消耗。我们实际项目中使用的是 slf4j 的 logback 来输出日志,效率挺高的,Spring Boot 提供了一套日志系统,logback 是最优的选择。 1. slf4j 介绍 引用百度百科里的一段话: SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统。按照官方的说法,SLF4J是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用其所希望的日志系统。 这段的大概意思是:你只需要按统一的方式写记录日志的代码,而无需关心日志是通过哪个日志系统,以什么风格输出的。因为它们取决于部署项目时绑定的日志系统。例如,在项目中使用了 slf4j 记录日志,并且绑定了 log4j(即导入相应的依赖),则日志会以 log4j 的风格输出;后期需要改为以 logback 的风格输出日志,只需要将 log4j 替换成 logback 即可,不用修改项目中的代码。这对于第三方组件的引入的不同日志系统来说几乎零学习成本,况且它的优点不仅仅这一个而已,还有简洁的占位符的使用和日志级别的判断。 正因为 sfl4j 有如此多的优点,阿里巴巴已经将 slf4j 作为他们的日志框架了。在《阿里巴巴Java开发手册(正式版)》中,日志规约一项第一条就强制要求使用 slf4j: 1.【强制】应用中不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架SLF4J中的API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。 “强制”两个字体现出了 slf4j 的优势,所以建议在实际项目中,使用 slf4j 作为自己的日志框架。使用 slf4j 记录日志非常简单,直接使用 LoggerFactory 创建即可。 2. application.yml 中对日志的配置 Spring Boot 对 slf4j 支持的很好,内部已经集成了 slf4j,一般我们在使用的时候,会对slf4j 做一下配置。application.yml 文件是 Spring Boot 中唯一一个需要配置的文件,一开始创建工程的时候是 application.properties 文件,个人比较细化用 yml 文件,因为 yml 文件的层次感特别好,看起来更直观,但是 yml 文件对格式要求比较高,比如英文冒号后面必须要有个空格,否则项目估计无法启动,而且也不报错。用 properties 还是 yml 视个人习惯而定,都可以。本课程使用 yml。 我们看一下 application.yml 文件中对日志的配置: logging: config: logbck.xml level: com.itcodai.course03.dao: trace logging.config 是用来指定项目启动的时候,读取哪个配置文件,这里指定的是日志配置文件是根路径下的 logback.xml 文件,关于日志的相关配置信息,都放在 logback.xml 文件中了。logging.level 是用来指定具体的 mapper 中日志的输出级别,上面的配置表示 com.itcodai.course03.dao 包下的所有 mapper 日志输出级别为 trace,会将操作数据库的 sql 打印出来,开发时设置成 trace 方便定位问题,在生产环境上,将这个日志级别再设置成 error 级别即可(本节课不讨论 mapper 层,在后面 Spring Boot 集成 MyBatis 时再详细讨论)。 常用的日志级别按照从高到低依次为:ERROR、WARN、INFO、DEBUG。 3. logback.xml 配置文件解析 在上面 application.yml 文件中,我们指定了日志配置文件 logback.xml,logback.xml 文件中主要用来做日志的相关配置。在 logback.xml 中,我们可以定义日志输出的格式、路径、控制台输出格式、文件大小、保存时长等等。下面来分析一下: 3.1 定义日志输出格式和存储路径 <configuration> <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" /> <property name="FILE_PATH" value="D:/logs/course03/demo.%d{yyyy-MM-dd}.%i.log" /> </configuration> 我们来看一下这个定义的含义:首先定义一个格式,命名为 “LOG_PATTERN”,该格式中 %date 表示日期,%thread 表示线程名,%-5level 表示级别从左显示5个字符宽度,%logger{36} 表示 logger 名字最长36个字符,%msg 表示日志消息,%n 是换行符。 然后再定义一下名为 “FILE_PATH” 文件路径,日志都会存储在该路径下。%i 表示第 i 个文件,当日志文件达到指定大小时,会将日志生成到新的文件里,这里的 i 就是文件索引,日志文件允许的大小可以设置,下面会讲解。这里需要注意的是,不管是 windows 系统还是 Linux 系统,日志存储的路径必须要是绝对路径。 3.2 定义控制台输出 <configuration> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <!-- 按照上面配置的LOG_PATTERN来打印日志 --> <pattern>${LOG_PATTERN}</pattern> </encoder> </appender> </configuration> 使用 节点设置个控制台输出(class="ch.qos.logback.core.ConsoleAppender")的配置,定义为 “CONSOLE”。使用上面定义好的输出格式(LOG_PATTERN)来输出,使用 ${} 引用进来即可。 3.3 定义日志文件的相关参数 使用 定义一个名为 “FILE” 的文件配置,主要是配置日志文件保存的时间、单个日志文件存储的大小、以及文件保存的路径和日志的输出格式。 3.4 定义日志输出级别 有了上面那些定义后,最后我们使用 来定义一下项目中默认的日志输出级别,这里定义级别为 INFO,然后针对 INFO 级别的日志,使用 引用上面定义好的控制台日志输出和日志文件的参数。这样 logback.xml 文件中的配置就设置完了。 4. 使用Logger在项目中打印日志 在代码中,我们一般使用 Logger 对象来打印出一些 log 信息,可以指定打印出的日志级别,也支持占位符,很方便。 启动该项目,在浏览器中输入 localhost:8080/test/log 后可以看到控制台的日志记录: ======测试日志info级别打印===== =====测试日志error级别打印==== ======测试日志warn级别打印===== ======测试:blog.csdn.net/eson_15 因为 INFO 级别比 DEBUG 级别高,所以 debug 这条没有打印出来,如果将 logback.xml 中的日志级别设置成 DEBUG,那么四条语句都会打印出来,这个大家自己去测试了。同时可以打开 D:\logs\course03\ 目录,里面有刚刚项目启动,以后后面生成的所有日志记录。在项目部署后,我们大部分都是通过查看日志文件来定位问题。 5. 总结 本节课主要对 slf4j 做了一个简单的介绍,并且对 Spring Boot 中如何使用 slf4j 输出日志做了详细的说明,着重分析了 logback.xml 文件中对日志相关信息的配置,包括日志的不同级别。最后针对这些配置,在代码中使用 Logger 打印出一些进行测试。在实际项目中,这些日志都是排查问题的过程中非常重要的资料。 第04课:Spring Boot中的项目属性配置 我们知道,在项目中,很多时候需要用到一些配置的信息,这些信息可能在测试环境和生产环境下会有不同的配置,后面根据实际业务情况有可能还会做修改,针对这种情况,我们不能将这些配置在代码中写死,最好就是写到配置文件中。比如可以把这些信息写到 application.yml 文件中。 1. 少量配置信息的情形 举个例子,在微服务架构中,最常见的就是某个服务需要调用其他服务来获取其提供的相关信息,那么在该服务的配置文件中需要配置被调用的服务地址,比如在当前服务里,我们需要调用订单微服务获取订单相关的信息,假设 订单服务的端口号是 8002,那我们可以做如下配置: server: port: 8001 # 配置微服务的地址 url: # 订单微服务的地址 orderUrl: http://localhost:8002 然后在业务代码中如何获取到这个配置的订单服务地址呢?我们可以使用 @Value 注解来解决。在对应的类中加上一个属性,在属性上使用 @Value 注解即可获取到配置文件中的配置信息,如下: @Value 注解上通过 ${key} 即可获取配置文件中和 key 对应的 value 值。我们启动一下项目,在浏览器中输入 localhost:8080/test/config 请求服务后,可以看到控制台会打印出订单服务的地址: =====获取的订单服务地址为:http://localhost:8002 说明我们成功获取到了配置文件中的订单微服务地址,在实际项目中也是这么用的,后面如果因为服务器部署的原因,需要修改某个服务的地址,那么只要在配置文件中修改即可。 2. 多个配置信息的情形 这里再引申一个问题,随着业务复杂度的增加,一个项目中可能会有越来越多的微服务,某个模块可能需要调用多个微服务获取不同的信息,那么就需要在配置文件中配置多个微服务的地址。可是,在需要调用这些微服务的代码中,如果这样一个个去使用 @Value 注解引入相应的微服务地址的话,太过于繁琐,也不科学。 所以,在实际项目中,业务繁琐,逻辑复杂的情况下,需要考虑封装一个或多个配置类。举个例子:假如在当前服务中,某个业务需要同时调用订单微服务、用户微服务和购物车微服务,分别获取订单、用户和购物车相关信息,然后对这些信息做一定的逻辑处理。那么在配置文件中,我们需要将这些微服务的地址都配置好: # 配置多个微服务的地址 url: # 订单微服务的地址 orderUrl: http://localhost:8002 # 用户微服务的地址 userUrl: http://localhost:8003 # 购物车微服务的地址 shoppingUrl: http://localhost:8004 也许实际业务中,远远不止这三个微服务,甚至十几个都有可能。对于这种情况,我们可以先定义一个 MicroServiceUrl 类来专门保存微服务的 url,如下: 细心的朋友应该可以看到,使用 @ConfigurationProperties 注解并且使用 prefix 来指定一个前缀,然后该类中的属性名就是配置中去掉前缀后的名字,一一对应即可。即:前缀名 + 属性名就是配置文件中定义的 key。同时,该类上面需要加上 @Component 注解,把该类作为组件放到Spring容器中,让 Spring 去管理,我们使用的时候直接注入即可。 需要注意的是,使用 @ConfigurationProperties 注解需要导入它的依赖: OK,到此为止,我们将配置写好了,接下来写个 Controller 来测试一下。此时,不需要在代码中一个个引入这些微服务的 url 了,直接通过 @Resource 注解将刚刚写好配置类注入进来即可使用了,非常方便。如下: 再次启动项目,请求一下可以看到,控制台打印出如下信息,说明配置文件生效,同时正确获取配置文件内容: =====获取的订单服务地址为:http://localhost:8002 =====获取的订单服务地址为:http://localhost:8002 =====获取的用户服务地址为:http://localhost:8003 =====获取的购物车服务地址为:http://localhost:8004 3. 指定项目配置文件 我们知道,在实际项目中,一般有两个环境:开发环境和生产环境。开发环境中的配置和生产环境中的配置往往不同,比如:环境、端口、数据库、相关地址等等。我们不可能在开发环境调试好之后,部署到生产环境后,又要将配置信息全部修改成生产环境上的配置,这样太麻烦,也不科学。 最好的解决方法就是开发环境和生产环境都有一套对用的配置信息,然后当我们在开发时,指定读取开发环境的配置,当我们将项目部署到服务器上之后,再指定去读取生产环境的配置。 我们新建两个配置文件: application-dev.yml 和 application-pro.yml,分别用来对开发环境和生产环境进行相关配置。这里为了方便,我们分别设置两个访问端口号,开发环境用 8001,生产环境用 8002. # 开发环境配置文件 server: port: 8001 # 开发环境配置文件 server: port: 8002 然后在 application.yml 文件中指定读取哪个配置文件即可。比如我们在开发环境下,指定读取 applicationn-dev.yml 文件,如下: spring: profiles: active: - dev 这样就可以在开发的时候,指定读取 application-dev.yml 文件,访问的时候使用 8001 端口,部署到服务器后,只需要将 application.yml 中指定的文件改成 application-pro.yml 即可,然后使用 8002 端口访问,非常方便。 4. 总结 本节课主要讲解了 Spring Boot 中如何在业务代码中读取相关配置,包括单一配置和多个配置项,在微服务中,这种情况非常常见,往往会有很多其他微服务需要调用,所以封装一个配置类来接收这些配置是个很好的处理方式。除此之外,例如数据库相关的连接参数等等,也可以放到一个配置类中,其他遇到类似的场景,都可以这么处理。最后介绍了开发环境和生产环境配置的快速切换方式,省去了项目部署时,诸多配置信息的修改。 ———————————————— 原文链接:https://blog.csdn.net/cuiqwei/article/details/118188540
上滑加载中
推荐直播
-
HDC深度解读系列 - Serverless与MCP融合创新,构建AI应用全新智能中枢2025/08/20 周三 16:30-18:00
张昆鹏 HCDG北京核心组代表
HDC2025期间,华为云展示了Serverless与MCP融合创新的解决方案,本期访谈直播,由华为云开发者专家(HCDE)兼华为云开发者社区组织HCDG北京核心组代表张鹏先生主持,华为云PaaS服务产品部 Serverless总监Ewen为大家深度解读华为云Serverless与MCP如何融合构建AI应用全新智能中枢
回顾中 -
关于RISC-V生态发展的思考2025/09/02 周二 17:00-18:00
中国科学院计算技术研究所副所长包云岗教授
中科院包云岗老师将在本次直播中,探讨处理器生态的关键要素及其联系,分享过去几年推动RISC-V生态建设实践过程中的经验与教训。
回顾中 -
一键搞定华为云万级资源,3步轻松管理企业成本2025/09/09 周二 15:00-16:00
阿言 华为云交易产品经理
本直播重点介绍如何一键续费万级资源,3步轻松管理成本,帮助提升日常管理效率!
回顾中
热门标签