Spring注解版的学习(4)-自动装配

什么是自动装配

自动装配就是让应用程序上下文为你找出依赖项的过程。说的通俗一点,就是Spring会在上下文中自动查找,并自动给bean装配与其关联的属性!

spring中实现自动装配的方式有两种,一种是通过xml文件、另一种是通过注解。
这里讲的是通过注解@Autowired实现的,xml的我也不会~


上面是网上对自动装配的理解,换做我来说的话,大概是这么捞的…
@Autowired的作用就是将在容器中的对象找出来给你 作用类似 new一个对象获取一个对象 当然默认单实例每次拿到都是同一个

@Autowired的小总结

@Autowired的装配总结:
1.默认根据组件类型去容器里面获取  等同于annotationcontext.getBean(Person.class);
	
2.如果根据类型找到多个bean  
		比如你person本身标注了@component然后又在配置了@bean注入了一个person02,
		他就会根据id去匹配容器中的对象 id默认是@Autowired Person person22 默认id是person22(我原本以为是类型首字母小写呢)

3.可以使用@Qualifier("person222")去强制限定id为这个person222  标注方式
	@Qualifier("person222")
	@Autowired Person person22

4.默认@Autowired使用了但是没找到对应的bean会报错  可以使用
		@Autowired(required=false)使得不一定必须,没有就拉倒

5.标注@Primary,作用是如果程序需要这个类型的bean,优先使用这个使用方式
		@Primary
    	@Bean(value = "person222")
        public Person myPerson(){
            return new Person("haha",20,"广东");
        }
      
总结起来:@Autowired优先级就是这样的
直接根据类去获取,找到一个就直接装配
找到超过一个的时候:
1.@Qualifier("person222")--->
2.@Primary标注的 -->
3.根据组件的id装配(默认id和人为设置)

除了@Autowired之外还有两个注解也可以实现自动装配的功能,他们之间的区别

JSR250规范@resource
	功能和@Autowired接近 但是没有@primary和required=false的功能


JSR330规范@inject
​	功能和@Autowired更接近,
	但是需要导依赖(导包)有@primary功能没有required=false的功能
	
并且:这两个规范是java定义的自动装配,
	脱离了spring还能够在别的ioc容器使用,而				 
	@Autowired是spring的规范,脱离了spring就无法使用。
<!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

@inject的依赖

@Autowired还可以标注在方法、参数、构造器上,分别有什么作用

标注在方法上

@Component
public class Father {
    Son son;

    public Father() {
        
    }

    @Override
    public String toString() {
        return "Father{" +
                "son=" + son +
                '}';
    }

    public Son getSon() {
        return son;
    }
    @Autowired
    public void setSon(Son son) {
        this.son = son;
    }
}

标注在构造函数上

@Component
public class Father {
    Son son;
    @Autowired
    public Father(Son son) {
        this.son=son;
    }

    @Override
    public String toString() {
        return "Father{" +
                "son=" + son +
                '}';
    }

    public Son getSon() {
        return son;
    }

    public void setSon(Son son) {
        this.son = son;
    }
}

标注在参数上

@Bean
    public Father father(@Autowired Son son){

        Father father = new Father();
        father.setSon(son);
        return father;
    }

测试类

AnnotationConfigApplicationContext as = as();
        Father father = as.getBean(Father.class);
        Son son = as.getBean(Son.class);
        System.out.println(father);
        System.out.println(son);
结果都是一样的:
Father{son=com.hjj.Bean.Son@15761df8}
com.hjj.Bean.Son@15761df8

这里说明,都是从容器里面获取的 标注了@Autowired
而且可以省略

小总结:我也不清楚...意思是在各种地方使用这个注解,
都是从ioc容器获取bean对象,然后很多都可以省略  莫得了

Aware接口的作用

aware接口的作用就是传递一些spring底层的组件到bean里面供开发者使用
业务按需要实现特定的Aware接口,spring容器会主动找到该bean,
然后调用特定的方法,将特定的参数传递给bean;

比如下面这个Father类 实现了几个aware接口,重写了他们的方法 每个方法都在构造之后初始化之前调用

因为他们是通过这个bean的后置处理器去调用的

下面是bean以及测试结果


@Component
public class Father implements EmbeddedValueResolverAware, BeanNameAware, ApplicationContextAware {
    Son son;
    ApplicationContext applicationContext;
    public Father(ApplicationContext applicationContext) {
        this.applicationContext=applicationContext;

    }

    @Override
    public String toString() {
        return "Father{" +
                "son=" + son +
                ", applicationContext=" + applicationContext +
                '}';
    }

