springboot中间件小demo

在我们开发的时候,如果用到一个功能,我们只需要导入starter依赖,就能够利用springboot的自动装配功能,使用starter包中的功能了.对应的我们也能自己实现一个starter来使用.
越是使用简单的框架越是有很多的约定,俗称:约定大于配置;

自动装配原理

1、springboot在启动的时候会去starter的包中寻找resources/META-INF/spring.factories文件,并读取文件中 key=org.springframework.boot.autoconfigure.EnableAutoConfiguration的所有value的AutoConfigure类进行加载.
2、在加载AutoConfigure的时候,会有一系列的@Conditional注解,满足条件就会自动配置,并把Bean注入到Spring容器中.也可以通过@ImportAutoConfiguration指定自动装配的类
3、Spring官方的starter一般采取spring-boot-starter-{name}的命名方式,比如spring-boot-starter-aop.非官方的starter,官方的建议artifactId命名应该遵循{name}-spring-boot-starter的格式.

项目结构

在这里插入图片描述

依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.itpluto.demo</groupId>
    <artifactId>list-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.7</version>
    </parent>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- 用到了aop的拦截 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <archive>
                        <addMavenDescriptor>false</addMavenDescriptor>
                        <index>true</index>
                        <manifest>
                            <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
                            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                        </manifest>
                        <manifestEntries>
                            <Implementation-Build>${maven.build.timestamp}</Implementation-Build>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DoList {
    String key() default "";
    String returnJson() default "";
}

自动装配需要的类和配置文件

1、在resources文件下创建一个META-INF文件夹,并创建spring.factories文件.

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  org.itpluto.demo.config.StarterAutoConfigure

2、创建StarterAutoConfigure类


@Configuration
@ConditionalOnClass(StarterService.class)
@EnableConfigurationProperties(StarterServiceProperties.class)
public class StarterAutoConfigure {
    
    // StarterServiceProperties 用来读取配置文件中
    @Autowired
    private StarterServiceProperties properties;

    // 把读取到的白名单存储在 StarterService里
    @Bean
    @ConditionalOnMissingBean //当没有这个Bean的时候才会创建
    // 当有以itpluto开头的配置,并且enabled的属性是ture的时候才生效
    @ConditionalOnProperty(prefix = "itpluto.door", value = "enabled", havingValue = "true")
    StarterService starterService() {
        return new StarterService(properties.getUserStr());
    }

    // doJoinPoint aop拦截,这里注册到Spring容器中
    @Bean
    @ConditionalOnMissingBean
    DoJoinPoint doJoinPoint(){
        return new DoJoinPoint();
    }
}

3、创建Properties类,用来yaml配置文件和属性进行绑定

@ConfigurationProperties("itpluto.door")
public class StarterServiceProperties {

    private String userStr;

    public String getUserStr() {
        return userStr;
    }

    public void setUserStr(String userStr) {
        this.userStr = userStr;
    }
}

4、创建StarterService类,用来存储配置文件配置的白名单,以及做进一步的处理

// 自动装配该service
public class StarterService {
	// 把读取到的白名单存储到userStr中
    private String userStr;

    public StarterService(String userStr) {
        this.userStr = userStr;
    }

    public String[] split(String separate){
        return this.userStr.split(separate);
    }
}

5、创建DoJoinPoint类

@Aspect
public class DoJoinPoint {

    private Logger logger = LoggerFactory.getLogger(DoJoinPoint.class);

    @Autowired
    private StarterService starterService;

    @Pointcut("@annotation(org.itpluto.demo.annotation.DoList)")
    public void aopPoint() {
    }

