SpringBoot原理篇:自定义starter、使用自定义starter、自定义starter灵活的自动配置、使用拦截器调用功能、开启yaml提示功能

目录

一 starter搭框架,引入自己写的starter

1.1 自己写starter步骤

1.2 使用自己写的starter

二 完善功能

三 自定义starter灵活的自动配置1

四 自定义starter灵活的自动配置2

五 使用拦截器调用自定义starter的功能

1 定义拦截器

2 让拦截器生效

六 开启yaml提示功能


案例实现功能:自己写一个starter,实现如下的功能:
1 每次访问网站,统计ip访问次数 
2 后台每10s输出一次监控信息

把坐标放上去,功能就好用,把坐标拿掉,功能就不好用.

一 starter搭框架,引入自己写的starter

1.1 自己写starter步骤


1 创建一个springboot的模块
2 service

package org.zoo.service;

import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

public class IpcountService {

    private Map<String, Integer> ipCountMap = new HashMap<String, Integer>();

    @Autowired
    //当前request对象的注入工作,由使用当前starter的工程自动装配
    private HttpServletRequest httpServletRequest;

    public void count() {
        //1 记录ip
        String ip = httpServletRequest.getRemoteAddr();
        System.out.println("-------------ip:"+ip+"--------------");

        //2 增加访问次数
        Integer count = ipCountMap.get(ip);
        if (count == null) {
            //ip第一次访问
            ipCountMap.put(ip, 1);

        } else {
            //曾经访问过
            ipCountMap.put(ip, ipCountMap.get(ip) + 1);
        }


    }
}


3 创建自动装配类

package org.zoo.autoconfig;

import org.springframework.context.annotation.Import;
import org.zoo.service.IpcountService;

@Import(IpcountService.class)
public class IpAutoConfiguration {

}


4 resources文件夹--新建文件夹META-INF--spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
 org.zoo.autoconfig.IpAutoConfiguration

5 maven -- clean --install


1.2 使用自己写的starter


1 找一个项目,导入自己写的starter坐标

<dependency>
    <groupId>org.zoo</groupId>
    <artifactId>ip_springboot_starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>


2 调用

package com.qing.controller;

import com.qing.domain.Book;
import com.qing.service.BookService3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.zoo.service.IpcountService;

import java.util.List;

@RestController
@RequestMapping("/books")
public class BookController3 {

    @Autowired
    private BookService3 bookService;

    @Autowired
    private IpcountService ipcountService;

    //查1
    @GetMapping("/{id}")
    public Book getById(@PathVariable Integer id){
        ipcountService.count();
        return bookService.getById(id);
    }

   


}

测试

 调用了自己写的 starter

二 完善功能

自动配置 

package org.zoo.autoconfig;

import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.zoo.service.IpcountService;

//开启定时任务
@EnableScheduling
@Import(IpcountService.class)
public class IpAutoConfiguration {

}

service

package org.zoo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

public class IpcountService {

    private Map<String, Integer> ipCountMap = new HashMap<String, Integer>();

    @Autowired
    //当前request对象的注入工作,由使用当前starter的工程自动装配
    private HttpServletRequest httpServletRequest;

    //计数
    public void count() {
        //1 记录ip
        String ip = httpServletRequest.getRemoteAddr();

        //2 增加访问次数
        Integer count = ipCountMap.get(ip);
        if (count == null) {
            //ip第一次访问
            ipCountMap.put(ip, 1);

        } else {
            //曾经访问过
            ipCountMap.put(ip, ipCountMap.get(ip) + 1);
        }


    }

    //展示
    //定时任务
    //cron = "秒 分 时 日 月 星期"
    //cron = "0/10 * * * * ?"   :从0秒开始每10s执行一次
    @Scheduled(cron = "0/10 * * * * ?")
    public void print(){
        System.out.println("            ip访问监控          ");
        System.out.println("|------ip address--------count--|");
        for (Map.Entry<String, Integer> entry : ipCountMap.entrySet()) {
            String key = entry.getKey();
            Integer value = entry.getValue();
            //%s:字符串、%d:整数
            //%20s:总共占20位的字符串 (默认右对齐)
            //%-20s:总共占20位的字符串 (左对齐)
            System.out.println(String.format("|%20s  |%6d  |",key,value));
        }

    }




}

写好了,给自定义的starter 做 maven 的 clean install

关于定时任务

三 自定义starter灵活的自动配置1

修改配置文件的属性配置 设置功能参数 → 就是修改引用自定义starter坐标的项目 的配置文件的值, 得到不一样的效果

如下图,修改配置文件的值,会影响统计的功能

 1 properties

