SpringBoot(三)之web开发

1、web基础配置

一、访问静态资源

1)进入规则为 / 时

若是进入SpringMVC的规则为/时,Spring Boot的默认静态资源的路径为:css

spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/

也就是说,在默认的Spring MVC进入规则下,classpath下的META-INF/resources目录、resources目录、static目录和public目录中的静态资料是能够直接经过 "http: // xxx.com/静态资源" 的方式访问到的。如:html

2)进入规则为*.xxx 或者 不指定静态文件路径时

若是进入SpringMVC的规则为*.xxx时(如:*.html),则上述目录下的静态资源将没法直接访问,须要将静态资源放置到webapp下的static目录中便可经过 "http://xxx.com/static/静态资源" 访问。此外,默认不配置SpringMVC的规则下也能够如此访问,也就是说这种访问静态资源的方式是通用的。如图所示:前端

二、自定义拦截器

增长一个拦截器,须要经过继承WebMvcConfigurerAdapter而后重写父类中的方法进行扩展。java

@Configuration
public class MySpringMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        HandlerInterceptor handlerInterceptor = new 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 {

            }
        };
		// 添加拦截器并设置拦截规则
        registry.addInterceptor(handlerInterceptor).addPathPatterns("/**");
    }
}

三、自定义消息转化器

自定义消息转化器有两种实现方式,一种是@Bean方式,另外一种是自定义拦截器。mysql

1)@Bean方式

只须要在@Configuration的类中添加消息转化器的@bean加入到Spring容器,就会被Spring Boot自动加入到容器中。git

// spring boot默认就有消息转化器,其编码格式为utf-8
@Bean
public StringHttpMessageConverter stringHttpMessageConverter() {
    StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
    return stringHttpMessageConverter;
}

2)自定义拦截器方式

WebMvcConfigurerAdapter的功能很强大,除了能够配置拦截器外,还能够配置消息转换器。github

@Configuration
public class MySpringMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
        converters.add(stringHttpMessageConverter);
    }
}

四、读取外部的配置文件

@Configuration
@PropertySource(value = { "classpath:jdbc.properties", "classpath:base.properties" }, ignoreResourceNotFound = true)
public class 任意类 {

}

五、Druid DataSource的配置

Druid提供了一个高效、功能强大、可扩展性好的数据库链接池,经常使用于替换DBCP和C3P0。但在Spring Boot上配置比较“恶心”,这里就简单的贴出我的参照网上的配置代码,没必要深究。web

1)引入依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.11</version>
</dependency>

2)jdbc.properties

项目中通常会建立一个jdbc.properties文件来记录数据库的链接信息。正则表达式

#MySQL
jdbc.url=jdbc:mysql://127.0.0.1:3306/dbxxx?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456
#Druid
jdbc.initialSize=0
jdbc.minIdle=0
jdbc.maxActive=150

3)配置Druid数据源

建议将配置Druid数据源的操做放在@SpringBootApplication注解的类中。spring

@SpringBootApplication
@Configuration
@PropertySource(value = {"classpath:jdbc.properties"})
public class MyWebApplication{

    @Value("${jdbc.url}")
    public String jdbcUrl;
    @Value("${jdbc.username}")
    public String jdbcUsername;
    @Value("${jdbc.password}")
    public String jdbcPassword;
    @Value("${jdbc.initialSize}")
    public int jdbcInitialSize;
    @Value("${jdbc.minIdle}")
    public int jdbcMinIdle;
    @Value("${jdbc.maxActive}")
    public int jdbcMaxActive;

    @Bean
    public ServletRegistrationBean druidServletRegistrationBean() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
        servletRegistrationBean.setServlet(new StatViewServlet());
        servletRegistrationBean.addUrlMappings("/druid/*");
        return servletRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean duridFilterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new WebStatFilter());
        HashMap<String, String> initParams = new HashMap<>();
        // 设置忽略请求
        initParams.put("exclusions", "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*");
        filterRegistrationBean.setInitParameters(initParams);
        filterRegistrationBean.addUrlPatterns("/*");
        return filterRegistrationBean;
    }

    @Bean(initMethod = "init", destroyMethod = "close")
    public DruidDataSource druidDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl(jdbcUrl);
        druidDataSource.setUsername(jdbcUsername);
        druidDataSource.setPassword(jdbcPassword);
        druidDataSource.setInitialSize(jdbcInitialSize);
        druidDataSource.setMinIdle(jdbcMinIdle);
        druidDataSource.setMaxActive(jdbcMaxActive);
        return druidDataSource;
    }
}

