springboot工作总结spring boot

Springboot @Enable*注解的工作原理

2017-07-20  本文已影响668人  二月_春风

先看一个简单的demo,我们定义一个springboot项目,最简单的依赖:

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

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

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

定义一个实体类:ServerBean

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

@Component
@ConfigurationProperties(prefix = "instance")
public class ServerBean {

    private String ip;

    private Integer port;

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    @Override
    public String toString() {
        return "ServerBean{" +
                "ip='" + ip + '\'' +
                ", port=" + port +
                '}';
    }
}

之前的博客springboot配置详解,详细的讲解了@ConfigurationProperties的使用。

配置文件配置:

instance.ip=192.168.1.111
instance.port=8090

启动类启动,

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

打印:

ServerBean{ip='192.168.1.111', port=8090}

我们发现这个程序自动的会把配置文件注入到bean中,原因在哪里呢?

之前我们讲过启动类上的注解@SpringBootApplication是一个复合注解,其由@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan三个注解组成,@SpringBootConfiguration和@ComponentScan我们在
springboot快速入门及@SpringBootApplication注解分析
分析过了,那么故名思义,我们知道肯定是@EnableAutoConfiguration注解的作用了。

修改启动类,

@EnableAutoConfiguration
@ComponentScan
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
        System.out.println(context.getBean(ServerBean.class));
    }
}

发现打印结果还是一样。然后再去修改一下启动类,使用@EnableConfigurationProperties也可以替换@EnableAutoConfiguration

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

@EnableConfigurationProperties注解一般和ConfigurationProperties注解搭配使用,可以将配置文件属性注入到bean中。

第二个demo

定义一个类UserRunnable,纳入到spring容器中,

package com.zhihao.miao.enable.bean;


import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class UserRunnable 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();
        }
    }
}

启动类:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
        UserRunnable userRunnable = context.getBean(UserRunnable.class);
        userRunnable.run();
        System.out.println("end");
    }
}

打印结果:

============0
============1
============2
============3
============4
============5
============6
============7
============8
============9
end

我们发现执行过程是一个同步的过程,只有userRunnable.run方法执行完毕之后才执行下面的打印过程。

修改一下代码,将UserRunnable的run方法上加入@Async注解

@Component
public class UserRunnable 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();
        }
    }
}

修改启动类,

@SpringBootApplication
@EnableAsync
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
        context.getBean(Runnable.class).run();
        System.out.println("end");
    }
}

执行结果:

============0
end
============1
============2
============3
============4
============5
============6
============7
============8
============9

我们查看上面二个demo的Enable*注解的源码,

@EnableAsync注解

@EnableAsync AsyncConfigurationSelector注解

最后实现ImportSelector接口

@EnableAutoConfiguration注解

都使用到了@Import注解,最后也是实现ImportSelector接口。

@Import注解

@Import注解

@Import其实就是引入一个或多个配置,可以导入普通类,也可以导入配置类。
@Import用来导入一个或多个类(会被spring容器管理),或者配置类(配置类里的@Bean标记的类也会被spring容器管理)

看一个demo,定义四个实体类,User,People,Cat,Dog

public class User {
}
public class People{  
}
public class Cat {
}
public class Dog {
}
public class MyConfig {

    @Bean
    public Dog dog(){
        return new Dog();
    }

    @Bean
    public Cat cat(){
        return new Cat();
    }
}

我们要将这四个类纳入到spring容器中,我们之前的做法是在User,People上加上了@Component注解(或者@Service,@Controller)或者在MyConfig类上加上@Configuration注解。很显然我们这边并没有这般做,使用@Import注解也可以加对象纳入到spring容器中。

启动类:

package com.zhihao.miao.imp;

import com.zhihao.miao.imp.bean.*;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;

@Import({User.class,People.class, MyConfig.class})
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(Dog.class));
        System.out.println(context.getBean(Cat.class));
        System.out.println(context.getBean(People.class));
    }
}

ImportSelector接口

ImportSelector接口

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.
接口被实现那些Configuration的类被导入到spring容器根据指定的一些条件,通常是一个到多个导入类的注解属性。

An ImportSelector may implement any of the following
org.springframework.beans.factory.Aware Aware interfaces, and their respective methods will be called prior to selectImports:

ImportSelectors are usually processed in the same way as regular @Import
annotations, however, it is also possible to defer selection of imports until all
@Configuration classes have been processed (see DeferredImportSelector
for details).
实现ImportSelectors接口的类通常与常规的@Import注解作用相同,然而,它也可能被延迟处理直到所有被@Configuration标记的类处理完之后采取处理。

