SpringBoot编写自定义的starter

在之前的文章中,我们分析过SpringBoot内部的自动化配置原理自动化配置注解开关原理

我们先简单分析一下mybatis starter的编写,然后再编写自定义的starter。

mybatis中的autoconfigure模块中使用了一个叫做MybatisAutoConfiguration的自动化配置类。

这个MybatisAutoConfiguration需要在这些Condition条件下才会执行:

  1. @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })。需要SqlSessionFactory和SqlSessionFactoryBean在classpath中都存在
  2. @ConditionalOnBean(DataSource.class)。 spring factory中需要存在一个DataSource的bean
  3. @AutoConfigureAfter(DataSourceAutoConfiguration.class)。需要在DataSourceAutoConfiguration自动化配置之后进行配置,因为mybatis需要数据源的支持

同时在META-INF目录下有个spring.factories这个properties文件,而且它的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration,这样才会被springboot加载:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

有了这些东西之后,mybatis相关的配置会被自动加入到spring container中,只要在maven中加入starter即可:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.1.1</version>
</dependency>

编写自定义的starter

接下来,我们来编写自定义的starter:log-starter。

这个starter内部定义了一个注解,使用这个注解修饰方法之后,该方法的调用会在日志中被打印并且还会打印出方法的耗时。starter支持exclude配置,在exclude中出现的方法不会进行计算。

pom文件:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starters</artifactId>
    <version>1.3.5.RELEASE</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
</dependencies>

定义修饰方法的注解@Log:

package me.format.springboot.log.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;    

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log { }

然后是配置类:

package me.format.springboot.log.autoconfigure;

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

import javax.annotation.PostConstruct;

@ConfigurationProperties(prefix = "mylog")
public class LogProperties {

    private String exclude;

    private String[] excludeArr;

    @PostConstruct
    public void init() {
        this.excludeArr = StringUtils.split(exclude, ",");
    }

    public String getExclude() {
        return exclude;
    }

    public void setExclude(String exclude) {
        this.exclude = exclude;
    }

    public String[] getExcludeArr() {
        return excludeArr;
    }
}

接下来是AutoConfiguration:

package me.format.springboot.log.autoconfigure;

import me.format.springboot.log.annotation.Log;
import me.format.springboot.log.aop.LogMethodInterceptor;
import org.aopalliance.aop.Advice;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

@Configuration
@EnableConfigurationProperties(LogProperties.class)
public class LogAutoConfiguration extends AbstractPointcutAdvisor {

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

    private Pointcut pointcut;

    private Advice advice;

    @Autowired
    private LogProperties logProperties;

    @PostConstruct
    public void init() {
        logger.info("init LogAutoConfiguration start");
        this.pointcut = new AnnotationMatchingPointcut(null, Log.class);
        this.advice = new LogMethodInterceptor(logProperties.getExcludeArr());
        logger.info("init LogAutoConfiguration end");
    }

    @Override
    public Pointcut getPointcut() {
        return this.pointcut;
    }

    @Override
    public Advice getAdvice() {
        return this.advice;
    }

}

由于计算方法调用的时候需要使用aop相关的lib,所以我们的AutoConfiguration继承了AbstractPointcutAdvisor。这样就有了Pointcut和Advice。Pointcut是一个支持注解的修饰方法的Pointcut,Advice则自己实现:

package me.format.springboot.log.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.List;

public class LogMethodInterceptor implements MethodInterceptor {
    private Logger logger = LoggerFactory.getLogger(LogMethodInterceptor.class);
    private List<String> exclude;
    public LogMethodInterceptor(String[] exclude) {
        this.exclude = Arrays.asList(exclude);
    }
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        String methodName = invocation.getMethod().getName();
        if(exclude.contains(methodName)) {
            return invocation.proceed();
        }
        long start = System.currentTimeMillis();
        Object result = invocation.proceed();
        long end = System.currentTimeMillis();
        logger.info("====method({}), cost({}) ", methodName, (end - start));
        return result;
    }
}

最后resources/META-INF/spring.factories中加入这个AutoConfiguration:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
me.format.springboot.log.autoconfigure.LogAutoConfiguration

我们在项目中使用这个log-starter:

<dependency>
    <groupId>me.format.springboot</groupId>
    <artifactId>log-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

使用配置:

mylog.exclude=core,log

然后编写一个简单的Service:

@Service
public class SimpleService {

    @Log
    public void test(int num) {
        System.out.println("----test---- " + num);
    }

    @Log
    public void core(int num) {
        System.out.println("----core---- " + num);
    }

    public void work(int num) {
        System.out.println("----work---- " + num);
    }

}

使用单元测试分别调用这3个方法,由于work方法没有加上@Log注解,core方法虽然加上了@Log注解,但是在配置中被exclude了,只有test方法可以正常计算耗时:

----test---- 666
2016-11-16 01:29:32.255  INFO 41010 --- [           main] m.f.s.log.aop.LogMethodInterceptor       : ====method(test),     cost(36)
----work---- 666
----core---- 666

总结:

自定义springboot的starter,注意这两点。

  1. 如果自动化配置类需要在程序启动的时候就加载,可以在META-INF/spring.factories文件中定义。如果本次加载还需要其他一些lib的话,可以使用ConditionalOnClass注解协助
  2. 如果自动化配置类要在使用自定义注解后才加载,可以使用自定义注解+@Import注解或@ImportSelector注解完成

参考:

http://www.jianshu.com/p/85460c1d835a

http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html

 

http://fangjian0423.github.io/2016/11/16/springboot-custom-starter/

 

时间: 2024-08-17 16:16:15

SpringBoot编写自定义的starter的相关文章

为Android Studio编写自定义Gradle插件的教程_Android

Google已经建议Android开发全部转向Android Studio开发,Android Studio 是使用gradle编译.打包的,那么问题来了,gradle可是有一堆东西...,为了彻底了解gradle,今天就来学习下如何写自己的gradle插件(当然插件源码是使用groovy写的),先看如下代码目录: 如上图所示,plugin目录是插件源码目录,sample是用来测试插件的. 1.在目录plugin/src/main/groovy/com/micky/gradle/下新建插件类My

用托管C++编写自定义Web组合控件

什么是自定义的组合控件 自定义的Web组合控件正如它名字说的那样:在单个控件中集成了一个或多个服务端程序及HTML控件.自定义的组合控件在功能上与用户控件非常类似,最大的不同之处是,它只存在于它自己的程序集中(或与其他控件共享),能被放在工具条中,并可提供它所包含控件的所见即所得视图方式. 另一方面,自定义Web组合控件比用户控件(user control)更加难创建,因为Visual Studio.NET的设计者们并没有提供可视化创建它们的任何工具,因此,问题是:为什么要用组合控件取代用户控件

为Android Studio编写自定义Gradle插件的教程

Google已经建议Android开发全部转向Android Studio开发,Android Studio 是使用gradle编译.打包的,那么问题来了,gradle可是有一堆东西...,为了彻底了解gradle,今天就来学习下如何写自己的gradle插件(当然插件源码是使用groovy写的),先看如下代码目录: 如上图所示,plugin目录是插件源码目录,sample是用来测试插件的. 1.在目录plugin/src/main/groovy/com/micky/gradle/下新建插件类My

编写自定义的 Android Preference 组件

  Android SDK 提供好几个 Preference 组件,例如 CheckBoxPreference.EditTextPreference.DialogPreference.ListPreference 等,这些组件是跟 Android 提供的 Preference 存储机制绑定的,你可以通过这些组件来修改应用的一些配置,如下图所示,这是 Android 自带的系统设置界面:   但这些组件毕竟还不能满足100%的要求,假设我们需要为应用程序提供一个选择不同图片做为应用背景图的设置,我

57. Spring 自定义properties升级篇【从零开始学Spring Boot】

 注解ConfigurationProperties和EnableAutoConfiguration的区别: @EnableConfigurationProperties tells Spring to treat this class as a consumer of application.yml/properties values( {@link ConfigurationProperties} beans can be registered in the standard way (fo

在JSP中编写你的第一个Tag

tag 编写一个Tag涉及三个步骤, (1)编写JSP (2)编写Tag的java程序 (3)编写tag库的描述文件tld(实际是一个XML文件) 这三个步骤之间没有顺序约束,下面是一个简单的例子: 1 编写HelloTag.jsp <%@page contentType="text/html"%><html><head><title>Hello Tags Page</title></head><body&

使用自定义标签实现JSP页面和代码的分离

js|页面 Test.jsp<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html>  <head>    <title>My JSP 'Test.jsp' starting page</title>  </head>   <body>    This is my JSP page. <br>    Date

J2EE 探索者:用五个容易的步骤实现 JSP 自定义标记

j2ee|js JSP 自定义标记为在动态 Web 页中将表示与业务逻辑分离提供了一种标准化的机制,使页面设计者可以将注意力放到表示上,而应用程序开发人员编写后端的代码.在 J2EE 探索者 的这篇文章中,具有企业观念的开发者 Kyle Gabhart 介绍了 JSP 自定义标记的基本知识,并引导您完成将它们加入到 JSP 页面的五步过程.您可能听说现在有上百种不同的方式,但是在开发 Web 应用程序时将表示逻辑与业务逻辑分离是很重要的.近年来,Java 平台已经发展为在体系结构层次上加入了这种

使用自定义验证组件库扩展 Windows 窗体

window 摘要:数据验证是确保正常的数据捕获以及后续处理和报告的关键步骤.本文介绍了 Windows 窗体固有的程序验证基础结构,并以此为基础开发了用于提供更高效验证功能的自定义验证组件库,该验证功能与使用 ASP.NET 的验证控件相似. 下载 winforms03162004_sample.msi 示例文件. 本页内容 引言 Windows 窗体验证的主要功能 程序验证与声明性验证 建立设计时支持 模仿是最真诚的恭维 必需字段验证程序简介 BaseValidator:分治法 一个放便士,