package org.zoo.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix ="tools.ip")
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;
        }
    }

    public Long getCycle() {
        return cycle;
    }

    public void setCycle(Long cycle) {
        this.cycle = cycle;
    }

    public boolean isCycleReset() {
        return cycleReset;
    }

    public void setCycleReset(boolean cycleReset) {
        this.cycleReset = cycleReset;
    }

    public String getModel() {
        return model;
    }

    public void setModel(String model) {
        this.model = model;
    }
}


2 配置类

package org.zoo.autoconfig;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.zoo.properties.IpProperties;
import org.zoo.service.IpcountService;

//加载IpProperties到spring容器
@EnableConfigurationProperties(IpProperties.class)
//开启定时任务
@EnableScheduling
@Import(IpcountService.class)
public class IpAutoConfiguration {

}


3 service

package org.zoo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.zoo.properties.IpProperties;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

public class IpcountService {

    private Map<String, Integer> ipCountMap = new HashMap<String, Integer>();


    @Autowired
    private IpProperties ipProperties;

    //展示
    //定时任务
    //cron = "秒 分 时 日 月 星期"
    //cron = "0/10 * * * * ?"   :从0秒开始每10s执行一次
    @Scheduled(cron = "0/10 * * * * ?")
    public void print(){

        //使用配置文件里的配置来动态控制输出的数据 → model
        //详细模式
        if(ipProperties.getModel().equals(IpProperties.LogModel.DETAIL.getValue())){
            //详细模式
            System.out.println("           ip访问监控--详细模式       ");
            System.out.println("|------ip address--------count--|");

            for (Map.Entry<String, Integer> entry : ipCountMap.entrySet()) {
                String key = entry.getKey();
                Integer value = entry.getValue();
                //%s:字符串、%d:整数
                //%20s:总共占20位的字符串 (默认右对齐)
                //%-20s:总共占20位的字符串 (左对齐)
                System.out.println(String.format("|%20s  |%6d  |",key,value));
            }
            System.out.println("|-------------------------------|");

        }else if(ipProperties.getModel().equals(IpProperties.LogModel.SIMPLE.getValue())){
            //简单模式只显示ip地址
            System.out.println("  ip访问监控--极简模式  ");
            System.out.println("|-------ip address-----|");


            for (String key : ipCountMap.keySet()) {

                System.out.println(String.format("|%20s  |",key));
            }

            System.out.println("|----------------------|");

        }

        //使用配置文件里的配置来动态控制输出的数据 → cycle-reset
        if(ipProperties.isCycleReset()){
            //是否清空数据为 true
            ipCountMap.clear();
        }

    }




}
maven-- clean install


测试 → 周期内重置数据

引用自定义starter项目的配置文件

10秒周期内,访问了6次,清空了数据,再过10秒,没有点击数据效果如下图

 测试 → 输出格式为简单模式

 

四 自定义starter灵活的自动配置2

关于cycle的配置

1 自定义该容器的名字

 2 手工导入IpProperties

package org.zoo.autoconfig;

import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.zoo.properties.IpProperties;
import org.zoo.service.IpcountService;

//加载IpProperties到spring容器
//@EnableConfigurationProperties(IpProperties.class)
//开启定时任务
@EnableScheduling
//手工导入IpProperties,因为要用spel表达式:bean.属性
// @EnableConfigurationProperties导入的类名有前缀tools.ip..,但是tools不是bean
@Import({IpcountService.class,IpProperties.class})
public class IpAutoConfiguration {

}

为什么要手动导入IpProperties?

正常@ConfigurationProperties(prefix = "XXX")和@EnableConfigurationProperties 是一对注解

关于@ConfigurationProperties和@EnableConfigurationProperties

 但是因为要用到IpProperties的默认日志显示周期cycle,需要用到spel表达式的bean名.属性名这种格式(见后面)

但是因为用@ConfigurationProperties(prefix = "XXX")和@EnableConfigurationProperties这对注解,导致spring容器里加载的该IpProperties bean是如下tools.ip这种前缀的名字,如下图:

 tools.ip...这种名字并不符合spel表达式的 bean名.属性名 格式

所以用了@Component("ipProperties")重新自定义了该IpProperties bean 的名字,更换使用了import

注入IpProperties类

 3 修改service里定时器的设置

使用spel表达式引入默认的cycle设置

完整的service

package org.zoo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.zoo.properties.IpProperties;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;


public class IpcountService {

    private Map<String, Integer> ipCountMap = new HashMap<String, Integer>();

    @Autowired
    //当前request对象的注入工作,由使用当前starter的工程自动装配
    private HttpServletRequest httpServletRequest;

