***SpringBoot实现国际化切换***
-
- 编写国际化配置文件,抽取页面需要显示的国际化消息;
注意文件位置,以及配置文件的命名:使用页面_语言代码_国家代码.properties
- SpringBoot自动配置好了管理国际化资源文件的组件;
我们的配置文件可以直接放在类路径下叫messages.properties;
但这里我们不是按照它的来弄的,使用要去全局配置文件里修改
#国际化资源文件的配置 spring: messages: basename: i18n.login
- 去页面获取国际化的值
名称与配置文件里的一致即可,注意: thymeleaf获取国际化值的表达式是:#{}
<!--引入thymeleaf模板引擎表达式--> <html lang="en" xmlns:th="http://www.thymeleaf.org">
th:text="#{pleaseSignIn}" ....
-
*点击链接实现国家化
thymeleaf发送请求是用@{},参数用()
第一步:
public class MyLocalResolver implements LocaleResolver { /** * 切换语言第一步 */ @Override public Locale resolveLocale(HttpServletRequest httpServletRequest) { String l = httpServletRequest.getParameter("l"); Locale locale = Locale.getDefault(); // 如果请求参数没有语言区域信息,我们就用SpringBoot默认的 if (!StringUtils.isEmpty(l)) { String[] split = l.split("_");
locale = new Locale(split[0], split[1]);} return locale; } @Override public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { } }
第二步:
@Configuration public class MyMvcConfig extends WebMvcConfigurerAdapter { /** * 切换语言第二步 */ @Bean public LocaleResolver localeResolver() { return new MyLocalResolver(); } }
- 如果页面乱码,是idea的编码问题哦
- 编写国际化配置文件,抽取页面需要显示的国际化消息;
SpringBoot整合日志框架
市面上的日志框架:
JUL、JCL、Jboss-logging、logback、log4j、log4j2、slf4j....SpringBoot:底层是Spring框架,Spring框架默认是用JCL;‘
SpringBoot选用 SLF4j和logback;
日志门面 (日志的抽象层) | 日志实现 |
JCL(Jakarta Commons Logging) Facade for Java) SLF4j(Simple Logging jboss-logging | Log4j JUL(java.util.logging) Log4j2 Logback |
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class); logger.info("Hello World"); } }
-
配置:
#日志配置 logging: level: com.soldier: trace #spring.profiles.active=dev
### set log levels ### log4j.rootLogger = debug , stdout , D , E ### 输出到控制台 ### log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{ 1 }:%L - %m%n #### 输出到日志文件 ### #log4j.appender.D = org.apache.log4j.DailyRollingFileAppender #log4j.appender.D.File = /home/soldier/SOLDIER/idea_project/logs/LearningNotes/log.log #log4j.appender.D.Append = true #log4j.appender.D.Threshold = DEBUG ## 输出DEBUG级别以上的日志 #log4j.appender.D.layout = org.apache.log4j.PatternLayout #log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n #### 保存异常信息到单独文件 ### #log4j.appender.D = org.apache.log4j.DailyRollingFileAppender #log4j.appender.D.File = /home/soldier/SOLDIER/idea_project/logs/LearningNotes/error.log ## 异常日志文件名 #log4j.appender.D.Append = true #log4j.appender.D.Threshold = ERROR ## 只输出ERROR级别以上的日志!!! #log4j.appender.D.layout = org.apache.log4j.PatternLayout #log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
<?xml version="1.0" encoding="UTF-8"?> <!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。 scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。 debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 --> <configuration scan="false" scanPeriod="60 seconds" debug="false"> <!-- 定义日志的根目录 --> <property name="LOG_HOME" value="/home/soldier/SOLDIER/idea_project/logs/LearningNotes" /> <!-- 定义日志文件名称 --> <property name="appName" value="spring_boot"></property> <!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 --> <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <!-- 日志输出格式: %d表示日期时间, %thread表示线程名, %-5level:级别从左显示5个字符宽度 %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 %msg:日志消息, %n是换行符 --> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </layout> </appender> <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 --> <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 指定日志文件的名称 --> <file>${LOG_HOME}/${appName}.log</file> <!-- 当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名 TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动 %i:当文件大小超过maxFileSize时,按照i进行文件滚动 --> <fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern> <!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动, 且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是, 那些为了归档而创建的目录也会被删除。 --> <MaxHistory>365</MaxHistory> <!-- 当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy --> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <!-- 日志输出格式: --> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern> </layout> </appender> <!-- logger主要用于存放日志对象,也可以定义日志类型、级别 name:表示匹配的logger类型前缀,也就是包的前半部分 level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出, false:表示只用当前logger的appender-ref,true: 表示当前logger的appender-ref和rootLogger的appender-ref都有效 --> <!-- hibernate logger --> <logger name="com.soldier" level="debug" /> <!-- Spring framework logger --> <logger name="org.springframework" level="debug" additivity="false"></logger> <!-- root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应, 要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。 --> <root level="info"> <appender-ref ref="stdout" /> <appender-ref ref="appLogAppender" /> </root> </configuration>
logback.xml:直接就被日志框架识别了; logback-spring.xml:日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot的高级Profile功能
***SpringBoot注册登陆拦截器***
- 在component包下创建HandlerInterceptor子类
public class LoginHandlerInterceptor implements HandlerInterceptor { // 目标方法执行之前 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object user = request.getSession().getAttribute("loginUser"); if (user == null) { // 未登陆,返回登陆页 request.setAttribute("msg", "没有权限,请先登陆"); request.getRequestDispatcher("/index").forward(request,response); return false; } else { // 已登陆 return true; } } }
- 使用WebMvcConfigurerAdapter可以扩展SpringMvc的功能,注册我们写的拦截,【注意:SpringBoot 2.X之后也会拦截静态资源】
@Configuration public class MyMvcConfig extends WebMvcConfigurerAdapter { @Bean public WebMvcConfigurer webMvcConfigurer() { WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() { // 登陆拦截器:第二步:注册拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { // 静态资源SpringBoot已经做好了映射,所以不用配置;但2.x以后会被拦截 registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**") // 不拦截访问登陆页面和登陆检验的请求,及静态资源 .excludePathPatterns("/","/index","/userLogin","/static/**","/webjars/**"); } }; return adapter; } }
***SpringBoot对请求参数的绑定***
- 使用占位符来获取请求参数
@GetMapping("/getDept/{id}") public Department getDepartment(@PathVariable("id") Integer id) { return departmentMapper.getDeptById(id); }
- 请求参数与入参对象属性的一一绑定
要求:请求参数的名称和JavaBean入参的对象的属性名一样的
// 添加员工 // SpringMvc自动将请求参数和入参对象的属性进行封装 @PostMapping("/addEmp") public String addEmp(Employee employee) { employeeDao.save(employee); // 来到员工列表页面 // redirect:重定向 // forward:转发 return "redirect:/ems"; }
SpringBoot定制错误页面(类似于web.xml的)--浏览器访问时
- *有模板引擎的情况下,静态资源文件夹/templates/error/状态码.html;
我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html);
页面能获取的信息;
timestamp:时间戳
status:状态码
error:错误提示
exception:异常对象
message:异常消息
errors:JSR303数据校验的错误都在这里 - 没有模板引擎(模板引擎找不到这个错误页面),静态资源文件夹下找;
- 以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面;
SpringBoot定制错误的json数据--其他客户端(如:postman)访问时
- 浏览器和客户端返回的都是json数据,没有自适应效果
@ControllerAdvice public class MyExceptionHandler { // 浏览器和客户端返回的都是json数据;UserNotExisitException是自己写的RuntimeException子类 @ResponseBody @ExceptionHandler(UserNotExisitException.class) public Map<String, Object> handlerException(Exception e) { Map<String, Object> map = new HashMap<>(); map.put("code", "用户消息不存在"); map.put("gogo", "is me"); return map; } }
- 转发到/error进行自适应响应效果处理
public String handlerException(Exception e, HttpServletRequest request) { Map<String, Object> map = new HashMap<>(); //传入我们自己的状态码 4xx 5xx request.setAttribute("javax.servlet.error.status_code", 400); map.put("code", "用户消息不存在"); map.put("gogo", "is me"); // 转发到/error return "forward:/error"; }
- 将我们的定制数据携带出去
出现错误以后,会来到/error请求,会被BasicErrorController处理,响应出去可以获取的数据是由getErrorAttributes得到的(是AbstractErrorController(ErrorController)规定的方法)
a、完全来编写一个ErrorController的实现类【或者是编写AbstractErrorController的子类】,放在容器中; --太复杂
b、页面上能用的数据,或者是json返回能用的数据都是通过errorAttributes.getErrorAttributes得到的,
而容器默认使用DefaultErrorAttributes.getErrorAttributes()进行数据处理的,
所以我们可以继承DefaultErrorAttributes来重写getErrorAttributes方法,实现自定义数据@Component public class MyErrorAttributes extends DefaultErrorAttributes { @Override public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace); map.put("made", "定制错误数据"); return map; } }
***SpringBoot对数据库的访问***
- 全局配置文件
#配置数据库 spring: datasource: username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/??
- *自动配置
参考DataSourceConfiguration,根据配置创建数据源,默认使用Tomcat连接池;可以使用spring.datasource.type指定自定义的数据源类型
SpringBoot默认可以支持:org.apache.tomcat.jdbc.pool.DataSource 、HikariDataSource 、BasicDataSource默认运行的建表语句:只需要将文件命名为【schema‐*.sql】
默认插入数据的建表语句:只需要将文件命名为【data‐*.sql】
还可以在yaml文件中指定,如下图:
***SpringBoot整合Druid数据源***
- pom.xml
<!-- 引入自定义数据源 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <!-- log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
- application.yaml
spring: #配置数据库 datasource: username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/LearningNotes #spring某个版本之后需要加上这句,否则不会自动执行sql文件 initialization-mode: always #设置自动运行的建表sql文件 schema: - classpath:/sql/department.sql - classpath:/sql/employee.sql #设置自动运行的数据sql文件 # data: # - xxx.sql #引入自定义数据源 type: com.alibaba.druid.pool.DruidDataSource #Druid数据源配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
- DruidConfig.class
@Configuration public class DruidConfig { /** * 把我们在配置文件的配置写入springBoot */ @ConfigurationProperties(prefix = "spring.datasource") @Bean public DataSource druid() { return new DruidDataSource(); } /** * 配置Druid的监控 */ //1、配置一个管理后台的Servlet @Bean public ServletRegistrationBean statViewServlet() { //处理/druid下的所有请求 ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); //配置一些初始化参数 Map<String, String> initParams = new HashMap<>(); initParams.put("loginUsername","admin"); initParams.put("loginPassword","123456"); initParams.put("allow","");//允许谁登录,为空或不写默认允许所有 // initParams.put("deny","localhost");//拒绝谁访问 bean.setInitParameters(initParams); return bean; } //2、配置一个监控了的filter @Bean public FilterRegistrationBean webStatFilter() { FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setFilter(new WebStatFilter()); //配置一些初始化参数 Map<String, String> initParams = new HashMap<>(); initParams.put("exclusions","*.js,*.css,/druid/*");//不拦截哪些请求 bean.setInitParameters(initParams); //拦截哪些请求 bean.setUrlPatterns(Arrays.asList("/*")); return bean; } }
***SpringBoot在Druid数据源下整合MaBatis持久层***
- 配置Druid数据源,上一个标题
- 注解版操作数据库(最简单)
@Mapper public interface DepartmentMapper { //声明是否使用自动递增主键,主键名是什么 @Options(useGeneratedKeys = true, keyProperty = "id") @Insert("insert into department(departmentName) values(#{departmentName})") public int insertDept(Department department); @Delete("delete from department where id=#{id}") public int deleteDeptById(Integer id); @Update("update set departmentName=#{departmentName} from department where id=#{id}") public int updateDept(Department department); @Select("select * from department where id=#{id}") public Department getDeptById(Integer id); }
@RestController public class DepartmentController { @Autowired DepartmentMapper departmentMapper; @GetMapping("/getDept/{id}") public Department getDepartment(@PathVariable("id") Integer id) { return departmentMapper.getDeptById(id); } @GetMapping("/dept") public Department insertDept(Department department) { departmentMapper.insertDept(department); return department; } }
【但实体类属性名与字段名必须一致,否则查询时无法绑定上指】-如驼峰命名法时,解决办法:
@Configuration public class MyBatisConfig {//MyBatis的自定义规则 //实现驼峰命名法 @Bean public ConfigurationCustomizer configurationCustomizer() { return new ConfigurationCustomizer() { @Override public void customize(org.apache.ibatis.session.Configuration configuration) { configuration.setMapUnderscoreToCamelCase(true); } }; } }
- 配置版操作数据库
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> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers> </configuration>
<?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="org.mybatis.example.BlogMapper"> <select id="selectBlog" resultType="Blog"> select * from Blog where id = #{id} </select> </mapper>
public interface EmployeeMapper { public int insertEmp(Employee employee); public int deleteEmpById(Integer id); public int updateEmp(Employee employee); public Employee getEmpById(Integer id); }
<?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="com.soldier.mapper.EmployeeMapper"> <!--id名对应mapper类的方法名,resultType对应实体类全路径--> <select id="getEmpById" resultType="com.soldier.bean.Employee"> select * from employee where id = #{id} </select> <!--resultType:返回结果类型 parameterType:参数类型 --> <insert id="insertEmp" parameterType="int" useGeneratedKeys="true" keyProperty="id"> insert into employee(lastName,email,gender,d_Id) VALUES (#{lastName},#{email},#{gender},#{dId}) </insert> <delete id="deleteEmpById"> delete from employee where id = #{id} </delete> <update id="updateEmp"> update set lastName=#{lastName},email=#{email},gender=#{gender},d_Id=#{dId}, from employee where id=#{id} </update> </mapper>
<?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> <!--<environments default="development">--> <!--<environment id="development">--> <!--<transactionManager type="JDBC"/>--> <!--<dataSource type="POOLED">--> <!--<property name="driver" value="${driver}"/>--> <!--<property name="url" value="${url}"/>--> <!--<property name="username" value="${username}"/>--> <!--<property name="password" value="${password}"/>--> <!--</dataSource>--> <!--</environment>--> <!--</environments>--> <!--配置驼峰命名法--> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <!--<mappers>--> <!--<mapper resource="org/mybatis/example/BlogMapper.xml"/>--> <!--</mappers>--> </configuration>
#配置mybatis mybatis: #全局mybatis配置文件路径 config-location: classpath:/mybatis/mybatis-config.xml #所有映射文件路径 mapper-locations: classpath:/mybatis/mapper/*.xml
//这一句是关键 @MapperScan("com.soldier.mapper") @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
springboot启动类中@MapperScan注解和全局配置yml文件中mybatis.mapper-locations 两者缺一不可
***SpringBoot整合JPA***
新建springboot项目选中web、JPA、Mysql、JDBC,底层使用hibernate
- 编写一个实体类(bean)和数据表进行映射,并且配置好映射关系
//使用JPA注解配置映射关系 @Entity //告诉JPA这是一个实体类(和数据表的类) @Table(name = "tbl_user") //@Table来指定和哪个数据表对应;如果省略默认表名就是实体类名小写 public class User { @Id //这是一个主键 @GeneratedValue(strategy = GenerationType.IDENTITY) //自增主键 private Integer id; @Column(name = "last_name", length = 50) //这是和数据表对应的一个列 private String lastName; @Column //省略默认列名就是属性名,长度255 private String email; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
- 编写一个Dao接口来操作实体类对应的数据表(Repository)
//继承JpaRepository来完成对数据库的操作 public interface UserRepository extends JpaRepository<User, Integer> { //父类JpaRepository泛型使用实体类加上主键类型 }
- 基本的配置JpaProperties
spring: #配置数据库 datasource: username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/LearningNotes jpa: hibernate: #更新或创建数据表结构 ddl-auto: update #显示sql语句 show-sql: true
- 测试
@RestController public class UserController { @Autowired UserRepository userRepository; @GetMapping("/user/{id}") public User getUser(@PathVariable("id") Integer id) { User user = userRepository.findById(id).get(); return user; } @GetMapping("/addUser") public User insertUser(User user) { User save = userRepository.save(user); return save; } }
thymeleaf公共页面元素抽取
1、抽取公共片段
<div th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</div>
2、引入公共片段
<div th:insert="~{footer :: copy}"></div>
~{templatename::selector}:模板名::选择器
~{templatename::fragmentname}:模板名::片段名3、默认效果:
insert的公共片段在div标签中
如果使用th:insert等属性进行引入,可以不用写~{}:
行内写法可以加上:[[~{}]];[(~{})];三种引入公共片段的th属性:
th:insert:将公共片段整个插入到声明引入的元素中
th:replace:将声明引入的元素替换为公共片段
th:include:将被引入的片段的内容包含进这个标签中
SpringBoot的相关依赖
<parent>
<groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐starter‐parent</artifactId> <version>2.1.5.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐starter‐web</artifactId> </dependency> </dependencies>
-
parent:Spring Boot的版本仲裁中心;以后我们导入依赖默认是不需要写版本;(没有在dependencies里面管理的依赖自然需要声明版本号)
-
spring‐boot‐starter:spring-boot场景启动器;帮我们导入了web模块正常运行所依赖的组件;
SpringBoot的部署
- 在pom文件中加入一个依赖,可以将应用打包成一个可执行的jar包
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
- IDEA左边的【Maven】-【项目名】-【Lifecycle】-【package】即可自动生成一个jar包
- 运行jar包:切换到jar包所在目录下
java -jar 项目名.jar
SpringBoot对静态资源的映射规则(SpringBoot 2.X后自定义拦截器时会拦截静态资源)
- 所有 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 找资源;
webjars:以jar包的方式引入静态资源; http://www.webjars.org/ localhost:8080/webjars/jquery/3.3.1/jquery.js1 <!‐‐引入jquery‐webjar‐‐>在访问的时候只需要写webjars下面资源的名称即可 <dependency> <groupId>org.webjars</groupId><artifactId>jquery</artifactId><version>3.3.1</version> </dependency>
- "/**" 访问当前项目的任何资源,都去(静态资源的文件夹)找映射
1 "classpath:/META‐INF/resources/", 2 "classpath:/resources/", 3 "classpath:/static/", 4 "classpath:/public/" 5 "/":当前项目的根路径 localhost:8080/abc === 去静态资源文件夹里面找abc
- 欢迎页; 静态资源文件夹下的所有index.html页面;被"/**"映射;
localhost:8080/ 找index页面
- 所有的 **/favicon.ico 都是在静态资源文件下找;
模板引擎(SpringBoot推荐的Thymeleaf)
JSP、Velocity、Freemarker、Thymeleaf都是模板引擎,可以通过它们自己的语法解析动态数据
- 引入
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐starter‐thymeleaf</artifactId> </dependency> 切换thymeleaf版本 <properties> <thymeleaf.version>3.0.9.RELEASE</thymeleaf.version> <!--布局功能的支持程序 thymeleaf3主程序 layout2以上版本 ‐--> <!--thymeleaf2 layout1--> <thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version> </properties>
- 创建页面
@ConfigurationProperties(prefix = "spring.thymeleaf") public class ThymeleafProperties { private static final Charset DEFAULT_ENCODING = Charset.forName("UTF‐8"); private static final MimeType DEFAULT_CONTENT_TYPE = MimeType.valueOf("text/html"); public static final String DEFAULT_PREFIX = "classpath:/templates/"; public static final String DEFAULT_SUFFIX = ".html";
只要我们把HTML页面放在classpath:/templates/,thymeleaf就能自动渲染;
- 语法规则及使用
一、页面导入thymeleaf的名称空间<html lang="en" xmlns:th="http://www.thymeleaf.org">
二、使用thymeleaf语法: th:任意html属性;来替换原生属性的值
<div th:text="${hello}">这是显示欢迎信息</div>
-
*支持的表达式(${}-->OGNL表达式)
***SpringBoot的全局配置文件:application.properties与yaml,能配置什么
<!‐‐导入配置文件处理器,配置文件进行绑定就会有提示‐‐>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐configuration‐processor</artifactId> <optional>true</optional> </dependency>
这里重点讲yaml文件
-
yaml文件基本语法:
k:(空格)v:表示一对键值对(空格必须有);以空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的【属性和值也是大小写敏感】server: port: 8081 path: /hello
-
yaml值的写法:
1、普通的值(数字,字符串,布尔):k: v:字面直接来写; 字符串默认不用加上单引号或者双引号; "":双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思 name: "zhangsan \n lisi":输出;zhangsan 换行 lisi '':单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据 name: ‘zhangsan \n lisi’:输出;zhangsan \n lisi
2、对象、Map(属性和值)(键值对):
写法一: friends: lastName: zhangsan age: 20 写法二:(行内写法) friends: {lastName: zhangsan,age: 18}
3、数组(List、Set):里面的猫狗都是对象
写法一: pets: ‐ cat ‐ dog ‐ pig 写法二:(行内写法) pets: [cat,dog,pig]
-
例子
1、配置文件person: lastName: hello age: 18 boss: false birth: 2017/12/12 maps: {k1: v1,k2: 12} lists: ‐ lisi ‐ zhaoliu dog: name: 小狗 age: 12
2、javaBean
/** * 将配置文件中配置的每一个属性的值,映射到这个组件中 * @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;、 * * 只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能; * prefix = "person":配置文件中哪个下面的所有属性进行一一映射 */ @Component @ConfigurationProperties(prefix = "person") public class Person { private String lastName; private Integer age; private Boolean boss; private Date birth; private Map<String,Object> maps; private List<Object> lists; private Dog dog; }
-
配置文件占位符
--占位符获取之前配置的值,如果没有可以是用:指定默认值
person.dog.name=${person.hello:hello}_dog
-
Profile
--我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml;默认使用application.properties的配置 其中yaml文件还支持多文档块的方式
server: port: 8081 spring: profiles: active: prod #指定激活使用哪个环境 ‐‐‐ server: port: 8083 spring: profiles: dev ‐‐‐ server: port: 8084 spring: profiles: prod #指定属于哪个环境
1、在配置文件中指定 spring.profiles.active=dev 2、命令行: java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev; 可以直接在测试的时候,配置传入命令行参数 3、虚拟机参数; -Dspring.profiles.active=dev
-
*配置文件加载位置
--springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件;
SpringBoot会从这四个位置全部加载主配置文件;互补配置;
优先级从高到低:
–file:./config/ –file:./ –classpath:/config/ –classpath:/
可以通过在配置文件中【spring.config.location】来改变默认的配置文件位置:指定配置文件和默认加载的这些配置文件共同起作用形成互补配置
- 外部配置文件加载顺序
SpringBoot也可以从以下位置加载配置; 优先级从高到低;高优先级的配置覆盖低优先级的配置,所有的配置会 形成互补配置 1.命令行参数 所有的配置都可以在命令行上进行指定 java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --server.port=8087 --server.context-path=/abc 多个配置用空格分开; --配置项=值 2.来自java:comp/env的JNDI属性 3.Java系统属性(System.getProperties()) 4.操作系统环境变量 5.RandomValuePropertySource配置的random.*属性值 由jar包外向jar包内进行寻找; 优先加载带profile 6.jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件 7.jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件 再来加载不带profile 8.jar包外部的application.properties或application.yml(不带spring.profile)配置文件 9.jar包内部的application.properties或application.yml(不带spring.profile)配置文件 10.@Configuration注解类上的@PropertySource 11.通过SpringApplication.setDefaultProperties指定的默认属性 所有支持的配置加载来源;
SpringBoot中的注解
- @EnableAutoConfiguration:SpringBoot启动的时候加载主配置类,开启了自动配置功能
- @PostMapping:处理post请求(以此类推:get、put...)
- @RequestParam:获取请求参数
- @RestController:不返回页面,返回json数据
-
【@Value】获取值和【@ConfigurationProperties】获取值比较:
@ConfigurationProperties | @Value | |
功能 | 批量注入配置文件中的属性 | 一个个指定 |
松散绑定(松散语法) | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303数据校验 | 支持 | 不支持 |
复杂类型封装 | 支持 | 不支持 |
- @PropertySourceL:加载指定的配置文件;
@PropertySource(value = {"classpath:person.properties"})
- @ImportResource:导入Spring的配置文件,让配置文件里面的内容生效;
@ImportResource(locations = {"classpath:beans.xml"})
- 配置类@Configuration
- @Bean用于某类中的方法,将方法的返回值添加到容器中;容器中这个组件默认的id就是方法名
报错:Failed to bind properties under 'spring.datasource' to javax.sql.DataSource
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to bind properties under 'spring.datasource' to javax.sql.DataSource:
Property: spring.datasource.filters
Value: stat,wall,log4j
Origin: class path resource [application.yaml]:39:14
Reason: org.apache.log4j.Logger
Action:
Update your application's configuration
解决办法:
<!-- log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
报错:log4j:WARN No appenders could be found for logger (druid.sql.Connection).
log4j:WARN No appenders could be found for logger (druid.sql.Connection).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
- application.yml
#日志配置 logging: level: com.soldier: trace #spring.profiles.active=dev
- 需要配置log4j.properties及logback-spring.xml
### set log levels ### log4j.rootLogger = debug , stdout , D , E ### 输出到控制台 ### log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{ 1 }:%L - %m%n #### 输出到日志文件 ### #log4j.appender.D = org.apache.log4j.DailyRollingFileAppender #log4j.appender.D.File = /home/soldier/SOLDIER/idea_project/logs/LearningNotes/log.log #log4j.appender.D.Append = true #log4j.appender.D.Threshold = DEBUG ## 输出DEBUG级别以上的日志 #log4j.appender.D.layout = org.apache.log4j.PatternLayout #log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n #### 保存异常信息到单独文件 ### #log4j.appender.D = org.apache.log4j.DailyRollingFileAppender #log4j.appender.D.File = /home/soldier/SOLDIER/idea_project/logs/LearningNotes/error.log ## 异常日志文件名 #log4j.appender.D.Append = true #log4j.appender.D.Threshold = ERROR ## 只输出ERROR级别以上的日志!!! #log4j.appender.D.layout = org.apache.log4j.PatternLayout #log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
<?xml version="1.0" encoding="UTF-8"?> <!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。 scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。 debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 --> <configuration scan="false" scanPeriod="60 seconds" debug="false"> <!-- 定义日志的根目录 --> <property name="LOG_HOME" value="/home/soldier/SOLDIER/idea_project/logs/LearningNotes" /> <!-- 定义日志文件名称 --> <property name="appName" value="spring_boot"></property> <!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 --> <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <!-- 日志输出格式: %d表示日期时间, %thread表示线程名, %-5level:级别从左显示5个字符宽度 %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 %msg:日志消息, %n是换行符 --> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </layout> </appender> <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 --> <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 指定日志文件的名称 --> <file>${LOG_HOME}/${appName}.log</file> <!-- 当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名 TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动 %i:当文件大小超过maxFileSize时,按照i进行文件滚动 --> <fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern> <!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动, 且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是, 那些为了归档而创建的目录也会被删除。 --> <MaxHistory>365</MaxHistory> <!-- 当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy --> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <!-- 日志输出格式: --> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern> </layout> </appender> <!-- logger主要用于存放日志对象,也可以定义日志类型、级别 name:表示匹配的logger类型前缀,也就是包的前半部分 level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出, false:表示只用当前logger的appender-ref,true: 表示当前logger的appender-ref和rootLogger的appender-ref都有效 --> <!-- hibernate logger --> <logger name="com.soldier" level="debug" /> <!-- Spring framework logger --> <logger name="org.springframework" level="debug" additivity="false"></logger> <!-- root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应, 要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。 --> <root level="info"> <appender-ref ref="stdout" /> <appender-ref ref="appLogAppender" /> </root> </configuration>
- 如图:
报错:log4j:ERROR Category option " 1 " not a decimal integer.
- 解决办法--log4j.properties 的配置文件中:把{1}中的空格去掉。