Spring5源码分析之启动类的相关接口和注解

一些基础但是核心的知识总结:

Spring Boot项目启动的时候需要加@Configuration、 @ComponentScan

@Configuration  + @Bean  把第三方jar包注入到容器中。 内部的直接 @Service @Controller等等之类配合 @ComponentSscan 的就OK了

@Scope可以实现单例

对于启动默认是饿汉式调用时候创建(但是项目启动时候比较耗费时间),另外一种是调用时候创建

@ComponentScan有排除的用法,排除那个组件  需要哪个组件可以控制

在config类上面加 @ComponentScan然后去控制其他注解的注入情况

使用@Condition多条件注册bean对象,根据环境判断进行juez

@Import快速注入第三方bean对象

SpringBoot Emablexxx开启原理

基于ImportBeanDefinitionRegister注册bean

基于FactoryBean注册Bean对象

Spring的扩展接口:  condition



对于控制某个Bean,满足某个条件后才可以允许注册到容器中:

随便写个系统实体类吧:

public class OS {
    private String name;
    private Integer version;

    public OS() {
    }

    @Override
    public String toString() {
        return "hello ********************* OS{" +
                "name=‘" + name + ‘\‘‘ +
                ", version=" + version +
                ‘}‘;
    }
}

配置文件(相当于xml)

@Configuration
public class MyConfig {

    @Bean
    @Conditional(MyCondition.class)
    public OS os(){
        System.out.println("ttttttttttttttttttttt");
        return new OS();
    }
}

控制条件:

public class MyCondition implements Condition {
    /**
     *
     * @param conditionContext   获取当前上下文
     * @param annotatedTypeMetadata 获取到当前注解的细节
     * @return
     */
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment environment = conditionContext.getEnvironment();
        String osName = environment.getProperty("os.name");
        if (osName.equals("Windows 7")){
            return false;
        }
        if (osName.equals("Windows 10")){
            return true;
        }
        return false;
    }
}

测试类:

public class test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanDefinitionNames){
            System.out.println(name);
        }
        OS osTest = (OS)applicationContext.getBean("os");
        System.out.println(osTest);
    }
}

结果:

2. @Import注解



思考 为啥使用@Import注解呢?

@Bean注解应用场景是什么呢? 注册加载外部的jar。如果有30个bean,逐个去写也是非常麻烦的啊

所以就用@Import简化操作,将外部的jar包注入到Spring ioc容器中。等同于@Bean。@Import注册的Bean对象,id为当前类的全路径。

配置类:

@Configuration
@Import(OS.class)
public class MyConfig {
}

测试方法:

public class test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanDefinitionNames){
            System.out.println(name);
        }
        //Import的特殊性
        OS osTest = (OS)applicationContext.getBean("com.toov5.config.beanTest.OS");
        System.out.println(osTest);
    }
}

@Import 和 @Bean的区别:

@Bean注册的bean的id是方法名称

@Import当前类完整地址

共同应用场景都是引用外部jar包。

3. @EnableXXX



开启某某功能。底层使用@Import注解实现的

底层调用的就是@Import注解

场景: 封装一个框架,把不支持springboot的,鼓捣成支持springboot的!

实体类:

public class PayEntity {
}

注解:

/**
 * 启动时候加入该注解,开启功能,会将实体类注入到容器中
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({PayEntity.class})
public @interface EnablePayEntity {

}

启动:

@Configuration
@EnablePayEntity
public class MyConfig {
}

测试:

public class test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanDefinitionNames){
            System.out.println(name);
        }
    }
}

结果:

补充下: 可以@Import多个 ”,“解决。

4. ImportSelector接口 (跟 @Import注解一样的,只不过这个是原生api罢了)



实体类1:

public class MemberEntity {
}

实体类2:

public class PersonEntity {
}

原生接口的实现:

public class MyImportSeletcor implements ImportSelector {
    /**
     * 注解信息
     * @param annotationMetadata
     * @return
     */
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.toov5.config.beanTest.MemberEntity", "com.toov5.config.beanTest.PersonEntity"};
    }
}

启动类:

@Configuration
@Import(MyImportSeletcor.class)
public class MyConfig {
}

测试类:

public class test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanDefinitionNames){
            System.out.println(name);
        }
    }
}

结果:

5. ImportBeanDefinitionRegister接口  手动往ioc 注入bean



Spring容器中 Bean的信息,都是由BeanDefinition描述的

可以看下:

各种方法,Bean的各种信息。

实体类:

public class PersonEntity {
}

接口实现,手动注入实体类

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * 注解信息
     * @param annotationMetadata
     * @param beanDefinitionRegistry
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        //手动注册到ioc容器中
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(PersonEntity.class);
        //IOC源码里面肯定有这个的!!! 对象放到IOC容器中就叫注册 (底层是个map 线程安全的)
        beanDefinitionRegistry.registerBeanDefinition("personEntity", rootBeanDefinition);
    }
}

启动类:

@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class MyConfig {

}

测试类:

public class test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanDefinitionNames){
            System.out.println(name);
        }
    }
}

运行结果:

6. 基于FactoryBean



FactoryBean也可以用来注册Bean

启动方式千万万,随便使用:

实体类:

public class PersonEntity {
}

FactoryBean:

public class MyFactoryBean implements FactoryBean<PersonEntity> {
    @Override
    public PersonEntity getObject() throws Exception {
        return new PersonEntity();
    }

