SpringBoot框架
1.什么是SpringBoot?
SpringBoot
是一个框架,一种全新的编程规范,它的产生简化了框架的使用,所谓简化是指简化了Spring众多框架中所需的大量且繁琐的配置文件,所以SpringBoot是一个服务于框架的框架,服务范围是简化配置文件。
1.1 SpringBoot的优缺点。
SpringBoot的优点
-
约定优于配置思想
-
专注与业务逻辑之间思维切换
-
基于Spring的开发提供更快入门体验
-
开箱即用,没有代码生成,无需XML配置。
-
支持修改默认配置满足特定需求
-
提供大型项目中常见的非功能性特性,如嵌入Tomcat服务器、安全、指标、健康检测、外部配置等
SpringBoot的缺点
1、缺少服务注册发现等外围方案;
2、缺少外围监控集成方案;
3、缺少外围安全管理方案;
4、缺少rest落地的URI规划方案。。。
5、将原有的spring项目转成springboot项目,很困难,更适合新建的项目。
6、集成度很高,对于了解底层不容易
2. SpringBoot功能
1.自动配置
springboot的自动配置是一个运行时的过程(更准确的说是应用程序启动时的过程),考虑了多种因素,才决定Spring配置应该用那个,不该用那个。该过程是SpringBoot自动完成的。
2.起步依赖
起步依赖本质上是一个Maven项目对象模型,定义了对其它库的传递依赖,这些东西加在一起即支持某项功能。 简单说,起步依赖是将具备某种功能的坐标打包到一起,并提供一些默认的功能。
3.辅助功能
提供了一些大项目中常见的非功能性特性,如嵌入式服务器,安全,指标,健康检测,外部配置等。
小结
SpringBoot并不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式
Spring的缺点:
配置繁琐
依赖繁琐
SpringBoot功能:
自动配置
起步依赖:依赖传递
辅助功能
3.SpringBoot入门
springboot在创建项目时,使用jar的打包方式。
springboot的引导类,是项目的入口,运行run方法就可以启动项目
4.SpringBoot配置
4.1 YAML配置文件
SpringBoot提供了两种配置文件类型,properties和yml/yaml。三个配置文件执行的顺序为:1.properties、2.yml、3.yaml。
YAML
properties:
server.port=8080
server.address=127.0.0.1
xml:
<server>
<port>8080</port>
<address>127.0.0.1</address>
</server>
yml
server:
port:8080
address:127.0.0.1
简洁,以数据为核心。
4.2 YAML基本语法
大小写敏感
数据值前边必须有空格,作为分隔符
使用缩进表示层级关系
缩进时不允许使用Tab键,只允许使用空格(各个系统Tab对应的空格数目可能不同,导致层次混乱)。
缩进的空格数目不重要,要相同层级的元素左侧对齐即可。
#表示注释,从这个字符一直到行尾,都会被解析器忽略。
server:
port:8080
address:127.0.0.1
name:abc
4.3 YAML数据格式
对象(map):键值对儿的集合
server:
port: 8081
name: abc
#对象
person:
name: zhangsan
age: 23
#对象的行内写法
person2: {name: lisi,age: 20}
数组:一组按次序排列的值
#数组
address:
- beijing
- shanghai
#数组的行内写法
address2: [beijing,shanghai]
纯量:单个的,不可再分的值
#纯量
msg1: 'hello \n world' #不会识别转义字符,会原样输出
msg2: "hello \n world" #会识别转移字符
4.4 读取配置文件的内容
1.@Value
2.Environment
3.@ConfigurationProperties
@RestController
public class ReadConfigFiles {
@Value("${name}")//大括号里面的值要和配置文件中要获取的key一样
private String name;
@Value("${person.name}")//大括号里面的值要和配置文件中要获取的key一样
private String personName;
@Value("${person.age}")//大括号里面的值要和配置文件中要获取的key一样
private int personAge;
@Value("${address1[0]}")//大括号里面的值要和配置文件中要获取的key一样
private String address1;
@Value("${msg1}")//大括号里面的值要和配置文件中要获取的key一样
private String msg1;
@Value("${msg2}")//大括号里面的值要和配置文件中要获取的key一样
private String msg2;
@Autowired //这个注解的功能 注入
private Environment environment;//只需要注入这一个对象就可以用这个对象获取配置文件中的其它值。
@RequestMapping("/config")
public String getConfigFilesName(){
System.out.println(name);
System.out.println(personName);
System.out.println(personAge);
System.out.println(address1);
System.out.println(msg1);
System.out.println(msg2);
System.out.println("---------------");
System.out.println(environment.getProperty("person.name"));//传入yml文件中的key获取对应的值
System.out.println(environment.getProperty("msg1"));
return "springboot的yml配置文件测试";
}
}
@ConfigurationProperties
@Component//表示这个类是个bean类,被spring识别
@ConfigurationProperties(prefix = "person")//为yml中的person下面的内容注入值
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
@RestController
public class ReadConfigFiles {
@Autowired
private Person person;
@RequestMapping("/config")
public String getConfigFilesName(){
System.out.println(person);
return "springboot的yml配置文件测试";
}
}
<!--yml提示(自己写的类也可以提示)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
4.5 profile(动态配置切换)
在我们开发SpringBoot应用时,一套程序会被安装到不同的环境,如:开发,测试,生产。其中数据库地址,服务器端口等等配置都是不同的,如果每次打包时,都要修改配置文件,那么非常麻烦。profile功能就是来进行动态配置切换的。
1.profile配置方式
多profile文件方式
#application.properties
spring.profiles.active=dev
#application-dev.properties 开发环境
server.port=8081
#application-test.properties 测试环境
server.port=8082
##application-pro.properties 生产环境
server.port=8083
yml多文档方式
在yml中使用 — 分隔不同的配置
---
server:
port: 8090
spring:
profiles: dev
---
server:
port: 8091
spring:
profiles: test
---
server:
port: 8092
spring:
profiles: pro
---
2.profile激活方式
配置文件:在配置文件中配置:spring.profiles.active=dev
虚拟机参数:再VM options指定:-Dspring.profiles.active=dev
命令行参数:java-jar xxx.jar --spring.profiles.active=dev
4.6 内部配置加载顺序
SpringBoot程序启动时,会从以下位置加载配置文件:
1. file:./config/:当前项目下的/config目录下
2. file:./ :当前项目的根目录
3. classpath:/config/:classpath的/config目录
4. classpath:/ :classpath的根目录
加载顺序为上文的排列顺序,高优先级配置的属性会生效
5. SpringBoot整合其它框架
5.1 SpringBoot整合Junit
首先创建一个service类
@Service
public class UserService {
public void add(){
System.out.println("add()....");
}
}
然后再test文件夹中创建
/**
* userService的测试类
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Springboot4JunitApplication.class)
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void testAdd(){
userService.add();
}
}
5.2 SpringBoot整合Redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
package com.lanou.springboot5redis;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
class Springboot5RedisApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testSet() {
//存入数据
redisTemplate.boundValueOps("name").set("zhangsan");
}
@Test
public void testGet() {
Object name = redisTemplate.boundValueOps("name").get();
System.out.println(name);
}
}
spring:
redis:
host: 127.0.0.1 #redis的主机IP
port: 6379
5.3 SpringBoot整合数据库
再pom文件中添加依赖
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
yml配置
#datasource
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mysql1?characterEncoding=utf-8&useSSL=false
username: root
password: 123456
测试代码
@Controller
public class DeptController {
@Autowired
private DataSource dataSource;
@Autowired
private JdbcTemplate jdbcTemplate;
@RequestMapping
@ResponseBody
public String test1(){
System.out.println(dataSource);
List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from status_inf");
System.out.println(list);
return "main";
}
}
5.4 SpringBoot整合MyBatis
1.搭建springboot工程
2.引入mybatis的起步依赖,添加mybatis驱动
3.编写DataSource和MyBatis相关配置
4.定义表和实体类
5.编写dao和mapper文件/纯注解开发
6.测试
方式1. 用注解的方式整合
首先添加依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
写出接口
public interface UserMapper {
@Select("select * from user")
public List<User> queryAll();
}
让mapper接口起作用
@MapperScan("com.lanou.mapper")
@SpringBootApplication
public class Springboot6MybatisApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot6MybatisApplication.class, args);
}
}
方式2. 用xml的方式整合
在yml中配置
#datasource
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/shiro?characterEncoding=utf-8&useSSL=false
username: root
password: 123456
#配置mybatis
mybatis:
mapper-locations: classpath: mapper/*Mapper.xml #mapper映射文件路径
type-aliases-package: com.lanou.bean;
config-location: classpath: mapper/mybatis-config.xml #指定mybatis的核心配置文件
#添加sql日志
logging:
level:
com:
lanou:
mapper: debug
mapper接口
@Mapper//加了@Mapper注解后,spring会自动扫包,创建工具类,加载映射文件并生成对相应的mapper
@Repository
public interface UserXmlMapper {
public List<User> findAll();
}
userMapper.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="com.lanou.mapper.UserXmlMapper">
<select id="findAll" resultType="User">
select * from user
</select>
</mapper>
测试
@Controller
public class DeptController {
@Autowired
private DataSource dataSource;
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private DeptMapper deptMapper;
@RequestMapping
@ResponseBody
public String test1(){
List<Dept> all = deptMapper.getAll();
System.out.println(all);
System.out.println(dataSource);
List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from status_inf");
System.out.println(list);
return "main";
}
}
5.5 SpringBoot整合Email
-
邮件发送需要引入spring-boot-start-mail
-
SpringBoot 自动配置MailSenderAutoConfiguration
-
定义MailProperties内容,配置在application.yml中
-
自动装配JavaMailSender
-
测试邮件发送
pom文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
properties文件
spring.mail.username=#发件人邮箱
spring.mail.password=#你的qq授权码
spring.mail.host=smtp.qq.com
#qq需要配置ssl
spring.mail.properties.mail.smtp.ssl.enable=true
测试类
@SpringBootTest
public class SpringbootAsyncEmailApplicationTests {
@Autowired
public JavaMailSender javaMailSender;
//发邮件
@Test
void contextLoads1() {
SimpleMailMessage message = new SimpleMailMessage();
//邮件设置
message.setSubject("springboot整合email发送信息或文件!");//设置标题
message.setText("你真好看。");//设置内容
message.setFrom("xxxxxx@qq.com");//发送端
message.setTo("xxxxx@163.com");//接收端
javaMailSender.send(message);
}
//发送含有html页面的邮件
@Test
void contextLoads2() throws MessagingException {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage);
//邮件设置
messageHelper.setSubject("验证码邮件");//主题 需要抛出异常
messageHelper.setText("邮件内容");
messageHelper.setText("<h1>验证码内容: 9093</h1>",true);
messageHelper.setFrom("xxxxxx@qq.com");//发送端
messageHelper.setTo("xxxxx@163.com");//接收端
javaMailSender.send(mimeMessage);
}
//发送内嵌图片的邮件
@Test
void contextLoads3() throws MessagingException {
MimeMessage mimeMessage2 = javaMailSender.createMimeMessage();
MimeMessageHelper messageHelper2 = new MimeMessageHelper(mimeMessage2,true);
//邮件设置
String id = "3";
String ImgPath = "C:\\Users\\24329\\Pictures\\Saved Pictures\\3.jpeg";
messageHelper2.setSubject("含有图片的邮件");//主题 需要抛出异常
messageHelper2.setText("<h1>图片:</h1><img src='cid:"+id+"'/>",true);
FileSystemResource fileSystemResource = new FileSystemResource(new File(ImgPath));
messageHelper2.addInline(id,fileSystemResource);
messageHelper2.setFrom("xxxxxx@qq.com");//发送端
messageHelper2.setTo("xxxxx@163.com");//接收端
javaMailSender.send(mimeMessage2);
}
//发送含有附件的邮件
@Test
void contextLoads4() throws MessagingException {
MimeMessage mimeMessage2 = javaMailSender.createMimeMessage();
MimeMessageHelper messageHelper2 = new MimeMessageHelper(mimeMessage2,true);
//邮件设置
String id = "3";
String ImgPath = "C:\\Users\\24329\\Pictures\\Saved Pictures\\1.jpeg";
messageHelper2.setSubject("含有附件的邮件");//主题 需要抛出异常
messageHelper2.setText("<h1>图片:</h1><img src='cid:"+id+"'/>",true);
FileSystemResource fileSystemResource = new FileSystemResource(new File(ImgPath));
messageHelper2.addInline(id,fileSystemResource);
//添加addAttachment方法可是实现发送附件
messageHelper2.addAttachment("1.jepg",fileSystemResource);
message.setFrom("xxxxxx@qq.com");//发送端
message.setTo("xxxxx@163.com");//接收端
javaMailSender.send(mimeMessage2);
}
}
5.6 SpringBoot异步任务
异步处理还是非常常用的,比如我们在网站上发送邮件,后台会去发送邮件,此时前台会造成响应不动,直到邮件发送完毕,响应才会成功,所以我们一般会采用多线程的方式去处理这些任务。
@EnableAsync //开启异步注解功能 这个注解需要在启动控制类上加
@Async //异步方法
spring.mail.username=#发件人邮箱
spring.mail.password=#你的qq授权码
spring.mail.host=smtp.qq.com
#qq需要配置ssl
spring.mail.properties.mail.smtp.ssl.enable=true
@EnableAsync //开启异步注解功能
@SpringBootApplication
public class SpringbootAsyncEmailApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootAsyncEmailApplication.class, args);
}
}
@Service
public class AsyncService {
@Autowired
public JavaMailSender javaMailSender;
@Async //声明email是一个异步方法
public void email() throws InterruptedException {
Thread.sleep(3000);//等3秒钟
SimpleMailMessage message = new SimpleMailMessage();
//邮件设置
message.setSubject("异步email发送信息或文件!");//设置主题
message.setText("你真好看。");//设置内容
message.setFrom("xxxxxx@qq.com");//发送端
message.setTo("xxxxxx@163.com");//接收端
javaMailSender.send(message);
}
}
@RestController
public class AsyncController {
@Autowired
public AsyncService asyncService;
@RequestMapping("/sendEmail")
public String sendEamil() throws InterruptedException {
asyncService.email();
return "ok";
}
}
5.7 SpringBoot整合定时器
项目开发中经常需要执行一些定时任务,比如需要在每天凌晨的时候,分析一次前一天的日志信息,Spring为我们提供了异步执行任务调度的方式,提供了两个接口。
- TaskExecutor接口
- TaskScheduler接口
两个注解:
- @EnableScheduling:开启定时任务
- @Scheduled:定时方法
cron表达式:
定时任务常用表达式(cron表达式)
https://www.bejson.com/othertools/cron/ 在线生成表达式工具
(1)0/2 * * * * ? 表示每2秒 执行任务
(1)0 0/2 * * * ? 表示每2分钟 执行任务
(1)0 0 2 1 * ? 表示在每月的1日的凌晨2点调整任务
(2)0 15 10 ? * MON-FRI 表示周一到周五每天上午10:15执行作业
(3)0 15 10 ? 6L 2002-2006 表示2002-2006年的每个月的最后一个星期五上午10:15执行作
(4)0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
(5)0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
(6)0 0 12 ? * WED 表示每个星期三中午12点
(7)0 0 12 * * ? 每天中午12点触发
(8)0 15 10 ? * * 每天上午10:15触发
(9)0 15 10 * * ? 每天上午10:15触发
(10)0 15 10 * * ? 每天上午10:15触发
(11)0 15 10 * * ? 2005 2005年的每天上午10:15触发
(12)0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触发
(13)0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
(14)0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
(15)0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
(16)0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发
(17)0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
(18)0 15 10 15 * ? 每月15日上午10:15触发
(19)0 15 10 L * ? 每月最后一日的上午10:15触发
(20)0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
(21)0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发
(22)0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
代码实现
主程序上增加**@EnableScheduling** 开启定时任务功能
@EnableScheduling//开启定时任务 在启动控制类上加
//cron表达式中参数的意义:秒 分 时 日 月 周几
@Scheduled(cron = "0/2 * * * * ?")//定时的方法 需要定时的方法上加上该注解
spring.mail.username=#发件人邮箱
spring.mail.password=#你的qq授权码
spring.mail.host=smtp.qq.com
#qq需要配置ssl
spring.mail.properties.mail.smtp.ssl.enable=true
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
@EnableAsync //开启异步注解功能
@EnableScheduling//开启定时任务
@SpringBootApplication
public class SpringbootAsyncEmailApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootAsyncEmailApplication.class, args);
}
}
@Service
public class ScheduleService {
@Autowired
public JavaMailSender javaMailSender;
//定时任务
//cron表达式中参数的意义:秒 分 时 日 月 周几
@Scheduled(cron = "0 3 5 * * ?")//需要定时的方法上加上该注解
public void task() throws InterruptedException {
System.out.println("启动执行定时任务!");
Thread.sleep(3000);//等3秒钟
SimpleMailMessage message = new SimpleMailMessage();
//邮件设置
message.setSubject("定时email发送信息或文件!");//设置主题
message.setText("你真好看。");//设置内容
message.setFrom("xxxxx@qq.com");//发送端
message.setTo("xxxxx@163.com");//接收端
javaMailSender.send(message);
}
}
5.9 SpringBoot整合redis
pom文件
<!--redis起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
properties文件
#配置redis
sping.redis.host=127.0.0.1
spring.redis.port=6379
代码实现
@RunWith(SpringRunner.class)
@SpringBootTest
class Springboot5RedisApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void contextLoads() {
//redisTemplate操作不同的数据类型和操作的指令是一样的
//opsForValue 操作字符串 类似于String
//opsForList 操作List 类似于List
//opsForHash
//opsForSet
//opsForZSet
//opsForGeo
//除了基本的操作,我们常用的方法都可直接使用redisTemplate操作,
//获取数据库连接
// RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
// connection.flushDb();
// connection.flushAll();
// connection.close();
redisTemplate.opsForValue().set("key1","zhangsan");
System.out.println(redisTemplate.opsForValue().get("key1"));
}
}
配置自己的RedisTemplate序列化模板(一般公司用的)
@Configuration
public class RedisConfig {
//编写我们自己的redisTemplate 固定模板
@Bean
@SuppressWarnings("all")
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory) {
//我们为了自己开发方便,一般直接使用<String,Object>类型
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//获取连接
redisTemplate.setConnectionFactory(factory);
//json序列化配置
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(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的序列化方式
redisTemplate.setKeySerializer(stringRedisSerializer);
//hash的key也采用String的序列化方式
redisTemplate.setHashKeySerializer(stringRedisSerializer);
//value序列化方式采用jackson
redisTemplate.setValueSerializer(stringRedisSerializer);
//hash的value序列化方式采用jackson
redisTemplate.setHashValueSerializer(stringRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
RedisUtils工具类
package com.lanou.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Component
public final class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// =============================common============================
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
* @param key 键
* @param delta 要增加几(大于0)
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
* @param key 键
* @param delta 要减少几(小于0)
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
* @param key 键
* @param map 对应多个键值
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
* @param key 键
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0)
expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
测试redisUtil
@Autowired
private RedisUtil redisUtil;
@Test
public void redisUtilTest(){
redisUtil.set("name","zhangsan")
System.out.println(redisUtil.get("name"));
}
5.9 SpringBoot整合Shiro
shiro三大组件
<!--
shiro
Subject:用户
SeurityManager:管理所有用户
Realm 连接数据
-->
pom文件
<!--连接数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--shiro整合Spring-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!--thymeleaf-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>6.1.25</version>
</dependency>
<!--shiro-thymeleaf-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
代码实现
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>首页</p>
<div th:if="${session.loginUser==null}">
<a th:href="@{/toLogin}">登录</a>
</div>
<p th:text="${msg}"></p><!--访问传递的msg信息-->
<hr>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add</a>
</div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">update</a>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h1>登录</h1>
<hr>
<p th:text="${msg}" style="color: red;"></p>
<form th:action="@{/login}">
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="text" name="password"></p>
<p><input type="submit"></p>
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>update</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>add</h1>
</body>
</html>
public class User {
private int id;
private String name;
private String password;
private String perms;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPerms() {
return perms;
}
public void setPerms(String perms) {
this.perms = perms;
}
public User() {
}
public User(int id, String name, String password, String perms) {
this.id = id;
this.name = name;
this.password = password;
this.perms = perms;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", perms='" + perms + '\'' +
'}';
}
}
@Mapper
@Repository
public interface UserMapper {
public User queryUserByName(String name);
}
<?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.lanou.mapper.UserMapper">
<select id="queryUserByName" parameterType="String" resultType="User">
select * from user where name = #{name}
</select>
</mapper>
public interface UserService {
public User queryUserByName(String name);
}
@Service
public class UserServiceImpl implements UserService{
@Autowired
public UserMapper userMapper;
@Override
public User queryUserByName(String name) {
return userMapper.queryUserByName(name);
}
}
package com.lanou.config;
import com.lanou.pojo.User;
import com.lanou.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
//自定义的UserRealm extends AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权方法=>授权doGetAuthorizationInfo");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// info.addStringPermission("user:add");
//拿到当前用户对象
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();//拿到user对象
//设置当前用户权限
info.addStringPermission(currentUser.getPerms());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了认证方法=>认证doGetAuthorizationInfo");
//用户名密码,数据库中取。先伪造数据
// String name = "root";
// String password = "123456";
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
//连接真实数据库
User user = userService.queryUserByName(userToken.getUsername());
if (null==user) {//没有这个人
return null;//抛出异常 UnknownAccountException
}
Subject subject1 = SecurityUtils.getSubject();
Session session = subject1.getSession();
session.setAttribute("loginUser",user);
//密码认证,shiro做
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}
package com.lanou.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean:3:连接到前端
@Bean("shiroFilterFactoryBean")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
/*
添加shiro的内置过滤器:(设置用户登陆权限)
anon:无需认证就可以访问
authc:必须认证了才能访问
user:必须拥有 记住我 功能才能使用
perms:拥有对某个资源的权限才能使用
role:拥有某个角色授权才能访问
// filterMap.put("/user/add","anon");
// filterMap.put("/user/update","authc");
*/
Map<String,String> filterMap = new LinkedHashMap<>();
//拦截
filterMap.put("/user/add","authc");
//filterMap.put("/user/*","authc");//认证之后才能访问
//授权,正常情况下,没有授权的会跳转到未授权页面
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");
//设置过滤器的链
bean.setFilterChainDefinitionMap(filterMap);
//如果没有权限 需要设置登录请求(拦截之后需要跳转到登录页面)
bean.setLoginUrl("/toLogin");
//设置未授权请求页面
bean.setUnauthorizedUrl("/noauth");
return bean;
}
//DafaultWebSecurityManage:2:管理realm对象
@Bean(name = "securityManager") //@Qualifier指定方法名
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm对象。需要自定义类UserRealm。1
//用户认证放在userRealm()方法里
@Bean//将UserRealm注入到bean
public UserRealm userRealm(){ return new UserRealm(); }
//整合Shiro方言:用来整合shiro thymeleaf
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
package com.lanou.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @PackageName: com.lanou.controller
* @ClassName: MyController
* @author: zhangpan
* @data: 2021/11/15 18:01
*/
@Controller
public class MyController {
@RequestMapping({"/","/index"})
public String toIndex(Model model){
model.addAttribute("msg","hello,Shiro");
return "index";
}
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/update")
public String update(){
return "user/update";
}
@RequestMapping("/toLogin")
public String toLogin(){ return "login"; }
@RequestMapping("/login")
public String login(String username, String password, Model model){
//1.获取当前的用户
Subject subject = SecurityUtils.getSubject();
//2.封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);//执行登录方法,如果没有异常,说明ok了
return "index";//登录成功返回首页
} catch (UnknownAccountException e) {//用户名不存在
model.addAttribute("msg","用户名错误");
System.out.println("用户名错误!");
return "login";
} catch (IncorrectCredentialsException e) {//密码不存在
model.addAttribute("msg","密码错误");
System.out.println("密码错误!");
return "login";
}
}
@RequestMapping("/noauth")
@ResponseBody
public String unauthorized(){//未授权页面
return "未经授权无法访问此页面。";
}
}
5.10 SpringBoot整合elsearch
5.11 SpringBoot整合fastdfs
pom文件
<!--fastdfs依赖-->
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
<version>1.26.1-RELEASE</version>
</dependency>
yml文件
fdfs:
so-timeout: 1501
connect-timeout: 601
thumb-image:
width: 60
height: 60
tracker-list: # tracker地址 虚拟机IP地址 + 22122
- IP:22122
配置文件类
@Configuration
@Import(FdfsClientConfig.class) // 解决jmx重复注册bean的问题
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
public class FastdfsClientImporter {}
实现代码
@RestController
public class UpLoadController {
@Autowired
private FastFileStorageClient storageClient;
@RequestMapping("/up")
public String testUpload() throws FileNotFoundException {
// 要上传的文件
File file = new File("D:\\123.jpg");
// 上传并保存图片,参数:1-上传的文件流 2-文件的大小 3-文件的后缀 4-可以不管他
StorePath storePath = this.storageClient.uploadFile( new FileInputStream(file), file.length(), "jpg", null);
// 带分组的路径
System.out.println(storePath.getFullPath());
// 不带分组的路径
System.out.println(storePath.getPath());
return "success";
}
}
5.11 SpringBoot整合rabbitmq
6. SpringBoot自动配置
6.1 Condition(条件,状况)
Condition是在Spring4.0增加的条件判断功能,通过这个可以实现选择性的创建Bean操作。
看matches方法的返回值
public class ConditionClass implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return true; //如果返回true,则创建bean对象
//return false; //如果返回false,则不创建bean对象
}
}
Condition案例1
<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
public class User {}
public class ConditionClass implements Condition {
/**
* @param context:上下文对象。用于获取环境,IOC容器,ClassLoader对象
* @param metadata:注解的原对象,可以用于获取注解定义的属性值
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//1.需求:导入Jedis坐标后创建Bean
//思路:判断redis.clients.jedis.Jedis.class文件是否存在
boolean flag = true;
try {
Class<?> aClass = Class.forName("redis.clients.jedis.Jedis");
} catch (ClassNotFoundException e) {
flag = false;
}
return flag;
}
}
@Configuration
public class UserConfig {
@Bean
@Conditional(ConditionClass.class) //如果ConditionClass类中的matches方法返回true则创建对象,返回false则不创建对象
public User user(){
return new User();
}
}
@SpringBootApplication
public class Springboot7ConditionApplication {
public static void main(String[] args) {
//启动SpringBoot应用,返回Spring的IOC容器
ConfigurableApplicationContext context = SpringApplication.run(Springboot7ConditionApplication.class, args);
//获取自己定义的Bean
Object user = context.getBean("user");
System.out.println(user);
}
}
Condition案例2 注意
自定义注解,判断类是否存在
public class User {}
public class ConditionClass implements Condition {
/**
* @param context:上下文对象。用于获取环境,IOC容器,ClassLoader对象
* @param metadata:注解的原对象,可以用于获取注解定义的属性值
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//1.需求:导入Jedis坐标后创建Bean
//思路:判断redis.clients.jedis.Jedis.class文件是否存在
boolean flag = true;
try {
Class<?> aClass = Class.forName("redis.clients.jedis.Jedis");
} catch (ClassNotFoundException e) {
flag = false;
}
return flag;
}
}
/**
* 创建一个Annotation类,自定义一个注解
*/
@Target({ElementType.TYPE, ElementType.METHOD}) //这个注解的意思是:我们自定义的注解可以加那个范围上,类上面和方法上面
@Retention(RetentionPolicy.RUNTIME) //注解生效的时机
@Documented //生成javaDoc文档
@Conditional(ConditionClass.class)
public @interface ConditionalOnClass {
String[] value();
}
@Configuration
public class UserConfig {
@Bean
// @Conditional(ConditionClass.class) //如果ConditionClass类中的matches方法返回true则创建对象,返回false则不创建对象
@ConditionalOnClass("redis.clients.jedis.Jedis")//自定义的注解
public User user(){
return new User();
}
}
@SpringBootApplication
public class Springboot7ConditionApplication {
public static void main(String[] args) {
//启动SpringBoot应用,返回Spring的IOC容器
ConfigurableApplicationContext context = SpringApplication.run(Springboot7ConditionApplication.class, args);
//使用自定义注解获取自己定义的Bean
Object user = context.getBean("user");
System.out.println(user);
}
}
Condition小结
自定义条件
-
定义条件类:自定义实现Condition接口,重写matches方法,在matches方法中进行逻辑判断,返回boolean值。matches方法两个参数:
context:上下文对象。用于获取环境,IOC容器,ClassLoader对象
metadata:注解的原对象,可以用于获取注解定义的属性值
- 判断条件:在初始化Bean时,使用 @Conditional(条件类.class) 注解
SpringBoot提供的常用条件注解
- ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化Bean
- ConditionalOnClass:判断环境中是否有对应字节码文件才初始化Bean
- ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean
6.2 切换内置web服务器
SpringBoot的web环境中默认使用tomcat作为内置服务器,其实SpringBoot提供了4中内置服务器供我们选择,我们可以很方便的进行切换。
<!--springboot的web项目依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--切换内置服务器-->
<!--从spring-boot-satrt-web中排除tomcat依赖-->
<exclusions>
<exclusion>
<groupId>spring-boot-starter-tomcat</groupId>
<artifactId>org.springframework.boot</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--引入jetty依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
6.3 Enable*注解
SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的。而其底层原理使用@Import注解导入一些配置类,实现Bean的动态加载。
首先创建两个模块
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qujqfbUT-1637053496554)(D:\zhangpan\JavaPDF\自学笔记\springbootenable.png)]
方式1:
在springboot8-enable-other中创建
public class User {}
package com.lanou.config;
@Configuration
public class UserConfig {
@Bean
public User user() {
return new User();
}
}
在springboot8-enable中创建
/**
* @ComponentScan("") :扫描范围是当前引导类所在包及其子包
* com.lanou.springboot8enable;
* com.lanou.config;
* 两个不属于父子包关系
* //1.使用@ComponentScan扫描com.lanou.config包
*/
@SpringBootApplication
@ComponentScan("com.lanou.config")//把配置类所在的包添加到扫描的范围里面
public class Springboot8EnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Springboot8EnableApplication.class, args);
//1.获取Bean
Object user = context.getBean("user");
System.out.println(user);
}
}
方式2:
在springboot8-enable-other中创建
public class User {}
@Configuration
public class UserConfig {
@Bean
public User user() {
return new User();
}
}
在springboot8-enable中创建
/**
* @ComponentScan("") :扫描范围是当前引导类所在包及其子包
* com.lanou.springboot8enable;
* com.lanou.config;
* 两个不属于父子包关系
* //2.使用@Import注解,加载类。这些类都会被Spring创建,并且放入IOC容器
*/
@SpringBootApplication
@Import(UserConfig.class)//把配置类所在的包添加到扫描的范围里面
public class Springboot8EnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Springboot8EnableApplication.class, args);
//1.获取Bean
Object user = context.getBean("user");
System.out.println(user);
}
}
方式3:
public class User {}
@Configuration
public class UserConfig {
@Bean
public User user() {
return new User();
}
}
/**
* 自定义注解
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(UserConfig.class)
public @interface EnableUser {}
/**
* 两个不属于父子包关系 需要扫描范围是当前引导类所在包及其子包
* //3.可以对Import注解进行封装。
*/
@SpringBootApplication
@EnableUser
public class Springboot8EnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Springboot8EnableApplication.class, args);
//1.获取Bean
Object user = context.getBean("user");
System.out.println(user);
}
}
-1637053496554)]
方式1:
在springboot8-enable-other中创建
public class User {}
package com.lanou.config;
@Configuration
public class UserConfig {
@Bean
public User user() {
return new User();
}
}
在springboot8-enable中创建
/**
* @ComponentScan("") :扫描范围是当前引导类所在包及其子包
* com.lanou.springboot8enable;
* com.lanou.config;
* 两个不属于父子包关系
* //1.使用@ComponentScan扫描com.lanou.config包
*/
@SpringBootApplication
@ComponentScan("com.lanou.config")//把配置类所在的包添加到扫描的范围里面
public class Springboot8EnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Springboot8EnableApplication.class, args);
//1.获取Bean
Object user = context.getBean("user");
System.out.println(user);
}
}
方式2:
在springboot8-enable-other中创建
public class User {}
@Configuration
public class UserConfig {
@Bean
public User user() {
return new User();
}
}
在springboot8-enable中创建
/**
* @ComponentScan("") :扫描范围是当前引导类所在包及其子包
* com.lanou.springboot8enable;
* com.lanou.config;
* 两个不属于父子包关系
* //2.使用@Import注解,加载类。这些类都会被Spring创建,并且放入IOC容器
*/
@SpringBootApplication
@Import(UserConfig.class)//把配置类所在的包添加到扫描的范围里面
public class Springboot8EnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Springboot8EnableApplication.class, args);
//1.获取Bean
Object user = context.getBean("user");
System.out.println(user);
}
}
方式3:
public class User {}
@Configuration
public class UserConfig {
@Bean
public User user() {
return new User();
}
}
/**
* 自定义注解
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(UserConfig.class)
public @interface EnableUser {}
/**
* 两个不属于父子包关系 需要扫描范围是当前引导类所在包及其子包
* //3.可以对Import注解进行封装。
*/
@SpringBootApplication
@EnableUser
public class Springboot8EnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Springboot8EnableApplication.class, args);
//1.获取Bean
Object user = context.getBean("user");
System.out.println(user);
}
}