#### 1、Spring Boot重要特性 1. 独立的Spring应用程序,嵌入式Tomcat/Jetty容器,无需部署War包 2. 尽量使用自动化配置,Spring Auto Configuration 3. 提供一批'starter' POM 简化Maven及Gradle配置 4. 提供一系列能够用到生产环境的应用度量、健康检查等特性(Actuator) #### 2、Spring Boot 快速上手 访问http://start.spring.io/,使用SPRING INITIALIZR选择须要的模块快速初始化  选择Web模块,快速建立项目 ```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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>myproject</artifactId> <version>0.0.1-SNAPSHOT</version> <!-- Inherit defaults from Spring Boot --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.3.RELEASE</version> </parent> <!-- Add typical dependencies for a web application --> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>x </dependencies> <!-- Package as an executable jar --> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> ``` 生成的项目结构 ``` myproject +- pom.xml +- src +- main +- java | +- com.example.myproject | +- Application.Java | +- resources | +- application.properties | +- test +- java | +- com.example.myproject | +- ApplicationTests.java ``` #### 3、项目分层结构及模块划分方式  ##### 1. 名称规范 - **包名规范** ``` com.pingan.haofang.${产品}.${模块}.${层次}.${className}.java ``` - **特殊类名规范** Spring配置类: configuration/*Configuration,例如WebConfiguration/DatasourceConfiguration Properties类: properties/*Properties,例如FtpProperties dao类:dao/*Dao或者dao/*Repository service类:service/*Service - ** 分层命名规范 ** domain: 数据库PO dao/repository:数据库访问层 service:业务逻辑层 dto:数据传输对象 constants:枚举常量 controller:web控制器 form:web请求对象 vo:web响应对象 validator:校验器 batch:批处理Job类 ##### 2. 项目划分 对于每一个项目能够按照以下方式进行划分为4个模块,独立为4个maven模块 - **Parent** 负责依赖管理,公用的maven依赖 - **Service (lib)** 包含整个项目的业务逻辑/数据访问代码 ``` com.pingan.haofang +- myproject +- customer +- domain | +- Customer.java | +- dao | +- CustomerDao.java | +- service | +- CustomerService.java | +- impl | +- CustomerServiceImpl.java | +- dto | +- CustomerDto.java | +- constants | +- CustomerConstants.java | +- CustomerStatus.java ``` - **Exportapi (lib 或 app)** 项目对外API提供,RPC等。依赖Service,可单独部署也可打进web包进行部署。 - **Web (app)** 项目web接口暴露代码,包括先后端接口暴露,文档,拦截器。依赖service、exportapi ``` com.pingan.haofang +- myproject +- WebApplication.java +- customer +- controller | +- CustomerController.java | +- form | +- CustomerForm.java +- vo | +- CustomerVo.java | +- validator | +- CustomerValidator.java ``` - **Batch (app)** 项目批处理任务,常驻进程任务或者定时任务,依赖service ``` com.pingan.haofang +- myproject +- BatchApplication.java +- customer +- batch | +- CustomerExportTask.java | +- CustomerExportRunner.java ``` #### 4、Spring经常使用模块应用 ##### 1. Spring MVC - **接口定义规范** ``` Http Method GET:读取数据,不容许有数据的修改等操做 列表URL设计:GET:/web/custmer 单条数据URL设计:GET:/web/customer/{custmerId} POST:新建数据 POST:/web/custmer PUT:修改数据 PUT:/web/custmer/{custormId} PUT:/web/custmer/status/{custormId} DELTE:删除数据 DELETE:/web/custmer/{custormId} 请求体与响应体 请求与响应除QueryString及PathVariable外,其他数据交互应以Json格式进行交互 Controller配置为@RestController, 先后端ContentType:application/json; charset=UTF8 ``` - **Swagger应用** 全部controller都用swagger annotation进行注解,springfox嵌入以提供接口文档及try out调试功能 - **TraceFilter** 添加TraceFilter,对于每一个请求随机生成RequestID并放入MDC进行日志打印,便于排查 - **异常消息定义及ExceptionHandler** 自定义完善的异常处理器,按照和前端定义好的接口产生异常消息体。经过HTTP CODE定义各种状态 ``` 200 成功 409 校验失败,例如非空、长度、格式等 400 客户端请求格式错误,例如不是合法的Json 401 未受权即未登陆 403 无权限访问 404 不存在,未找到响应对象 500 服务器内部错误 ``` 异常消息体定义 ```json { "errorCode": 1, // 保留错误码字段 "message": "全局异常消息", "fieldErrors": [ { "name": "名称不容许重复" }, { "desc": "描述过短" } ] } ``` ##### 2. JPA用法 - **数据源及数据库链接池配置** 建议使用Alibaba Druid链接池配置,见 com.pingan.haofang.myproject.common.configuration.DataSourceConfiguration, 同时建议配置DruidStat,便可经过web管理数据源监测数据,见 ```java @Bean public ServletRegistrationBean druidServlet(DruidStatProperties druidStatProperties) { ServletRegistrationBean reg = new ServletRegistrationBean(); reg.setServlet(new StatViewServlet()); reg.addUrlMappings("/druid/*"); reg.addInitParameter("loginUsername", druidStatProperties.getUsername()); reg.addInitParameter("loginPassword", druidStatProperties.getPassword()); return reg; } ``` - **继承Repository接口** 查询可使用QueryMethod/@Query/Example/Specification四种方式,前面三种适合作简单查询时用,Specification(即CriteriaQuery)建议在复杂查询,例如分业列表有较多筛选条件时使用 demo见com.pingan.haofang.myproject.customer.service.impl#CustomerServiceImpl#queryList,CustomerSpecs.pageListSpec(dto) - **合理使用实体关联** - **使用Auditing** Auditing提供了以下四个annotation能够方便设置建立人、最后修改人、建立时间、最后修改时间。须要和@EnableJpaAuditing,AuditingEntityListener配合使用 @CreatedBy,@LastModifiedBy,@CreatedDate,@LastModifiedDate ```java @Bean public AuditorAware<Long> auditorProvider() { return () -> Optional.ofNullable(SearchThreadContext.getSessionVisitor()) .map(Visitor::getUserId) .orElse(0L); } @MappedSuperclass public abstract class BaseDomain { @Column(name = "create_time") @CreatedDate private Date createTime; @Column(name = "create_by") @CreatedBy private Long createBy; @Column(name = "update_time") @LastModifiedDate private Date updateTime; @Column(name = "update_by") @LastModifiedBy private Long updateBy; ``` ##### 3. 使用Spring Boot Actuator/Spring Boot Admin Spring Boot actuator能够帮助咱们提供方便的健康页面、jmx等监控和排查功能。故指望全部项目的Actuator知足以下规范。 - **Spring Boot Actuator的Context-path为/actuator** context-path配置为统一前缀,方便将来配置内部管理域名时proxy的统一配置。 ```properties endpoints.sensitive=false endpoints.enabled=true endpoints.actuator.enabled=true endpoints.shutdown.enabled=true endpoints.shutdown.sensitive=false management.security.enabled=false management.context-path=/actuator management.address=127.0.0.1 ``` ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> ``` - **为便于管理和排查Spring Boot App,每一个APP都配置spring boot admin** 引入Jar ```xml <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>1.5.0</version> </dependency> <!--以下plugin也建议配置,主要用于生成buildinfo,方便/actuator/info返回项目的基本信息--> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <!--生成build-info文件--> <goal>build-info</goal> </goals> </execution> </executions> </plugin> <!--以下插件生成git信息,包括构建的git分支,最后提交人及注释,版本号--> <plugin> <groupId>pl.project13.maven</groupId> <artifactId>git-commit-id-plugin</artifactId> <executions> <execution> <goals> <goal>revision</goal> </goals> </execution> </executions> <configuration> <!--日期格式;默认值:dd.MM.yyyy '@' HH:mm:ss z;--> <dateFormat>yyyyMMddHHmmss</dateFormat> <!--,构建过程当中,是否打印详细信息;默认值:false;--> <verbose>true</verbose> <!-- ".git"文件路径;默认值:${project.basedir}/.git; --> <dotGitDirectory>${project.basedir}/.git</dotGitDirectory> <!--若项目打包类型为pom,是否取消构建;默认值:true;--> <skipPoms>false</skipPoms> <!--是否生成"git.properties"文件;默认值:false;--> <generateGitPropertiesFile>true</generateGitPropertiesFile> <!--指定"git.properties"文件的存放路径(相对于${project.basedir}的一个路径);--> <generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties </generateGitPropertiesFilename> <!--".git"文件夹未找到时,构建是否失败;若设置true,则构建失败;若设置false,则跳过执行该目标;默认值:true;--> <failOnNoGitDirectory>true</failOnNoGitDirectory> </configuration> </plugin> </plugins> </build> ``` 配置注册admin server地址 ```properties #spring admin ##目前st/ci将注册到ci环境的admin,ga将注册到ga环境的admin spring.boot.admin.url=http://actuator.a.pa.com/ ##若是dev及其余开发机可能网络不通,请使用下面配置 #spring.boot.admin.url=http://10.59.72.187:9596 ##下面配置是spring-boot的name,配置后才能在admin有漂亮的名称,请自行取名 spring.application.name=${applicationName} ##默认若是hosts中配置了当前IP的hostname可能没法访问,因此能够加上以下设置 spring.boot.admin.client.prefer-ip=true management.info.git.mode=full ``` - **访问SpringAdminServer进行管理** ``` st/ci/开发: http://actuator.anhouse.com.cn/ 用户名:admin 密码:admin-st anhouse http://actuator.an2.ipo.com/ ga: http://actuator.proxy.ipo.com/ ```   ##### 4. 校验 jsr303 functional validation fail fast/ fail over ##### 5. 单测 - **内存数据库H2** 请使用内存数据库模拟数据库,初始化脚本请添加SchemaSQL,初始化数据可以使用对应的data.sql,或者使用testEntityManager 详见myproject-service/src/test - **Mock/MockBean** 须要配置MockitoTestExecutionListener,见BaseTest 当单测测试逻辑类有依赖其余的逻辑类,这个时候若是只想测试本身的逻辑,可使用@MockBean,mock掉依赖的逻辑类,这里mock的对象若是没有使用mockito指定 相应逻辑,则都会返回null 见com.pingan.haofang.myproject.customer.service.impl.CustomerServiceImplTest#isCustomerBuyProduct - **Spy/SpyBean** 须要配置MockitoTestExecutionListener,见BaseTest 与Mock和MockBean不一样之处在于,mock的对象若是没有对方法使用mockito指定相应逻辑,则会执行真实代码,可是@Spy中若是先when,再ThenReturn则仍是会先执行 一次mock方法的真实逻辑,可能会由于不可预知的错误而失败。 ```java // when去设置模拟返回值时,里面的方法object.callMethod()会先执行一次 when(object.callMethod()).thenReturn("result"); // 使用doReturn则不会产生上面的问题 doReturn("result").when(object).callMethod(); ``` 更多请参照com.pingan.haofang.myproject.customer.service.impl.CustomerServiceImplTest#getOne ##### 6. Spring Session @EnableRedisHttpSession 为避免redis共享时出现问题,建议设置redisNamespace区分key, 不然默认都是spring:session,很差区分, 同时按照须要设置session过时时间 @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 43200, redisNamespace = "search_cloud") ``` 配置redisNamespace后的rediskey "spring:session:search_cloud:sessions:5259b7fb-c882-4f57-8d32-d967148b1338" 未配置namespace后的rediskey "spring:session:sessions:expires:c1698de0-618b-455d-a63b-b4809decb1fd" ``` ##### 7. ThreadContext 线程上下文,请扩展使用com.pingan.haofang.module.common.ThreadContext ##### 8. 日志规范 日志请使用Spring-Boot默认提供的模板,springboot默认提供的模板已经预约义了变量,能够进行赋值扩展,主要分console-pattern和file-pattern, 二者格式相同,console-pattern还包含颜色美化,便于阅读 若是无额外appender配置,能够直接在application.properties中配置,SpringBoot提供的日志级别自定义 可按照以下配置示例扩展 ``` logging.pattern.level=%X{REQUEST_ID} %p ``` 但若是日志比较复杂,可以使用SpringBoot提供的logback base.xml,defaults.xml进行组合配置,spring boot base提供的fileAppender不支持按时间滚动, 这块能够本身写 ```xml <?xml version="1.0" encoding="UTF-8"?> <configuration> <jmxConfigurator/> <property name="LOG_FILE" value="${LOG_PATH}/myproject.log"/> <property name="ADDITIONAL" value="%X{REQUEST_ID} %X{TRACE_ID}"/> <property name="LOG_LEVEL_PATTERN" value="${ADDITIONAL} %5p"/> <include resource="org/springframework/boot/logging/logback/defaults.xml"/> <include resource="org/springframework/boot/logging/logback/console-appender.xml"/> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <encoder> <pattern>${FILE_LOG_PATTERN}</pattern> </encoder> <file>${LOG_FILE}</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}</fileNamePattern> </rollingPolicy> </appender> <!--customer专用appender--> <appender name="customerAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_PATH}/customer.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_PATH}/customer-%d{yyyy-MM-dd}.log</fileNamePattern> </rollingPolicy> <encoder> <pattern>${ADDITIONAL} %d{HH:mm:ss.SSS} - %msg%n</pattern> </encoder> </appender> <logger name="com.pingan.haofang.myproject.customer.controller.CustomerController" level="INFO" additivity="false"> <appender-ref ref="customerAppender"/> </logger> <root level="INFO"> <appender-ref ref="CONSOLE"/> <appender-ref ref="FILE"/> </root> </configuration> ``` 日志文件路径指定,对于主程序日志,请经过配置文件或者Jvm参数指定logging.path和logging.file #### 5、好房Spring Boot Starter 基于各类开发场景,好房framework模块开发了若干开发组件,例如历史操做记录,批处理框架,校验工具等。须要使用请先引入以下pom ```xml <dependencyManagement> <dependencies> <dependency> <groupId>com.pingan.haofang.framework</groupId> <artifactId>pinganfang-framework-dependencies</artifactId> <version>1.0.0-SNAPSHOT</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ``` ##### 1. pinganfang-common-module,通用工具模块 该模块主要封装了各类经常使用UTIL类库,Exception定义,例如StringUtils,ThreadContext,BaseException等。 ```mvn <dependency> <groupId>com.pingan.haofang.framework</groupId> <artifactId>pinganfang-common-module</artifactId> </dependency> ``` ##### 2. pinganfang-rpc-starter, RPC封装 该模块封装了好房各业务模块通讯的RPC,包括服务端开放RPC服务,以及RPC客户端调用。 ```mvn <dependency> <groupId>com.pingan.haofang.framework</groupId> <artifactId>pinganfang-rpc-starter</artifactId> </dependency> ``` 要启用RPC,请在Application.java,或者配置类上面添加@EnableHaofangRPC,并定义以下Filter ```java @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public FilterRegistrationBean rpcFilter() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new RPCFilter()); filterRegistrationBean.addUrlPatterns("/rpc"); filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); return filterRegistrationBean; } ``` **声明RPC服务**,详见com.pingan.haofang.myproject.demo.rpc.DemoRPCExportService ```java @RPCExporter(value = "findByIds", defaultErrorMessage = "rpc error", successCode = "0") public List<DemoDTO> findByIds(List<Integer> ids, int type) { List<DemoDTO> list = new ArrayList<DemoDTO>(); list.add(new DemoDTO(101, "demo1", Arrays.asList(1, 2, 3, 4, 5))); list.add(new DemoDTO(102, "demo2", Arrays.asList(1, 2, 3, 4, 5))); list.add(new DemoDTO(103, "demo3", Arrays.asList(1, 2, 3, 4, 5))); return list; } ``` **声明RPC客户端**,详见com.pingan.haofang.myproject.demo.rpc.DemoRPCService ```java @RPCClient(value = "User\\User.getMobileByUserIDs", config = "rpc.user", successCode = "0000") public Map<Integer, String> getUserInfo(List<Integer> ids); ``` ##### 3. pinganfang-validator-starter, 校验器封装 该模块封装了jsr303校验器,扩展了现有的校验器,既支持hibernate validator,同时校验器能够配置fail over/fail fast等高级特性 ```xml <dependency> <groupId>com.pingan.haofang.framework</groupId> <artifactId>pinganfang-validator-starter</artifactId> </dependency> ``` 若要使用,请先在Application.java上或者配置类配置@EnableHaofangValidator 在须要校验的controller方法上配置以下注解 ```java @Valid(CustomerValidator.class) public List<CustomerVO> queryList(CustomerQueryForm form) { ``` 同时写好validator ```java @Component public class CustomerValidator { @ValidHandler public void queryList(ValidationResult result, CustomerQueryForm form) { /** * countryId为40的时候cityId不能>20 */ if (form.getCountryId() == 40 && form.getCityId() > 20) { result.addError(ValidationError.of("cityId", "cityId > 20")); } } } ``` demo见com.pingan.haofang.myproject.customer.controller.CustomerController#queryList ##### 4. pinganfang-jpa-starter, JPA封装 该模块封装了BaseDomain, BaseRepository,对Spring Data Jpa 进行了进一步扩展 ```xml <dependency> <groupId>com.pingan.haofang.framework</groupId> <artifactId>pinganfang-validator-starter</artifactId> </dependency> ``` - BaseDomain封装了createTime,createBy,updateTime,updateBy,推荐在定义domain时继承 - 对于Repository,能够继承BaseRepository,提供了众多新的数据库操做方法,例如返回Map, listMap, Java 8支持等 - 提供了PageQueryDTO等基础类 ##### 5. pinganfang-history-starter, 历史操做记录封装 history封装了历史操做记录handler bean注册,相应切面等逻辑,可是按照何种格式记录日志,则由具体业务而定,在HistoryOpHandler中实现便可。 ```xml <dependency> <groupId>com.pingan.haofang.framework</groupId> <artifactId>pinganfang-history-starter</artifactId> </dependency> ``` - 1.在项目中配置注解@EnableHaofangHistory,启用history功能 - 2.定义HistoryOpHandler - 3.在须要记录日志的地方配置注解HistoryOpLog,这里注解配置参数以下,须要注意 ``` value: 对应的处理器方法名称 beanName: 处理器在spring中的BeanName errMessage: 异常消息 ignoreError: 若是为true,则写入日志时失败会抛出异常,不然会忽略继续执行主体流程 force: 若是为true,则不论请求是否成功均会记录日志,不然只会在正常返回时才记录日志 ``` 示例见 com.pingan.haofang.myproject.customer.service.impl.CustomerLogService, com.pingan.haofang.myproject.customer.service.impl.CustomerServiceImpl ##### 6. pinganfang-batch-starter, 批处理封装 batch封装了批处理定义基础类库,支持一次性任务,常驻任务类型两种 ```xml <dependency> <groupId>com.pingan.haofang.framework</groupId> <artifactId>pinganfang-batch-starter</artifactId> </dependency> ``` 若是使用batch,请配置@EnableHaofangBatch batch 建议两种用法 - 一次性任务 见com.pingan.haofang.myproject.demo.batch.DemoCronTaskRunner - 常驻进程任务 见com.pingan.haofang.myproject.demo.batch.DemoScheduleTaskRunner 如上任务启动类均为BatchMain,如要启动某做业,启动JVM参数为-DrunnerName=${batchName} ##### 7. pinganfang-web-common, web基础工具类库 该模块主要提供web程序要的通用基础类库Utils等 ``` <dependency> <groupId>com.pingan.haofang.framework</groupId> <artifactId>pinganfang-web-common</artifactId> </dependency> ``` 目前提供的基础类有 - ContextFilter,提供请求ID生成并写入MDC,可在logback中打印 #### 6、项目构建及部署 ##### 打包方式 ``` sh build.sh ${mvn_profile} ``` 打包为 ``` output/myproject-web.tar.gz output/myproject-batch.tar.gz ``` 包结构为 ``` tar.gz +- bin | app_control.bash +- conf | logback.xml | application.properties +- myproject-web.jar ``` ##### App启动方式 ``` bash app_control.bash start|shutdown|kill|force|restart|status start 启动app shutdown 关闭app kill 杀掉app进程 force 强制杀掉app进程 restart 重启app status 查看app状态 ``` ##### war 包使用方式 见项目myproject-web-war,目前须要将配置文件打进war包,相关配置都配置在application.properties中