1. 程式人生 > >007-Spring Boot @Enable*註解的工作原理

007-Spring Boot @Enable*註解的工作原理

sync express override factor run ext soft navi itself

一、@Enable* 啟用某個特性的註解

1、EnableConfigurationProperties

回顧屬性裝配

application.properties中添加

tomcat.host=192.168.2.1
tomcat.port=8080

增加屬性類TomcatProperties

技術分享圖片
package com.lhx.spring.springboot_enable;

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

@Component
@ConfigurationProperties(prefix 
= "tomcat") public class TomcatProperties { private String host; private String port; public String getHost() { return host; } public void setHost(String host) { this.host = host; } public String getPort() { return port; }
public void setPort(String port) { this.port = port; } @Override public String toString() { return "TomcatProperties [host=" + host + ", port=" + port + "]"; } }
View Code

在App中添加代碼

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        ConfigurableApplicationContext context 
= SpringApplication.run(App.class, args); System.out.println(context.getBean(TomcatProperties.class)); context.close(); } }

查看註解SpringBootApplication,其中使其起作用的是@EnableAutoConfiguration,繼續追蹤代碼,其實生效的是:@EnableConfigurationProperties

故以上代碼配置

@ComponentScan
@EnableConfigurationProperties
public class App {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
        System.out.println(context.getBean(TomcatProperties.class));
        context.close();
    }
}

註解:是用來啟用一個特性的,特性可以把配置文件的屬性註入到Bean類中,一般和@ConfigurationProperties一起使用

2、EnableAsync

增加一個異步打印類

技術分享圖片
package com.lhx.spring.springboot_enable;

import java.util.concurrent.TimeUnit;

import org.springframework.stereotype.Component;

@Component
public class Jeep implements Runnable {

    @Override
    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                System.out.println("---------------"+i);
                TimeUnit.SECONDS.sleep(1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}
View Code

在App中調用

技術分享圖片
@ComponentScan
@EnableConfigurationProperties
public class App {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
        System.out.println(context.getBean(TomcatProperties.class));
        context.getBean(Runnable.class).run();
        System.out.println("------end------");
        context.close();
    }
}
View Code

發現此時並沒有異步執行

需要在異步打印類中啟用@EnableAsync,並在具體方法上標註@Async即可

技術分享圖片
package com.lhx.spring.springboot_enable;

import java.util.concurrent.TimeUnit;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;

@Component
@EnableAsync
public class Jeep implements Runnable {

    @Override
    @Async
    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                System.out.println("---------------"+i);
                TimeUnit.SECONDS.sleep(1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}
View Code

這時在調用App方法即可異步

註解:啟用異步,一般和Async一起使用

二、原理

1、註解Import

  跟蹤EnableAsync或EnableConfigurationProperties進入發現,共同註解@Import。  

技術分享圖片
/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context.annotation;

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

/**
 * Indicates one or more {@link Configuration @Configuration} classes to import.
 *
 * <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML.
 * Allows for importing {@code @Configuration} classes, {@link ImportSelector} and
 * {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component
 * classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}).
 *
 * <p>{@code @Bean} definitions declared in imported {@code @Configuration} classes should be
 * accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired}
 * injection. Either the bean itself can be autowired, or the configuration class instance
 * declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly
 * navigation between {@code @Configuration} class methods.
 *
 * <p>May be declared at the class level or as a meta-annotation.
 *
 * <p>If XML or other non-{@code @Configuration} bean definition resources need to be
 * imported, use the {@link ImportResource @ImportResource} annotation instead.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.0
 * @see Configuration
 * @see ImportSelector
 * @see ImportResource
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

    /**
     * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
     * or regular component classes to import.
     */
    Class<?>[] value();

}
View Code

  import作用:Indicates one or more {@link Configuration @Configuration} classes to import.

  用來導入一個或多個類(bean被spring容器托管)、或者配置類(配置類裏面的Bean都會被spring容器托管)

  Enable*其實就是使用了Import,Import其實就是導入了配置類

示例

新建一個User類

技術分享圖片
package com.lhx.spring.springboot_enable;

public class User {

}
View Code

新建一個Role類

技術分享圖片
package com.lhx.spring.springboot_enable;

public class Role {

}
View Code

使用Import在App中導入

技術分享圖片
package com.lhx.spring.springboot_enable;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;

@ComponentScan
@Import(User.class)
public class App2 {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(App2.class, args);
        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(Role.class));
        context.close();
    }
}
View Code

可以看到導入一個,另一個會失敗

也可以導入配置類

技術分享圖片
package com.lhx.spring.springboot_enable;

import org.springframework.context.annotation.Bean;

public class MyConfiguration {
    @Bean
    public Runnable createRunnable() {
        return () -> {
        };
    }

    @Bean
    public Runnable createRunnable2() {
        return () -> {
        };
    }
}
View Code

導入

@Import({User.class,Role.class,MyConfiguration.class})  

2、ImportSelector接口

作用:方法selectImports的返回值,必須是一個class(全稱),改class會被Spring容器托管

