第一次开始动手写博客,不知怎么写,就先从刚学习的简单知识开始记录吧。希望后面会继续坚持下去。喽喽喽~~

这次博客标题是叫spring源码,但本文不会过多介绍源码知识,旨在于巩固下spring应用,源码的进一步探讨会在后续更新。

此博客内容基于毁三观的spring自动注入

自动装配模型概念细谈

从spring官网上面可以看到spring只提出了4中自动装配模型no,byName,byType,这四个模型分别用一个整形来表示,存在spring的beanDefinition当中,任何一个类默认是no这个装配模型,也就是一个被注解的类默认的装配模型是no也就是手动装配;其中no用0来表示;byName用1来表示,bytype用2来表示;如果某个类X,假设X的bean对应的beanDefinition当中的autowireMode=2则表示这个类X的自动装配模型为bytype;如果autowireMode=1则表示为byname装配模型

springmachine 自动流转状态设置_System

类型

autowrieMode码

no

0

byName

1

byType

2

至于为什么是0,1,2,后面我们会验证。

 

自动注入需要相对于手动装配来说;在spring应用程序当中假设你的A类依赖了B类,需要在A类当中提供一个B类的属性,再加上setter,继而在xml当中配置、描述一下这两个类之间的依赖关系。如果做完当容器初始化过程中会实例化A,在实例化A的过程中会填充属性,由于在xml中已经配置、描述好两者的关系,故而spring会把B给A装配上;这种由程序员自己配置、描述好依赖关系的写法叫做手动装配;反之不指定装配的具体参数,容器自动查询后装配的叫自动装配。

下面先看下基于xml配置的。

public class A {

    public A(){
        System.out.println("A 已创建!");
    }
}

public class B {
    A a;

    public void setA(A a) {
        this.a = a;
        System.out.println(a);
    }

    public B(){
        System.out.println("B 已创建!");
    }
}

xml配置

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="a" class="com.lqq.bean.A"></bean>
    <bean id="b" class="com.lqq.bean.B">
        <property name="a" ref="a"/>
    </bean>
</beans>

执行代码

public class App 
{
    public static void main( String[] args )
    {
        ClassPathXmlApplicationContext applicationContext = new       ClassPathXmlApplicationContext("classpath:application.xml");
    }
}

结果如下:

springmachine 自动流转状态设置_spring_02

说明a,b类spring容器已创建完毕,并且b类也成功注入a属性。但此时a属性的注入是属于手动注入的。下面我们看一下自动注入的情况。

自动注入的xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
        default-autowire="byType">

    <bean id="a" class="com.lqq.bean.A"></bean>
    <bean id="b" class="com.lqq.bean.B">
    </bean>
</beans>

此时B没有配置A属性,此时已自动注入。关键是在xml中配置了一个default-autowire="byType"的属性。在beans标签上面配置,表示已开启自动装配,适用于所有bean。如果配置在具体的某个bean标签上,则只对此bean起效果。

springmachine 自动流转状态设置_System_03

这种不指定装配的具体参数,容器自动查询后装配才是自动注入。这两种的注入方式都是基于setter方法依赖注入的,所以setter方法不能省略!(后面的@Autowired注入的方式跟这个有区别!)

 

下面我们基于xml自动配置,先验证一下autowireMode值

首先提供一个后置处理器来获取A的自动装配模型,主要是实现了BeanFactoryPostProcessor 的一个类,BeanFactoryPostProcessor 这个接口的功能就是对bean生产前进行加工处理。具体含义请参考其他博客,这样我们的内容会更加专一!(其实我想偷个懒~)。参考链接:Spring高级进阶:BeanFactoryPostProcessor。

public class MyBeanFactoryPostProcess implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanFactory.getBeanDefinition("a");
        System.out.println("a的autowireMode值:"+beanDefinition.getAutowireMode());
    }
}

xml配置-no

springmachine 自动流转状态设置_spring_04

执行效果:

springmachine 自动流转状态设置_spring_05

xml配置-byName

springmachine 自动流转状态设置_xml_06

执行效果:

springmachine 自动流转状态设置_xml_07

xml配置-byType

springmachine 自动流转状态设置_spring、spring自动配置_08

执行效果:

springmachine 自动流转状态设置_spring_09

 

 

下面我们在来看一下基于注解配置的

//java配置
@Configuration
@ComponentScan("com.lqq")
public class MyConfig {
}

//下面是两个类,都用@Component注解标识

@Component
public class A {

    public A(){
        System.out.println("A 已创建!");
    }
}


@Component
public class B {
    @Autowired
    A a;
    public void setA(A a) {
        this.a = a;
        System.out.println(a+"被注入!");
    }

    public A getA() {
        return a;
    }

    public B(){
        System.out.println("B 已创建!");
    }
}

执行测试方法

public class App 
{
    public static void main( String[] args )
    {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        B bean = applicationContext.getBean(B.class);
        System.out.println(bean.getA());
    }
}

结果如下:

springmachine 自动流转状态设置_System_10

发现用@Autowire修饰的a属性,虽然已被注入。但setter方法没有调用。这种方式注入属性的方式就是利用了java的反射知识,field.set(value,targetObject);所以@Autowired这种注入的方式是setter注入方式的一种变体。@Autowire注入方式区别于xml属性配置的settter方式注入。

但此时使用@Autowrie方式注入的,b的autowireMode的值为0,不能说是自动注入。

question:那么我们怎么才能向xml那样开启注解的自动装配模式呢?

基于自动装配模式概念细谈,我们先把B类中A属性中的@Autowire注解去掉。

springmachine 自动流转状态设置_System_11

然后修改MyBeanFactoryPostProcess的postProcessBeanFactory方法。

@Component
public class MyBeanFactoryPostProcess implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanFactory.getBeanDefinition("b");
        System.out.println("b的autowireMode值:"+beanDefinition.getAutowireMode());
        beanDefinition.setAutowireMode(1);
        System.out.println("b的autowireMode值(修改过后):"+beanDefinition.getAutowireMode());
}
}

这里我们在对B类型的bean加工处理阶段,手动修改了autowireMode模式,把模式值改为1,也就是byName。

执行效果如下:

springmachine 自动流转状态设置_spring、spring自动配置_12

发现B类中的setter方法竟然执行了!而且依赖属性A也成功注入。此时我们基于注解的自动注入模式开启成功。

Question:那么@Autowire到底是通过什么怎样的方式注入属性的呢?

前面我们简单说到@Autowire这种方式注入属性的方式就是利用了java的反射知识,field.set(value,targetObject);

下面我们通过自定注解来进行模拟下这种机制。首先先创建个MyAnnotion注解,然后在B类中对A属性进行注解,MyBeanFactoryPostProcess的postProcessBeanFactory方法中对B类型的bean进行加工修饰。

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotion {
}


@Component
public class B {
    
    @MyAnnotion
    A a;
 

    public A getA() {
        return a;
    }

    public B(){
        System.out.println("B 已创建!");
    }
}

@Component
public class MyBeanFactoryPostProcess implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        /*获取B*/
        B b = beanFactory.getBean(B.class);
        try {
            /*获取B中a字段*/
            Field field = B.class.getDeclaredField("a");
            /*判断字段是否被MyAnnotion注解*/
            if(field.isAnnotationPresent(MyAnnotion.class)){
                field.setAccessible(true);
                /*从bean工厂中获取A执行字段赋值*/
                field.set(b,beanFactory.getBean(A.class));
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

debug结果:

springmachine 自动流转状态设置_spring_13

可以看到,我们自定的注解MyAnnotion也实现了属性手动注入。从中我们也初步窥探到,@Autowire这种注入方式的机制。哈哈,有点小兴奋呢~~


总结:我们介绍了4中spring注入模型分别是no,byName,byType,construct,两种注入方式setter方式,和构造方法方式。然后对手动装配和自动装配进行对比说明,进一步指出@Autowire这个注解是手动装配的,然后对注解开启自动装配进行了简单的演示,也通过一个自定义注解简单说明@Autowire 通过反射的注入机制。