定义一个MyImportSelector继承ImportSelector,ImportSelector返回的String[]数组是类的全类名会被纳入到spring容器内。

/**
 * selectImports方法的返回值,必须是一个class(全称),该class会被spring容器所托管起来
 */
public class MyImportSelector implements ImportSelector{

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //获取注解的属性信息
        System.out.println(importingClassMetadata.getAllAnnotationAttributes(EnableLog.class.getName()));
       //这里可以获取到注解的详细信息,然后根据信息去动态的返回需要被spring容器管理的bean
        return new String[]{"com.zhihao.miao.imp.bean.User",People.class.getName(),MyConfig.class.getName()};
    }

}

定义一个EnableLog注解,可以得到属性的值,@Import(MyImportSelector.class),可以在MyImportSelector中获取name属性值。

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

启动类,在启动类上加入@EnableLog(name="com.zhihao.miao")注解,
我们知道@EnableLog中@Import(MyImportSelector.class)会将MyImportSelector对象纳入到容器中。

package com.zhihao.miao.imp;
import com.zhihao.miao.imp.bean.*;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;

@EnableLog(name="com.zhihao.miao")
public class Application2 {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application2.class,args);
        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(Dog.class));
        System.out.println(context.getBean(Cat.class));
        System.out.println(context.getBean(People.class));
    }

}

打印结果:

{name=[com.zhihao.miao]}
2017-07-20 11:27:08.446  INFO 11551 --- [           main] com.zhihao.miao.imp.Application2         : Started Application2 in 11.754 seconds (JVM running for 12.614)
com.zhihao.miao.imp.bean.User@1e4d3ce5
com.zhihao.miao.imp.bean.Dog@3ddc6915
com.zhihao.miao.imp.bean.Cat@704deff2
com.zhihao.miao.imp.bean.People@379614be

ImportBeanDefinitionRegistrar接口

ImportBeanDefinitionRegistrar接口

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.
接口实现可以额外的注册类的定义到spring容器中。

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}).

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}:

{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}

See implementations and associated unit tests for usage examples.

定义MyImportBeanDefinitionRegistrar实现ImportBeanDefinitionRegistrar接口,将User类,People类,Myconfig中的Dog和Cat类注入到spring容器中

package com.zhihao.miao.imp.bean;


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;

/**
 * registerBeanDefinitions方法的参数有一个BeanDefinitionRegistry,
 * BeanDefinitionRegistry可以用来往spring容器中注入bean
 * 如此,我们就可以在registerBeanDefinitions方法里面动态的注入bean
 */
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        BeanDefinitionBuilder bdb = BeanDefinitionBuilder.rootBeanDefinition(People.class);
        registry.registerBeanDefinition(People.class.getName(),bdb.getBeanDefinition());

        BeanDefinitionBuilder bdb2 = BeanDefinitionBuilder.rootBeanDefinition(User.class);
        registry.registerBeanDefinition(User.class.getName(),bdb2.getBeanDefinition());

        BeanDefinitionBuilder bdb3 = BeanDefinitionBuilder.rootBeanDefinition(MyConfig.class);
        registry.registerBeanDefinition(MyConfig.class.getName(),bdb3.getBeanDefinition());
    }
}

主启动类:

package com.zhihao.miao.imp;

import com.zhihao.miao.imp.bean.*;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;

@Import(MyImportBeanDefinitionRegistrar.class)
public class Application3 {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application3.class,args);
        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(Dog.class));
        System.out.println(context.getBean(Cat.class));
        System.out.println(context.getBean(People.class));
    }
}

打印结果

...
com.zhihao.miao.imp.bean.User@3e694b3f
com.zhihao.miao.imp.bean.Dog@1bb5a082
com.zhihao.miao.imp.bean.Cat@78691363
com.zhihao.miao.imp.bean.People@41d477ed
...

当然也可以写成一个注解,@EnableImportConfig

package com.zhihao.miao.imp.bean;

import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MyImportBeanDefinitionRegistrar.class)
public @interface EnableImportConfig {
}

启动类:

package com.zhihao.miao.imp;

import com.zhihao.miao.imp.bean.*;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;

@EnableImportConfig
public class Application4 {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application4.class,args);
        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(Dog.class));
        System.out.println(context.getBean(Cat.class));
        System.out.println(context.getBean(People.class));
    }
}

也是可以将这些对象注入到spring容器的。

上一篇 下一篇

猜你喜欢

热点阅读