    @Override
    public Class<?> getObjectType() {
        return PersonEntity.class;
    }

    @Override
    public boolean isSingleton() {
        //默认情况下是true。单例
        return true;
    }
    //往ioc容器中注入对象

}

启动类:

@Configuration
public class MyConfig {

    @Bean
    public MyFactoryBean myFactoryBean(){
        return new MyFactoryBean();
    }
}

测试类:

public class test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        PersonEntity personEntity1 = (PersonEntity)applicationContext.getBean("myFactoryBean");
        PersonEntity personEntity2 = (PersonEntity)applicationContext.getBean("myFactoryBean");
        System.out.println(personEntity1 == personEntity2);
    }
}



自己写“抄”个注解玩玩

自定义一个注解:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface toov5 {
}

实体类:

@toov5
public class Hello {
}

启动采用扫包的方式:扫包的范围

@Configuration
@ComponentScan("com.toov5.config.beanTest.entity")
public class MyConfig {

}

测试:

public class test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanDefinitionNames){
            System.out.println(name);
        }
    }
}

测试结果:

小结: spring 注入bean到容器方式:

@Bean  @Import ,一般用于外部的jar包

其他的 @Service @Repository 注入对象底层其实都一样,就是区分不同的场景。使用的时候需要@ComponentScan注解扫包

还有就是实现一系列相应的接口去实现注入Bean 的方式

原文地址:https://www.cnblogs.com/toov5/p/11223350.html

时间: 2024-08-26 19:58:04

Spring5源码分析之启动类的相关接口和注解的相关文章

Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938395.html 前面粗略分析start_kernel函数,此函数中基本上是对内存管理和各子系统的数据结构初始化.在内核初始化函数start_kernel执行到最后,就是调用rest_init函数,这个函数的主要使命就是创建并启动内核线

Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938396.html 在基本分析完内核启动流程的之后,还有一个比较重要的初始化函数没有分析,那就是do_basic_setup.在内核init线程中调用了do_basic_setup,这个函数也做了很多内核和驱动的初始化工作,详解

Cordova Android源码分析系列二(CordovaWebView相关类分析)

本篇文章是Cordova Android源码分析系列文章的第二篇,主要分析CordovaWebView和CordovaWebViewClient类,通过分析代码可以知道Web网页加载的过程,错误出来,多线程处理等. CordovaWebView类分析 CordovaWebView类继承了Android WebView类,这是一个很自然的实现,共1000多行代码.包含了PluginManager pluginManager,BroadcastReceiver receiver,CordovaInt

Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 【转】

原文地址:Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938390.html 在构架相关的汇编代码运行完之后,程序跳入了构架无关的内核C语言代码:init/main.c中的start_kernel函数,在这个函数中Linux内核开始真正进入初始化阶段, 下面我就顺这代码逐个函数的解释,但是这里并不会过于深入

Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938393.html 在分析start_kernel函数的时候,其中有构架相关的初始化函数setup_arch. 此函数根据构架而异,对于ARM构架的详细分析如下: void __init setup_arch(char **cmdlin

cocos2d-x 源码分析 : control 源码分析 ( 控制类组件 controlButton)

源码版本来自3.1rc 转载请注明 cocos2d-x源码分析总目录 http://blog.csdn.net/u011225840/article/details/31743129 1.继承结构 control的设计整体感觉挺美的,在父类control定义了整个控制事件的基础以及管理,虽然其继承了Layer,但其本身和UI组件的实现并没有关联.在子类(controlButton,controlSwitch,controlStepper等中实现不同的UI组件).下面通过源码来分析control与

Hadoop源码分析(2)——Configuration类

这篇文章主要介绍Hadoop的系统配置类Configuration. 接着上一篇文章介绍,上一篇文章中Hadoop Job的main方法为: public static void main(String[] args) throws Exception { int res = ToolRunner.run(new Configuration(), new CalculateSumJob(),args); System.exit(res); } 其中ToolRunner.run方法传入的第一个变量

Nginx源码分析 - Nginx启动以及IOCP模型

Nginx 源码分析 - Nginx启动以及IOCP模型 版本及平台信息 本文档针对Nginx1.11.7版本,分析Windows下的相关代码,虽然服务器可能用linux更多,但是windows平台下的代码也基本相似 ,另外windows的IOCP完成端口,异步IO模型非常优秀,很值得一看. Nginx启动 曾经有朋友问我,面对一个大项目的源代码,应该从何读起呢?我给他举了一个例子,我们学校大一大二是在紫金港校区,到了 大三搬到玉泉校区,但是大一的时候也会有时候有事情要去玉泉办.偶尔会去玉泉,但

转:Ogre源码分析之Root类、Facade模式

Ogre源码分析(一)Root类,Facade模式 Ogre中的Root对象是一个Ogre应用程序的主入口点.因为它是整个Ogre引擎的外观(Façade)类.通过Root对象来开启和停止Ogre是最简单的一种方式:当你构造构造一个Root实例的时候你就启动了整个Ogre,当析构的时候(让它停止活动或者执行delete删除它)Ogre也就关闭了. API手册中这样介绍到:Ogre::Root 类代表了客户应用程序的入口点.在这里,应用程序可以获得系统的重要的访问权,也就是获取渲染系统 ,管理配置