    @Around("aopPoint()")
    public Object doRouter(ProceedingJoinPoint jp) throws Throwable {
        //获取内容
        Method method = getMethod(jp);
        DoList door = method.getAnnotation(DoList.class);
        //获取字段值
        String keyValue = getFiledValue(door.key(), jp.getArgs());
        logger.info("itpluto list handler method:{} value:{}", method.getName(), keyValue);
        if (null == keyValue || "".equals(keyValue)) return jp.proceed();
        //配置内容
        String[] split = starterService.split(",");
        //白名单过滤
        for (String str : split) {
            if (keyValue.equals(str)) {
                return jp.proceed();
            }
        }
        //拦截
        return returnObject(door, method);
    }

    private Method getMethod(JoinPoint jp) throws NoSuchMethodException {
        Signature sig = jp.getSignature();
        MethodSignature methodSignature = (MethodSignature) sig;
        return getClass(jp).getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
    }

    private Class<? extends Object> getClass(JoinPoint jp) throws NoSuchMethodException {
        return jp.getTarget().getClass();
    }

    //返回对象
    private Object returnObject(DoList doGate, Method method) throws IllegalAccessException, InstantiationException {
        Class<?> returnType = method.getReturnType();
        String returnJson = doGate.returnJson();
        if ("".equals(returnJson)) {
            return returnType.newInstance();
        }
        // 注意因为这里底层用的是构造器来转换的,
        // 所以需要把code和info也加到构造器中,否则转换不成功
        return JSON.parseObject(returnJson, returnType);
    }

    //获取属性值
    private String getFiledValue(String filed, Object[] args) {
        String filedValue = null;
        for (Object arg : args) {
            try {
                if (null == filedValue || "".equals(filedValue)) {
                    filedValue = BeanUtils.getProperty(arg, filed);
                } else {
                    break;
                }
            } catch (Exception e) {
                if (args.length == 1) {
                    return args[0].toString();
                }
            }
        }
        return filedValue;
    }
}

上面中间件就到此为止了,下面演示一下他的用法.
创建一个使用案例:itpluto-demo-springboot-helloworld

项目结构

在这里插入图片描述

依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.itpluto.demo</groupId>
    <artifactId>itpluto-demo-springboot-helloworld</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.7</version>
    </parent>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--  刚才的中间件小demo导入      -->
        <dependency>
            <groupId>org.itpluto.demo</groupId>
            <artifactId>list-spring-boot-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.13.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.13.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.13.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
</project>

配置文件

server:
  port: 8080
  
# 自定义中间件配置
itpluto:
  door:
    enabled: true
    userStr: 1001,aaaa,ccc #白名单用户ID,多个逗号隔开

实体类

@Data
public class UserInfo {

    private String code;

    private String info;

    private String name;

    private Integer age;

    private String address;

    public UserInfo(String code,String info,String name, Integer age, String address) {
        this.code = code;
        this.info = info;
        this.name = name;
        this.age = age;
        this.address = address;
    }
}

Controller类

@RestController
@RequestMapping("/base")
public class HelloWorldController {

    @DoList(key = "userId", returnJson = "{\"code\":\"403\",\"info\":\"不在白名单内,没有权限!\"}")
    @GetMapping("/api/queryUserInfo")
    public UserInfo queryUserInfo(@RequestParam String userId) {
        return new UserInfo("200","success","火锅:" + userId, 1, "上海市宝山区宝安公路");
    }
}

启功类


@SpringBootApplication
public class HelloWorldApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(HelloWorldApplication.class, args);
        // 测试一下启动的时候,中间件中的doJoinPoint是否注入到Spring容器
        Object doJoinPoint = run.getBean("doJoinPoint");
        System.out.println("doJoinPoint = " + doJoinPoint);
    }
}

测试结果

1、注入成功!
在这里插入图片描述
2、白名单
在这里插入图片描述
3、黑名单
在这里插入图片描述
注意:在引用中间件的时候,因为只是在我们的本地,所以需要先install到我们的本地仓库,不然会找不到这个包,每次修改中间件的内容记得要clean install,这样使用方使用的才是最新的.
这是我从一个大佬的博客里面学习的小demo,他的demo不是全的,我自己按照他的demo写了一遍.有不妥的地方请指教.大佬博客地址

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值