以后就能够经过@AutoWried注解获得数据源(druidDataSource)了。

六、数据库框架集成

1)jpa集成

Jpa在使用上跟Hibernate很像,由于它们以前的关系非同通常,有兴趣能够看看《Hibernate与Jpa的关系,终于弄懂》这篇文章。Spring Boot对jpa的支持也是很好的,配置起来很是简单。

在pom.xml中引用jpa及数据库驱动(如:mysql)依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

在application.yml文件中配置

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1/dbgirl
    username: root
    password: 123456
  jpa:
    hibernate:
      ddl-auto: update  #第一次加载hibernate时根据model类会自动创建起表的结构(前提是先创建好数据库),之后加载hibernate时根据 model类自动更新表结构,即便表结构改变了但表中的行仍然存在不会删除之前的行。要注意的是当部署到服务器后,表结构是不会被立刻创建起来的,是要等 应用第一次运行起来后才会。
    show-sql: true

2)MyBatis集成

Mybatis和Spring Boot的整合有两种方式:

第一种:使用mybatis官方提供的Spring Boot整合包实现,地址:spring-boot-starter

第二种:使用mybatis-spring整合的方式,也就是咱们传统的方式

这里推荐并使用第二种方式,由于能够很方便的控制Mybatis的各类配置。这里假设你已经配置过数据源了(数据源能够是druid、dbcp、c3p0...)。

首先,须要在pom.xml文件中引用mybatis依赖

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.3.0</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.2.2</version>
</dependency>

而后,建立一个Mybatis的配置类:

@Configuration
public class MybatisConfig {

    @Autowired
    DruidDataSource druidDataSource;

    @Bean
    @ConditionalOnMissingBean// 当Spring容器中没有SqlSessionFactoryBean时才建立
    public SqlSessionFactoryBean sqlSessionFactoryBean() {
        SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
        // 设置数据源
        sqlSessionFactory.setDataSource(druidDataSource);
        // 设置别名扫描包
        sqlSessionFactory.setTypeAliasesPackage("com.lqr.demo3.bean");
        // 设置Mybatis的配置文件位置
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource mybatisConfigXml = resolver.getResource("classpath:mybatis-config.xml");
        sqlSessionFactory.setConfigLocation(mybatisConfigXml);
        return sqlSessionFactory;
    }

}

最后,建立Mapper接口的扫描类MapperScannerConfig:

@Configuration
@AutoConfigureAfter(MybatisConfig.class)// Mybatis的扫描配置必须在SqlSessionFactory被建立以后
public class MapperScanConfig {

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setBasePackage("com.lqr.demo3.mapper");
        return mapperScannerConfigurer;
    }

}

七、设置事务管理

Spring Boot中推荐使用@Transactional注解来申明事务。

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

当引入jdbc依赖以后,Spring Boot会自动默认分别注入DataSourceTransactionManager或JpaTransactionManager,因此咱们不须要任何额外配置就能够用@Transactional注解进行事务的使用。

@Service
@Transactional
public class GirlService {

    @Transactional
    public void insertGirl() {
        Girl girlA = new Girl();
        girlA.setCupSize("A");
        girlA.setAge(18);
        girlRespository.save(girlA);
    }
}

@Transactional不只能够注解在方法,也能够注解在类上。当注解在类上时,意味着此类全部public方法都会开启事务。若是类级别和方法级别同时使用了@Transactional注解,则使用在类级别的注解会重载方法级别的注解。

八、开启jsp支持

Spring boot默认内嵌的tomcat是不支持jsp页面的,若是项目中使用到了jsp,须要导入以下依赖才能正常访问。

<dependency>
	<groupId>org.apache.tomcat.embed</groupId>
	<artifactId>tomcat-embed-jasper</artifactId>
	<scope>provided</scope>
</dependency>

2、Web编码进阶

这部分可能跟Spring Boot的关系并非很大(或者说,并不是Spring Boot特有),但很值得咱们在编码方面做为参考。

一、Spring MVC中新的注解

1)@RestController

如今的Web项目已经愈来愈趋向于将后端与前端分别开发了,若是贵公司的项目就是使用json来进行先后端交互,且使用Spring MVC来开发的话,就必定会用到下面这2个注解:

@Controller
@ResponseBody
public class GirlController {
	...
}

而在Spring MVC4以后,咱们可使用@RestController 注解来开发基于Spring MVC4的REST风格的JSON服务。