技術分享圖片
/*
 * Copyright 2002-2013 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context.annotation;

import org.springframework.core.type.AnnotationMetadata;

/**
 * Interface to be implemented by types that determine which @{@link Configuration}
 * class(es) should be imported based on a given selection criteria, usually one or more
 * annotation attributes.
 *
 * <p>An {@link ImportSelector} may implement any of the following
 * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
 * methods will be called prior to {@link #selectImports}:
 * <ul>
 * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
 * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}</li>
 * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}</li>
 * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}</li>
 * </ul>
 *
 * <p>ImportSelectors are usually processed in the same way as regular {@code @Import}
 * annotations, however, it is also possible to defer selection of imports until all
 * {@code @Configuration} classes have been processed (see {@link DeferredImportSelector}
 * for details).
 *
 * @author Chris Beams
 * @since 3.1
 * @see DeferredImportSelector
 * @see Import
 * @see ImportBeanDefinitionRegistrar
 * @see Configuration
 */
public interface ImportSelector {

    /**
     * Select and return the names of which class(es) should be imported based on
     * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
     */
    String[] selectImports(AnnotationMetadata importingClassMetadata);

}
View Code

示例

創建一個MyImportSelector

技術分享圖片
package com.lhx.spring.springboot_enable;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[] {"com.lhx.spring.springboot_enable.User",Role.class.getName(),MyConfiguration.class.getName()};
    }

}
View Code

在App中使用

技術分享圖片
package com.lhx.spring.springboot_enable;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportSelector;

@ComponentScan
@Import(MyImportSelector.class)
public class App3 {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(App3.class, args);
        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(Role.class));
        System.out.println(context.getBeansOfType(Runnable.class));
        
        context.close();
    }
}
View Code

增加一個Enable開頭的註解,可以結合註解使用

增加一個註解EnableLog

技術分享圖片
package com.lhx.spring.springboot_enable;

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

import org.springframework.context.annotation.Import;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MyImportSelector2.class)
public @interface EnableLog {
    String name();
}
View Code

增加一個ImportSelector實現MyImportSelector2

技術分享圖片
package com.lhx.spring.springboot_enable;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportSelector2 implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        System.out.println(importingClassMetadata.getAllAnnotationAttributes(EnableLog.class.getName()));
        return new String[] { "com.lhx.spring.springboot_enable.User", Role.class.getName(),
                MyConfiguration.class.getName() };
    }

}
View Code

使用

技術分享圖片
package com.lhx.spring.springboot_enable;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportSelector;

@ComponentScan
@EnableLog(name="my annon")
public class App4 {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(App4.class, args);
        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(Role.class));
        System.out.println(context.getBeansOfType(Runnable.class));
        
        context.close();
    }
}
View Code

3、ImportBeanDefinitionRegistrar

作用:方法的參數有一個BeanDefinitionRegistry,BeanDefinitionRegistry可以用來網Spring容器中註入Bean,如此,就可以動態註入bean

技術分享圖片
/*
 * Copyright 2002-2013 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context.annotation;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.core.type.AnnotationMetadata;

/**
 * Interface to be implemented by types that register additional bean definitions when
 * processing @{@link Configuration} classes. Useful when operating at the bean definition
 * level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
 *
 * <p>Along with {@code @Configuration} and {@link ImportSelector}, classes of this type
 * may be provided to the @{@link Import} annotation (or may also be returned from an
 * {@code ImportSelector}).
 *
 * <p>An {@link ImportBeanDefinitionRegistrar} may implement any of the following
 * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
 * methods will be called prior to {@link #registerBeanDefinitions}:
 * <ul>
 * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
 * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
 * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
 * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
 * </ul>
 *
 * <p>See implementations and associated unit tests for usage examples.
 *
 * @author Chris Beams
 * @since 3.1
 * @see Import
 * @see ImportSelector
 * @see Configuration
 */
public interface ImportBeanDefinitionRegistrar {

    /**
     * Register bean definitions as necessary based on the given annotation metadata of
     * the importing {@code @Configuration} class.
     * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
     * registered here, due to lifecycle constraints related to {@code @Configuration}
     * class processing.
     * @param importingClassMetadata annotation metadata of the importing class
     * @param registry current bean definition registry
     */
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

}
View Code

自定義實現MyImportBeanDefinitionRegistrar

技術分享圖片
package com.lhx.spring.springboot_enable;

import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        registry.registerBeanDefinition("user",
                BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition());

        registry.registerBeanDefinition("role",
                BeanDefinitionBuilder.rootBeanDefinition(Role.class).getBeanDefinition());
        registry.registerBeanDefinition(MyConfiguration.class.getName(),
                BeanDefinitionBuilder.rootBeanDefinition(MyConfiguration.class).getBeanDefinition());

    }

}
View Code

此時就可以在EnableLog上使用

技術分享圖片
package com.lhx.spring.springboot_enable;

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

import org.springframework.context.annotation.Import;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//@Import(MyImportSelector2.class)
@Import(MyImportBeanDefinitionRegistrar.class)
public @interface EnableLog {
    String name();
}
View Code

示例

代碼地址:https://github.com/bjlhx15/spring-boot.git 中的 springboot-enable即可

007-Spring Boot @Enable*註解的工作原理