    public Son getSon() {
        return son;
    }

    public void setSon(Son son) {
        this.son = son;
    }

    @Override
    public void setBeanName(String s) {
        System.out.println("当前bean的名字"+s);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("获取到的ioc容器对象"+applicationContext);
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
        String s = stringValueResolver.resolveStringValue("你好${os.name}我是#{25*4},我住在${person.address}");
        System.out.println("解析的字符串"+s);
    }
}


    @Test
    public void test01(){
        AnnotationConfigApplicationContext as = as();
        Father father = as.getBean(Father.class);
        Son son = as.getBean(Son.class);
        System.out.println(father);
        System.out.println(son);
    }

测试结果

测试结果:
当前bean的名字father
解析的字符串你好Windows 10我是100,我住在广东省
获取到的ioc容器对象org.springframework.context.annotation.AnnotationConfigApplicationContext@19e1023e: startup date [Tue Jun 02 13:43:42 CST 2020]; root of context hierarchy
Father{son=null, applicationContext=org.springframework.context.annotation.AnnotationConfigApplicationContext@19e1023e: startup date [Tue Jun 02 13:43:42 CST 2020]; root of context hierarchy}
com.hjj.Bean.Son@4c9f8c13
上面三个aware的接口作用,EmbeddedValueResolverAware, BeanNameAware, ApplicationContextAware
EmbeddedValueResolverAware是初始化前将一个字符串值解析器传入bean内
BeanNameAware在bean初始化前将bean在容器的id(名字)传进bean
ApplicationContextAware在bean初始化前将ioc容器对象传进bean里面

所以可以看出来 aware接口的作用就是传递一些spring底层的组件到bean里面供开发者使用

另外:EmbeddedValueResolverAware可以解析什么,可以使用${}获取环境变量,包括配置文件的key-value
因为前面说了吧 配置文件的键值对是加载到环境变量里面的,所以能获取环境变量就能获取配置文件
但是首先配置文件得先加载进容器里,你可以使用
@PropertySource(value = {"classpath:/person.properties"})在配置类上标注
或者xml的方式加载配置文件 
<context:property-placeholder location="classpath:person.properties"/>

我们拿ApplicationContextAware作为例子 看看bean的后置处理器是如何将对象传给bean的

try {
			/********构造bean并且赋值之类的********/
            this.populateBean(beanName, mbd, instanceWrapper);
            if (exposedObject != null) {
            		/**********初始化****************/
                exposedObject = this.initializeBean(beanName, exposedObject, mbd);
            }
   这里就是前面讲bean的生命周期函数的时候后置处理器那里分析过的内容  先构造 然后初始化  
   初始化里面还有嵌套的流程
   			后置处理器的预处理-->调用初始化方法--->后置处理器的后处理
   			
   		if (mbd == null || !mbd.isSynthetic()) {
   			/*******************后置处理器的预处理**************/
            wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
        }

        try {
        	/*****************调用初始化方法******************/
            this.invokeInitMethods(beanName, wrappedBean, mbd);
        } catch (Throwable var6) {
            throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
        }

        if (mbd == null || !mbd.isSynthetic()) {
        	/***************后置处理器的后处理****************/
            wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
            
            
            先循环迭代遍历所有的后置处理器的预处理部分
            Iterator var4 = this.getBeanPostProcessors().iterator();

        do {
            if (!var4.hasNext()) {
                return result;
            }

            BeanPostProcessor beanProcessor = (BeanPostProcessor)var4.next();
            /*********在这个方法里面调用aware接口判断的 方法**************/
            result = beanProcessor.postProcessBeforeInitialization(result, beanName);
        } while(result != null);
        
        /***********这里做aware接口的判断*************/
         this.invokeAwareInterfaces(bean);
        
        /**********这里判断bean实现了哪个aware接口然后传相应的组件********/
             private void invokeAwareInterfaces(Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof EnvironmentAware) {
                ((EnvironmentAware)bean).setEnvironment(this.applicationContext.getEnvironment());
            }

            if (bean instanceof EmbeddedValueResolverAware) {
                ((EmbeddedValueResolverAware)bean).setEmbeddedValueResolver(this.embeddedValueResolver);
            }

            if (bean instanceof ResourceLoaderAware) {
                ((ResourceLoaderAware)bean).setResourceLoader(this.applicationContext);
            }

            if (bean instanceof ApplicationEventPublisherAware) {
                ((ApplicationEventPublisherAware)bean).setApplicationEventPublisher(this.applicationContext);
            }

            if (bean instanceof MessageSourceAware) {
                ((MessageSourceAware)bean).setMessageSource(this.applicationContext);
            }

            if (bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
            }
        }

    }
    
    然后判断这个bean是实现的哪个接口  就将哪个接口要传的参数传过去 比如这里是执行这句
    if (bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
            }
            
            
         