通俗的说就是@RestController = @Controller + @ResponseBody。

@Controller和@RestController的区别:

若是只是使用@RestController注解Controller,则Controller中的方法没法返回jsp页面,配置的视图解析器InternalResourceViewResolver不起做用,返回的内容就是Return 里的内容。 例如:原本应该到success.jsp页面的,则其显示success.

2)http组合注解

Spring4.3中引进了{@GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping},来帮助简化经常使用的HTTP方法的映射,并更好地表达被注解方法的语义。

@GetMapping => 
	@RequestMapping(value = "/xxx",method = RequestMethod.GET)
@PostMapping => 
	@RequestMapping(value = "/xxx",method = RequestMethod.POST)
@PutMapping => 
	@RequestMapping(value = "/xxx",method = RequestMethod.PUT)
@DeleteMapping => 
	@RequestMapping(value = "/xxx",method = RequestMethod.DELETE)
...

二、数据校验

Web开发中,对从前端传过来的数据作数据校验已是屡见不鲜的事了,但若是校验的数据不少,那么,一方面在开发上就须要作不少if判断,另外一方面则是代码上显得再也不简洁。其实,使用@Valid + BindingResult就能够优雅地解决这样的问题。使用示例以下:

1)@Valid + BindingResult

首先,使用一个Java Bean来接收前端提交的数据。在这个Java Bean以前加上@Valid,在这个Java Bean以后加上BindingResult(BindingResult参数必须紧跟在@Valid参数以后。)

/**
 * 添加一个女生
 */
@PostMapping(value = "/girls")
public Result<Girl> girlAdd(@Valid Girl girl, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        return ResultUtils.error(-1, bindingResult.getFieldError().getDefaultMessage());
    }
    return ResultUtils.success(girlRespository.save(girl));
}

2)设置校验规则

而后,须要在@Valid注解的Java Bean中,使用各类"规则注解"对其中的属性进行校验规则的设定。示例以下:

public class Girl {
    
    private Integer id;

    @NotBlank(message = "这个字段必传")
    private String cupSize;

    @Min(value = 18, message = "未成年少女禁止入内")
    private Integer age;

    @NotNull(message = "金额必传")
    private Double money;

    ...
}

示例中,"规则注解"的message值能够在Controller中经过bindingResult.getFieldError().getDefaultMessage()获取。

这类用于数据校验的注解还有不少,有兴趣的能够好好研究一下:

注解 类型 说明
@AssertFalse Boolean,boolean 验证注解的元素值是false
@AssertTrue Boolean,boolean 验证注解的元素值是true
@NotNull 任意类型 验证注解的元素值不是null
@Null 任意类型 验证注解的元素值是null
@Min(value=值) BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存储的是数字)子类型 验证注解的元素值大于等于@Min指定的value值
@Max(value=值) 和@Min要求同样 验证注解的元素值小于等于@Max指定的value值
@DecimalMin(value=值) 和@Min要求同样 验证注解的元素值大于等于@ DecimalMin指定的value值
@DecimalMax(value=值) 和@Min要求同样 验证注解的元素值小于等于@ DecimalMax指定的value值
@Digits(integer=整数位数, fraction=小数位数) 和@Min要求同样 验证注解的元素值的整数位数和小数位数上限
@Size(min=下限, max=上限) 字符串、Collection、Map、数组等 验证注解的元素值的在min和max(包含)指定区间以内,如字符长度、集合大小
@Past java.util.Date,java.util.Calendar;Joda Time类库的日期类型 验证注解的元素值(日期类型)比当前时间早
@Future 与@Past要求同样 验证注解的元素值(日期类型)比当前时间晚
@NotBlank CharSequence子类型 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不一样于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的首位空格
@Length(min=下限, max=上限) CharSequence子类型 验证注解的元素值长度在min和max区间内
@NotEmpty CharSequence子类型、Collection、Map、数组 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@Range(min=最小值, max=最大值) BigDecimal,BigInteger,CharSequence, byte, short, int, long等原子类型和包装类型 验证注解的元素值在最小值和最大值之间
@Email(regexp=正则表达式,flag=标志的模式) CharSequence子类型(如String) 验证注解的元素值是Email,也能够经过regexp和flag指定自定义的email格式
@Pattern(regexp=正则表达式,flag=标志的模式) String,任何CharSequence的子类型 验证注解的元素值与指定的正则表达式匹配
@Valid 任何非原子类型 指定递归验证关联的对象;如用户对象中有个地址对象属性,若是想在验证用户对象时一块儿验证地址对象的话,在地址对象上加@Valid注解便可级联验证

