zoukankan      html  css  js  c++  java
  • Springboot入门:


    Springboot入门:
    1.springboot是基于spring的全新框架,设计目的:简化spring应用配置和开发过程。
    该框架遵循“约定大于配置”原则,采用特定的方式进行配置,从而事开发者无需定义大量的xml配置。
    2.springboot不需要重复造轮子,而是在原因的spring的基础上封装一层,并集成一些类库,用于简化开发。
    3.springboot提供了默认的配置,再启动类加入@SpringBootApplication注解,则这个类就是整个应用程序的启动类。

    4.properties和yaml
    springboot整个应用程序只要一个配置文件,那就是properties或yml文件。
    properties以逗号隔开,yml以换行+tab隔开,需要注意的是冒号后面必须空格,否则会报错。
    yaml 文件格式更清晰,更易读
    springboot启动时,就会从application.yml中读取配置信息,并加载到内存中。


    5.项目打包成war
    <packaging>war</packaging>
    <build>
    <finalName>index</finalName>
    <resources>
    <resource>
    <directory>src/main/resources</directory>
    <filtering>true</filtering>
    </resource>
    </resources>

    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>

    <plugin>
    <artifactId>maven-resources-plugin</artifactId>
    <version>3.0.2</version>
    <configuration>
    <encoding>UTF-8</encoding>
    </configuration>
    </plugin>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.18.1</version>
    <configuration>
    <skipTests>true</skipTests>
    </configuration>
    </plugin>
    </plugins>
    </build>

    6.项目打包成jar
    <packaging>jar</packaging>
    <build>
    <finalName>api</finalName>
    <resources>
    <resource>
    <directory>src/main/resources</directory>
    <filtering>true</filtering>
    </resource>
    </resources>
    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
    <fork>true</fork>
    <mainClass>com.example.demo.DemoApplication</mainClass>
    </configuration>
    <executions>
    <execution>
    <goals>
    <goal>repackage</goal>
    </goals>
    </execution>
    </executions>
    </plugin>
    <plugin>
    <artifactId>maven-resources-plugin</artifactId>
    <version>3.0.2</version>
    <configuration>
    <encoding>UTF-8</encoding>
    <useDefaultDelimiters>true</useDefaultDelimiters>
    </configuration>
    </plugin>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.18.1</version>
    <configuration>
    <skipTests>true</skipTests>
    </configuration>
    </plugin>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.3.2</version>
    <configuration>
    <source>1.8</source>
    <target>1.8</target>
    </configuration>
    </plugin>
    </plugins>
    </build>

    7.基础的项目结构:
    Application:程序的启动类
    StartUp:程序启动完成前执行的类
    WebConfig:配置类,所有的bean注入、配置、拦截器注入都放到这个类里面。

    8.多环境配置
    在一个企业级系统中,我们可能会遇到这样一个问题:
    开发时使用开发环境,测试时使用测试环境,上线时使用生产环境。每个环境的配置都可能不一样,
    比如开发环境的数据库是本地地址,而测试环境的数据库是测试地址。
    那我们在打包的时候如何生成不同环境的包呢?
    这里的解决方案有很多:
    1.每次编译之前手动把所有配置信息修改成当前运行的环境信息。这种方式导致每次都需要修改,相当麻烦,也容易出错。
    2.利用 Maven,在 pom.xml 里配置多个环境,每次编译之前将 settings.xml 里面修改成当前要编译的环境 ID。
    这种方式会事先设置好所有环境,缺点就是每次也需要手动指定环境,如果环境指定错误,发布时是不知道的。
    3.第三种方案就是本文重点介绍的,也是作者强烈推荐的方式。
    首先,创建 application.yml 文件,在里面添加如下内容
    spring:
    profiles:
    active: dev
    含义是指定当前项目的默认环境为 dev,即项目启动时如果不指定任何环境,Spring Boot 会自动从 dev 环境文件中读取配置信息。
    我们可以将不同环境都共同的配置信息写到这个文件中。然后创建多环境配置文件,文件名的格式为:application-{profile}.yml,
    其中,{profile} 替换为环境名字,如 application-dev.yml,我们可以在其中添加当前环境的配置信息

    9.常用注解
    @SpringbootApplication:告诉SpringBoot是这个类是程序入口。
    @SpringBootConfiguration:SpringBoot的配置注解
    @EnableAutoConfiguration:自动配置
    @ComponentScan:SpringBoot扫码bean的规则
    @Configuration:通过代码设置配置
    @Bean:这个注解是方法级别上的注解,
    主要添加在@Configuration或@SpringBootConfiguration注解的类,
    有时也可以添加再@Component注解的类,他的作用是定义一个bean
    @Value:
    通常情况下,我们需要定义一些全局变量,都会想到的方法是定义一个 public static 变量,在需要时调用,是否有其他更好的方案呢?答案是肯定的。下面请看代码:
    @Value("${server.port}")
    String port;
    @RequestMapping("/hello")
    public String home(String name) {
    return "hi "+name+",i am from port:" +port;
    }
    它的好处不言而喻:
    定义在配置文件里,变量发生变化,无需修改代码。
    变量交给Spring来管理,性能更好。

    10.注入任何类
    本节通过一个实际的例子来讲解如何注入一个普通类,并且说明这样做的好处。
    假设一个需求是这样的:项目要求使用阿里云的 OSS 进行文件上传。
    我们知道,一个项目一般会分为开发环境、测试环境和生产环境。
    OSS 文件上传一般有如下几个参数:appKey、appSecret、bucket、endpoint 等。
    不同环境的参数都可能不一样,这样便于区分。按照传统的做法,我们在代码里设置这些参数,
    这样做的话,每次发布不同的环境包都需要手动修改代码。
    这个时候,我们就可以考虑将这些参数定义到配置文件里面,
    通过前面提到的 @Value 注解取出来,再通过 @Bean 将其定义为一个 Bean,
    这时我们只需要在需要使用的地方注入该 Bean 即可。

    11.拦截器
    我们在提供 API 的时候,经常需要对 API 进行统一的拦截,比如进行接口的安全性校验。
    本节,我会讲解 Spring Boot 是如何进行拦截器设置的,请看接下来的代码。
    创建一个拦截器类:ApiInterceptor,并实现 HandlerInterceptor 接口:
    public class ApiInterceptor implements HandlerInterceptor {
    //请求之前
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
    System.out.println("进入拦截器");
    return true;
    }
    //请求时
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }
    //请求完成
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }

    @SpringBootConfiguration 注解的类继承 WebMvcConfigurationSupport 类,
    并重写 addInterceptors 方法,将 ApiInterceptor 拦截器类添加进去,代码如下:
    @SpringBootConfiguration
    public class WebConfig extends WebMvcConfigurationSupport{

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
    super.addInterceptors(registry);
    registry.addInterceptor(new ApiInterceptor());
    }
    }
    12. 异常处理
    我们在 Controller 里提供接口,通常需要捕捉异常,并进行友好提示,否则一旦出错,
    界面上就会显示报错信息,给用户一种不好的体验。最简单的做法就是每个方法都使用 try catch 进行捕捉,
    报错后,则在 catch 里面设置友好的报错提示。如果方法很多,每个都需要 try catch,
    代码会显得臃肿,写起来也比较麻烦。
    我们可不可以提供一个公共的入口进行统一的异常处理呢?当然可以。方法很多,
    这里我们通过 Spring 的 AOP 特性就可以很方便的实现异常的统一处理。
    @Aspect
    @Component
    public class WebExceptionAspect {
    private static final Logger logger = LoggerFactory.getLogger(WebExceptionAspect.class);
    //凡是注解了RequestMapping的方法都被拦截
    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    private void webPointcut() {
    }
    /**
    * 拦截web层异常,记录异常日志,并返回友好信息到前端 目前只拦截Exception,是否要拦截Error需再做考虑
    * @param e
    * 异常对象
    */
    @AfterThrowing(pointcut = "webPointcut()", throwing = "e")
    public void handleThrowing(Exception e) {
    e.printStackTrace();
    logger.error("发现异常!" + e.getMessage());
    logger.error(JSON.toJSONString(e.getStackTrace()));
    //这里输入友好性信息
    writeContent("出现异常");
    }
    /**
    * 将内容输出到浏览器
    * @param content
    * 输出内容
    */
    private void writeContent(String content) {
    HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
    response.reset();
    response.setCharacterEncoding("UTF-8");
    response.setHeader("Content-Type", "text/plain;charset=UTF-8");
    response.setHeader("icop-content-type", "exception");
    PrintWriter writer = null;
    try {
    writer = response.getWriter();
    } catch (IOException e) {
    e.printStackTrace();
    }
    writer.print(content);
    writer.flush();
    writer.close();
    }
    }
    这样,我们无需每个方法都添加 try catch,一旦报错,则会执行 handleThrowing 方法。

    13.优雅的输入合法性校验
    为了接口的健壮性,我们通常除了客户端进行输入合法性校验外,在 Controller 的方法里,我们也需要对参数进行合法性校验,
    传统的做法是每个方法的参数都做一遍判断,这种方式和上一节讲的异常处理一个道理,不太优雅,也不易维护。
    其实,SpringMVC 提供了验证接口,下面请看代码:
    @GetMapping("authorize")
    public void authorize(@Valid AuthorizeIn authorize, BindingResult ret){
    if(result.hasFieldErrors()){
    List<FieldError> errorList = result.getFieldErrors();
    //通过断言抛出参数不合法的异常
    errorList.stream().forEach(item -> Assert.isTrue(false,item.getDefaultMessage()));
    }
    }
    public class AuthorizeIn extends BaseModel{

    @NotBlank(message = "缺少response_type参数")
    private String responseType;
    @NotBlank(message = "缺少client_id参数")
    private String ClientId;

    private String state;

    @NotBlank(message = "缺少redirect_uri参数")
    private String redirectUri;

    public String getResponseType() {
    return responseType;
    }

    public void setResponseType(String responseType) {
    this.responseType = responseType;
    }

    public String getClientId() {
    return ClientId;
    }

    public void setClientId(String clientId) {
    ClientId = clientId;
    }

    public String getState() {
    return state;
    }

    public void setState(String state) {
    this.state = state;
    }

    public String getRedirectUri() {
    return redirectUri;
    }

    public void setRedirectUri(String redirectUri) {
    this.redirectUri = redirectUri;
    }
    }
    在 controller 的方法需要校验的参数后面必须跟 BindingResult,否则无法进行校验。但是这样会抛出异常,对用户而言不太友好!
    那怎么办呢?
    很简单,我们可以利用上一节讲的异常处理,对报错进行拦截:
    @Component
    @Aspect
    public class WebExceptionAspect implements ThrowsAdvice{
    public static final Logger logger = LoggerFactory.getLogger(WebExceptionAspect.class);
    //拦截被GetMapping注解的方法
    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
    private void webPointcut() {
    }
    @AfterThrowing(pointcut = "webPointcut()",throwing = "e")
    public void afterThrowing(Exception e) throws Throwable {
    logger.debug("exception 来了!");
    if(StringUtils.isNotBlank(e.getMessage())){
    writeContent(e.getMessage());
    }else{
    writeContent("参数错误!");
    }
    }
    /**
    * 将内容输出到浏览器
    * @param content
    */
    private void writeContent(String content) {
    HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
    .getResponse();
    response.reset();
    response.setCharacterEncoding("UTF-8");
    response.setHeader("Content-Type", "text/plain;charset=UTF-8");
    response.setHeader("icop-content-type", "exception");
    PrintWriter writer = null;
    try {
    writer = response.getWriter();

    writer.print((content == null) ? "" : content);
    writer.flush();
    writer.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    这样当我们传入不合法的参数时就会进入 WebExceptionAspect 类,从而输出友好参数。
    我们再把验证的代码单独封装成方法:
    protected void validate(BindingResult result){
    if(result.hasFieldErrors()){
    List<FieldError> errorList = result.getFieldErrors();
    errorList.stream().forEach(item -> Assert.isTrue(false,item.getDefaultMessage()));
    }
    }
    这样每次参数校验只需要调用 validate 方法就行了,我们可以看到代码的可读性也大大的提高了。

    14. 接口版本控制
    一个系统上线后会不断迭代更新,需求也会不断变化,有可能接口的参数也会发生变化,如果在原有的参数上直接修改,
    可能会影响线上系统的正常运行,这时我们就需要设置不同的版本,这样即使参数发生变化,由于老版本没有变化,
    因此不会影响上线系统的运行。
    一般我们可以在地址上带上版本号,也可以在参数上带上版本号,还可以再 header 里带上版本号,
    这里我们在地址上带上版本号,大致的地址如:http://api.example.com/v1/test,其中,v1 即代表的是版本号。
    具体做法请看代码:
    @Target({ElementType.METHOD,ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Mapping
    public @interface ApiVersion {

    /**
    * 标识版本号
    * @return
    */
    int value();
    }
    public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {

    // 路径中版本的前缀, 这里用 /v[1-9]/的形式
    private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("v(\d+)/");

    private int apiVersion;

    public ApiVersionCondition(int apiVersion){
    this.apiVersion = apiVersion;
    }

    @Override
    public ApiVersionCondition combine(ApiVersionCondition other) {
    // 采用最后定义优先原则,则方法上的定义覆盖类上面的定义
    return new ApiVersionCondition(other.getApiVersion());
    }

    @Override
    public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
    Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
    if(m.find()){
    Integer version = Integer.valueOf(m.group(1));
    if(version >= this.apiVersion)
    {
    return this;
    }
    }
    return null;
    }

    @Override
    public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
    // 优先匹配最新的版本号
    return other.getApiVersion() - this.apiVersion;
    }

    public int getApiVersion() {
    return apiVersion;
    }
    }
    public class CustomRequestMappingHandlerMapping extends
    RequestMappingHandlerMapping {

    @Override
    protected RequestCondition<ApiVersionCondition> getCustomTypeCondition(Class<?> handlerType) {
    ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
    return createCondition(apiVersion);
    }

    @Override
    protected RequestCondition<ApiVersionCondition> getCustomMethodCondition(Method method) {
    ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
    return createCondition(apiVersion);
    }

    private RequestCondition<ApiVersionCondition> createCondition(ApiVersion apiVersion) {
    return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value());
    }
    }
    @SpringBootConfiguration
    public class WebConfig extends WebMvcConfigurationSupport {

    @Bean
    public AuthInterceptor interceptor(){
    return new AuthInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new AuthInterceptor());
    }

    @Override
    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
    RequestMappingHandlerMapping handlerMapping = new CustomRequestMappingHandlerMapping();
    handlerMapping.setOrder(0);
    handlerMapping.setInterceptors(getInterceptors());
    return handlerMapping;
    }
    }
    Controller 类的接口定义如下:
    @ApiVersion(1)
    @RequestMapping("{version}/dd")
    public class HelloController{}
    这样我们就实现了版本控制,如果增加了一个版本,则创建一个新的 Controller,方法名一致,ApiVersion 设置为2,
    则地址中 v1 会找到 ApiVersion 为1的方法,v2 会找到 ApiVersion 为2的方法。

    15.自定义JSON解析
    Spring Boot 中 RestController 返回的字符串默认使用 Jackson 引擎,它也提供了工厂类,
    我们可以自定义 JSON 引擎,本节实例我们将 JSON 引擎替换为 fastJSON,
    首先需要引入 fastJSON:
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>${fastjson.version}</version>
    </dependency>
    其次,在 WebConfig 类重写 configureMessageConverters 方法:
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    super.configureMessageConverters(converters);
    /*
    1.需要先定义一个convert转换消息的对象;
    2.添加fastjson的配置信息,比如是否要格式化返回的json数据
    3.在convert中添加配置信息
    4.将convert添加到converters中
    */
    //1.定义一个convert转换消息对象
    FastJsonHttpMessageConverter fastConverter=new FastJsonHttpMessageConverter();
    //2.添加fastjson的配置信息,比如:是否要格式化返回json数据
    FastJsonConfig fastJsonConfig=new FastJsonConfig();
    fastJsonConfig.setSerializerFeatures(
    SerializerFeature.PrettyFormat
    );
    fastConverter.setFastJsonConfig(fastJsonConfig);
    converters.add(fastConverter);
    }
    16.单元测试
    Spring Boot 的单元测试很简单,直接看代码:
    @SpringBootTest(classes = Application.class)
    @RunWith(SpringJUnit4ClassRunner.class)
    public class TestDB {

    @Test
    public void test(){
    }
    }
    17.模板引擎
    在传统的 SpringMVC 架构中,我们一般将 JSP、HTML 页面放到 webapps 目录下面,
    但是 Spring Boot 没有 webapps,更没有 web.xml,如果我们要写界面的话,该如何做呢?
    Spring Boot 官方提供了几种模板引擎:
    FreeMarker、Velocity、Thymeleaf、Groovy、mustache、JSP。
    这里以 FreeMarker 为例讲解 Spring Boot 的使用。
    首先引入 FreeMarker 依赖:
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
    </dependency>
    在 resources 下面建立两个目录:static 和 templates,如图所示:
    其中 static 目录用于存放静态资源,譬如:CSS、JS、HTML 等,
    templates 目录存放模板引擎文件,
    我们可以在 templates 下面创建一个文件:index.ftl(freemarker 默认后缀为 .ftl),并添加内容:
    <!DOCTYPE html>
    <html>
    <head>
    </head>
    <body>
    <h1>Hello World!</h1>
    </body>
    </html>

  • 相关阅读:
    怎么把pdf转换成word文件
    怎么把pdf文件转换成word文件教程
    pdf转换成word转换器哪个好用
    pdf格式怎么转换成word格式
    pdf文件怎么转换成word教程
    怎么把pdf文件转换成word文件
    福大软工1816 · 第二次作业
    福大软工1816 · 第一次作业
    ER图
    数据定义分析、数据操纵分析、数据完整性分析、数据安全性分析、并发处理分析、数据库性能分析
  • 原文地址:https://www.cnblogs.com/youqc/p/10517464.html
Copyright © 2011-2022 走看看