简洁版

上面的方法栈调用分析的看得麻烦就看这个流程总结吧:(我压根觉得这不配叫源码分析-  -所以叫他方法栈调用顺序分析)

	1.this.populateBean(beanName, mbd, instanceWrapper);构造bean对象
	2.exposedObject = this.initializeBean(beanName, exposedObject, mbd);初始化对象

	点进2里面看
	2.1wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
	2.2 this.invokeInitMethods(beanName, wrappedBean, mbd);
	2.3wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		2.1后置处理器的预处理--》2.2调用初始化方法--》后置处理器的后处理
	
	点进2.1 applyBeanPostProcessorsBeforeInitialization里面看
	2.1.1一个迭代器遍历(视频的版本是for循环 一样的效果)遍历执行后置处理器的预处理
		result = beanProcessor.postProcessBeforeInitialization(result, beanName);
	再点进2.1.1看看遍历的时候后置处理器的预处理逻辑
		postProcessBeforeInitialization里面是调用了这个方法
		 this.invokeAwareInterfaces(bean);
		 再点进去看看里面做了很多这种判断
		 if (bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
            }
            如果这个bean实现了ApplicationContextAware就将applicationContext传到bean里面 
     
     哦吼 完事了
     

	

然后我其实自己也没怎么理解(甚至不知道这些东西的使用场景,感觉学了跟没学一样,希望哪个路过的大佬救救我!!感觉这样学没什么进步)

然后我其实自己也没怎么理解(甚至不知道这些东西的使用场景,感觉学了跟没学一样,希望哪个路过的大佬救救我!!感觉这样学没什么进步)

@profile的使用

给bean或者配置类上标注一个标志 可以说是一个环境…比如说开发环境、测试环境、生产环境
配置类:里面注入了三个数据源,然后用@profile设置不同的标志

@Configuration
public class MyProfileConfig {
    @Profile("test")
    @Bean
    public DataSource dataSourceTest() throws PropertyVetoException {
        ComboPooledDataSource dataSource=new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setPassword("root");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }
    @Profile("Dev")
    @Bean
    public DataSource dataSourceDev() throws PropertyVetoException {
        ComboPooledDataSource dataSource=new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setPassword("root");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test01");
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }
    @Profile("Pro")
    @Bean
    public DataSource dataSourcePro() throws PropertyVetoException {
        ComboPooledDataSource dataSource=new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setPassword("root");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/testttt");
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }
}

测试类版本1:创建ioc容器的时候构造函数使用带参数的,然后运行的时候修改jvm参数:

-Dspring.profiles.active=test
在这里插入图片描述
测试类1:

public class ProfileTest {


    public AnnotationConfigApplicationContext as(){
        AnnotationConfigApplicationContext annotationcontext = new AnnotationConfigApplicationContext(MyProfileConfig.class);
        return annotationcontext;
    }
    @Test
    public void test01(){
        AnnotationConfigApplicationContext as = as();
        String[] beanNamesForType = as.getBeanNamesForType(DataSource.class);
        for(String s:beanNamesForType){
            System.out.println(s);
        }
    }
}

测试类版本2:创建ioc容器使用无参构造 然后再去设置它的环境参数

public class ProfileTest {
    public AnnotationConfigApplicationContext as(){
   		/*使用无参构造...*/
        AnnotationConfigApplicationContext annotationcontext = new AnnotationConfigApplicationContext();
        /*设置jvm参数*/
        annotationcontext.getEnvironment().setActiveProfiles("Dev","test");
        /*设置配置类*/
        annotationcontext.register(MyProfileConfig.class);
        /*刷新容器*/
        annotationcontext.refresh();
        return annotationcontext;
    }
    @Test
    public void test01(){
        AnnotationConfigApplicationContext as = as();
        String[] beanNamesForType = as.getBeanNamesForType(DataSource.class);
        for(String s:beanNamesForType){
            System.out.println(s);
        }
    }
}

这里当时没有复制测试结果,反正意思就是使用@profile来使得某些bean或者标注在配置类上,这个配置类这个bean只有在是这个环境(有点类似条件判断,但是只是判断运行的时候jvm的某一个参数,@conditional可以判断更多的条件吧)
用来切换生产环境和测试环境等等

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值