《Spring Cloud微服务架构进阶》——3.3.7 自制一个Starter

举报
华章计算机 发表于 2019/06/03 01:50:24 2019/06/03
【摘要】 本书摘自《Spring Cloud微服务架构进阶》——书中的第3章,第3.3.7节作者是朱荣鑫、张天、黄迪璇。

3.3.7 自制一个Starter

下面通过自定义一个Starter来理解Starter的运行机制。自定义Starter的名字叫做filter-log-starter,它的作用非常简单:为应用添加一个过滤器,在控制台中打印每次访问的URI。

1.创建Starter项目和导入依赖

首先创建一个新的Spring Boot项目,导入以下依赖:

<dependencies>

    <dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-web</artifactId>

    </dependency>

</dependencies>

因为过滤器属于Serlvet包中的类,所以需要引入Spring WebMvc相关依赖。

2.定义过滤器

定义LogFilter地址日志过滤器,具体代码如下:

public class LogFilter implements Filter{

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

    @Override

    public void init(FilterConfig filterConfig) throws ServletException {

        logger.info("logFilter init...");

    }

    @Override

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

         //  从request中获取到访问的地址,并在控制台中打印出来

        HttpServletRequest request = (HttpServletRequest) servletRequest;

        logger.info("uri {} is working.", request.getRequestURI());

        filterChain.doFilter(servletRequest, servletResponse);

    }

    @Override

    public void destroy() {

        logger.info("logFilter destroy...");

    }

}

定义LogFilterRegistrationBean用于将LogFilter过滤器封装成Spring Bean,具体代码如下:

public class LogFilterRegistrationBean extends FilterRegistrationBean<LogFilter>{

    public LogFilterRegistrationBean(){

        super();

        this.setFilter(new LogFilter()); //添加LogFilter过滤器

        this.addUrlPatterns("/*"); // 匹配所有路径

        this.setName("LogFilter"); // 定义过滤器名

        this.setOrder(1); // 设置优先级

    }

}

3.定义自动配置类

接着,定义一个自动配置类将LogFilterRegistrationBean注入到Spring的上下文中,具体代码如下:

@Configuration

@ConditionalOnClass({LogFilterRegistrationBean.class, LogFilter.class})

public class LogFilterAutoConfiguration {


    @Bean

    @ConditionalOnMissingBean(LogFilterRegistrationBean.class)

    public LogFilterRegistrationBean logFilterRegistrationBean(){

        return new LogFilterRegistrationBean();

    }


}

@Configuration通常与@Bean相配合,使用这两个注解可以创建一个简单的Spring配置类,代替相应的xml配置文件。@ConditionalOnClass声明只有当某个或某些class位于类路径上,才会实例化一个Bean。如上述代码中只有当LogFilterRegistrationBean和LogFilter的class在类路径上,LogFilterAutoConfiguration配置类才会生效。

添加@Bean注解的方法将返回一个对象,该对象会被注册为Spring上下文中的Bean。@ConditionalOnMissingBean声明仅仅在当前Spring上下文中不存在某个对象时,才会实例化一个Bean。上述代码中,当LogFilterRegistrationBean不存在于Spring上下文时,才会创建LogFilterRegistrationBean的Bean并注入到Spring上下文中。

4.定义使自动配置类生效的注解

然后我们需要一个注解使LogFilterAutoConfiguration配置类生效。因为Starter是通过jar包的方式引入项目中,对应的classes并不在项目的Spring扫描范围内,所以无法自动引入项目的Spring管理中。对此需要用额外的方式将LogFilterAutoConfiguration引入到项目的Spring管理中,如通过注解的方式将配置类引入项目的Spring扫描范围内。

定义EnableLogFilter引入LogFilterAutoConfiguration配置类到项目的Spring扫描范围内,具体代码如下:

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Import(LogFilterAutoConfiguration.class) //引入LogFilterAutoConfiguration配置类

public @interface EnableLogFilter {

}

除了在注解中直接通过@Import引入对应的配置类外,还可以通过ImportSelector的方式从resources/META-INF/spring.factories中读入需要加载的自动配置类。

首先需要在META-INF中定义spring.factories,代码如下所示:

com.xuan.chapter3.starter.annotation.EnableLogFilter=\

    com.xuan.chapter3.starter.config.LogFilterAutoConfiguration

声明EnableLogFilter注解将使LogFilterAutoConfiguration自动配置类生效。

定义EnableLogFilterImportSelector用于从spring.factories获取需要加载的自动配置类。具体代码如下:

public class EnableLogFilterImportSelector

        implements DeferredImportSelector, BeanClassLoaderAware, EnvironmentAware {


        private static final Logger logger = LoggerFactory.getLogger(EnableLogFilterImportSelector.class);

        private Class annotationClass = EnableLogFilter.class;

        ...

        @Override

        public String[] selectImports(AnnotationMetadata metadata) {

             // 是否生效,默认为true

            if (!isEnabled()) {

                return new String[0];

            }

            // 获取注解中的属性

            AnnotationAttributes attributes = AnnotationAttributes.fromMap(

                metadata.getAnnotationAttributes(this.annotationClass.getName(), true));

            Assert.notNull(attributes, "can not be null…");

            // 从spring.factories中获取所有通过EnableLogFilter注解引入的自动配置类,并

    进行去重操作

            List<String> factories = new ArrayList<>(new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(this.annotationClass, this.beanClassLoader)));

            if (factories.isEmpty() && !hasDefaultFactory()) {

                throw new IllegalStateException("…");

            }

            if (factories.size() > 1) {

                logger.warn("More than one implementation ");

            }

            return factories.toArray(new String[factories.size()]);

        }

    }

    ...

}

通过覆盖selectImports方法,使用SpringFactoriesLoader从spring.factories中获取通过EnableLogFilter注解引入的自动配置类,获取的自动配置类是在spring.factories中配置的LogFilterAutoConfiguration。EnableLogFilterImportSelector可以扩展成一个通用的配置加载类,从而实现在spring.factories中通过不同的注解引入不同的自动配置类的功能。

修改EnableLogFilter注解,代码如下所示:

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Import(EnableLogFilterImportSelector.class) // 引入自动配置加载类

public @interface EnableLogFilter {

}

5.使用Starter

最后我们使用一下Starter,在boot-demo项目中添加Starter的依赖,如下所示:

<dependency>

    <groupId>com.xuan</groupId>

    <artifactId>chapter3-filter-log-starter</artifactId>

    <version>0.0.1-SNAPSHOT</version>

</dependency>

在启动类中添加@EnableLogFilter注解,修改后代码如下所示:

@SpringBootApplication

@RestController

@EnableLogFilter

public class Chapter3BootDemoApplication {

    public static void main(String[] args) {

        SpringApplication.run(Chapter3BootDemoApplication.class, args);

    }


    @GetMapping("/test")

    public String test() {

        return "this is a demo boot.";

    }

}

启动项目,访问http://localhost:8000/test接口,将会在控制台中看到如下访问日志:

2018-04-09 21:30:36.045  INFO 32080 --- [nio-8008-exec-1] c.x.chapter1.starter.filter.LogFilter    : uri /test is working.


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。