配置文件的位置
配置文件可以放四个位置
- file: ./config/
- file: ./
- classpath: /config/ (classpath就是resources目录)
- classpath: /
优先级从上往下依次降低,即第四个优先级最低,这也是官方推荐使用的配置文件位置
- classpath: /
多环境配置
- resources目录下实现多环境配置,例如resources下有application.properties、application-dev.properties、application-test.properties三个文件,默认使用application.properties。但是如果在application.properties中配置下面信息,则会使用application-dev.properties配置。
spring.profiles.active=dev
这样可以实现多环境切换,在生成环境、测试环境等来回改变。
- yml的多环境配置如下,配置全部写在application.yml一个文件中,默认使用8081端口
server:
port: 8081
---
server:
port: 8082
spring:
profiles: dev
---
server:
port: 8083
spring:
profiles: test
多配置的切换如下,这样就能切换到dev配置使用8082端口
server:
port: 8081
spring:
profiles:
active: dev
---
server:
port: 8082
spring:
profiles: dev
---
server:
port: 8083
spring:
profiles: test
thymeleaf模板引擎
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
html页面,主要是顶部要添加xmlns字段
<!DOCTYPE html>
<html lang="en" xmlns:th="http://wwww.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>test test</h1>
<div th:text="${msg}"></div>
</body>
</html>
整合JDBC使用
package com.meng.controller;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
/**
* @author Administrator
*/
@RestController
public class JdbcController {
@Autowired
JdbcTemplate jdbcTemplate;
@GetMapping("/articleList")
public List<Map<String,Object>> articleList(){
String sql="SELECT * FROM `article`";
List<Map<String,Object>> maps=jdbcTemplate.queryForList(sql);
return maps;
}
@GetMapping("/addArticle")
public String addArticle(){
String sql="INSERT INTO article(id,title,author) VALUES(3,'春','朱自清')";
jdbcTemplate.update(sql);
return "添加成功!";
}
@GetMapping("/deleteArticle")
public String deleteArticle(){
String sql="DELETE FROM article WHERE id=3";
jdbcTemplate.update(sql);
return "删除成功!";
}
@GetMapping("/updateArticle/{id}")
public String updateArticle( @PathVariable("id") Integer id){
String sql="UPDATE article SET author=? WHERE id="+id;
Object[] objects = new Object[1];
objects[0]="朱自清";
jdbcTemplate.update(sql,objects);
return "修改成功!";
}
}
整合Druid数据源
阿里巴巴提供的数据源
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
spring:
datasource:
url: jdbc:mysql://120.26.142.247/down_up?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
filters: stat #属性类型的字符串,通过别名的方式配置扩展插件, 监控统计用的stat 日志用log4j 防御sql注入:wall
initialSize: 2 #初始化时池中建立的物理连接个数。
maxp-active: 300 #最大的可活跃的连接池数量
maxWait: 60000 #获取连接时最大等待时间,单位毫秒,超过连接就会失效。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降, 如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
timeBetweenEvictionRunsMillis: 60000 #连接回收器的运行周期时间,时间到了清理池中空闲的连接,testWhileIdle根据这个判断
minEvictableIdleTimeMillis: 300000
添加依赖后,在yml中加入type字段
添加配置文件,config文件下添加DruidController.java
package com.meng.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.HashMap;
/**
* @author Administrator
*/
@Configuration
public class DruidController {
@ConfigurationProperties(prefix = "spring.datasourse")
@Bean
public DataSource druidDataSource(){
return new DruidDataSource();
}
/**
* 后台监控,相当于web.xml
* @return
*/
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
//后台需要有人登陆,账号密码配置
HashMap<String,String> initParameters=new HashMap<>();
//增加配置,key值固定,value自己配
initParameters.put("loginUsername","root");
initParameters.put("loginPassword","root");
//允许谁可以访问
initParameters.put("allow","localhost");
//紧张谁访问 initParameters.put("meng","111.111.111.111");
//设置初始化参数
bean.setInitParameters(initParameters);
return bean;
}
}
在浏览器输入http://localhost:8028/druid即可
SpringSecurity
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
首先导入依赖,其次编写配置类SecurityConfig.java
package com.meng.config;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 授权
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//首页所有人可以访问,功能页需要权限
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
//没有权限则跳转到login页面
http.formLogin();
//注销,开启注销功能跳转到首页
http.logout();
}
/**
* 认证
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//从内存中读取,正常情况下一般在数据库里面读
auth.inMemoryAuthentication()
.withUser("username1").password("1234").roles("vip1","vip2")
.and()
.withUser("username2").password("456123").roles("vip1");
}
}
新版SpringSecurity可能会报错,它要求password要加密,需要做如下修改
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//从内存中读取,正常情况下一般在数据库里面读
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("username1").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2")
.and()
.withUser("username2").password(new BCryptPasswordEncoder().encode("654321")).roles("vip1");
}
如果不从内存读数据,而从数据库读取,代码如下
@Autowired
private DataSource dataSource;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
User.UserBuilder user = User.withDefaultPasswordEncoder();
auth.jdbcAuthentication().dataSource(dataSource) .withDefaultSchema()
.withUser(user.username("username1").password("123546").roles("vip1"))
.withUser(user.username("username2").password("123546").roles("vip2"));
}
Shiro
安全框架
Swagger
号称世界上最流行的API框架
导入依赖,用的2.9.2版本,3.0版本会导致页面刷不出来
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
随便建个测试Controller
package com.meng.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Administrator
*/
@RestController
public class TestController {
@GetMapping("/test")
public String test(){
return "test!";
}
}
然后写配置类config==>SwaggerConfig.java
package com.meng.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
/**swagger配置文件
* @author Administrator
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
/**
* 配置swagger的Docket bean实例
* @return
*/
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo());
}
private ApiInfo apiInfo(){
//作者信息
Contact contact = new Contact("猛男", "", "");
return new ApiInfo(
"猛男的API文档",
"平凡中孕育着万物!",
"v1.0",
"Https://www.alibaba.com",//API接口
contact,
"",//其他啥的不想写就空着
"",
new ArrayList());
}
}
然后浏览器测试连接http://localhost:8080/swagger-ui.html/访问,这样一个最简单的swagger就写好了。
后面接着是swagger对于扫描接口的配置。
下面是配置扫描的接口,补充Docket
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
//.enable(false)//默认值是true,false之后浏览器将不再能访问swagger页面
.select()
//指定扫描的路径,默认是扫描全部包括Test
.apis(RequestHandlerSelectors.basePackage("com.meng.controller"))
//path过滤
.paths(PathSelectors.none())
.build();
}
下面是多个界面情况的配置
@Configuration
@EnableSwagger2
public class SwaggerConfig {
/**
* 配置swagger的Docket bean实例
* @return
*/
@Bean
public Docket docke1(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo1())
.groupName("猛男1")
.select()
.apis(RequestHandlerSelectors.basePackage("com.meng.controller"))
//path过滤
.build();
}
@Bean
public Docket docket2(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo2())
.groupName("猛男2")
.select()
.apis(RequestHandlerSelectors.basePackage("com.meng.controller"))
//path过滤,这样写表示什么都不过滤
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo1(){
//作者信息
Contact contact = new Contact("猛男1", "", "");
return new ApiInfo(
"猛男1的API文档",
"平凡中孕育着万物!",
"v1.0",
"Https://www.alibaba.com",
contact,
"",
"",
new ArrayList());
}
private ApiInfo apiInfo2(){
//作者信息
Contact contact = new Contact("猛男2", "", "");
return new ApiInfo(
"猛男2的API文档",
"再小的帆也能远航",
"v1.0",
"Https://www.baidu.com",
contact,
"",
"",
new ArrayList());
}
}
此外,实体类可以加上注解
@ApiModel("用户")
public class User implements Serializable {
private static final long serialVersionUID = -6613686341202168918L;
/**
* 用户id
*/
@ApiModelProperty("用户id")
private Integer id;
邮件
首先是导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
其次是配置文件application.properties
[email protected]
spring.mail.password=pxoskqsviqlzeddd #16位秘钥
spring.mail.host=smtp.qq.com
#qq邮箱开启加密验证
spring.mail.properties.mail.smtp.ssl.enable=true
基本上就完成了,下面是测试类
@SpringBootTest
class EmailDemoApplicationTests {
@Autowired
JavaMailSenderImpl mailSender;
@Test
void contextLoads() {
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setSubject("标题:通知");
mailMessage.setText("邮件内容");
mailMessage.setTo("[email protected]");
mailMessage.setFrom("[email protected]");
mailSender.send(mailMessage);
}
}
定时任务
cron
首先在主程序上开启异步注解和定时执行的注释
/**
* 开启异步注解功能
*/
@EnableAsync
/**
* 开启定时功能的注解
*/
@EnableScheduling
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
然后Bean一个时间指令ScheduledService.java
package com.meng.service;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
/**
* @author Administrator
*/
@Service
public class ScheduledService {
/**
* 在一个指定的时间执行该方法;秒 分 时 日 月 周几(1~6表示周一至周六,0、7表示周日)
*/
@Scheduled(cron = "0 * * * * 0-7")
public void hello(){
System.out.println("你被执行了!");
}
}
上述表示在每分钟的0秒时刻,控制台会打印出“你被执行了!”字样。
集成Redis
导入pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置文件application.yml
spring:
redis:
host: localhost:xxxx
port: 6379
简单的测试类
package com.meng;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
@SpringBootTest
class RedisDemoApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
redisTemplate.opsForValue().set("mykey","mmmmmmmmmmmmm");
System.out.println(redisTemplate.opsForValue().get("mykey"));
}
}
接着,稍微复杂一点,可以编写个配置类RedisConfig.java,配置具体的序列化实现方式
默认的是使用jdk序列化,要改成自己想用的序列化方式
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate1(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
//为了开发方便,一般采用<String, Object>类型
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(redisConnectionFactory);
//序列化配置
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
//String的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
//hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
//value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
//hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
同时在测试类中,要写别名识别出来
@Autowired
@Qualifier("redisTemplate1")
private RedisTemplate redisTemplate;
在测试类中,要自己写redisTemplate.opsForValue().set很麻烦,一般是写RedisUtil.java工具类
/**Redis工具类
* @author Administrator
*/
@Component
public final class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @return true=设置成功;false=设置失败
*/
public boolean expire(String key,long timeout) {
return expire(key, timeout, TimeUnit.SECONDS);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @param unit 时间单位
* @return true=设置成功;false=设置失败
*/
public boolean expire(String key, long timeout, TimeUnit unit) {
Boolean ret = redisTemplate.expire(key, timeout, unit);
return ret != null && ret;
}
/**
* 删除单个key
*
* @param key 键
* @return true=删除成功;false=删除失败
*/
public boolean del(String key) {
Boolean ret = redisTemplate.delete(key);
return ret != null && ret;
}
/**
* 删除多个key
*
* @param keys 键集合
* @return 成功删除的个数
*/
public long del(Collection<String> keys) {
Long ret = redisTemplate.delete(keys);
return ret == null ? 0 : ret;
}
/**
* 存入普通对象
*
* @param key Redis键
* @param value 值
*/
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
// 存储普通对象操作
/**
* 存入普通对象
*
* @param key 键
* @param value 值
* @param timeout 有效期,单位秒
*/
public void set(String key, Object value, long timeout) {
redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
}
/**
* 获取普通对象
*
* @param key 键
* @return 对象
*/
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
// 存储Hash操作
/**
* 往Hash中存入数据
*
* @param key Redis键
* @param hKey Hash键
* @param value 值
*/
public void hPut( String key, String hKey,Object value) {
redisTemplate.opsForHash().put(key, hKey, value);
}
/**
* 往Hash中存入多个数据
*
* @param key Redis键
* @param values Hash键值对
*/
public void hPutAll(String key, Map<String, Object> values) {
redisTemplate.opsForHash().putAll(key, values);
}
/**
* 获取Hash中的数据
*
* @param key Redis键
* @param hKey Hash键
* @return Hash中的对象
*/
public Object hGet(String key, String hKey) {
return redisTemplate.opsForHash().get(key, hKey);
}
/**
* 获取多个Hash中的数据
*
* @param key Redis键
* @param hKeys Hash键集合
* @return Hash对象集合
*/
public List<Object> hMultiGet(String key, Collection<Object> hKeys) {
return redisTemplate.opsForHash().multiGet(key, hKeys);
}
// 存储Set相关操作
/**
* 往Set中存入数据
*
* @param key Redis键
* @param values 值
* @return 存入的个数
*/
public long sSet(String key, Object... values) {
Long count = redisTemplate.opsForSet().add(key, values);
return count == null ? 0 : count;
}
/**
* 删除Set中的数据
*
* @param key Redis键
* @param values 值
* @return 移除的个数
*/
public long sDel( String key, Object... values) {
Long count = redisTemplate.opsForSet().remove(key, values);
return count == null ? 0 : count;
}
// 存储List相关操作
/**
* 往List中存入数据
*
* @param key Redis键
* @param value 数据
* @return 存入的个数
*/
public long lPush(String key, Object value) {
Long count = redisTemplate.opsForList().rightPush(key, value);
return count == null ? 0 : count;
}
/**
* 往List中存入多个数据
*
* @param key Redis键
* @param values 多个数据
* @return 存入的个数
*/
public long lPushAll(String key, Collection<Object> values) {
Long count = redisTemplate.opsForList().rightPushAll(key, values);
return count == null ? 0 : count;
}
/**
* 往List中存入多个数据
*
* @param key Redis键
* @param values 多个数据
* @return 存入的个数
*/
public long lPushAll(String key, Object... values) {
Long count = redisTemplate.opsForList().rightPushAll(key, values);
return count == null ? 0 : count;
}
/**
* 从List中获取begin到end之间的元素
*
* @param key Redis键
* @param start 开始位置
* @param end 结束位置(start=0,end=-1表示获取全部元素)
* @return List对象
*/
public List<Object> lGet(String key, int start, int end) {
return redisTemplate.opsForList().range(key, start, end);
}
}