大家家好,我是一名网络怪咖,北漂五年。相信大家和我一样,都有一个大厂梦,作为一名资深Java选手,深知Spring重要性,现在普遍都使用SpringBoot来开发,面试的时候SpringBoot原理也是经常会问到,SpringBoot是为了简化Spring开发,但是底层仍然是Spring。如果不了解Spring源码,那就更别提SpringBoot源码了,接下来我准备用两个月时间,从基础到源码彻彻底底的学习一遍Spring。
学习框架一定要踏踏实实一步一个脚印的学,很多人都比较喜欢急功近利,选择跳着学,包括我有时候也是,最终会发现你不但节约不了时间,反而会更耗时,而且学着学着很容易放弃。
上篇文章中介绍了依赖注入中的手动注入,所谓手动注入是指在xml中采用硬编码的方式来配置注入的对象,比如通过构造器注入或者set方法注入,这些注入的方式都存在不足,比如下面代码:
public class A{
private B b;
private C c;
private D d;
private E e;
....
private N n;
//上面每个private属性的get和set方法
}
使用spring容器来管理,xml配置如下:
<bean class="b" class="B"/>
<bean class="c" class="C"/>
<bean class="d" class="D"/>
<bean class="e" class="E"/>
<bean class="a" class="A">
<property name="b" ref="b"/>
<property name="c" ref="c"/>
<property name="d" ref="d"/>
<property name="e" ref="e"/>
...
bean>
上面的注入存在的问题:
为了解决上面这些问题,spring为我们提供了更强大的功能:自动注入
在介绍自动注入之前需要先介绍一些基础知识。
isAssignableFrom是Class类中的一个方法,看一下这个方法的定义:
public native boolean isAssignableFrom(Class<?> cls)
用法如下:
c1.isAssignableFrom(c2)
用来判断c2和c1是否相等,或者c2是否是c1的子类。
案例:
import java.util.Collection;
import java.util.List;
public class Client {
public static void main(String[] args) {
System.out.println(Object.class.isAssignableFrom(Integer.class)); //true
System.out.println(Object.class.isAssignableFrom(int.class)); //false
System.out.println(Object.class.isAssignableFrom(List.class)); //true
System.out.println(Collection.class.isAssignableFrom(List.class)); //true
System.out.println(List.class.isAssignableFrom(Collection.class)); //false
}
}
自动注入是采用约定大约配置的方式来实现的,程序和spring容器之间约定好,遵守某一种都认同的规则,来实现自动注入。
xml中可以在bean元素中通过autowire属性来设置自动注入的方式:
<bean id="" class="" autowire="byType|byName|constructor|default" />
下面我们详解每种注入方式的用法。
autowire设置为byName
<bean id="" class="X类" autowire="byName"/>
setter方法,以set开头,后跟首字母大写的属性名,如:setUserName,简单属性一般只有一个方法参数,方法返回值通常为void;
以setTestService为例,spring容器会按照setTestService名称的首字母小写testService去容器中查找同名的bean对象,找到后会通过该set方法进行属性注入。
案例:
public class TestService {
public TestService() {
System.out.println("TestService初始化了");
}
}
public class TestController {
private TestService testService;
public TestController() {
System.out.println("testController初始化了");
}
// 这里注意他是按照set方法的参数名称去容器当中找这个对象的
public void setTestService(TestService testService) {
System.out.println("testService的set方法执行了");
this.testService = testService;
}
@Override
public String toString() {
return "TestController{" +
"testService=" + testService +
'}';
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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="testService" class="com.gzl.cn.service.TestService"/>
<bean id="testController" class="com.gzl.cn.controller.TestController" autowire="byName"/>
beans>
import com.gzl.cn.controller.TestController;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Client {
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:bean.xml");
TestController testController = (TestController)classPathXmlApplicationContext.getBean("testController");
System.out.println(testController);
}
}
运行结果:
现在存在几个疑问
(1)Spring到底是根据属性去找set的,还是直接扫描的所有set方法?
基于这个问题,我们可以直接把属性给干掉,看看到底set方法还会不会执行
答案是会的,从而也说明了Spring就是循环遍历对象当中的所有set方法。跟属性压根没有一点关系,他甚至都不管你有没有属性。
(2)假如setTestService,根据testService找到了bean,但是参数并不是TestService或者是没有参数,会访问成功吗?
答案是不会的
autowire设置为byType
<bean id="" class="X类" autowire="byType"/>
spring容器会遍历x类中
所有的set方法
,会在容器中查找和set参数类型相同的bean对象
,将其通过set方法进行注入,未找到对应类型的bean对象则set方法不进行注入。需要注入的set属性的类型和被注入的bean的类型需要满足isAssignableFrom
关系。按照类型自动装配的时候,如果按照类型找到了多个符合条件的bean,系统会报错
。set方法的参数如果是下面的类型或者下面类型的数组的时候,这个set方法会被跳过注入:
Object,Boolean,boolean,Byte,byte,Character,char,Double,double,Float,float,Integer,int,Long,Short,shot,Enum,CharSequence,Number,Date,java.time.temporal.Temporal,java.net.URI,java.net.URI,java.util.Locale,java.lang.Class
案例:
<bean id="testService" class="com.gzl.cn.service.TestService"/>
<bean id="testController" class="com.gzl.cn.controller.TestController" autowire="byType">bean>
运行结果:
按照类型注入还有2中比较牛逼的用法:
案例:
被static修饰的内部类可以直接作为一个普通类来使用
import java.util.List;
import java.util.Map;
public class DiAutowireByTypeExtend {
// 定义了一个接口
public interface IService1 {
}
public static class BaseServie {
private String desc;
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "BaseServie{" +
"desc='" + desc + '\'' +
'}';
}
}
// Service1实现了IService1接口,还继承了BaseServie
public static class Service1 extends BaseServie implements IService1 {
}
// Service1实现了IService1接口,还继承了BaseServie
public static class Service2 extends BaseServie implements IService1 {
}
private List<IService1> serviceList;//@1
private List<BaseServie> baseServieList;//@2
private Map<String, IService1> service1Map;//@3
private Map<String, BaseServie> baseServieMap;//@4
public void setServiceList(List<IService1> serviceList) {//@5
this.serviceList = serviceList;
}
public void setBaseServieList(List<BaseServie> baseServieList) {//@6
this.baseServieList = baseServieList;
}
public void setService1Map(Map<String, IService1> service1Map) {//@7
this.service1Map = service1Map;
}
public void setBaseServieMap(Map<String, BaseServie> baseServieMap) {//@8
this.baseServieMap = baseServieMap;
}
@Override
public String toString() { //9
return "DiAutowireByTypeExtend{" +
"serviceList=" + serviceList +
", baseServieList=" + baseServieList +
", service1Map=" + service1Map +
", baseServieMap=" + baseServieMap +
'}';
}
}
<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="service1" class="com.gzl.cn.bean.DiAutowireByTypeExtend.Service1">
<property name="desc" value="service1"/>
bean>
<bean id="service2" class="com.gzl.cn.bean.DiAutowireByTypeExtend.Service2">
<property name="desc" value="service2"/>
bean>
<bean id="service2-1" class="com.gzl.cn.bean.DiAutowireByTypeExtend.Service2">
<property name="desc" value="service2-1"/>
bean>
<bean id="diAutowireByTypeExtend" class="com.gzl.cn.bean.DiAutowireByTypeExtend" autowire="byType"/>
beans>
public class Client {
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:bean.xml");
DiAutowireByTypeExtend diAutowireByTypeExtend = (DiAutowireByTypeExtend)classPathXmlApplicationContext.getBean("diAutowireByTypeExtend");
System.out.println(diAutowireByTypeExtend);
}
}
这个是格式化后的运行结果:
autowire设置为constructor
<bean id="" class="X类" autowire="constructor"/>
spring会找到x类中所有的构造方法(一个类可能有多个构造方法),然后将这些构造方法进行排序(先按修饰符进行排序,public的在前面,其他的在后面,如果修饰符一样的,会按照构造函数参数数量倒叙,也就是采用贪婪的模式进行匹配,spring容器会尽量多注入一些需要的对象)得到一个构造函数列表,会轮询这个构造器列表,判断当前构造器所有参数是否在容器中都可以找到匹配的bean对象,如果可以找到就使用这个构造器进行注入,如果不能找到,那么就会跳过这个构造器,继续采用同样的方式匹配下一个构造器,直到找到一个合适的为止。
案例:
public class DiAutowireByConstructor {
public static class BaseServie {
private String desc;
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "BaseServie{" +
"desc='" + desc + '\'' +
'}';
}
}
// Service1实现了IService1接口,还继承了BaseServie
public static class Service1 extends BaseServie {
}
// Service1实现了IService1接口,还继承了BaseServie
public static class Service2 extends BaseServie {
}
private Service1 service1;
private Service2 service2;
public DiAutowireByConstructor() { //@0
}
public DiAutowireByConstructor(Service1 service1) { //@1
System.out.println("DiAutowireByConstructor(Service1 service1)");
this.service1 = service1;
}
public DiAutowireByConstructor(Service1 service1, Service2 service2) { //@2
System.out.println("DiAutowireByConstructor(Service1 service1, Service2 service2)");
this.service1 = service1;
this.service2 = service2;
}
public void setService1(Service1 service1) {
this.service1 = service1;
}
public void setService2(Service2 service2) {
this.service2 = service2;
}
@Override
public String toString() {
return "DiAutowireByConstructor{" +
"service1=" + service1 +
", service2=" + service2 +
'}';
}
}
<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="service1" class="com.gzl.cn.bean.DiAutowireByConstructor.Service1">
<property name="desc" value="service1"/>
bean>
<bean id="service2" class="com.gzl.cn.bean.DiAutowireByConstructor.Service2">
<property name="desc" value="service2"/>
bean>
<bean id="diAutowireByConstructor" class="com.gzl.cn.bean.DiAutowireByConstructor" autowire="constructor"/>
beans>
运行结果: 很明显他是使用了 public DiAutowireByConstructor(Service1 service1, Service2 service2)
构造器
当我们把service2给去掉后运行:
<bean id="service2" class="com.gzl.cn.bean.DiAutowireByConstructor.Service2">
<property name="desc" value="service2"/>
bean>
他会使用public DiAutowireByConstructor(Service1 service1)
构造器,也就是他会使用最大限度的注入所有依赖的对象。
bean xml的根元素为beans,注意根元素有个default-autowire
属性,这个属性可选值有(no|byName|byType|constructor|default),这个属性可以批量设置当前文件中所有bean的自动注入的方式
,bean元素中如果省略了autowire属性,就相当于是autowire=default,那么会取default-autowire的值作为其autowire的值,而每个bean元素还可以单独设置自己的autowire覆盖default-autowire的配置
,如下:
<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="byName">
beans>
案例:
public class DiAutowireByConstructor {
public static class BaseServie {
private String desc;
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "BaseServie{" +
"desc='" + desc + '\'' +
'}';
}
}
// Service1实现了IService1接口,还继承了BaseServie
public static class Service1 extends BaseServie {
}
// Service1实现了IService1接口,还继承了BaseServie
public static class Service2 extends BaseServie {
}
private Service1 service1;
private Service2 service2;
public DiAutowireByConstructor() { //@0
}
public DiAutowireByConstructor(Service1 service1) { //@1
System.out.println("DiAutowireByConstructor(Service1 service1)");
this.service1 = service1;
}
public DiAutowireByConstructor(Service1 service1, Service2 service2) { //@2
System.out.println("DiAutowireByConstructor(Service1 service1, Service2 service2)");
this.service1 = service1;
this.service2 = service2;
}
public void setService1(Service1 service1) {
System.out.println("setService1执行了");
this.service1 = service1;
}
public void setService2(Service2 service2) {
System.out.println("setService2执行了");
this.service2 = service2;
}
@Override
public String toString() {
return "DiAutowireByConstructor{" +
"service1=" + service1 +
", service2=" + service2 +
'}';
}
}
<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="byName">
<bean id="service1" class="com.gzl.cn.bean.DiAutowireByConstructor.Service1">
<property name="desc" value="service1"/>
bean>
<bean id="service2" class="com.gzl.cn.bean.DiAutowireByConstructor.Service2">
<property name="desc" value="service2"/>
bean>
<bean id="diAutowireByConstructor" class="com.gzl.cn.bean.DiAutowireByConstructor" autowire="default"/>
beans>
运行结果:
default-autowire设置的是byName,然后bean标签设置的是autowire=“default”,运行结果如下
设置为no的时候他就不会随着default-autowire来自动配置了
<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="byName">
<bean id="service1" class="com.gzl.cn.bean.DiAutowireByConstructor.Service1">
<property name="desc" value="service1"/>
bean>
<bean id="service2" class="com.gzl.cn.bean.DiAutowireByConstructor.Service2">
<property name="desc" value="service2"/>
bean>
<bean id="diAutowireByConstructor" class="com.gzl.cn.bean.DiAutowireByConstructor" autowire="no"/>
beans>
运行结果:
bean名称->bean对象
的映射方式注入给一个Map对象,这种用法比较重要,用途比较大,要掌握