三、面积切面编程(AOP配置)

AOP是Spring中一大核心,若在SpringBoot中要使用AOP只需两步:

1)引入AOP的starter依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2)编写切面类

@Aspect
@Component
public class HttpAspect {
    private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);

    @Pointcut("execution(public * com.lqr.controller.GirlController.*(..))")
    public void log() {
    }

    @Before("log()")
    public void deBefore(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // url
        logger.info("url={}", request.getRequestURL());
        // method
        logger.info("method={}", request.getMethod());
        // ip
        logger.info("ip={}", request.getRemoteAddr());
        // 类方法
        logger.info("class_method={}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        // 参数
        logger.info("args={}", joinPoint.getArgs());

    }

    @After("log()")
    public void doAfter() {
        logger.info("doAfter...");
    }

    @AfterReturning(returning = "object", pointcut = "log()")
    public void doAfterReturning(Object object) {
        if (object != null)
            logger.info("response={}", object.toString());
    }

}

四、统一异常处理(配置通知Advice)

1)自定义异常

这里之因此继承RuntimeException,是为了方便事务回滚。而自定义异常的好处在于:一方面可使代码语义化,另外一方面使得咱们编码更加方便。

public class GirlException extends RuntimeException {
    private Integer code;

    public GirlException(ResultEnum resultEnum) {
        super(resultEnum.getMsg());
        this.code = resultEnum.getCode();
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}

2)配置全局异常捕获器

使用全局异常捕获器,一方面能够捕获到整个项目中的Exception及其子类(包含RuntimeException等),另外一方面能够对异常进行统一处理并返回统一的数据格式,为前端提供友好的数据交互。

@ControllerAdvice
public class ExceptionHandle {

    private final Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result handle(Exception e) {
        if (e instanceof GirlException) {
            GirlException girlException = (GirlException) e;
            return ResultUtils.error(girlException.getCode(), girlException.getMessage());
        } else {
            logger.error("【系统异常】{}", e);
            return ResultUtils.error(-1, "未知错误");
        }
    }

}

3、开发与生产

一、热部署

Web开发中,没有热部署怎么能行呢?因此,下面就介绍下Spring Boot的热部署配置。

1)在pom.xml中配置热部署须要的依赖与插件

<dependencies>
    ...
    <!--spring boot 热部署-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>
<build>
    <plugins>
        <!--spring boot 热部署-->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <fork>true</fork>
            </configuration>
        </plugin>
    </plugins>
	...
</build>

2)开启自动编译

打开Idea的Settings界面,找到"Build,Execution,Depolyment",在Compiler中将"Build project automatically"打钩。

3)修改pom.xml的维护(Maintenance)登记项

使用快捷键 "ctrl+shift+alt+/" 打开pom.xml的维护(Maintenance)菜单,找到登记(registry)项,单击打开。

4)容许应用程序运行时自动编译

在登记(registry)中找到"compiler.automake.allow.when.app.running"这一项打钩,关闭。最后重启项目便可!!!

二、发布到独立的tomcat中运行

尽管Spring Boot项目会内置一个tomcat,仅只需经过一个简单的指令即可启动项目,但在生产环境下,咱们仍是习惯将项目发布到第三外的servlet容器中,下面将介绍若是将一个Spring Boot项目团部署到第三方tomcat中运行。

1)修改工程的打包方式为war

2)将spring-boot-starter-tomcat的范围设置为provided

spring-boot-starter-tomcat是Spring Boot默认就会配置的,即上面说到的内嵌tomcat,将其设置为provided是在打包时会将该包(依赖)排除,由于要放到独立的tomcat中运行,Spring Boot内嵌的Tomcat是不须要用到的。

<!--spring boot tomcat(默承认以不用配置,但当须要把当前web应用布置到外部servlet容器时就须要配置,并将scope配置为provided)-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>

3)修改代码,设置启动配置

须要继承SpringBootServletInitializer,并重写configure()方法,将Spring Boot的入口类设置进去。

// 若要部署到外部servlet容器,须要继承SpringBootServletInitializer并重写configure()
@SpringBootApplication
@Configuration
public class MyWebApplication extends SpringBootServletInitializer
{
    
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        // 设置启动类,用于独立tomcat运行的入口
        return builder.sources(MyWebApplication.class);
    }
}

4)打war包并部署到tomcat

相关文章
相关标签/搜索