第一次开始动手写博客,不知怎么写,就先从刚学习的简单知识开始记录吧。希望后面会继续坚持下去。喽喽喽~~
这次博客标题是叫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装配模型
类型 | 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");
}
}
结果如下:
说明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起效果。
这种不指定装配的具体参数,容器自动查询后装配才是自动注入。这两种的注入方式都是基于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
执行效果:
xml配置-byName
执行效果:
xml配置-byType
执行效果:
下面我们在来看一下基于注解配置的
//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());
}
}
结果如下:
发现用@Autowire修饰的a属性,虽然已被注入。但setter方法没有调用。这种方式注入属性的方式就是利用了java的反射知识,field.set(value,targetObject);所以@Autowired这种注入的方式是setter注入方式的一种变体。@Autowire注入方式区别于xml属性配置的settter方式注入。
但此时使用@Autowrie方式注入的,b的autowireMode的值为0,不能说是自动注入。
question:那么我们怎么才能向xml那样开启注解的自动装配模式呢?
基于自动装配模式概念细谈,我们先把B类中A属性中的@Autowire注解去掉。
然后修改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。
执行效果如下:
发现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结果:
可以看到,我们自定的注解MyAnnotion也实现了属性手动注入。从中我们也初步窥探到,@Autowire这种注入方式的机制。哈哈,有点小兴奋呢~~
总结:我们介绍了4中spring注入模型分别是no,byName,byType,construct,两种注入方式setter方式,和构造方法方式。然后对手动装配和自动装配进行对比说明,进一步指出@Autowire这个注解是手动装配的,然后对注解开启自动装配进行了简单的演示,也通过一个自定义注解简单说明@Autowire 通过反射的注入机制。