# springboot-tutorials-parent **Repository Path**: coder_chenjun/springboot-tutorials-parent ## Basic Information - **Project Name**: springboot-tutorials-parent - **Description**: spring boot案例 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 4 - **Forks**: 0 - **Created**: 2024-01-04 - **Last Updated**: 2024-01-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 建立spring boot 最新版本spring boot的官网:https://docs.spring.io/spring-boot/docs/current/reference/html/ ## 在线网站建立 使用https://start.spring.io/或者https://start.aliyun.com/网站在线建立 ## IDEA中建立 ![image-20240104094657760](images/idea_springboot_init.png) ## 本质方式 ### 独立maven模块 pom文件主要是导入依赖管理 ```xml org.springframework.boot spring-boot-dependencies ${spring-boot.version} pom import ``` 接着就引入依赖 ```xml org.springframework.boot spring-boot-starter ``` 最后配置boot的maven插件 ```xml org.springframework.boot spring-boot-maven-plugin 2.6.13 com.example.demo.DemoApplication true repackage repackage ``` skip一定要设置为false或删掉,因为其含义是表示跳过此插件的功能,上面的配置可以精简为下面这样 ```xml org.springframework.boot spring-boot-maven-plugin 2.6.13 repackage ``` ### 作为子模块 pom文件中,首先让自己成为spring-boot-parent的子模块 ```xml spring-boot-starter-parent org.springframework.boot 2.6.13 ``` 接着添加依赖 ```xml org.springframework.boot spring-boot-starter ``` 接着配置插件,这里不需要配置executions以及goal,因为会从父模块里继承插件的配置 ```xml org.springframework.boot spring-boot-maven-plugin 2.6.13 ``` ## 启动运行 ### IDEA中直接启动 ### java -jar ### spring-boot:run ## Spring boot项目三要素 ### SpringBootApplication注解 ### pom文件 ### SpringApplication.run ## 启动回调接口 spring boot项目启动时如果想执行一些代码,实现一些初始化的工作,可以实现两个回调接口,两者主要的区别是对参数的解析处理不同,后者是已经把参数解析好了的,所以建议实际使用后一个接口 - CommandLineRunner - ApplicationRunner ### CommandLineRunner 假定指定的程序参数是abc def --x1=1 --x1=2 --y1=200 ```java @Component public class MyInit implements CommandLineRunner{ @Autowired private ApplicationContext context; @Override public void run(String... args) throws Exception { //初始化的工作。。。。 System.out.println("----chushihua"); System.out.println(context); for (String arg : args) { System.out.println("arg = " + arg); } } } ``` 结果如下,可以就是字符串原样输出并没有解析,不区分选项值还是非选项值 ```shell arg = abc arg = def arg = --x1=1 arg = --x1=2 arg = --y1=200 ``` MyInit也是一个普通的被spring管理的bean,所以也可以注入其它被spring管理的bean,比如上面的ApplicationContext ### ApplicationRunner ```java @Component public class MyInit implements CommandLineRunner, ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println("---app runner"); List nonOptionArgs = args.getNonOptionArgs(); for (String nonOptionArg : nonOptionArgs) { System.out.println("nonOptionArg = " + nonOptionArg); } Set optionNames = args.getOptionNames(); for (String optionName : optionNames) { System.out.println("optionName = " + optionName); List optionValues = args.getOptionValues(optionName); for (String optionValue : optionValues) { System.out.println("optionValue = " + optionValue); } System.out.println("==================="); } } } ``` 输出结果如下 ```shell ---app runner nonOptionArg = abc nonOptionArg = def optionName = y1 optionValue = 200 =================== optionName = x1 optionValue = 1 optionValue = 2 =================== ``` # starter与非starter对比 ## 使用JdbcTemplate ### 非spring boot项目使用JdbcTemplate 步骤如下 - 添加spring-jdbc依赖 - 创建配置类,注册JdbcTemplate,可选的也把DataSource也注册到spring中 - 把JdbcTemplate注册到dao里去使用 ### spring boot项目使用JdbcTemplate 步骤如下: - 添加spring-boot-starter-jdbc依赖 - 配置文件里编写连接数据库的信息 - 把把JdbcTemplate注册到dao里去使用 对比发现,spring boot下使用少了配置类的编写,这是因为这些配置类由对应的starter完成了,这也是spring boot项目的核心特点:自动配置 ### starter规范 ​ 官方的starter命名都是`spring-boot-starter-xxx`,非官方的starter命名都是`xxx-spring-boot-starter` 官方的starter有如下这些: ![image-20240104114231110](images/image-20240104114231110.png) ![image-20240104114311575](images/image-20240104114311575.png) ## 配置文件 ### properties ```properties spring.datasource.url=jdbc:mysql://localhost:3306/demo spring.datasource.username=root spring.datasource.password=root ``` ### yml(yaml) ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/demo username: root password: root ``` > IDEA有插件可以在两种类型文件间进行转换的 ## druid的使用 ### 非starter版本使用 步骤如下 - 添加druid依赖 - 编写配置类,注册DruidDataSource - 把JdbcTemplate注入到dao中 手动配置的DataSource总是优先于自动配置的HikariDataSource ### starter版本的使用 步骤如下 - 添加druid-spring-boot-starter - 在项目配置文件配置连接信息,仍然可以使用spring.datasource前缀开头的配置项 - 把JdbcTemplate注入到dao中 > 为什么添加了Druid的starter就不会用上jdbc starter自带的HikariDataSource了呢? > > TODO: ## 第三方组件使用套路 当我们使用spring boot开发项目时,要使用某个第三方的技术时 首先就是去找有没有对应的starter,有就***优先使用starter*** starter的用法基本上就是以下几步: - 引入starter的依赖 - 可选的进行一些配置(比如druid只需要配置连接信息) - 注入一些功能类(DataSource)到你的bean中 没有的话就采用传统的方式 - 引入普通的依赖 - 编写配置类,让功能类被spring管理起来 - 注入一些功能类(比如druid的DruidDataSource)到你的bean中 # ssm整合 ## 整合mvc 添加下面依赖就能使用spring mvc框架了,基本功能不再需要额外的配置 ```xml org.springframework.boot spring-boot-starter-web ``` ## 整合druid 添加下面的依赖 ```xml com.alibaba druid-spring-boot-starter 1.2.16 ``` 配置下面的内容 ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/demo username: root password: root ``` ## 整合mybatis 添加下面的依赖即可,如果有添加pagehelper的starter,由于传递依赖的特性,mybatis的starter是可以不用添加的 ```xml org.mybatis.spring.boot mybatis-spring-boot-starter 2.3.2 ``` 配置下面的内容 ```yaml mybatis: mapper-locations: classpath*:mappers/**/*.xml type-aliases-package: com.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl ``` 在任何一个配置类上添加MapperScan注解 ```java @SpringBootApplication @MapperScan("com.dao") public class XxxApplication{} ``` 接口这样写 ```java public interface DeptMapper { List getPagedAll(@Param("pageNum")int pageNum, @Param("pageSize")int pageSize); } ``` 映射文件这样写 ```xml ``` ## 整合pagehelper 添加下面的依赖 ```xml com.github.pagehelper pagehelper-spring-boot-starter 2.1.0 ``` 配置下面的内容 ```yaml pagehelper: support-methods-arguments: true reasonable: true helper-dialect: mysql ``` ## 事务 添加了上面的依赖之后,事务已经是直接可以使用的了。在相关方法上添加@Transactional注解即可,见`TransactionAutoConfiguration` # 配置属性类处理 目标是把配置文件中的数据赋值到你写的属性类,比如下面的属性类 ```java @Data public class UserInfoProperties { private int id; private String name; } ``` yml文件如下 ```yaml a: id: 100 name: abc ``` 本质的做法是实现下面2个逻辑即可 - 在属性类上有ConfigurationProperties注解,用来设置前缀 - 属性类能被spring管理起来,通常的实现手段有 - 加Component之类的注解 - Bean方法注册属性类 - 使用EnableConfigurationProperties - 配置类上使用ConfigurationPropertiesScan注解设定要扫描的属性类 ## ConfigurationProperties 典型用法两种,针对两种场景 - 属性类是你自己写的,可以修改源码 - 属性类源码不可以修改 第一种情况的实现 ```java @Data @ConfigurationProperties(prefix = "a") @Component public class UserInfoProperties { private int id; private String name; } ``` 第二种情况的实现 ```java @Configuration public class MyConfig { @Bean @ConfigurationProperties(prefix = "a") public UserInfoProperties userInfoProperties(){ return new UserInfoProperties(); } } ``` ## EnableConfigurationProperties 此类的作用是启用对配置属性类的支持能力,通常是在任一一个配置类上添加此注解,并指定要支持的属性类即可,比如 ```java @SpringBootApplication @EnableConfigurationProperties({UserInfoProperties.class}) public class Demo3423Application{ } ``` 对应的属性类要有ConfigurationProperties # yml ## 基本语法 基本语法大致有如下特性 1. 缩进规则 2. 松散的命名:不区分大小写,可以省略连字符等 3. 随机数 4. 引用其它变量: ${} 5. 数组 6. 键值对的编写(map) 7. 嵌套对象(address) 假定有一个类 ```java @Data @ConfigurationProperties(prefix = "yml") public class UserProperties { private int id; private String firstName; private String lastName; private List lists; private Map maps; private Address addr; } @Data public class Address { private String province; private String city; } ``` application.yml配置文件如下 ```yaml yml: firstName: asdfa id: ${random.int(10,20)} lastName: ${yml.first-name}def lists: - a - b - c maps: x: 100 y: 200 addr: province: guangdong city: zhuhai ``` ## 多配置文件 假定有application.yml,application-dev.yml,application-prod.yml,application.yml放2个环境的共同配置,其它两个分别对应开发环境与生产环境配置,用法如下 ```yaml yml: firstName: asdfa id: ${random.int(10,20)} lastName: ${yml.first-name}def lists: - a - b - c maps: x: 100 y: 200 addr: province: guangdong city: zhuhai ``` application-dev.yml文件如下 ```yaml yml: firstName: dev ``` application-prod.yml文件如下 ```yaml yml: firstName: prod ``` # web 添加了web这个starter之后,核心就是添加了嵌入式容器与spring mvc框架相关功能,mvc相关功能如下 > spring boot提供的自动配置对于大多数应用来说是可以很好的工作的,自动配置在spring的基础之上添加了如下的一些特性 > > - 包含 `ContentNegotiatingViewResolver` and `BeanNameViewResolver` beans. > - 支持处理静态资源, 也支持对webjar的支持 > - 自动注册 `Converter`, `GenericConverter`, 和 `Formatter` beans. > - 支持 `HttpMessageConverters` > - 自动注册 `MessageCodesResolver` > - 静态 `index.html` 的支持 > - 自动使用 `ConfigurableWebBindingInitializer` bean > > 如果你想保留这些自动配置,但也要进行一些自定义的配置调整,比如添加拦截器、格式化器(formatters)、视图控制器或其它特性,你可以编写一个配置类,并可选的实现WebMvcConfigurer接口,但配置类上不要添加@EnableWebMvc注解,否则会让spring boot提供的自动配置失效 > > 如果你想提供如下类型的一些定制实例并且仍然保留spring boot mvc的自动配置,你可以声明类型为WebMvcRegistrations类型的bean,并利用此类型来提供下面3种类型的自定义bean > > - `RequestMappingHandlerMapping` > - `RequestMappingHandlerAdapter` > - `ExceptionHandlerExceptionResolver` > > 实现代码类似下面这样,WebMvcRegistrations是一个接口,下面的方式是返回了一个匿名内部类的实现方式,你也可以写一个bean类实现此接口来调整自动配置 > > ```java > @Bean > public WebMvcRegistrations mvcRegistrations(){ > return new WebMvcRegistrations() { > @Override > public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { > return WebMvcRegistrations.super.getRequestMappingHandlerMapping(); > } > > @Override > public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() { > return WebMvcRegistrations.super.getRequestMappingHandlerAdapter(); > } > > @Override > public ExceptionHandlerExceptionResolver getExceptionHandlerExceptionResolver() { > return WebMvcRegistrations.super.getExceptionHandlerExceptionResolver(); > } > }; > } > ``` > > > > 如果你想对spring boot的mvc进行完全的控制,不使用spring boot mvc的任何自动配置功能,你可以编写一个配置类在其上添加@EnableWebMvc注解或者你自己的配置类继承DelegatingWebMvcConfiguration即可,类似下面这样 > > ```java > @Configuration > public class MyMvcConfiguration extends DelegatingWebMvcConfiguration {} > ``` 如果你不想使用web starter自动提供的所有mvc相关功能,那么就可以提供自己的配置类,然后在上面添加@EnableWebMvc注解即可 ## 嵌入式容器设置 ### 端口 ```yaml server: port: 8080 ``` ### 上下文地址 ```yaml server: servlet: context-path: /demo ``` ### URI编码设置 等价于设置了conf/server.xml中的connector元素的URIEncoding属性的值 ```yaml server: tomcat: uri-encoding: UTF-8 ``` ### 替换使用的内嵌服务器 ```xml org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-tomcat org.springframework.boot spring-boot-starter-jetty ``` ### 请求体编码设置 老版本的spring boot设置 ```yaml spring: http: encoding: charset: UTF-8 force: true ``` 2.5.6版本的设置 ```yaml server: servlet: encoding: enabled: true charset: UTF-8 force: true ``` ## mvc框架设置 ### 序列化处理 因为添加web starter的时候,传递添加了json的依赖,这个starter有对json进行处理,spring boot默认支持jackson,gson,json-b.有了这个starter之后就不需要再添加jackson的相关依赖了,只需要进行一些配置调整即可,比如下面的调整日期的这一块 ```yaml spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 ``` ### 跨域处理 编写一个配置或普通的bean类,实现WebMvcConfigurer接口,实现addCorsMappings方法并在其中进行跨域的设置 ```java @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowCredentials(true) .allowedMethods("GET", "POST", "DELETE", "PUT", "OPTIONS", "HEAD") .allowedOrigins("http://127.0.0.1:5500"); } ``` ### 文件上传 默认已经就是支持文件上传了,只需要进行一些参数调整,比如设定最大文件尺寸等 ```yaml spring: # spring boot默认设置的文件最大值是1m,最大请求数是10M servlet: multipart: enabled: true file-size-threshold: 20MB max-file-size: 10MB max-request-size: 20MB location: d:/tmp ``` 具体的单位设置见包 `org.springframework.util.unit.DataSize`类的注释 ### 拦截器 - 方法一:写一个配置类实现WebMvcConfigurer接口,实现此接口的addInterceptors方法,在此方法内注册一个拦截器对象,拦截器只需要实现HandlerInterceptor接口即可,不需要在上面添加扫描用的注解 - 方法二(推荐):写一个拦截器,在其上添加扫描用的注解,比如Component,然后就在配置类中注册拦截器,这种方法的优点是往拦截器里面注入一些其它被spring管理的对象,比如你自己写的业务类,dao类等 ## 注册传统的组件 > https://segmentfault.com/a/1190000022971721 传统的组件指的是servlet的3大核心组件 - servlet - 过滤器 - 监听器 非内置容器下,传统组件注册有两种方式。 - 方式一:编写一个组件,在web.xml中注册 - 方式二:编写一个组件,添加相关注解,比如@WebServlet,@WebFilter,@WebListener - 方式三:利用servlet3的动态注册技术注册(ServletContextInitializer) 第一种方法 编写过滤器,并添加过滤器注解 ```java @WebFilter public class MyFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { System.out.println("----myfilter-----"); chain.doFilter(req,resp); } } ``` 接着在任何一个配置类上添加注解 ```java @ServletComponentScan("com.example.demo.filter") ``` 最大的缺点:无法控制多个过滤器之间执行的顺序 方法二:直接写一个过滤器并让spring管理起来即可 ```java @Component public class MyFilter implements Filter {} ``` 优点:多个过滤器可以通过Order注解指定顺序,另外一个优点是此类由于被spring管理起来,所以可以注入一些其它的bean到此过滤器 缺点:无法指定要拦截的一些设置信息,比如地址,默认过滤所有的地址(`/*`) 方法三:写一个过滤器,并成为容器的一个bean,比如 ```java @Component public class MyFilter implements Filter {} ``` 接着写一个配置类(可选),在里面写一个bean方法来方法 ```java @Configuration public class MyFilterConfiguration { @Bean public FilterRegistrationBean registration(MyFilter filter) { FilterRegistrationBean registration = new FilterRegistrationBean<>(filter); // 设置为true才能注册 registration.setEnabled(true); registration.setUrlPatterns(Arrays.asList("/abc/**","/def/**")); return registration; } } ``` 它唯一的缺点就是麻烦,它支持顺序控制与注入其它的bean,也支持过滤器的相关设置,比如路径模式 # log spring boot 自带日志功能,见spring-boot-starter的传递依赖 ## 基本使用 在类上添加@Slf4j注解,在相关代码中编写日志,比如下面 ```java @Service @Slf4j public class UserServiceImpl { public void demo(){ System.out.println(log.getName()); log.trace("trace----"); log.debug("debug---"); log.info("info---"); log.warn("warn---"); log.error("error---"); } } ``` @Slf4j注解是lombok的注解,用来在类中生成类似下面的一个字段 ```java private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class); ``` ## 日志框架架构 https://www.cnblogs.com/antLaddie/p/15867893.html ![java_log](images/java_log.png) spring boot框架默认就支持logback日志框架,见starter的传递依赖 ## logback ### 核心组件 有如下三个核心组件 - appender:决定日志输出到哪里 - layout:日志的格式布局 - logger:输出日志 ### 日志的配置 日志的配置可以直接在application.yml中配置或专门创建一个日志配置文件进行配置,建议只选择一种方式配置,不要混用。 比如在application.yml中配置两个日志器的级别 ```yaml logging: level: root: info com.service: trace ``` 一个典型的logback文件内容如下 ```xml %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ``` springboot会自动在类路径下查找如下几个文件名 - logback-spring.xml - logback-spring.groovy - logback.xml - logback.groovy 如果你想指定别的文件名,就需要用下面的配置来指定 ```yaml logging: config: classpath:mylogback-spring.xml ``` ### 日志器 当写下下面的代码 ```java private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class); ``` 那么这个日志器的名字就是UserServiceImpl类的全程,假定是com.nf.service.UserServiceImpl,那么它的父是如下几个 - com.nf.service - com.nf - com - root 最顶级的是root,子日志器会继承所有父日志器的设置 ### 日志布局 ```yaml logging: pattern: console: "%clr(%d{yyyy-MM-dd HH:mm:ss}--%m%n)" ``` 上面转义符的含义 - %clr:表示彩色 - %d{yyyy-MM-dd HH:mm:ss} - %m:日志消息(message) - %n:换行 ## 日志组 主要的目标是人为的把一些日志器归类进行统一的设置,先命名组,比如下面的组yj就把3个日志器及其子都归为一类 ```yaml logging: group: yj: "org.springframework,org.mybatis,com.github" ``` 接下来就可以对此组进行统一的设置 ```yaml logging: level: root: info yj: trace ``` ## grep console插件 # Actuator # devtools https://www.jianshu.com/p/de544b13b9d5 # 自定义starter(*) ## 单模块starter的编写 单模块的starter是针对简单的库而言的,复杂的项目还是建议用标准的多模块的方式实现starter 单模块的maven项目,其artifactId的名字通常是xxx-spring-boot-starter,版本改成一个正式版本,不要用snapshot ### 创建maven项目 pom文件如下 ```xml 4.0.0 com.nf name-spring-boot-starter 1.0.0 8 8 UTF-8 UTF-8 2.6.13 1.18.22 org.springframework.boot spring-boot-autoconfigure ${spring-boot.version} true org.projectlombok lombok ${lombok.version} true org.springframework.boot spring-boot-autoconfigure-processor true ${spring-boot.version} org.springframework.boot spring-boot-configuration-processor true ${spring-boot.version} ``` 除了跟功能类有关的依赖,其它一些依赖"基本"都可以设置为optional=true,两个生成元数据文件的依赖一定要设置为可选的 ### 编写属性类 ```java /** * 规则:firstname:姓,lastname:名 * 女的话,统一改成姓spring(本来想实现结婚之后女的随丈夫姓,不想那么麻烦), */ @Data @ConfigurationProperties(prefix = "name") public class UserProperties { private String firstname; private String lastname; private boolean gender=true; } ``` 其中spring-boot-configuration-processor依赖只有在有ConfigurationProperties与EnableConfigurationProperties注解的情况下才会生成元数据文件以便给开发工具提供在配置时的智能提示用,这个元数据文件是在maven的编译阶段生成的 其中字段设置的值就是默认值,字段的文档化注释会变成描述 ### 编写功能类 ```java public class NameServiceImpl { @Autowired private UserProperties userProperties; public String generate() { return userProperties.getFirstname() + userProperties.getLastname(); } } ``` ### 编写配置类 ```java @Configuration @ConditionalOnClass(NameServiceImpl.class) @EnableConfigurationProperties(UserProperties.class) public class NameConfig { @Bean @ConditionalOnMissingBean public NameServiceImpl nameService() { return new NameServiceImpl(); } } ``` 典型的一个配置类的2个条件注解与EnableConfigurationProperties,依赖spring-boot-autoconfigure-processor就是用来生成自动配置的元文件,用来加快自动配置类的条件评估,以便提供springboot项目的启动速度。 这个依赖需要配置类里有相关的条件注解才能生成元数据文件,这个文件的生成是在maven的编译阶段生成的 ### spring.factories配置 在类路径下的META-INF文件夹下建立文件spring.factories文件,里面配置上你用的自动配置类 ```properties org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.config.NameConfig ``` ### Configurer接口 ## 条件类型 条件类型通常是用来评估是否应用自动配置类或自动配置类的bean方法的,这会影响自动配置类往spring容器中bean的注册,spring boot包含如下类型的条件类型 - [Class Conditions](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration.condition-annotations.class-conditions) - [Bean Conditions](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration.condition-annotations.bean-conditions) - [Property Conditions](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration.condition-annotations.property-conditions) - [Resource Conditions](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration.condition-annotations.resource-conditions) - [Web Application Conditions](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration.condition-annotations.web-application-conditions) - [SpEL Expression Conditions](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration.condition-annotations.spel-conditions) 条件类型就是`Condition`接口的实现类,但使用时很少直接使用这些接口的实现类,通常是通过注解的形式来使用,比如下面的一个条件注解 ```java @Conditional(OnClassCondition.class) public @interface ConditionalOnClass {} ``` 这些条件注解上都有一个元注解`@Conditional`,此元注解指定的类就是`Condition`接口的实现类,spring 校验框架也是采用类似的逻辑实现的 ### 常见条件注解 常用的条件注解有如下 - ConditionalOnClass:当有什么类 - ConditionalOnMissingClass:当缺失什么类 - ConditionalOnBean:当有什么bean - ConditionalOnMissingClass :当缺失什么bean - ConditionalOnProperty :当有什么属性值时 - ConditionalOnResource :当有什么资源时 - ConditionalOnWebApplication :当是web项目时 - ConditionalOnNotWebApplication :当不是web项目时 - ConditionalOnExpression:当el表达式返回true时 ### 顺序控制 控制自动配置类解析顺序主要是下面两个注解 - AutoConfigureBefore - AutoConfigureAfter DruidDataSource ## 多模块starter的编写 这里以为c3p0连接池创建一个starter为例来编写多模块的starter的写法,这种多模块的方式才是典型的starter的编写方式,通常情况下包含2个模块 - autoconfigure :包含功能类与自动配置代码 - starter:此模块没有代码,主要是进行pom的依赖设定,以确保功能库能正确使用 官方并没有要求这两个模块有一个共同的父模块,但实际编写这种starter的时候,通常是会编写一个父模块用来管理这2个子模块的 ### 命名 各个模块命名建议如下 - 项目名xxx-spring-boot-版本号,比如:xxx-spring-boot-1.0.0 - 父模块artifactId: xxx-spring-boot - autoconfigure模块的artifactId: xxx-spring-boot-autoconfigure - starter模块的artifactId: xxx-spring-boot-starter ### 父模块的编写 pom文件如下 ```xml 4.0.0 com.nf c3p0-spring-boot pom ${revision} c3p0-spring-boot-autoconfigure c3p0-spring-boot-starter 8 8 1.0.0 2.6.13 0.9.5.2 org.springframework.boot spring-boot-dependencies ${spring-boot.version} pom import com.mchange c3p0 ${c3p0.version} org.codehaus.mojo flatten-maven-plugin 1.1.0 true resolveCiFriendliesOnly flatten process-resources flatten flatten.clean clean clean ``` 此文件就配置了一个依赖管理与插件配置,此插件是用来处理maven install到本地仓库时修改pom文件的,主要是处理版本问题用的,详细情况见https://juejin.cn/post/6946138904802099231,以后只要更改`revision`这个变量就实现了版本的变更 ### autoconfigure模块的编写 此模块主要用来编写自动配置相关的代码,特别是已经有功能类的情况下,比如已经有c3p0了,自动配置类代码如下 ```java @Configuration @ConditionalOnClass(ComboPooledDataSource.class) @AutoConfigureBefore(DataSourceAutoConfiguration.class) @EnableConfigurationProperties(DataSourceProperties.class) public class C3p0Configuration { @Bean @ConditionalOnMissingBean(DataSource.class) public DataSource dataSource(DataSourceProperties properties){ ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setJdbcUrl(properties.getUrl()); dataSource.setUser(properties.getUsername()); dataSource.setPassword(properties.getPassword()); return dataSource; } } ``` 其中用上了DataSourceProperties,这样就可以直接使用过spring.datasource前缀进行连接相关信息的配置了 并且设定了配置类是在spring boot自带的DataSourceAutoConfiguration.class之前进行处理 此模块的pom文件如下 ```xml c3p0-spring-boot com.nf ${revision} 4.0.0 c3p0-spring-boot-autoconfigure org.springframework.boot spring-boot-autoconfigure true com.mchange c3p0 true org.springframework.boot spring-boot-autoconfigure-processor true ``` 此模块的依赖我都设置为optional了,因为主要是在starter里进行依赖的处理。 接着在META-INF下的spring.factories文件里配置上自动配置类就完成了此模块的编写 ```properties org.springframework.boot.autoconfigure.EnableAutoConfiguration=config.C3p0Configuration ``` ### starter模块的编写 此模块无任何代码(可以把src目录整个删除),pom文件如下 ```xml c3p0-spring-boot com.nf ${revision} 4.0.0 c3p0-spring-boot-starter com.nf c3p0-spring-boot-autoconfigure ${revision} org.springframework.boot spring-boot-starter com.mchange c3p0 org.springframework spring-jdbc ``` 在这里配置的依赖基本没有optional了,因为这是让你编写的starter能正确使用的所有依赖设置,设置为optional=true通常导致你的starter无法正常使用了 其中关于spring-jdbc的依赖是因为autoconfigure模块用到的DataSourceProperties类型内部的初始化方法afterPropertiesSet使用到了EmbeddedDatabaseConnection类型,而这些类型来自于spring-jdbc依赖。 > 注意:对spring-jdbc的依赖不要改变为依赖spring-boot-starter-jdbc,技术上依赖spring-boot-starter-jdbc会传递依赖spring-jdbc,可以解决问题,但不符合现实逻辑关系,因为是spring-boot-starter-jdbc依赖DataSource相关的starter而不是反之 ## 编写自己的minio starter 练习用 # redis 任何官方或者第三方的starter,他们的使用基本就是三步曲 - 依赖 - 配置:配置又分成三个方面 - 在application.yml之类的地方进行调整配置 - 更改pom依赖 - 实现starter提供的某些接口来调整starter的自动配置行为或者功能类的功能调整,这些接口通常名字是Configurer结尾或Customizer结尾 - 编写自己的配置类或Bean方法,取代starter的自动配置行为 - 使用starter提供的功能,通常的情况是两种 - 注入某个功能类,比如RedisTemplate - 无感使用了starter的功能,比如官方提供的事务功能 ## 依赖 ```xml org.springframework.boot spring-boot-starter-data-redis ``` 添加了这个依赖,不进行任何的配置就可以直接使用了,比如下面的代码 ```java @Autowired private StringRedisTemplate stringRedisTemplate; @Override public void run(ApplicationArguments args) throws Exception { stringRedisTemplate.opsForValue().set("boot","abc"); } ``` ## 配置 ### 配置文件中进行配置 比如在application.yml中调整主机与端口,连接密码等 ```yaml spring: reids: host: localhost ``` ### 更改依赖 如果你不想使用默认的lettuce库而是想用jedis,那么就换成jedis的库就可以了。实现这一点只需要改依赖 先排除掉lettuce的依赖,再添加上jedis的依赖就可以了。 ```xml org.springframework.boot spring-boot-starter-data-redis io.lettuce lettuce-core redis.clients jedis ``` > 可以看看不排除会出现什么情况,并思考为什么? > > 答案:不报错,但以lettuce的方式操作es ### 实现接口进行配置 有些配置的调整是无法通过在配置文件中进行设置处理的,必须写代码实现,这种情况starter一般会提供一些接口给用户实现以达成此功能,比如redis starter中的JedisClientConfigurationBuilderCustomizer接口就是用来处理连接配置的,比如超时,客户名字等 ```java @Component public class MyClientConfigCustomizer implements JedisClientConfigurationBuilderCustomizer { @Override public void customize(JedisClientConfiguration.JedisClientConfigurationBuilder builder) { builder.clientName("asdf") .connectTimeout(Duration.ofSeconds(10)); } } ``` 这种类一定记得要加@Component注解,要被spring扫描管理起来,不能只是实现接口就完事了。 如果你用的是lettuce,那么你实现的接口就是LettuceClientConfigurationBuilderCustomizer ### 编写自己的配置类进行配置 当starter提供的自动配置类怎么调整配置都达不到自己要求时,可以编写自己的配置类和bean方法来取代starter的自动配置类。比如,redis starter使用默认的序列化,你想改成jackson序列化就只能自己编写RedisTemplate bean方法的注册了,代码如下 ```java @Configuration public class RedisConfiguration { @Bean public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory){ RedisTemplate template = new RedisTemplate(); template.setConnectionFactory(connectionFactory); GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); template.setKeySerializer(stringRedisSerializer); template.setHashKeySerializer(stringRedisSerializer); template.setValueSerializer(serializer); template.setHashValueSerializer(serializer); return template; } } ``` 上面bean方法注册的bean的名字必须是`redisTemplate`,否则是不会让自动配置类里的RedisTemplate类型的bean的注册失效的。 由于使用到了jackson相关的库,项目中需要添加json的starter,这个starter主要就是jackson库相关的东西 ```xml org.springframework.boot spring-boot-starter-json ``` ## 使用功能类 redis starter的功能类主要就是RedisTemplate与StringRedisTemplate,直接注入到你的业务类中即可,比如下面的注入方式(也可以用构造函数的方式) ```java @Autowired private RedisTemplate redisTemplate; @Autowired private StringRedisTemplate stringRedisTemplate; ``` # RabbbitMq ## 依赖 ```xml org.springframework.boot spring-boot-starter-amqp ``` 添加依赖之后就已经是可以用的 ## 配置 ### 配置连接信息 默认就是以guest账号连接到本地,所以下面的配置是可以不用配的 ```yaml spring: rabbitmq: username: guest password: guest host: localhost virtual-host: / ``` ### 消息队列组件配置 ```java @Configuration public class HelloConfiguration { @Bean public Queue helloQueue(){ return new Queue("hello"); } } ``` ### 序列化配置 只需要在配置类中配置一个bean即可,会自动帮我们设定发送与接收都用这个序列化 ```java @Bean public Jackson2JsonMessageConverter messageConverter(){ return new Jackson2JsonMessageConverter(); } ``` 当然,也需要在项目中添加`spring-boot-starter-json`的依赖 ## 使用功能类 可以直接往业务类里注入RabbitTemplate类型来发送消息,用RabbitListener注解或者RabbitListener +RabbitHandler注解的形式来接收消息 # es ## 依赖 ```xml org.springframework.boot spring-boot-starter-data-elasticsearch ``` 添加之后就可以使用了 ## 配置 ### 连接信息配置 ```yaml spring: elasticsearch: uris: "localhost:9200" socket-timeout: "10s" # username: "user" # password: "secret" ``` ## 使用功能类 ### 原生API方式操作es 可以在项目中直接注入high level的客户端,也可以通过high level客户端获取 low level的客户端,见RestHighLevelClientServiceImpl类 ```java @Service public class RestHighLevelClientServiceImpl { @Autowired private RestHighLevelClient highLevelClient; public void doSth(){ System.out.println("highLevelClient = " + highLevelClient); //通过高级别的客户端来获取低级别 RestClient lowLevelClient = highLevelClient.getLowLevelClient(); System.out.println("lowLevelClient = " + lowLevelClient); } } ``` ### Template方式操作es 直接在bean中注入ElasticsearchRestTemplate类型即可 ```java @Autowired private ElasticsearchRestTemplate restTemplate; ``` ### Repository方式操作es 核心是创建一个接口继承es相关的repository接口即可 ```java public interface EmpEsRepository extends ElasticsearchRepository { } ``` 实体类 ```java @Data @AllArgsConstructor @NoArgsConstructor @Document(indexName = "emps",createIndex = false) public class Emp { private int id; private int age; private String name; private String birthplace; } ``` # spring security ## 依赖 ## 配置 ## 使用功能类 # spring session ## 依赖 ## 配置 ## 使用功能类