用来简化spring应用的初始搭建以及开发过程
快速启动(cmd命令行)
jar支持命令行启动需要maven插件的支持:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
配置文件优先级:
application.properties > application.yml > application.yaml
引导类:
springboot工程提供引导类来启动程序
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
springboot工程启动后创建并初始化spring容器,扫描引导类所在包加载bean
常用依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
lombok:
项目整合mybatis-plus:
mybatis-plus:
global-config:
db-config:
table-prefix: tb_
id-type: auto #开启主键id自增
configuration:
#开启日志详细过程,方便调试
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
业务异常处理:
//作为springmvc的异常处理器
@RestControllerAdvice
public class ProjectExceptionAdvice {
//拦截所有异常信息
@ExceptionHandler(Exception.class)
public R doException(Exception e) {
//记录日志,通知运维,通知开发
e.printStackTrace();
return new R("服务器故障,请稍后再试!");
}
}
业务消息统一返回:
@NoArgsConstructor
@AllArgsConstructor
@Data
public class R {
private Boolean flag;
private Object data;
private String msg;
public R(Boolean flag) {
this.flag = flag;
}
public R(Boolean flag, Object data) {
this.flag = flag;
this.data = data;
}
public R(Boolean flag, String msg) {
this.flag = flag;
this.msg = msg;
}
public R(String msg) {
this.flag = false;
this.msg = msg;
}
}
工程打包与运行:
打包插件:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
windows端口被占用
# 查询端口
netstat -ano
# 查询指定端口
netstat -ano |findstr "端口号"
# 根据进程PID查询进程名称
tasklist |findstr "进程PID号"
# 根据PID杀死任务
taskkill /F /PID "进程PID号"
# 根据进程名称杀死任务
taskkill -f -t -im "进程名称"
在Linux中:
#后台挂载运行.jar项目
nohup java -jar xxx.jar > server.log 2>&1 &
[1]30669
#server.log为项目执行过程的日志
#查看运行着的jar项目PID
ps -ef|grep "java -jar"
#结束进程
kill -9 30669
临时属性: 带属性数启动springboot,携带多个属性用空格分隔
java -jar xxx.jar --server.port=8080 --spring.datasource.druid.password=123
//在开发环境下设置临时属性
public static void main(String[] args) {
String[] arg = new String[1];
arg[0] = "--server.port=8081";
SpringApplication.run(Application.class, arg);
}
配置文件分类:
自定义配置文件:通过启动参数加载指定文件路径下的配置文件时可以加载多个配置
--spring.config.name=ebank
--spring.config.location=classpath:/ebank.yml,classpath:/ebank-server.yml //后面的配置覆盖前面的
多环境开发:
#应用环境,公共配置
spring:
profiles:
active: dev
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test1?useSSL=false&serverTimezone=UTC
username: root
password: 123456
mybatis-plus:
global-config:
db-config:
table-prefix: tb_
id-type: auto
# configuration:
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
---
#设置环境
#开发环境
server:
port: 8080
spring:
config:
activate:
on-profile: dev
---
#生产环境
server:
port: 81
spring:
profiles: pro
---
#测试环境
server:
port: 82
spring:
profiles: test
多环境开发多配置文件格式:
主启动配置文件application.yml
spring:
profiles:
active: dev
环境分类配置文件application-pro.yml
server:
port: 80
环境分类配置文件application-dev.yml
server:
port: 81
环境分类配置文件application-test.yml
server:
port: 82
多环境分组管理:
1.根据功能对配置文件中的信息进行拆分,并制作成独立的配置文件,命名规则如下
2.使用include属性在激活指定环境的情况下,同时对多个环境进行加载使其生效,多个环境间使用逗号分隔
spring:
profiles:
active: dev
include: devDB,devRedis,devMvc
3.使用group属性替代include属性,降低了配置书写量
spring:
profiles:
active: dev
group:
"dev": devDB,devRedis,devMvc
"pro": proDB,proRedis,proMvc
开启开发者工具
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
激活热部署:CTRL+F9
重启(restart):自定义开发代码,包含类、页面、配置文件等,加载位置restart类加载器
热部署范围配置:
#设置不热部署的目录,自定义不参与重启排除项
devtools:
restart:
exclude: static/**,config/application.yml
关闭热部署:
public static void main(String[] args) {
System.setProperty("spring.devtools.restart.enabled","false");
SpringApplication.run(Application.class, arg);
}
@ConfigurationProperties(prefix = “xxx”):为第三方bean绑定属性,支持宽松绑定,如驼峰模式(ipAddress)、下划线模式(ip_address)、中划线(ip-address)和常量模式(IP_ADDRESS)
@Component
@Data
@ConfigurationProperties(prefix = "servers")
public class ServerConfig {
private String ipAddress;
private int port;
private long timeout;//超时时间
}
@Bean
@ConfigurationProperties(prefix = "datasource")
public DruidDataSource datasource {
DruidDataSource ds=new DruidDataSource();
return ds;
}
servers:
ipAddress: 192.168.0.1
port: 8081
timeout: -1
datasource:
driverClassName: com.mysql.jdbc.Driver
@Value(“${xxx.yyy}”)也可以进行属性绑定,但是不支持松散绑定
数据校验:
导入JSR303规范
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<!--实用hibernate框架提供的校验器做实现类-->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
开启对当前bean的属性注入校验
@Validated
public class ServerConfig {
private String ipAddress;
@Max(value = 8888,message = "最大值不能超过8888")
private int port;
private long timeout;//超时时间
}
加载测试专用属性:用于小范围测试环境
在启动测试环境时可以通过properties参数设置测试环境专用的属性,仅对当前测试类有效
@SpringBootTest(properties = {"test.prop=testValue1"})
class SpringbootPlusApplicationTests {
@Value("${test.prop}")
private String msg;
@Test
void contextLoads() {
System.out.println(msg);
}
}
在启动测试环境时可以通过args参数设置测试环境专用的传入属性
@SpringBootTest(args = {"--test.arg=testValue2"})
class SpringbootPlusApplicationTests {
@Value("${test.arg}")
private String msg;
@Test
void contextLoads() {
System.out.println(msg);
}
}
加载测试专用配置:
使用@Import注解加载当前测试类专用的配置
@SpringBootTest
@Import(MsgConfig.class)
class SpringbootPlusApplicationTests {
@Autowired
private String msg;
@Test
void contextLoads() {
System.out.println(msg);
}
}
web环境模拟测试
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//开启虚拟MVC调用
@AutoConfigureMockMvc
public class WebTest {
@Test
void testWeb(@Autowired MockMvc mvc) throws Exception {
//创建虚拟请求,当前访问/books
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
//执行请求
mvc.perform(builder);
}
@Test
void testStatus(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
ResultActions action = mvc.perform(builder);
//设定预期值,与真实值进行比较
//定义本次调用的预期值
StatusResultMatchers status = MockMvcResultMatchers.status();
//预计本次调用成功的:状态200
ResultMatcher ok = status.isOk();
//添加预期值到本次调用过程中进行匹配
action.andExpect(ok);
}
@Test
void testBody(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
ResultActions action = mvc.perform(builder);
//定义执行结果匹配器
ContentResultMatchers content = MockMvcResultMatchers.content();
//定义执行结果
ResultMatcher result = content.string("springboot");
//添加预期值到本次调用过程中进行匹配
action.andExpect(result);
}
@Test
void testRandomPort() {
}
}
内置数据源
设定
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test1?useSSL=false&serverTimezone=UTC
username: root
password: 123456
hikari:
maximum-pool-size: 50
内置持久化解决方案——JdbcTemplate
JdbcTemplate是 Spring 对 JDBC 的封装,目的是使JDBC更加易于使用,JdbcTemplate是Spring的一部分
org.springframework.boot
spring-boot-starter-jdbc
@Repository
public class BookDaoImp implements BookDao{
//按类型、自动注入了JdbcTemplate对象
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void add(Book book) {
//创建添加的sql语句
String addsql = "insert into Book values(?,?,?)";
//调用jdbcTemplate.update实现添加,book.getUserid()、book.getUsername()、book.getUstatus()对应values(?,?,?)里的三个问号
Object[] args = {book.getUserid(), book.getUsername(), book.getUstatus()};
int update = jdbcTemplate.update(addsql,args);
//返回值update代表添加了几行
System.out.println(update);
}
@Override
public void update(Book book) {
//根据id修改username ustatus
String updatesql = "update Book set username=?,ustatus=?where user_id=?";
Object[] args = {book.getUsername(), book.getUstatus(),book.getUserid(),};//注意参数顺序
int update = jdbcTemplate.update(updatesql,args);
}
@Override
public void delete(String id) {
//根据user_id删除
String deletesql = "delete from Book where user_id=?";
int update = jdbcTemplate.update(deletesql,id);
}
}
内嵌数据库——H2
导入H2相关坐标
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
设置当前项目为web工程,并且配置H2管理控制台参数
server:
port: 80
spring:
h2:
console:
path: /h2
enabled: true
启动
NoSQL解决方案:
Redis:
springboot整合redis:
导入坐标:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
相关配置
spring:
redis:
host: localhost
port: 6379
client-type: lettuce
使用操作接口RedisTemplate进行操作,注意:RedisTemplate以对象作为key和value,内部对数据进行序列化;StringRedisTemplate以字符串作为key和value,与Redis客户端操作等效
@SpringBootTest
public class RedisTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
void test() {
ValueOperations ops = redisTemplate.opsForValue();
ops.set("age", 23);
Object age = ops.get("age");
System.out.println(age);
HashOperations opsForHash = redisTemplate.opsForHash();
opsForHash.put("info","name","springboot");
opsForHash.put("info","age",22);
Object name = opsForHash.get("info", "name");
System.out.println(name);
}
}
MongoDB
导坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
配置mongodb访问uri
spring:
data:
mongodb:
uri: mongodb://localhost/test
提供操作mongodb接口对象MongoTemplate
Elasticsearch:
分词,索引,倒排索引,创建文档,使用文档
IK分词器,设置索引创建规则
文档操作
创建文档
POST http://localhost:9200/books/_doc #使用系统生成id
POST http://localhost:9200/books/_create/1 #使用指定id
POST http://localhost:9200/books/_doc/1 #使用指定id,不存在创建,存在更新
查询文档
GET http://localhost:9200/books/_doc/1 #查询单个文档
GET http://localhost:9200/books/_search #查询全部文档
条件查询
GET http://localhost:9200/books/_search?q=name:springboot
删除文档
DELETE http://localhost:9200/books/_doc/1
修改文档(全量修改)
PUT http://localhost:9200/books/_doc/1
修改文档(部分修改)
POST http://localhost:9200/books/_update/1
整合springboot
<!--低级别-->
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
@SpringBootTest
public class ElasticSearchTest {
// 低级别
// @Autowired
// private ElasticsearchRestTemplate template;
private RestHighLevelClient client;
@BeforeEach
void setUp() {
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
client = new RestHighLevelClient(builder);
}
@AfterEach
void tearDown() throws IOException {
client.close();
}
@Test
void test() {
}
}
缓存
缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质
使用缓存可以有效的减少低速数据读取过程的次数,提高系统性能
spring boot使用缓存
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
@EnableCaching
@Cacheable(value = "cacheSpace",key = "#id")
public Book getById(Integer id){
return bookDao.selectById(id);
}
@Component
public class CodeUtils {
private String[] patch = {"000000", "00000", "0000", "000", "00", "0", ""};
public String generator(String tel) {
int hash = tel.hashCode();
int encryption = 20206666;
long result = hash ^ encryption;
long nowTime = System.currentTimeMillis();
result = result ^ nowTime;
long code = result % 1000000;
code = code < 0 ? -code : code;
String codeStr = code + "";
int len = codeStr.length();
return patch[len] + codeStr;
}
@Cacheable(value = "smsCode", key = "#tel")
public String get(String tel) {
return null;
}
/*public static void main(String[] args) {
System.out.println(new CodeUtils().generator("12345678765"));
}*/
}
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@Override
@CachePut(value = "smsCode", key = "#tel")
public String sendCodeToSMS(String tel) {
return codeUtils.generator(tel);
}
@Override
public boolean checkCode(SMSCode smsCode) {
String code = smsCode.getCode();
String cacheCode = codeUtils.get(smsCode.getTel());
return code.equals(cacheCode);
}
}
@RestController
@RequestMapping("/sms")
public class SMSCodeController {
@Autowired
private SMSCodeService smsCodeService;
@GetMapping
public String getCode(String tel) {
return smsCodeService.sendCodeToSMS(tel);
}
@PostMapping
public boolean checkCode(SMSCode code) {
return smsCodeService.checkCode(code);
}
}
jetcache
任务
Quartz是一个定时任务调度框架,比如你遇到这样的问题:
比如淘宝的待支付功能,后台会在你生成订单后24小时后,查看订单是否支付,未支付则取消订单
比如vip的每月自动续费功能
…
想定时在某个时间,去做某件事
Quartz是一套轻量级的任务调度框架,只需要定义了 Job(任务),Trigger(触发器)和 Scheduler(调度器),即可实现一个定时调度能力。支持基于数据库的集群模式,可以做到任务幂等执行。
springboot整合Quartz
--1.导坐标--
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
--2.实现类--
public class MyQuartz extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("quartz task run");
}
}
--3.配置信息--
@Configuration
public class QuartzConfig {
@Bean
public JobDetail printJobDetail() {
//绑定具体工作
return JobBuilder.newJob(MyQuartz.class).storeDurably().build();
}
@Bean
public Trigger printTrigger() {
//绑定对应的工作明细
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
return TriggerBuilder.newTrigger().forJob(printJobDetail()).withSchedule(scheduleBuilder).build();
}
}
springboot整合task
--1.在主启动类上添加注解,开启定时任务功能--
@EnableScheduling
--2.在具体任务上添加cron表达式,设置定时执行的任务,并设定执行周期--
@Component
public class MyBean {
@Scheduled(cron = "0/1 * * * * ?")
public void print() {
System.out.println("spring task run...");
}
}
邮件
springboot整合JavaMail
--1.导坐标--
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
--2.配置--
spring:
mail:
host: smtp.qq.com
username: 1474150648@qq.com
password: orqbnhnhpietgjii
--3.发送简单邮件--
@Service
public class SendMailServiceImpl implements SendMailService {
@Autowired
private JavaMailSender sender;
//发送人
private String from = "[email protected]";
//接收人
private String to = "[email protected]";
//标题
private String subject = "测试邮件";
//正文
private String context = "测试邮件正文内容";
@Override
public void sendMail() {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(from);
message.setTo(to);
message.setSubject(subject);
message.setText(context);
sender.send(message);
}
}
--3.发送多部件邮件--
@Service
public class SendMailServiceImpl2 implements SendMailService {
@Autowired
private JavaMailSender sender;
//发送人
private String from = "[email protected]";
//接收人
private String to = "[email protected]";
//标题
private String subject = "测试邮件";
//正文
private String context = "点开有惊喜";
@Override
public void sendMail() {
try {
MimeMessage message = sender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from + "派大星");
helper.setTo(to);
helper.setSubject(subject);
helper.setText(context, true);
//添加附件
File file = new File("xxxx");
helper.addAttachment(file.getName(), file);
sender.send(message);
} catch (MessagingException e) {
e.printStackTrace();
}
}
}
消息
消息发送方——生产者;消息接收方——消费者
JMS:一个规范,提供了与消息服务相关的API接口
AMQP:一种协议,规范了网络交换的数据格式,兼容JMS
ActiveMQ
--1.导坐标--
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
--2.做配置--
spring:
activemq:
broker-url: tcp://localhost:61616
jms:
template:
default-destination: springboot
--3.使用模板进行操作--
@Service
public class MessageServiceImpl implements MessageService {
@Autowired
private JmsMessagingTemplate messagingTemplate;
@Override
public void sendMessage(String id) {
System.out.println("待发送短信的订单已纳入处理队列,id:" + id);
messagingTemplate.convertAndSend(id);
}
@Override
public String doMessage() {
String id = messagingTemplate.receiveAndConvert(String.class);
System.out.println("已完成短信发送业务,id:" + id);
return id;
}
}
RabbitMQ:基于Erlang语言编写,需要进行安装
RocketMQ
监控的意义
可视化监控平台——springbootadmin
--Admin服务端--
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.3.1</version>
</dependency>
server:
port:8080
@EnableAdminServer
--Admin客户端--
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.3.1</version>
</dependency>
注意:admin的版本号必须与springboot的一致
spring:
boot:
admin:
client:
url: http://localhost:8080
management:
endpoint:
health:
show-details: always
#端点功能信息开启
info:
enabled: true
#配置通过web端读取监控信息
endpoints:
web:
exposure:
include: "*"
#端点功能信息开启
enabled-by-default: true
监控原理
自定义监控指标
info端点指标控制
--方法1:yml--
info:
author: xin
appName: @project.artifactId@
version: @project.version@
--方法2:实现InfoContributor接口--
@Component
public class AppInfoContributor implements InfoContributor {
@Override
public void contribute(Info.Builder builder) {
Map infoMap = new HashMap();
infoMap.put("buildTime", "2023");
builder.withDetail("company", "itheima");
builder.withDetails(infoMap);
}
}
health端点指标控制
@Component
public class HealthConfig extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
Map infoMap = new HashMap();
infoMap.put("buildTime", "2023");
builder.withDetail("company", "itheima");
builder.withDetails(infoMap);
builder.status(Status.UP);
}
}
自定义端点
@Component
@Endpoint(id = "pay", enableByDefault = true)
public class PayEndpoint {
@ReadOperation
public Object getPay() {
//调用业务操作,获取支付相关信息结果,最终return出去
Map payMap = new HashMap();
payMap.put("level1", 200);
payMap.put("level2", 300);
return payMap;
}
}
方式一:XML方式声明bean
<beans>
<!--声明自定义bean-->
<bean id="bookService" class="com.xin.service.impl.BookServiceImpl"/>
<!--声明第三方bean-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"/>
</beans>
方式二:
使用@Component以及衍生注解@Controller、@Service、@Repository定义bean
@Service
public class OrderServiceImpl implements OrderService {}
使用@Bean定义第三方bean,并将所在类定义为配置类或Bean
//@Component
@Configuration(proxyBeanMethods = true)
public class MPConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
proxyBeanMethods可以保障用此方法得到的对象是从容器中获取的而不是重新创建的,@Configuration默认的proxyBeanMethods就为true
通过xml组件扫描bean
<!--扫描基于注解的方式配置bean-->
<context:component-scan base-package="com.xin.service.impl"/>
方式三: 注解方式声明配置类代替了配置文件XML
@Configuration
@ComponentScan("com.xin")
public class SpringConfig {}
方式四:
使用@Import注解导入要注入的bean对应的字节码
@Import(Book.class)
public class SpringConfig{
}
被导入的bean无需使用注解声明为bean
public class Book{}
方式五:使用上下文对象在容器初始化完毕后注入bean
public class AppImport {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
//手工加载bean
ctx.register(Book.class);
}
}
方式六:导入实现了ImportSelector接口的类,实现对导入源的编程式处理
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata metadata) {
boolean flag = metadata.hasAnnotation("org.springframework.context.annotation.Import");
if (flag) {
return new String[]{"com.xin.pojo.Book"};
}
return new String[]{"com.xin.pojo.People"};
}
}
方式七:导入实现了ImportBeanDefinitionRegistrar接口的类,通过BeanDefinition的注册器注册实名bean,实现对bean的裁定
public class MyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//1.使用元数据去做判定
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Book.class).getBeanDefinition();
registry.registerBeanDefinition("book", beanDefinition);
}
}
方式八: 导入实现了BeanDefinitionRegistryPostProcessor接口的类,通过BeanDefinition的注册器注册实名bean,实现对容器中bean的最终裁定
public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl.class).getBeanDefinition();
registry.registerBeanDefinition("bookService", beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
根据特定情况对bean进行选择性加载以达到适用于项目的目标
bean的加载方式五、六、七、八可以达到控制(编程式)
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata metadata) {
Class<?> flag = Class.forName("com.xin.pojo.Cat);
if (flag!=null) {
return new String[]{"com.xin.pojo.Book"};
}
return new String[]{"com.xin.pojo.People"};
}
}
注解式:
匹配指定类
public class SpringConfig {
@Bean
// @ConditionalOnClass(Book.class)
@ConditionalOnClass(name = "com.xin.pojo.Book")
public Book book01() {
return new Book();
}
}
未匹配指定类
public class SpringConfig {
@Bean
@ConditionalOnMissingClass("com.xin.pojo.Book")
public Book book01() {
return new Book();
}
}
匹配指定类型的bean
@Import(Book.class)
public class SpringConfig {
@Bean
@ConditionalOnBean(Book.class)
public Book book01() {
return new Book();
}
}
…
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
将业务功能bean运行需要的资源抽取成独立的属性类(…Properties),设置读取配置文件信息
@Data
@ConfigurationProperties(prefix = "cartoon")
public class CartoonProperties {
private Cat cat;
private Dog dog;
}
配置文件中使用固定格式为属性类注入数据
cartoon:
cat:
name: tom
age: 5
dog:
name: merry
age: 4
使用 @EnableConfigurationProperties 注解设定使用属性类时加载bean
@Component
@Data
//根据条件加载bean:当加载该类时去加载读取CartoonProperties类
@EnableConfigurationProperties(CartoonProperties.class)
public class Cartoon {
private Cat cat;
private Dog dog;
@Autowired
private CartoonProperties cartoonProperties;
/* public Cartoon() {
cat = new Cat();
cat.setName("tom");
cat.setAge(5);
dog = new Dog();
dog.setName("merry");
dog.setAge(4);
}*/
public void play() {
//System.out.println(cat.getAge() + "岁的" + cat.getName() + "和" + dog.getAge() + "岁的" + dog.getName() + "打了一架");
System.out.println(cartoonProperties.toString());
}
}
配置过程:
@SpringBootApplication
/* @SpringBootConfiguration
@Configuration
@Indexed
@EnableAutoConfiguration
@AutoConfigurationPackage
@Import({AutoConfigurationPackages.Registrar.class}) 设置当前配置所在的包作为扫描包,后续要针对当前包进行扫描
@Import({AutoConfigurationImportSelector.class})
@ComponentScan(
excludeFilters = {@ComponentScan.Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @ComponentScan.Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)*/
底层配置流程:
业务功能开发
public class IpCountService {
//计数集合
private Map<String, Integer> ipCountMap = new HashMap<>();
@Autowired
//当前的request对象的注入工作由使用当前starter的工程提供自动装配
private HttpServletRequest httpServletRequest;
public void count() {
//每次调用当前操作,就记录当前访问的IP,然后累加访问次数
//1.获取当前操作的IP地址
String ip = httpServletRequest.getRemoteAddr();
System.out.println("---------------------------" + ip);
//2.根据IP地址从Map中取值,并递增
ipCountMap.put(ip, ipCountMap.getOrDefault(ip, 0) + 1);
}
}
自动配置类
//@Import(IpCountService.class)
public class IpAutoConfiguration {
@Bean
public IpCountService ipCountService() {
return new IpCountService();
}
}
配置(/META-INF/spring.factories),开启自动配置功能,项目启动自动加载
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.itcast.autoconfig.IpAutoConfiguration
在其他项目中引入该自定义starter依赖,并在控制层进行模拟调用
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private IpCountService ipCountService;
@GetMapping
public String test() {
ipCountService.count();
return "ok";
}
}
使用属性配置设置功能参数
@ConfigurationProperties("tools.ip")
@Data
public class IpProperties {
/**
* 日志显示周期
*/
private Long cycle = 5L;
/**
* 是否周期内重置数据
*/
private Boolean cycleReset = false;
/**
* 日志输出模式 detail:详细模式;simple:极简模式
*/
private String model = LogModel.DETAIL.value;
public enum LogModel {
DETAIL("detail"),
SIMPLE("simple");
private String value;
LogModel(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
}
@EnableConfigurationProperties(IpProperties.class)
@EnableScheduling
public class IpAutoConfiguration {
@Bean
public IpCountService ipCountService() {
return new IpCountService();
}
}
@Scheduled(cron = "0/5 * * * * ?")
public void print() {
if (ipProperties.getModel().equals(IpProperties.LogModel.DETAIL.getValue())) {
System.out.println(" ip访问监控");
System.out.println("+-----ip-address-----+--num--+");
for (Map.Entry<String, Integer> entry : ipCountMap.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(String.format("|%18s |%5d |", key, value));
}
System.out.println("+--------------------+-------+");
} else if (ipProperties.getModel().equals(IpProperties.LogModel.SIMPLE.getValue())) {
System.out.println(" ip访问监控");
System.out.println("+-----ip-address-----+");
for (String key : ipCountMap.keySet()) {
System.out.println(String.format("|%18s |", key));
}
System.out.println("+--------------------+");
}
if (ipProperties.getCycleReset()) {
ipCountMap.clear();
}
}
1.SpringBoot启动流程
2.容器类型选择
3.监听器
导坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
将target/classes/META-INF/spring-configuration-metadata.json复制到项目/META-INF下,移除processor依赖即可
自定义提示功能开发
"hints": [
{
"name": "tools.ip.model",
"values": [
{
"value": "detail",
"description": "明细模式"
},
{
"value": "simple",
"description": "极简模式"
}
]
}
]