SpringBoot讲义
什么是SpringBoot?
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
Spring 从4.x版本开始提倡java配置和注解结合使用,慢慢离开xml繁琐的配置,所以要入门SpringBoot,需要从java配置开始。在Spirng Boot和Spring Cloud中,大量使用了注解与JavaConfig。
JavaConfig诞生的原因是什么?
Spring IOC有一个非常核心的概念——Bean。由Spring容器来负责对Bean的实例化,装配和管理。早些时候XML是用来描述Bean最为流行的配置方式。随着Spring的日益发展,由于Spring会把几乎所有的业务类都以Bean的形式配置在XML文件中,造成了大量的XML文件。使用XML来配置Bean失去了编译时的类型安全检查。大量的XML配置使得整个项目变得更加复杂。
当随着JAVA EE 5.0的发布,其中引入了一个非常重要的特性——Annotations(注解)。注解是源代码的标签,这些标签可以在源代码层进行处理或通过编译器把它熔入到class文件中。在JAVA EE 5以后的版本中,注解成为了一个主要的配置选项。Spring使用注释来描述Bean的配置与采用XML相比,因类注解是在一个类源代码中,可以获得类型安全检查的好处。可以良好的支持重构。
JavaConfig就是使用注解来描述Bean配置的组件。JavaConfig 是Spring的一个子项目。
JavaConfig常见的注解有哪些?
@Configuration注解
该注解贴在类上,表示是一个配置类。相当于xml配置
@Bean注解
Bean是一个方法级别上的注解,主要用在@Configuration注解的类里,也可以用在@Component注解的类里。添加的bean的id为方法名
也可以执行生命周期的回调函数,类似@PostConstruct(初始化方法) 和 @PreDestroy(销毁方法)的方法
@Scope注解
@Scope 注解:用来设置交给spring容器管理的Bean是单例方式,还是多例方式
相当于bean标签中的scope属性
@ComponentScan注解
创建一个配置类,在配置类上添加 @ComponentScan 注解。该注解默认会扫描该类所在的包下所有的配置类,相当于之前的xml <context:component-scan>。
@Import注解
@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
@ImportResource注解
@ImportResource和@Value进行资源文件读取
@PropertySource, @value 注解
Spring框架提供了PropertySource注解,目的是加载指定的属性文件 @Value 可以获取配置文件中的值
@Profile,@ActiveProfile注解
@profile注解是spring提供的一个用来标明当前运行环境的注解。我们正常开发的过程中经常遇到的问题是,开发环境是一套环境,QA测试是一套环境,线上部署又是一套环境。这样从开发到测试再到部署,会对程序中的配置修改多次,尤其是从QA到上线这个环节,让QA的也不敢保证改了哪个配置之后能不能在线上运行。 为了解决上面的问题,我们一般会使用一种方法,就是配置文件,然后通过不同的环境读取不同的配置文件,从而在不同的场景中跑我们的程序。
那么,spring中的@profile注解的作用就体现在这里。在spring使用DI来依赖注入的时候,能够根据当前制定的运行环境来注入相应的bean。最常见的就是使用不同的DataSource了。
JavaConfig配置的应用场景
在IOC中
需求一:把一个Bean交给Spring管理
回顾使用xml配置方式
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="someBean" class="cn.wolfcode.javaconfig._01_xmlconfig.SomeBean"/>
</beans>
SomeBean:
public class SomeBean {
public void print() {
System.out.println("print xmlConfig");
}
}
XMLConfigTest:
public class XMLConfigTest {
@Test
public void testXMLConfig() throws Exception {
// 加载spring的配置文件
ApplicationContext ctx =
new ClassPathXmlApplicationContext("cn/wolfcode/javaconfig/_01_xmlconfig/applicationContext.xml");
// 通过spring容器获取SomeBean
SomeBean someBean = ctx.getBean(SomeBean.class);
// 执行print方法
someBean.print();
}
}
使用javaConfig方式
@Configuration :spring的配置标签,打在哪个类上,这个类就表示spring的配置类, 相当于applicationContext.xml文件
@Bean :打在方法上方法返回的实例对象交给spring容器管理,方法的名称就是Bean标签中的id值。
相当于配置文件中bean标签。
如:
<bean id="someBean" class="cn.wolfcode._01xmlconfig.SomeBean" />
ApplicationConfig:
@Configuration
public class ApplicationConfig {
@Bean
public SomeBean someBean(){
return new SomeBean();
}
}
SomeBean:
public class SomeBean
{
public void print(){
System.out.println("print JavaConfig");
}
}
这里不再是通过xml配置文件获取spring容器对象,而是通过加载配置类。因此应该选用 AnnotationConfigApplicationContext 类,加载配置类的字节码文件。
JavaConfigTest:
public class JavaConfigTest {
@Test
public void testJavaConfig() throws Exception {
// 加载配置类
ApplicationContext ctx =
new AnnotationConfigApplicationContext(ApplicationConfig.class);
// 从spring容器中获取SomeBean对象
SomeBean someBean = ctx.getBean(SomeBean.class);
// 打印
someBean.print();
}
}
@ComponentScan
使用该注解改良代码。
通过上述的案例,其实我们发现,上面的写法也显得很麻烦。还不如使用配置文件简洁。
在配置类中,通过给方法添加注解@Bean,把该方法的返回值交给spring管理。代码量太大,如果需要交给Spring管理很多Bean的话,我们要写很多个方法,在配置类中。
在javaConfig中,给我们提供一个注解@ComponentScan,可以把需要管理的Bean 贴上注解@Component,然后通过在配置类上面贴注解@ComponentScan来进行扫描贴了注解的Bean,然后把这些Bean交给Spring管理。
@ComponentScan 这个标签也相当于之前在配置文件中的配置的
<context:component-scan base-package="" />
//开启 ioc组件自动扫描功能
注意:@ComponentScan注解也可以通过配置指定扫描哪个包。
如:@ComponentScan("cn.wolfcode")
表示扫描“cn.wolfcode”这个包以及它下面的子包,把包中的贴了注解的Bean ,交给spring容器管理。
如果不指定包,表示扫描当前包以及下面的子包
改良方案如下:
ApplicationConfig:
@Configuration
@ComponentScan("cn.wolfcode.javaconfig._03_scan")
public class ApplicationConfig {
}
JavaConfigTest:
public class JavaConfigTest {
@Test
public void testJavaConfig() throws Exception {
// 加载配置类
ApplicationContext ctx =
new AnnotationConfigApplicationContext(ApplicationConfig.class);
// 从spring容器中获取SomeBean对象
SomeBean someBean = ctx.getBean(SomeBean.class);
// 打印
someBean.print();
}
}
SomeBean:
@Component
public class SomeBean
{
public void print(){
System.out.println("print ScanConfig");
}
}
@Bean中的一些属性的补充
属性:initMethod
该属性相当于bean标签中的initMethod属性,用来设置bean被初始化所调用的方法。
如:
<bean id="someBean" class="" init-method="init" />
属性:destroyMethod
该属性相当于bean标签中的destroyMethod 属性,用来设置bean被销毁时,执行的操作。
如:
<bean id="someBean" class="" destroy-method="" />
注意:必须是容器正常关闭,销毁bean,才会调用销毁方法。
使用普通的junit测试,是不能关闭spring容器的。原因是,junit测试方法执行完毕,进程就结束了。所以导致spring容器不能正常关闭。
正常关闭容器的方案:
方案一: 容器使用完手动调用close方法,进行关闭。
如:
//获取容器
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ApplicationConfig.class)
// 关闭容器
ctx.close();
方案二:使用java1.7的新特性try(),来进行关闭。
如:
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ApplicationConfig.class)) {
SomeBean someBean = ctx.getBean(SomeBean.class);
System.out.println(someBean);
} catch (Exception e) {
e.printStackTrace();
}
方案三:使用Spring整合的junit单元测试。
如:
在测试类上面添加整合单元测试的注解.
注意:因为不再是加载配置文件的方式,所以需要使用classes属性加载配置类信息。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class)
@Scope注解
@Scope 该注解贴方法上表示是多例还是单例, 相当于配置文件中bean标签的属性scope 的作用。
@Scope("prototype") 表示多例
@Scope("singleton") 表示单例,如果配置@Scope标签,其默认值也是单例。
一般开发中中使用的是单例。
在DI中
需求二:把一个Bean注入给另外一个Bean中
方式一:直接调用方法的形式
@Bean
public SomeBean someBean() {
SomeBean someBean = new SomeBean();
System.out.println("someBean create");
someBean.setOtherBean(otherBean());
return someBean;
}
@Bean
public OtherBean otherBean() {
OtherBean otherBean = new OtherBean();
System.out.println("otherBean create");
return otherBean;
}
方式二:使用传参的形式
@Bean
public SomeBean someBean(OtherBean otherBean){
someBean.setOtherBean(otherBean);
return someBean;
}
@Bean
public OtherBean otherBean(){
return new OtherBean();
}
注意:
@Bean标签的生命周期
在一个@Configuration类中,重复调用一个方法,得到的对象默认也是singleton的
配置类/文件的导入
为何需要导入配置类/配置文件?
在我们实际开发中,项目很大的情况下,采用JavaConfig配置类的方式,为了便于开发和管理,我们的配置类也会有很多个。那么多个配置类之间,只有一个主配置类,这时就需要,把其他的配置类或者配置文件导入到主配置类上面。所以我们需要导入配置类或者配置文件。
导入配置类/配置文件需要哪些注解?
@Import注解
比如一个场景:我们之前项目中可以有多个 配置文件,那么在javaConfig中,也可能存在多个配置类,那么在很多个配置类的情况下,应该有一个主配置类,其他的配置都关联到主配置类上面,那么问题是怎么关联其他配置类呢? 使用@Import注解
@ImportResource注解
比如一个场景:有时候根据项目的需求和对项目的设计,我们可能需要配置类和配置文件混用的情况。例如,使用spring的AOP帮我们处理事务。可能使用javaConfig的方式,比较繁琐,修改代码太过于频繁,那么这块我们就可以继续使用xml文件的方式统一处理。那么这种情形就需要,在主配置类中关联配置文件了。
@Import注解的用法:
导入普通类
例子如下:
- 新建一个TestA
public class TestA {
public void fun(String str) {
System.out.println(str);
}
public void printName() {
System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName());
}
}
- 新建一个ImportConfig,在类上面加上@Configuration,加上@Configuration是为了能让Spring 扫描到这个类,并且直接通过@Import引入TestA类
@Import({TestA.class})
@Configuration
public class ImportConfig {
}
导入带有@Configuration的配置类
- 新建TestB
@Configuration
public class TestB {
public void fun(String str) {
System.out.println(str);
}
public void printName() {
System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName());
}
}
- 在ImportConfig.class里面直接引入TestB
@Import({TestA.class,TestB.class})
@Configuration
public class ImportConfig {
}
通过ImportSelector 方式导入的类
- 新建TestC.class
public class TestC {
public void fun(String str) {
System.out.println(str);
}
public void printName() {
System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName());
}
}
- 新建SelfImportSelector.class 实现ImportSelector 接口,注入TestC.class
public class SelfImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.test.importdemo.TestC"};
}
}
- ImportConfig上面引入SelfImportSelector.class
@Import({TestA.class,TestB.class,SelfImportSelector.class})
@Configuration
public class ImportConfig {
}
通过 ImportBeanDefinitionRegistrar 方式导入的类
- 新建TestD.class
public class TestD {
public void fun(String str) {
System.out.println(str);
}
public void printName() {
System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName());
}
}
- 新建SelfImportBeanDefinitionRegistrar.class,实现接口ImportBeanDefinitionRegistrar,注入TestD.class
public class SelfImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition root = new RootBeanDefinition(TestD.class);
registry.registerBeanDefinition("testD", root);
}
}
- ImportConfig类上加上导入SelfImportBeanDefinitionRegistrar.class
@Import({TestA.class,TestB.class,SelfImportSelector.class,
SelfImportBeanDefinitionRegistrar.class})
@Configuration
public class ImportConfig {
}
@ImportResource注解的用法:
有时候根据项目的需求和对项目的设计,我们可能需要配置类和配置文件混用的情况。
比如,使用spring的AOP帮我们处理事务。可能使用javaConfig的方式,比较繁琐,修改代码太过于频繁,那么这块我们就可以继续使用xml文件的方式统一处理。那么这种情形就需要,在主配置类中关联配置文件了。
使用@ImportResource注解
是引入spring配置文件.xml,实现xml配置的装载;
通过locations属性或者value属性,加载对应的xml配置文件,同时需要配合@Configuration注解一起使用,定义为配置类;引入的配置类必须在启动类中扫描到.
例子如下:
ApplicationConfig:
@Configuration
@ImportResource("classpath:cn/wolfcode/javaconfig/_07_import_resource/applicationContext.xml")
public class ApplicationConfig {
@Bean
public SomeBean someBean(){
return new SomeBean();
}
}
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="cn.wolfcode.javaconfig._07_import_resource.OtherBean"/>
</beans>
ImportResourceConfigTest:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class)
public class ImportResourceConfigTest {
@Autowired
private SomeBean someBean;
@Autowired
private OtherBean otherBean;
@Test
public void testImportResource() throws Exception {
System.out.println("someBean = " + someBean);
System.out.println("otherBean = " + otherBean);
}
}
资源文件的导入
为何需要导入资源文件?
Spring Boot 官网使用的是application.properties文件来实现文件的配置。但是实际情况下一个配置文件是不够用的,比如项目集成redis,mq,以及数据库比如mysql的时候,多个配置文件有利于开发及维护的管理。
导入资源文件需要学习哪些注解?
@PropertySource注解
大多数情况,项目需要配置的内容会比较复杂,这时候需要使用多个文件进行配置
@PropertySource是Spring boot为了方便引入properties配置文件提供的一个注解,可以标注在SpringBoot的启动类上,还可以标注在配置类(使用@Configuration标注的类)上。
@Value注解
@Value用于将外部的值动态地注入Bean中,相当于使用.xml中的如下的代码。通常用于对某个值进行单个的注入,毕竟如果对整个Bean内的值进行注入我们有更好用的@ConfigurtionProperties可以使用。
@ConfigurtionProperties注解
@Value通常用于对某个位置获取配置文件中的的某一个变量的时候,而@ConfigurationProperties用于javaBean和配置文件进行整体映射。上述的使用场景只是说通常情况,具体的使用,还是要根据@Value和@ConfigurationProperties各自的特点来进行判断。
@Value主要作用到变量上,而@ConfigurationProperties,主要用于方法。
@PropertySource注解的用法:
加载指定的属性文件;
注解的属性value,是一个String数组,内容为一个或多个xml/properties文件的路径(相对于classpath);
注解内部的属性使用,配合@value注解.
properties属性文件是key-value格式的,因此使用起来 是采用@value("${key}"); 这种形式
如:
@Configuration
@PropertySource(value = "classpath:db.properties",
ignoreResourceNotFound = false ,
encoding = "utf-8")
public class ApplicationConfig {
}
属性value
value值是设置需要加载的属性文件,可以一次性加载多个。
属性encoding
encoding用于指定读取属性文件所使用的编码,我们通常使用的是UTF-8;
属性ignoreResourceNotFound
ignoreResourceNotFound含义是当指定的配置文件不存在是否报错,默认是false;实际项目开发中,最好设置为false。
Environment对象的使用
回想mybaits配置文件中的 Environment ,表示mybatis的当前环境
Environment :代表了spring的当前环境,在该环境中只有两个东西
1,外部读入的资源文件 :如通过PropertySource 引入的资源文件
2,还有一个东西叫 profile
那么在javaconfig中如何使用 Environment 实现资源文件的注入呢 ?
@Autowired
private Environment evn;
@Configuration
//@PropertySource:把资源文件导入到Spring当前环境
@PropertySource("classpath:jdbc.properties")
public class ApplicationConfig
{
// Environment spring的环境对象,其中有两个东西:① propertis ② profile
@Bean
public MyDataSource myDataSource(Environment environment){
return new MyDataSource(environment.getProperty("jdbc.username")
,environment.getProperty("jdbc.password")
,environment.getProperty("jdbc.driverClassName")
,environment.getProperty("jdbc.url"));
}
}
@Value注解的用法:
我们可以点进去看一下@Value的源码,如下,我们发现它其中只包括一个String类型的变量,也就是说我们@Value注解后面只能接一个String类型的变量,无论什么内容都必须包括在双引号之下。
可注入类型:
- 字面量:包括布尔、数值型、字符串
- a. 字符串
@Value("字符串内容")
适用于一般的自变量、数组、文件资源和URL资源,Spring Boot会根据配置的变量类型对字符串内容进行转换。
例如:
@Value("true")
private Boolean blog;
- b. SpEL
@Value("#{SpEL内容}")。
通常SpEL内的字符串使用单引号''括起来。适用于需要计算的自变量、特殊数组、键值对、其他bean属性等。
例如:
@Value("#{12*15}")
private Integer clickNum;
- c. 注入配置文件中的数据
@Configuration
@PropertySource("classpath:jdbc.properties")
public class ApplicationConfig
{
//取资源文件中的内容,对于的值设置到该字段上
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.driverClassName}")
private String driverClassName;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.password}")
private String password;
@Bean
public MyDataSource myDataSource(){
return new MyDataSource(username,password,driverClassName,url);
}
注意:
如果spring的jar版本过低,必须配置PropertySourcesPlaceholderConfigurer
PropertySourcesPlaceholderConfigurer ,完成资源文件属性到对象参数的绑定,从spring环境对象中拿到资源文件配置,完成对 {key} 表达式的替换
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}
}
PropertySourcesPlaceholderConfigurer 类最终实现了一个 BeanFactoryPostProcessor 接口 ,他的作用是在BeanFactory创建每一个bean的过程当中加入一些自己的业务逻辑
而实现了BeanFactoryPostProcessor 接口的bean 会在其他所有bean 初始化之前去做一些事情 , 而 PropertySourcesPlaceholderConfigurer 就是在其他所有的bean初始化之前去寻找是否有 value 标签, 如果有完成属性的替换, 所以我们得需要保证 PropertySourcesPlaceholderConfigurer 在其他所有类之前被创建,所以需要在 该bean的定义方法是 static
- 数组(List)
字符串:与字面量类似,字符串之间使用英文的“,”隔开。
如:
@Value("Spring Boot, IDEA")
private List<String> key;
SpEL:比如我们需要字符串中有逗号的时候,使用SpEL就会更合适一些。
如:
@Value("#{'Spring Boot;IDEA'.split(';')}")
private List<String> key;
- 键值对(Map)
对于键值对我们不能直接使用字符串,可以使用SpEL,也可以配合配置文件来进行使用。
a. SpEL,注意第二层的括号一定要有:
@Value("#{{color:'red', language:'Chinese'}}")
private Map<String,String> otherProperties;
b. 如果结合配置文件使用,我们这里使用.properties格式和.yml格式的配置的方式有些不同,先来看.properties配置文件代码:
valuesMap={color:'red', language:'Chinese'}
代码中获取:
@Value("#{${valuesMap}}")
private Map<String,String> otherProperties;
如果配置文件使用yml,需要注意。
yml在进行单行键值对赋值的时候使用{},所以需要加上双引号"",因为单引号会将字符串内容转义,所以我们这里使用双引号。.yml配置文件代码:
valuesMap: "{color:'red', language:'Chinese'}"
代码中获取:
@Value("#{${valuesMap}}")
private Map<String,String> otherProperties;
- 注入其它bean属性
otherBean是一个Bean对象,name是它的一个属性,可以通过@Value(“#{otherBean.name}”)将这个bean的属性注入@Value声明的属性里
@Value("#{otherBean.name}")
private String someName;
- URL资源
与字面量中的字符串类型变量的注入差不多,支持多种形式,我们只示例字符串的形式:
如:
@Value("http://www.baidu.com")
private Resource address;
- 操作系统属性
操作系统的属性是静态全局变量systemEnvironment存入,可通过它获取到操作系统的属性。
@Value("#{systemProperties['os.name']}")
private String name;
- 文件资源
与字面量中的字符串类型变量的注入差不多,支持多种形式,我们只示例一种。注意这里我们一定要在文件路径前面加上classpath,并且将路径从文件夹处复制过来的时候,最好更改格式,把里面的“\”和“\”改为“/”。当然你也可以不改,Spring Boot也能够识别:
如:
@Value("classpath:D:/code/application/a.txt")
private Resource intro;
开发环境的切换
为何需要学习@Profile注解?
由于我们平时在开发中,通常会出现在开发的时候使用一个开发数据库,测试的时候使用一个测试的数据库,而实际部署的时候需要一个数据库。以前的做法是将这些信息写在一个配置文件中,当我把代码部署到测试的环境中,将配置文件改成测试环境;当测试完成,项目需要部署到现在了,又要将配置信息改成现在的。非常麻烦。
而使用了Profile之后,我们就可以分别定义3个配置文件,一个用于开发、一个用户测试、一个用户生产,其分别对应于3个Profile。当在实际运行的时候,只需给定一个参数来激活对应的Profile即可,那么容器就会只加载激活后的配置文件,这样就可以大大省去我们修改配置信息而带来的烦恼。
@Profile/@ActiveProfile注解
spring3.1开始引入,一般用于多环境配置
@Profile注解用于实现通过修改运行时参数,切换不同的开发环境
@Profile注解可以加在类上,也可以加载注入bean的方法上
@Profile只有一个默认的java.lang.String[]类型的属性value,因此它可以指定一个或多个配置名。
@Profile注解,匹配的名字才会去处理,没有匹配的则会被抛弃。
@Profile标签能够定义不同的运行环境,在测试的时候,可以通过
@ActiveProfile来指定运行环境
如何使用两个注解完成环境的切换
1.png开发环境配置:
@Configuration
@PropertySource("classpath:dev.properties")
@Profile("dev")
public class ApplicationDevConfig
{
@Bean
public MyDataSource myDataSource(Environment environment){
return new MyDataSource(environment.getProperty("jdbc.username") ,environment.getProperty("jdbc.password") ,environment.getProperty("jdbc.driverClassName") ,environment.getProperty("jdbc.url"));
}
}
测试环境配置:
@Configuration
@PropertySource("classpath:test.properties")
@Profile("test")
public class ApplicationTestConfig
{
@Bean
public MyDataSource myDataSource(Environment environment){
return new MyDataSource(environment.getProperty("jdbc.username") ,environment.getProperty("jdbc.password")
,environment.getProperty("jdbc.driverClassName")
,environment.getProperty("jdbc.url"));
}
}
主配置:
@Configuration
@Import({ApplicationDevConfig.class,ApplicationTestConfig.class})
public class ApplicationConfig
{
}
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class)
@ActiveProfiles("test")
public class JavaConfigTest {
@Autowired
private MyDataSource myDataSource;
@Test
public void someBeanTest(){
System.out.println(myDataSource.toString());
}
}
HelloWorld案例
第一个helloWorld案例:
pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/>
</parent>
<!--整合web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
HelloWordController:
@Controller
@SpringBootApplication
public class HelloWordController {
@RequestMapping("/helloboot")
@ResponseBody
public String hello() {
System.out.println("hello springBoot");
return "hello springBoot";
}
public static void main(String[] args) {
SpringApplication.run(HelloWordController.class,args);
}
}
运行原理分析:
1,使用 <parent> 继承spring-boot-starter-parent,引入基本的依赖管理配置;
2,引入spring-boot-starter-web,自动引入了springweb相关的包;
@SpringBootApplication 标签做了哪些事情 ?
@SpringBootApplication由三个主要的标签构成:@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan
1)@SpringBootConfiguration:本质就是一个@Configuration,代表这是spring容器的主配置类;
2)@EnableAutoConfiguration:开启自动配置,Springboot使用这个标签自动的把内置的符合条件的@Configuration类加载进入应用;
可以查看spring-boot-autoconfigure包中的META-INF/spring.factories文件中的配置项(原理,由@EnableAutoConfiguration标签引入的AutoConfigurationImportSelector类中,使用Spring的SpringFactoriesLoader类实现加载)
3)@ComponentScan:自动扫描;
总结一句话:
@SpringBootApplication:这个注解告诉springboot自动的去完成相关配置,包括基础类的加载,bean的扫描等等,这个后面详细介绍;简单理解为这个标签为我们的应用配置完成了很多基本功能;
SpringApplication.run 又做了哪些事情?
读取配置对象,配置相关自动配置对象,启动tomcat加载应用
总结一句话:
SpringApplication.run:这个是springboot为我们提供的最大的区别,在于springboot不再是一个web应用,需要我们自己去打包,部署,启动tomcat,springboot默认把tomcat打包到应用中,我们可以以正常的运行jar的方式来运行springboot应用;
springBoot项目独立运行方式:
注意:默认的Maven打包方式是不能正常的打包SpringBoot项目的,需要额外的引入打包插件,才能正常的对SpringBoot项目 打成jar包,以后只要拿到该jar包就能脱离IDE工具独立运行了
- 使用插件 springboot:run
添加插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
对于eclipse 项目要打包,引入插件之后 直接用 package 命令、
对于idea直接点击打包插件即可
- 打包独立运行
执行java -jar xxx.jar 运行(在字节码编译目录中会有打包好的 jar)
- 使用main方法启动
方式一:直接调用run方法
SpringApplication.run(StartModeController.class,args);
方式二:可以通过创建对象的方式来运行SpringApplication
SpringApplication application = new SpringApplication(StartModeController.class);
application.run(args);
方式三:通过builder完成:
SpringApplication application = new SpringApplicationBuilder(StartModeController.class).build();
application.run(args);
Springboot的优缺点:
-
创建独立的Spring应用程序
-
嵌入的Tomcat,无需部署WAR文件
-
简化Maven配置
-
自动配置Spring
-
提供生产就绪型功能,如日志,健康检查和外部配置
-
XML没有要求配置
-
非常容易和第三方框架集成起来;
缺点:
1,版本更新较快,可能出现较大变化;
2,因为约定大于配置,所以经常会出现一些很难解决的问题;
Springboot基本使用
应用构建
使用官网进行构建springboot项目
构建项目的地址:http://start.spring.io/
使用开发工具构建springboot项目
新建一个项目 ,选则 spring Initializr 选项。
填写相关的信息。选则相关的jar依赖。
热部署
直接添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
即可
可以在application.properties文件中设置一些属性
#默认排除的资源 spring.devtools.restart.exclude=static/**,templates/**,public/** #增加额外的排除资源
spring.devtools.restart.additional-exclude=public/** #处理默认配置排除之外的 spring.devtools.restart.enabled=false #禁用自动重启
热部署的原理
SpringBoot重启是restart重启,通过监控classpath的变化,如果classpath中的文件发生变化,即出发重启。springboot通过两个classpath来完成reload,一个basic classloader中加载不变的类,一个restart classloader中加载classpath中的类,重启的时候,restart classloader中的类丢弃并重新加载,所以
设置自动编译:如果您觉得 SpringBoot的热部署不怎么好用,那你也可以去掉热部署的依赖,使用自动编译:
1,开启自动编译 :setting -> Build... -> compiler -> Build project automatically
2,ctrl + alt + shift +/ -> registry -> compiler.automake.allow.when.app.running
设置banner
1,可以直接在classpath下添加一个banner.txt文件即可;
2,介绍springboot的配置文件。springboot默认在classpath中通过application.properties文件来进行配置;比如:
spring.main.banner-mode=off
就可以关闭banner输出
SpringApplication的参数
可以通过注入一个ApplicationArguments对象来完成参数的封装;
@Autowired
private ApplicationArguments args;
@RequestMapping("/args")
public String args() {
return args.getNonOptionArgs().toString();
}
Springboot开发WEB应用
集成日志框架
为什么要用日志?
1,比起System.out.println,日志框架可以把日志的输出和代码分离;
2,日志框架可以方便的定义日志的输出环境,控制台,文件,数据库;
3,日志框架可以方便的定义日志的输出格式和输出级别;
java常用日志框架类别介绍
- Log4j
Apache Log4j是一个基于Java的日志记录工具。它是由Ceki Gülcü首创的,现在则是Apache软件基金会的一个项目。 Log4j是几种Java日志框架之一。
- Log4j 2
Apache Log4j 2是apache开发的一款Log4j的升级产品。
- Commons Logging
Apache基金会所属的项目,是一套Java日志接口,之前叫Jakarta Commons Logging,后更名为Commons Logging。
- Slf4j
类似于Commons Logging,是一套简易Java日志门面,本身并无日志的实现。(Simple Logging Facade for Java,缩写Slf4j)。
- Logback
一套日志组件的实现(slf4j阵营)。
Java常用日志框架历史
1996年早期,欧洲安全电子市场项目组决定编写它自己的程序跟踪API(Tracing API)。经过不断的完善,这个API终于成为一个十分受欢迎的Java日志软件包,即Log4j。后来Log4j成为Apache基金会项目中的一员。
期间Log4j近乎成了Java社区的日志标准。据说Apache基金会还曾经建议sun引入Log4j到java的标准库中,但Sun拒绝了。
2002年Java1.4发布,Sun推出了自己的日志库JUL(Java Util Logging),其实现基本模仿了Log4j的实现。在JUL出来以前,log4j就已经成为一项成熟的技术,使得log4j在选择上占据了一定的优势。
接着,Apache推出了Jakarta Commons Logging,JCL只是定义了一套日志接口(其内部也提供一个Simple Log的简单实现),支持运行时动态加载日志组件的实现,也就是说,在你应用代码里,只需调用Commons Logging的接口,底层实现可以是log4j,也可以是Java Util Logging。
后来(2006年),Ceki Gülcü不适应Apache的工作方式,离开了Apache。然后先后创建了slf4j(日志门面接口,类似于Commons Logging)和Logback(Slf4j的实现)两个项目,并回瑞典创建了QOS公司,QOS官网上是这样描述Logback的:The Generic,Reliable Fast&Flexible Logging Framework(一个通用,可靠,快速且灵活的日志框架)。
Apache眼看有被Logback反超的势头,于2012-07重写了log4j 1.x,成立了新的项目Log4j 2。Log4j 2具有logback的所有特性。Log4j2与Log4j1发生了很大的变化,log4j2不兼容log4j1。
Springboot的默认日志使用
1,Springboot默认已经开启日志;默认的日志格式为:时间 日志级别 PID 线程名称 日志类 日志说明
2,Springboot的日志区别系统日志和应用日志;
3,Springboot推荐使用Logback作为日志框架
Logback使用方法(推荐使用logback自己的配置文件)
1,springboot默认支持logback.xml或者logback-spring.xml,推荐使用logback-spring.xml,springboot会增加额外功能;
2,可以通过logging.config=classpath:mylogback.xml指定自己的logback配置文件(不推荐);
3,一个典型的logback配置文件:
Logback使用介绍:
使用日志框架的步骤:
a. 在resources文件夹下面创建一个xml文件,文件的名称使用 logback-spring.xml。
b. 在logback-spring.xml文件中编写日志文件的内容:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!--日志的输出方式: 输出到控制台-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd-HH:mm:ss.SSS} %level [%thread]-%class:%line>>%msg%n</pattern>
</encoder>
</appender>
<!--配置日志的类型以及输出日志的级别: 使用系统的日志, 日志级别为info-->
<root level="info">
<!--必须要和appender中的name属性的值保持一致-->
<appender-ref ref="STDOUT"/>
</root>
</configuration>
c.启动项目即可。
参考日志格式:
%d{yyyy-MM-dd-HH:mm:ss} %level [%thread]-%class:%line >> %msg %n
格式中的标识符组成:
%logger{n}: 输出Logger对象类名,n代表长度
%class{n}: 输出所在类名,
%d{pattern}或者date{pattern}: 输出日志日期,格式同java %L/line: 日志所在行号
%m/msg: 日志内容
%method: 所在方法名称
%p/level: 日志级别
%thread: 所在线程名称
关于logback-spring.xml配置文件的详细解析:
1,configuration:Logback配置根元素
属性包括:
a,scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
b,scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
c,debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
子元素:
<contextName>:上下文名字;
<property>:定义属性,可以使用${}在配置文件中使用;
2,appender:在logback中是用于具体记录日志的组件,可以用来配置日志记录方式,日志记录格式等;
属性包括:
name:appender的名字,用于下面在配置日志的时候指定;
class:使用到的appender类;
常见的appender:
1,ch.qos.logback.core.ConsoleAppender:输出到控制台;
<!--输出到控制台-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!--日志格式化-->
<encoder>
<pattern>%d{yyyy-MM-dd-HH:mm:ss.SSS} %level [%thread]-%class:%line>>%msg%n</pattern>
</encoder>
</appender>
2,ch.qos.logback.core.FileAppender:输出到文件;
file:被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。
append:文件结尾,如果是 false,清空现存文件,默认是true。
encoder:对日志进行格式化
<!--输出到文件中-->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<encoder>
<pattern>%d{yyyy-MM-dd-HH:mm:ss.SSS} %level [%thread]-%class:%line>>%msg%n</pattern>
</encoder>
<append>false</append>
<file>mylog.log</file>
</appender>
3,ch.qos.logback.core.rolling.RollingFileAppender:输出到文件,可以配置滚动策略,当日志达到某个条件之后分文件记录;
append:如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。
rollingPolicy:滚动策略,涉及文件移动和重命名。
常用滚动策略:
ch.qos.logback.core.rolling.TimeBasedRollingPolicy:按照时间控制来控制记录文件;
fileNamePattern:文件名称格式,以%d{pattern};
maxHistory: 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。(以文件最小时间为准)
<!--输出到文件,可以设置文件滚动(分割)条件
按照时间分割-->
<appender name="RollingTimeFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{yyyy-MM-dd-HH:mm:ss.SSS} %level [%thread]-%class:%line>>%msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>mylog.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy:按照时间和大小控制记录文件;
fileNamePattern:文件名称格式,可以使用%i来控制索引编号;
maxFileSize:这是活动文件的大小,默认值是10MB
maxHistory:可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。(以文件最小时间为准)
<!--输出到文件,可以设置文件滚动(分割)条件
按照文件大小分割-->
<appender name="RollingSizeFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{yyyy-MM-dd-HH:mm:ss.SSS} %level [%thread]-%class:%line>>%msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>mylog.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>1kb</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
<logger>元素:用来设置某一个包或具体的某一个类的日志打印级别及对应的appender;
属性:
name: 用来指定受此loger约束的某一个包或者具体的某一个类;
子节点:
appender-ref:可以配置多个,标识这个appender将会添加到这个logger
如:
<!--使用自己的日志-->
<logger level="trace" name="cn.wolfcode.springboot">
<appender-ref ref="STDOUT"/>
</logger>
<root>元素:特殊的logger,代表根配置,如果没有单独指定日志包层级,都默认使用root定义的日志输出级别;
属性:
level:指定日志级别;
如:
<!--使用系统的日志-->
<root level="info">
<appender-ref ref="STDOUT"/>
</root>
在代码中可以使用自定义的Logger对象,输出日志信息。
实现的方式一:通过LoggerFactory工厂对象创建Logger对象
// 方式一:自己定义一个Logger对象
private static final Logger LOGGER = LoggerFactory.getLogger(LogController.class);
@RequestMapping("/log")
@ResponseBody
public String log() {
// 方式一的出生日志的方式
LOGGER.info("方式一:我是info");
LOGGER.debug("方式一:我是debug");
LOGGER.error("方式一:我是error");
return "log";
}
实现的方式二:使用注解的方式
@Slf4j // 方式二:使用注解来输出日志,前提是需要依赖lombok包
public class LogController {
@RequestMapping("/log")
@ResponseBody
public String log() {
// 方式二的输出方式
log.trace("方式二: trace信息");
log.debug("方式二: debug信息");
log.info("方式二: info{}信息", 33333);
log.error("方式二: error信息");
return "log";
}
SpringBoot项目的目录结构
我们通过idea创建出来的SpringBoot项目,会生成一个标准的项目的目录结构。
2.pngapplication.properties优先级
一个项目中可以有多个application.properties文件存放在不同目录中,此时他们会遵循固定的优先级来处理有冲突的属性配置
-
项目/config/application.properties
-
项目/application.properties
-
classpath:config/application.properties
-
classpath:application.properties
一般都在classpath:application.properties做配置,其他方式不使用
静态资源
-
默认情况下,Springboot会从classpath下的 /static , /public , /resources , /META-INF/resources下加载静态资源
-
可以在application.properties中配置spring.resources.staticLocations属性来修改静态资源加载地址
spring.resources.static-locations=classpath:/hello
注意设置完以后,会覆盖之前的设置。
- 因为应用是打成jar包,所以之前的src/main/webapp就作废了,如果有文件上传,那么就的必须去配置图片所在的路径
spring.resources.static-locations=classpath:/static,file:///Users/tony/Desktop/image/
集成Freemarker
为何要集成Freemarker
1.性能。velocity应该是最好的,其次是jsp,普通的页面freemarker性能最差(虽然只是几毫秒到十几毫秒的差距)。但是在复杂页面上(包含大量判断、日期金额格式化)的页面上,freemarker的性能比使用tag和el的jsp好。
2、宏定义比jsp tag方便
3、内置大量常用功能。比如html过滤,日期金额格式化等等,使用非常方便
4、支持jsp标签
5、可以实现严格的mvc分离
6、易学易用
我是看了一天文档就用得挺熟练了,freemarker文档写得太好了,例子丰富,照做一遍全都会了。
7、功能强大
比Velocity强大多了,还支持JSP Tag。不过最有意义的是macro功能,可以自定义常用的macro,实现页面常规操作的可复用性。
8、报错信息友好
很多应用服务器的JSP报错信息是无法定位到源代码行的。不过freemarker报错定位很准确,丝毫不差,而且信息丰富,一看就知道怎么回事(虽然那个黄黄的页面看起来让人难受)
与JSP相比,FreeMarker的一个优点在于不能轻易突破模板语言开始编写Java代码,因此降低了领域逻辑漏进视图层的危险几率。但缺点是需要一点附加配置来将其平稳地集成到应用程序中,一些IDE(集成开发环境)可能并不完全支持它,当然还有开发者或设计者也许需要学习一门陌生的模板语言。相关的JAR文件将要添加到WEB-INF/lib(在需要的时候,它们包含在Spring中) 。
Freemarker的一些常见语法回顾
获取当前日期,必须要格式化显示
<input value="${.now?string('yyyy-MM-dd')}"/>
获取Controller控制器中共享给页面的对象中的普通属性
<input id="${user.name}" name="${user.name}" value="${user.name}"/>
获取Controller控制器中共享给页面的对象中的日期类型属性,必须要格式化显示
<input value="${(user.birthday?string('yyyy-MM-dd'))!}"/>
获取Controller控制器中共享给页面的对象中的boolean类型属性,必须要处理显示
<input value="${user.active?string("是","否")}">
对null 或者不存在的对象进行取值,可以设置默认值
例:${var!'我是默认值'}
即,有值时显示正常值,无值时显示默认值
如果需要判断,使用if标签判断
<#--判断是否小于25-->
<#if user.age < 25 >
young
<#else>
old
</#if>
<br/>
<#--判断是否大于25-->
<#if user.age > 25 >
old
<#else>
young
</#if>
如果需要判断,可以使用switch标签判断
<#switch user.age>
<#case 30>
very old
<#break >
<#case 25>
old
<#break >
<#default >
young
<#break >
</#switch>
获取Controller控制器中共享给页面的list集合,必须要遍历进行显示
<#list userList as person>
<tr>
<td>${person.name}</td>
<td>${(person.age>25) ?string("old","young")}</td>
<td>${(person.birthday?string('yyyy-MM-dd hh:mm:ss'))!}</td>
<td>${person.active?string("是","否")}</td>
</tr>
</#list>
userList 这个名字必须和controller控制器中的共享的key保持一致。
获取Controller控制器中共享给页面的map集合,必须要遍历进行显示
<#list map?keys as key>
${key}:${map[key]}<br/>
</#list>
map 这个名字必须和controller控制器中的共享的key保持一致。
自定义变量
<#assign num = 100 />
自定义的变量参与计算
<font color="red"> ${num * 10} </font><br>
处理list集合
first: 取List值第一个值<br/>
<span>${userList?first}</span>
last: 取List值最后一个值<br/>
<span>${userList?last}</span>
size: 集合大小<br/>
<span>${userList?size}</span>
sort: 对集合进行排序<br/>
<#list userList?sort as item>
<span>${item.name}</span>
</#list>
<br/>
如何集成Freemarker
- 导入相关的依赖:
<!--整合freemarker-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
- 在resources/templates目录下面创建对应的freemarker模板。
freemarker.ftl:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<table>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>备注</th>
<th>生日</th>
<th>是否激活</th>
</tr>
<#list userList as person>
<tr>
<td>${person.name}</td>
<td>${person.age}</td>
<td>${(person.age>25) ?string("old","young")}</td>
<td>${(person.birthday?string('yyyy-MM-dd hh:mm:ss'))!} </td>
<td>${person.active?string("是","否")}</td>
</tr>
</#list>
</body>
</table>
- 编写相应的Controller
FreemarkerController:
@Controller
@RequestMapping("/freemarker")
public class FreemarkerController {
@RequestMapping("/hello")
public String hello(ModelMap map) {
User u = new User();
u.setName("haozz");
u.setAge(24);
u.setPassword("qwerty");
u.setBirthday(new Date());
u.setBirthday(null);
u.setActive(false);
User u1 = new User();
u1.setName("nico robin");
u1.setAge(35);
u1.setPassword("qwerty");
u1.setBirthday(new Date());
u1.setActive(true);
List<User> userList = new ArrayList<>();
userList.add(u);
userList.add(u1);
map.addAttribute("userList",userList);
return "freemarker";
}
集成原理
SpringBoot的自动配置中含有FreeMarkerAutoConfiguration配置对象,该配置对象又导入了 FreeMarkerReactiveWebConfiguration配置对象,在里面创建了FreeMarkerConfigurer和FreeMarkerViewResolve 两个对象交给Spring管理,并且设置了默认的属性值,这些属性值来源于FreeMarkerProperties对象
常见的属性配置
spring.freemarker.enabled=true: 是否开启freemarker支持
spring.freemarker.allow-request-override: 是否允许request中的属性覆盖model中同名属性,默认false
spring.freemarker.allow-session-override: 是否允许session中的属性覆盖model中同名属性,默认false
spring.freemarker.cache: 是否支持模板缓存,默认false
spring.freemarker.charset=UTF-8: 模板编码
spring.freemarker.content-type=text/html: 模板contenttype
spring.freemarker.expose-request-attributes: 是否开启request属性暴露,默认false
spring.freemarker.expose-session-attributes: 是否开启session属性暴露,默认false
spring.freemarker.expose-spring-macro-helpers: 是否开启spring的freemarker宏支持,默认为false
spring.freemarker.prefer-file-system-access: 默认为true,支持实时检查模板修改
spring.freemarker.prefix: 加载模板时候的前缀
spring.freemarker.settings.*: 直接配置freemarker参数
spring.freemarker.suffix: 模板文件后缀
spring.freemarker.template-loader-path=classpath:/templates/: 模板加载地址
集成Thymeleaf
为何要集成Thymeleaf
Thymeleaf是个XML/XHTML/HTML5模板引擎,可以用于Web与非Web应用。
Thymeleaf的主要目标在于提供一种可被浏览器正确显示的、格式良好的模板创建方式,因此也可以用作静态建模。你可以使用它创建经过验证的XML与HTML模板。相对于编写逻辑或代码,开发者只需将标签属性添加到模板中即可。接下来,这些标签属性就会在DOM(文档对象模型)上执行预先制定好的逻辑。Thymeleaf的可扩展性也非常棒。你可以使用它定义自己的模板属性集合,这样就可以计算自定义表达式并使用自定义逻辑。这意味着Thymeleaf还可以作为模板引擎框架。
thymeleaf优点:静态html嵌入标签属性,浏览器可以直接打开模板文件,便于前后端联调。
Thymeleaf模板引擎是springboot中默认配置,与freemarker相似,可以完全取代jsp,在springboot中,它的默认路径是src/main/resources/templates 静态文件css, js 等文件默认路径是src/main/resources/static,所有项目中如果没有这个目录需要手动加上了
Thymeleaf的一些常见语法回顾
获取Controller控制器中共享给页面的对象中的普通属性
<input th:value="${user.age}"/>
获取Controller控制器中共享给页面的对象中的日期类型属性
<input th:value="${#dates.format(user.birthday,'yyyy-MM-dd')}"/>
获取Controller控制器中共享给页面的对象中的boolean类型属性
<td th:text="${person.flag}"></td>
获取Controller控制器中共享给页面的list集合,必须要遍历进行显示
<tr th:each="person:${userList}">
<td th:text="${person.name}"></td>
<td th:text="${person.age ge 25} ? old : young"></td>
<td th:text="${#dates.format(person.birthday,'yyyy-MM-dd hh:mm:ss')}"></td>
<td th:text="${person.flag}"></td>
</tr>
如果需要判断,使用if标签判断
<div th:if="${user.age lt 25}">young</div>
如果需要判断,可以使用switch标签判断
<div th:switch="${user.name}">
<p th:case="'haozz'">最帅的</p>
<p th:case="'nico robin'">妮可罗宾</p>
<p th:case="'nami'">娜美</p>
</div>
处理表单(action跳转地址,method设置请求方式,object抽取相同的部分)
<form th:action="@{/th/postform}" th:object="${user}" th:method="post">
<!--th:field="*{name}"相当于id="name" name="name" value="具体的name值"-->
<input type="text" th:field="*{name}"/>
<input type="submit"/>
</form>
处理html内容被解析,转译
<span th:utext="${user.desc}"></span>
th:utext 会进行转译<br/>
th:text 不会进行转译<br/>
常见的th标签汇总
th标签汇总图.png如何集成Thymeleaf
- 导入相关的依赖。
<!--整合thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
- 在resources/templates目录下面创建对应的thymeleaf模板。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml" xmlns:method="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<table>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>备注</th>
<th>生日</th>
<th>婚否</th>
</tr>
<tr th:each="person:${userList}">
<td th:text="${person.name}"></td>
<td th:text="${person.age}"></td>
<td th:text="${person.age ge 25} ? old : young"></td>
<td th:text="${#dates.format(person.birthday,'yyyy-MM-dd hh:mm:ss')}"></td>
<td th:text="${person.active}"></td>
</tr>
</table>
</body>
</html>
- 编写相应的Controller
@Controller
@RequestMapping("/freemarker")
public class FreemarkerController {
@RequestMapping("/hello")
public String hello(ModelMap map) {
User u = new User();
u.setName("haozz");
u.setAge(24);
u.setPassword("qwerty");
u.setBirthday(new Date());
u.setBirthday(null);
u.setActive(false);
User u1 = new User();
u1.setName("nico robin");
u1.setAge(35);
u1.setPassword("qwerty");
u1.setBirthday(new Date());
u1.setActive(true);
List<User> userList = new ArrayList<>();
userList.add(u);
userList.add(u1);
map.addAttribute("userList",userList);
return "freemarker";
}
集成原理
SpringBoot的自动配置中含有ThymeleafAutoConfiguration配置对象,该配置对象的里面创建了视图解析器ThymeleafViewResolver和模板引擎 SpringTemplateEngine 和模板解析器SpringResourceTemplateResolver 三个对象交给Spring管理,并且设置了默认的属性值,这些属性值来源于ThymeleafProperties对象
常见属性设置
spring.thymeleaf.prefix=classpath:/templates/ 设置thymeleaf路径默认为src/main/resources/templates
spring.thymeleaf.suffix=.html 设置thymeleaf模板后缀
spring.thymeleaf.content-type=text/html
spring.thymeleaf.cache=false 是否开启缓存默认为true
spring.thymeleaf.mode=LEGACYHTML5 设置thymeleaf严格校验
spring.thymeleaf.encoding=UTF-8 设置编码
了解@ConfigurtionProperties注解的用法:
属性名匹配规则
@ConfigurationProperties的属性名匹配规则(Relaxed binding):Spring Boot可以对下面四种类型的命名进行自动匹配,也就是说配置文件和类文件中的名称不需要完全相同,使用下面的任意两种,系统就会将二者匹配上
标准方式:变量之间通过下一个单词的首字母大写的方式隔开,
eg:student.isFemale
使用“-”进行单词之间的连接:
eg:student.is-female
使用“_”进行单词之间的连接:
eg:student.is_female
全部大写,使用“_”连接:通常对系统属性变量推荐这种写法。
eg:STUDENT_IS_FEMALE
属性ignoreInvalidFields
标记当有非法字段绑定到这个对象时可以被忽略。"非法"指根据使用的binder非法,通常这意味着字段类型错误(或者无法强制转为正确的类型).默认false。这意味着不能忽略字段非法,所以只能报错
属性ignoreUnknownFields
标记当有未知字段绑定到这个对象时可以被忽略。未知字段可能是属性中的一个符号错误。默认true。
属性prefix/value
要绑定到这个对象的有效的属性的名字前缀。value 和 prefix 是同义词
案例如下:
@ConfigurationProperties(prefix = "spring.datasource")
@Setter@Getter@ToString
@Repository
public class MyDataSource {
private String username;
private String url;
private String password;
private String driverClassName;
}
application.properties:
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///crm
spring.datasource.username=root
spring.datasource.password=root123
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
@Controller
@SpringBootApplication
public class DataSourcesController {
@Autowired
private MyDataSource myDataSource;
@RequestMapping("/source")
@ResponseBody
public String hello() {
return myDataSource.toString();
}
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
}
集成Druid连接池
为何要集成Druid
1、性能方面 hikariCP>druid>tomcat-jdbc>dbcp>c3p0 。hikariCP的高性能得益于最大限度的避免锁竞争。
2、druid功能最为全面,sql拦截等功能,统计数据较为全面,具有良好的扩展性。
3、综合性能,扩展性等方面,可考虑使用druid或者hikariCP连接池,比较方便对jdbc接口进行监控跟踪等。
4、可开启prepareStatement缓存,对性能会有大概20%的提升。
psCache是connection私有的,所以不存在线程竞争的问题,开启pscache不会存在竞争的性能损耗。
psCache的key为prepare执行的sql和catalog等,value对应的为prepareStatement对象。开启缓存主要是减少了解析sql的开销。
5、3p0历史悠久,代码及其复杂,不利于维护。并且存在deadlock的潜在风险。
6、Druid可以打印SQL,慢查询方面的日志
回顾Druid连接池在SSM框架中的用法
在pom.xml文件中导入依赖:
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
<scope>runtime</scope>
</dependency>
<!-- druid连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.26</version>
</dependency>
<!--spring jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
spring的主配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--组件扫描器-->
<context:component-scan base-package="cn.wolfcode.crm"/>
<!--自动DI解析器-->
<context:annotation-config/>
<!--加载数据库的配置信息-->
<context:property-placeholder location="classpath:db.properties" system-properties-mode="NEVER"/>
<!-- 连接池对象 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="username" value="${jdbc.username}"/>
<property name="url" value="${jdbc.url}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
如何集成Druid
方式一
pom.xml:
<!--整合DrudiDataSource-->
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
application.properties:
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///crm
spring.datasource.username=root
spring.datasource.password=root123
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
方式二
pom.xml:
<!--整合DrudiDataSource-->
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
application.properties:
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql:///crm
spring.datasource.druid.username=root
spring.datasource.druid.password=root123
测试案例:
@RestController
public class DataSourceController {
@Autowired
private DataSource dataSource;
@RequestMapping("/dataSource")
public String showData() throws SQLException {
return dataSource.getConnection().toString();
}
}
集成原理
SpringBoot的自动配置中含有DataSourceAutoConfiguration配置对象,该配置对象含有个内部类,该内部类也是配置 类,当容器中没有连接池对象时,就会Generic方式来创建连接池对象交给Spring管理,用到是属性值来源于 DataSourceProperties对象
集成Junit框架
为何要集成Junit
我们在开发的过程中,肯定需要不断的对我们自己写的代码进行测试。所以离不开Junit单元测试。
- 导入依赖:
<!--junit-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
- 准备配置类
public class SomBean {
public SomBean() {
System.out.println("构造器");
}
}
@Configuration
public class JunitConfig {
@Bean
public SomBean somBean(){
return new SomBean();
}
}
- 编写测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes = JunitConfig.class)
public class App
{
@Autowired
private SomBean somBean;
@Test
public void test(){
System.out.println("somBean = " + somBean);
}
}
集成MyBatis框架
为何要集成MyBatis
1、简单易学
mybatis本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
2、灵活
mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。
3、解除sql与程序代码的耦合
通过提供DAL层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
4、提供映射标签,支持对象与数据库的orm字段关系映射
5、提供对象关系映射标签,支持对象关系组建维护
6、提供xml标签,支持编写动态sql(主要优势之一)。
回顾MyBatis在之前SSM框架中集成的方式
pom.xml:
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
applicationContext.xml:
<!-- 配置SessionFactory -->
<bean id="sessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 1:连接池 -->
<property name="dataSource" ref="dataSource"/>
<!-- 2:读取MyBatis总配置文件 -->
<property name="configLocation" value="classpath:mybatis.xml"/>
<!-- 3:配置别名扫描 -->
<property name="typeAliasesPackage" value="cn.wolfcode.crm.domain"/>
<!-- 4:加载mapper文件 -->
<property name="mapperLocations" value="classpath:cn/wolfcode/crm/mapper/*Mapper.xml"/>
<!--集成pagehelper-->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<!-- 这里的几个配置主要演示如何使用,如果不理解,一定要去掉下面的配置 -->
<property name="properties">
<value>
helperDialect=mysql
reasonable=true
pageSizeZero=true
</value>
</property>
</bean>
</array>
</property>
</bean>
<!--Mapper接口代理扫描器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.wolfcode.crm.mapper"/>
</bean>
mybatisCfg.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>
<settings>
<!--配置允许懒加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--取消关联查询积极性-->
<setting name="aggressiveLazyLoading" value="false"/>
<!--哪些方法触发关系查询-->
<setting name="lazyLoadTriggerMethods" value="clone"/>
</settings>
</configuration>
如何集成MyBatis
pom.xml:
<!--整合mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
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>
<settings>
<!--配置允许懒加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--取消关联查询积极性-->
<setting name="aggressiveLazyLoading" value="false"/>
<!--哪些方法触发关系查询-->
<setting name="lazyLoadTriggerMethods" value="clone"/>
</settings>
</configuration>
application.properties:
#mybatis
#给包起别名,因为使用MBG生成的mapper,
#sql里面默认是使用的类的全限定名,因此我们可以不配别名,都使用全名
#mybatis.type-aliases-package=cn.wolfcode.springboot.integrase._5_mybatis.domain
#加载mybatis的主配置文件
mybatis.config-location=classpath:mybatis-config.xml
我们也可以去掉MyBatis的主配置文件:把懒加载的配置配置到SpringBoot的配置文件中:
如:
mybatis.configuration.lazy-load-trigger-methods=clone
mybatis.configuration.aggressive-lazy-loading=false
mybatis.configuration.lazy-loading-enabled=true
domain:
@Setter
@Getter
@ToString
public class Department {
private Long id;
private String name;
private String sn;
}
mapper:
public interface DepartmentMapper {
List<Department> selectAll();
}
mapper.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="cn.wolfcode.springboot._06_mybatis.mapper.DepartmentMapper">
<select id="selectAll"
resultType="cn.wolfcode.springboot._06_mybatis.domain.Department">
select id,name,sn from department
</select>
</mapper>
主配置类:
注意:通过@MapperScan扫描器来扫描mapper的接口
@SpringBootApplication
@MapperScan("cn.wolfcode.springboot._06_mybatis.mapper")
public class MyBatisConfig {
}
App测试类:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyBatisConfig.class)
public class App
{
@Autowired
private DepartmentMapper departmentMapper;
@Test
public void test(){
List<Department> list = departmentMapper.selectAll();
list.forEach(System.out::println);
}
}
常见属性
#懒加载
mybatis.configuration.lazy-loading-enabled=true mybatis.configuration.lazy-load-trigger-methods=clone
mybatis.mapper-locations=classpath:cn/wolfcode/xxx/mapper/*Mapper.xml
mybatis.type-aliases-package=cn.wolfcode.xxx.domain #连接池对象不用配置,会自动注入
集成PageHelper
回顾PageHelper在之前SSM框架中集成的方式
pom.xml:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.6</version>
</dependency>
配置文件中配置pageHelper
方式一:分页插件配置在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>
<settings>
<!--配置允许懒加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--取消关联查询积极性-->
<setting name="aggressiveLazyLoading" value="false"/>
<!--哪些方法触发关系查询-->
<setting name="lazyLoadTriggerMethods" value="clone"/>
</settings>
<!--配置在Spring中-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="reasonable" value="true"/>
<property name="helperDialect" value="mysql"/>
<property name="pageSizeZero" value="true"/>
</plugin>
</plugins>
</configuration>
方式二:分页插件配置在spring的配置文件中
<!-- 配置SessionFactory -->
<bean id="sessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 1:连接池 -->
<property name="dataSource" ref="dataSource"/>
<!-- 2:读取MyBatis总配置文件 -->
<property name="configLocation" value="classpath:mybatis.xml"/>
<!-- 3:配置别名扫描 -->
<property name="typeAliasesPackage" value="cn.wolfcode.crm.domain"/>
<!-- 4:加载mapper文件 -->
<property name="mapperLocations" value="classpath:cn/wolfcode/crm/mapper/*Mapper.xml"/>
<!--集成pagehelper-->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<!-- 这里的几个配置主要演示如何使用,如果不理解,一定要去掉下面的配置 -->
<property name="properties">
<value>
helperDialect=mysql
reasonable=true
pageSizeZero=true
</value>
</property>
</bean>
</array>
</property>
</bean>
如何集成PageHelper
pom.xml:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.10</version>
</dependency>
application.properties:
#pageHelper
pagehelper.helper-dialect=mysql
pagehelper.reasonable=true
pagehelper.page-size-zero=true
集成事务管理
回顾事务管理在之前SSM框架中的使用
方式一:在spring的xml配置文件中
<!-- 配置事务 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="list*" read-only="true"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* cn.wolfcode.crm.service.*Service.*(..))" id="txPoint"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
</aop:config>
方式二:使用注解的方式
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 启用事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
Spring中注解的方式@Transactional标注事务方法。为了将方法定义为支持事务处理,可以在方法上添加@Transactional注解。根据Spring AOP基于代理机制,只能标注公有方法。如果在类上标注@Transactional注解,那么这个类中所有公有方法都会被定义为支持事务。
代码如下:
//添加事务注解
//1.使用 propagation 指定事务的传播行为, 即当前的事务方法被另外一个事务方法调用时
//如何使用事务, 默认取值为 REQUIRED, 即使用调用方法的事务
//REQUIRES_NEW: 事务自己的事务, 调用的事务方法的事务被挂起.
//2.使用 isolation 指定事务的隔离级别, 最常用的取值为 READ_COMMITTED
//3.默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚. 也可以通过对应的
//属性进行设置. 通常情况下去默认值即可.
//4.使用 readOnly 指定事务是否为只读. 表示这个事务只读取数据但不更新数据,
//这样可以帮助数据库引擎优化事务. 若真的事一个只读取数据库值的方法, 应设置 readOnly=true
//5.使用 timeout 指定强制回滚之前事务可以占用的时间.
@Transactional(propagation=Propagation.REQUIRES_NEW,
isolation=Isolation.READ_COMMITTED,
noRollbackFor={UserAccountException.class},
rollbackFor = IOException.class,
readOnly=false,
timeout=3)
@Override
public void purchase(String username, String isbn) {}
注意有一种情况事务会失效:
@Transactional
@Override
public void purchase(String username, String isbn) {
this.update(username, isbn);
}
@Transactional
public void update(String username, String isbn) {
// TO DO
}
失效的原因是:
因为spring事务是基于aop的代理机制,当方法中调用this本身的方法时候即使在this的方法标明事务注解,但是事务注解会失效。
解决办法:
在配置中添加如下内容
<!--开启aspectj代理,并暴露aop代理到ThreadLocal-->
<aop:aspectj-autoproxy expose-proxy="true"/>
将上述调用的地方改成如下:
@Transactional
@Override
public void purchase(String username, String isbn) {
((BookShopServiceImpl)AopContext.currentProxy()).update(username, isbn);
}
如何集成事务管理
方式一: 使用注解的方式
在Service的实现类上面添加@Transactional注解即可。
如果是使用的springboot的较低版本,需要在配置类上面使用@EnableTransactionManagement开启注解,可以理解为相当于
<tx:annotation-driven transaction-manager="transactionManager"/>
方式二:使用xml文件的方式
- 提供一个配置事务的xml文件。
<!-- 3:配置事务管理 -->
<!-- 3w: what where when -->
<!-- 3.1:what 做什么增强 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 3.2:where: 在哪个包哪个类哪个方法下做增强 -->
<aop:config>
<aop:pointcut expression="execution( * cn.wolfcode.crm.service.*Service.*(..))" id="pc"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>
<!-- 3.3:when :在什么时候做增强 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="list*" read-only="true"/>
<tx:method name="select*" read-only="true"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="check*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
2.在主配置文件中通过@ImportResources
@ImportResource("classpath:tx.xml")
集成FastJSON
为何要集成FastJSON
fastjson可以只依赖于jdk环境,其他的条件可以不用依赖
fastjson可以进行序列化和反序列换(所谓的序列化就是将对象转换成对应的json字符串,反序列化就是将json字符串进行转换成对应的对象)
fastjson进行json转换的的速度远远大于jackjson
回顾FastJSON在之前SSM框架中的使用
pom.xml:
<!--fastJson解析json -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.29</version>
</dependency>
mvc.xml
<mvc:annotation-driven >
<mvc:message-converters register-defaults="true">
<!-- 配置Fastjson支持 -->
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json</value>
<value>application/xml;charset=UTF-8</value>
</list>
</property>
<property name="features">
<list>
<!--配置返回json字符串是返回null值 -->
<value>WriteMapNullValue</value>
<value>QuoteFieldNames</value>
<!-- 配置日期格式化 -->
<value>WriteDateUseDateFormat</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
@jsonFiled(format="yyyy-MM-dd HH:mm:ss")
private Date creadate;
如何集成FastJSON
pom.xml:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.29</version>
</dependency>
方式一:
在一个配置类中定义一个bean
@Bean
public HttpMessageConverters fastjsonHttpMessageConverters(){
//定义一个convert转换消息的对象
FastJsonHttpMessageConverter converter=new FastJsonHttpMessageConverter();
//添加fastjson的配置信息,比如是否要格式化返回的json信息
FastJsonConfig fastJsonConfig=new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
//解决fastjson 中文乱码
List<MediaType> fastMediaTypes = new ArrayList<MediaType>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
//将配置信息添加到convert中
converter.setFastJsonConfig(fastJsonConfig);
//将编码配置加入convert中
converter.setSupportedMediaTypes(fastMediaTypes);
HttpMessageConverter<?> httpMessageConverter=converter;
return new HttpMessageConverters(converter);
}
方式二:
让配置类实现 接口 WebMvcConfigurer ,然后覆盖一个方法configureMessageConverters 如:
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 1、需要先定义一个 convert 转换消息的对象;
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
//2、添加fastJson 的配置信息,比如:是否要格式化返回的json数据;
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(
SerializerFeature.PrettyFormat
);
//3、在convert中添加配置信息.
fastConverter.setFastJsonConfig(fastJsonConfig);
//4、将convert添加到converters当中.
converters.add(fastConverter);
}
注解:
// 输出到页面,格式化日期格式
@JSONField(format = "yyyy-MM-dd")
// serialize:是否需要序列化属性.如果设置成false,页面上就不会出现
@JSONField(serialize = false)
集成Jackson
SPringBoot已经集成了Jackson,所以我们不需要导入依赖。可直接使用。
private Long id;
private String name;
// 输出日期到表单,进行格式化 。注意:必须要带上时区,不然时间会减少一天
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
// 接收表单传来到日期,进行格式化。
@DateTimeFormat(pattern ="yyyy-MM-dd")
private Date hiredate;
@JsonIgnore // 忽略该属性在浏览器中进行显示数据
private String password;
集成SpringData JPA
JPA是什么
SpringData:其实SpringData就是Spring提供了一个操作数据的框架。而SpringData JPA只是SpringData框架下的一个基于JPA标准操作数据的模块。
SpringData JPA:基于JPA的标准数据进行操作。简化操作持久层的代码。只需要编写接口就可以。
如何集成JPA
- 导入依赖
<!--jpa-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
- application.properties文件中添加配置
#jpa
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
- 实体类
@Entity
@Table(name="role")
public class Role {
@Id //主键id
@GeneratedValue(strategy = GenerationType.IDENTITY)//主键生成策略
@Column(name = "id")//数据库字段名
private Long id;
@Column(name = "name")
private String name;
@Column(name = "sn")
private String sn;
@Override
public String toString() {
return "Role{" +
"id=" + id +
", name='" + name + '\'' +
", sn='" + sn + '\'' +
'}';
}
}
- 编写Dao接口
@Repository
public interface RoleRepository extends JpaRepository<Role,Long> {
Role findRoleById(Long id);
Role findRoleByName(String name);
}
- 测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class JPATest {
@Autowired
private RoleRepository roleRepository;
@Test
public void testFindById(){
Role role = roleRepository.findRoleById(2L);
System.out.println(role);
}
@Test
public void testFindByName(){
Role role = roleRepository.findRoleByName("人事管理");
System.out.println(role);
}
}
SpringBoot统一异常处理
SpringBoot为何需要统一异常处理
在 Java EE 项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过 程,都不可避免会遇到各种可预知的、不可预知的异常需要处理。 每个过程都单独处理异常,系统的代码耦合度高,工作量大且不好统一,维护的工作量也很大。 那么将所有类型的异常处理从各处理过程解耦出来,这样既保证了相关处理过程的功能较单一, 也实现了异常信息的统一处理和维护
统一异常处理在之前SSM框架中的处理方式回顾
Spring MVC 处理异常有 3 种方式:
(1)使用 Spring MVC 提供的简单异常处理器 SimpleMappingExceptionResolver;
(2)实现 Spring 的异常处理接口 HandlerExceptionResolver 自定义自己的异常处理器;
(3)使用@ExceptionHandler 注解实现异常处理;
回顾方式一:
SecurityException自定义异常类:
public class SecurityException extends Exception {
public SecurityException() {
super();
}
public SecurityException(String message){
super(message);
}
}
springmvc.xml中的配置全局异常处理器:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 定义默认的异常处理页面,当该异常类型的注册时使用 -->
<property name="defaultErrorView" value="commons/error" />
<!-- 定义异常处理页面用来获取异常信息的变量名,默认名为 exception -->
<property name="exceptionAttribute" value="ex" />
<!-- 定义需要特殊处理的异常,用类名或完全路径名作为 key,异常也页名作为值 -->
<property name="exceptionMappings">
<value> cn.wolfcode.web.exception.SecurityException=commons/nopermission
<!-- 这里还可以继续扩展不同异常类型的异常处理 -->
</value>
</property>
</bean>
回顾方式二:
CustomException自定义异常类:
/**
*
* 系统自定义异常类
*/
public class CustomException extends Exception {
//异常信息
public String message;
public CustomException(String message){
super(message);
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
全局异常处理器:
/**
*
* 全局异常处理器
*/
public class CustomExceptionResolver implements HandlerExceptionResolver{
/**
* 系统抛出的异常
* @param httpServletRequest
* @param httpServletResponse
* @param o
* @param e 系统抛出的异常
* @return
*/
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
// 解析出异常类型
CustomException customException = null;
// 若该异常类型是系统自定义的异常,直接取出异常信息在错误页面展示即可。
if(e instanceof CustomException){
customException = (CustomException)e;
}else{
// 如果不是系统自定义异常,构造一个系统自定义的异常类型,信息为“未知错误”
customException = new CustomException("未知错误");
}
//错误信息
String message = customException.getMessage();
ModelAndView modelAndView = new ModelAndView();
//将错误信息传到页面
modelAndView.addObject("message",message);
//指向错误页面
modelAndView.setViewName("error");
return modelAndView;
}
}
错误页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>失败!</title>
</head>
<body>
${message}
</body>
</html>
springmvc.xml中的配置全局异常处理器:
<!--
全局异常处理器
只要类实现了HandlerExceptionResolver接口,就是一个全局异常处理器哦
-->
<bean class="com.alex.ssm.exception.CustomExceptionResolver"/>
回顾方式三:
@ControllerAdvice
通过使用@ControllerAdvice定义统一的异常处理类,而不是在每个Controller中逐个定义。@ExceptionHandler用来定义函数针对的异常类型。
�ErrorControllerAdvice:
@ControllerAdvice
public class ErrorControllerAdvice {
@ExceptionHandler(Exception.class)
public String error(Model model, Exception e){
model.addAttribute("error",e.getMessage());
return "error";
}
}
error.ftl:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
${error}
</body>
</html>
在SpringBoot中如何统一异常处理
方式一:
通过使用@ControllerAdvice定义统一的异常处理类,而不是在每个Controller中逐个定义。@ExceptionHandler用来定义函数针对的异常类型。
ErrControllerAdvice:
@ControllerAdvice
public class ErrControllerAdvice {
@ExceptionHandler
public String error(Exception e , Model model){
model.addAttribute("error",e.getMessage());
return "error";
}
}
注意:如果项目中报错,会被上述异常进行拦截。上述方法,它会拦截到异常,并把异常信息存入model中,然后跳转error的页面,这个页面需要自己编写,在templates模版文件夹中。
error.ftl:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
我是通过注解ControllerAdvice进来的错误页面
error : ${error}
</body>
</html>
利用统一异常的处理类,我们可以自定义自己的异常,比如,自定义异常,返回json格式的数据。
第一步:自定义异常类
public class MyCustomException extends RuntimeException {
public MyCustomException(String message) {
super(message);
}
public MyCustomException(Throwable cause) {
super(cause);
}
}
第二步:在异常的处理类中添加自定义异常的拦截方法
@ExceptionHandler
@ResponseBody
public JSONResult customException(MyCustomException e) {
JSONResult jsonResult = new JSONResult();
jsonResult.setMark(e.getMessage());
return jsonResult;
}
第三步:在service层添加异常的处理(try...catch(自定义异常))
@Service
public class ExceptionServiceImpl {
public void exception() {
try {
int i = 1 / 0;
}catch (Exception e){
throw new MyCustomException(e.getMessage());
}
}
}
第四步: 编写对应的controller,在方法中,调用service中的方法。
@RequestMapping("/ex")
@ResponseBody
public JSONResult list2(){
ExceptionServiceImpl service = new ExceptionServiceImpl();
service.exception();
return new JSONResult();
}
方式二:
1,SpringBoot默认情况下,把所有错误都重新定位到/error这个处理路径上,由BasicErrorController类完成处理;
2,SpringBoot提供了默认的替换错误页面的路径:
1,静态错误页面默认结构:
src/
resources/
public/
error/
404.html
401.html
5xx.html�
2,也可以使用模板页面:
src/
resources/
templates/
error/
5xx.ftl
该路径方式是通过ErrorMvcAutoConfiguration中的DefaultErrorViewResolver完成的;
网友评论