    //计数
    public void count() {
        //1 记录ip
        String ip = httpServletRequest.getRemoteAddr();

        //2 增加访问次数
        Integer count = ipCountMap.get(ip);
        if (count == null) {
            //ip第一次访问
            ipCountMap.put(ip, 1);

        } else {
            //曾经访问过
            ipCountMap.put(ip, ipCountMap.get(ip) + 1);
        }


    }

    @Autowired
    private IpProperties ipProperties;

    //展示
    //定时任务
    //cron = "秒 分 时 日 月 星期"
    //cron = "0/10 * * * * ?"   :从0秒开始每10s执行一次
//    @Scheduled(cron = "0/10 * * * * ?")
    @Scheduled(cron="0/#{ipProperties.cycle} * * * * ?")
    public void print(){

        //使用配置文件里的配置来动态控制输出的数据 → model
        //详细模式
        if(ipProperties.getModel().equals(IpProperties.LogModel.DETAIL.getValue())){
            //详细模式
            System.out.println("           ip访问监控--详细模式       ");
            System.out.println("|------ip address--------count--|");

            for (Map.Entry<String, Integer> entry : ipCountMap.entrySet()) {
                String key = entry.getKey();
                Integer value = entry.getValue();
                //%s:字符串、%d:整数
                //%20s:总共占20位的字符串 (默认右对齐)
                //%-20s:总共占20位的字符串 (左对齐)
                System.out.println(String.format("|%20s  |%6d  |",key,value));
            }
            System.out.println("|-------------------------------|");

        }else if(ipProperties.getModel().equals(IpProperties.LogModel.SIMPLE.getValue())){
            //简单模式只显示ip地址
            System.out.println("  ip访问监控--极简模式  ");
            System.out.println("|-------ip address-----|");


            for (String key : ipCountMap.keySet()) {

                System.out.println(String.format("|%20s  |",key));
            }

            System.out.println("|----------------------|");

        }

        //使用配置文件里的配置来动态控制输出的数据 → cycle-reset
        if(ipProperties.isCycleReset()){
            //是否清空数据为 true
            ipCountMap.clear();
        }

    }



}

maven -- clean install

 测试

1秒打印一次

 没有设置cyle的值,按默认的5s是一个周期打印一次

 

 总结

 

 

五 使用拦截器调用自定义starter的功能

 原来是在使用自定义starter的项目里 调用的服务. 现在要改用在拦截器里调用服务

1 定义拦截器

package org.zoo.interceptor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.zoo.service.IpcountService;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class IpCountInterceptor implements HandlerInterceptor {

    @Autowired
    private IpcountService ipcountService;

    //运行拦截之前做的事情
    //返回值是  拦截器走到这儿还走不走后面的原始调用
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        ipcountService.count();
        return true;
    }
}

2 让拦截器生效

package org.zoo.interceptor;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

//用@Configuration:默认创建代理对象 → 默认情况创建的对象是一样的 (创建了一次就不会再创建了,而是取出容器中的对象)
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //如果有多个拦截器就registry.add..写多个
        //拦截所有:/**
        registry.addInterceptor(ipCountInterceptor()).addPathPatterns("/**");
    }

    @Bean
    public IpCountInterceptor ipCountInterceptor(){
        return new IpCountInterceptor();
    }

}

备注:拦截器写在自定义的 starter项目里.

为什么拦截器要写在自定义的 starter项目里?

因为如果把拦截器写在 使用starter坐标的项目 里, 当把starter坐标删掉之后, 使用拦截器的地方就会报错

maven -- clean install

测试

总结

定义拦截器

让拦截器生效

六 开启yaml提示功能

想实现如下yaml出提示的功能

 

 

target下的resources-META-INF 文件夹目前是这样子

1 自定义starter的pom文件加坐标

<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>


2 刷新maven ,clean install

此时 target下的resources-META-INF 文件夹 多了一个文件,这个文件就是出提示的配置文件

看一下这个json文件里面

 decription就是提示的内容,这个内容是 之前写的 文档注释里的内容

 

3 把 新生成的json文件,剪切到项目的resources-META-INF 里

 

4 去掉pom文件坐标,刷新maven ,clean install

否则会出2组相同的注释

功能已经基本实现

 我们也可以自己定义更多的提示,实现如下的效果

 

 在json文件里修改 hints

"hints": [
    {
      "name": "tools.ip.cycle-reset",
      "values": [
        {
          "value": "true",
          "description": "周期内清空数据重置"
        },
        {
          "value": "false",
          "description": "周期内不重置"
        }
      ]
    }
  ]

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值