前言:去年到现在一直没有很好的时间完成这个spring基础+源码的博客目标,去年一年比较懒吧,所以今年我希望我的知识可以分享给正在奋斗中的互联网开发人员,以及未来想往架构师上走的道友们我们一起进步,从一个互联网职场小白到一个沪漂湿人,一路让我知道分享是一件多么重要的事情,总之不对的地方,多多指出,我们一起徜徉代码的海洋!
我这里做每个章节去说的前提,不是一定很标准的套用一些官方名词,目的是为了让大家可以理解更加清楚,如果形容的不恰当,可以留言出来,万分感激
1、软件版本
- jdk1.8+
- maven3.5+
- IDEA2019.2
- Spring Framework 5.1.4(spring官网:http://spring.io)
2、环境搭建
pom.xml中设定spring的jar依赖
#设置pom 依赖 <!-- https://mvnrepository.com/artifact/org.springframework/springcontext --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.4.RELEASE</version> </dependency>
此处新手会有疑问,我们为什么要导入这个context呢?
看下官网spring的核心组件有哪些(Core Container中有四个,Beans,Core,Context,SpEL)
我们为什么先选择用Context,因为对于Context来说,你可以理解为Spring既然是一种解决方案,一定是为了解耦而出现,所以它是作用在整个应用上下文,也就是官方文档中强烈建议我们xml设置为applicationContext.xml,我这边不严格对照官网的方式来,我们按照我们最熟悉spring的基础来走,所以我们先引用了Spring的Context包;
3、Spring的配置文件
- 配置文件放置位置:任意位置,没有硬性要求
- 配置文件命名:也没有硬性要求,但是官网强烈建议applicationContext.xml
4、Spring核心API
- ApplicationContext
- 作用:Spring提供的ApplicationContext这个工厂,用于对象的创建
- 好处:解耦合
- 接口类型:屏蔽实现的差异
非web环境:ClassPathApplicationContext(main unit)
web环境:XmlWebApplicationContext(这个需要引入spring-web的才会有,因为这个实现是在web应用中)
找到ApplicationContext.java这个类,然后使用Ctrl+H就可以看到这个接口有那些实现类
注意:ApplicationContext这个工厂对象占用大量的内存,所以
- 不会频繁的创建对象:一个应用只会创建一个工厂对象
- 既然对象只有一个,那么一定会有涉及到共享资源多线程并发访问,那么也一定是线程安全的(后面解释)
5、程序开发
- 创建类型,配置文件在resources下建立applicationContext.xml
- 配置文件添加(前提是要创建一个Person类)
<bean id="person" class="com.doctchen.spring5.entity.Person"> </bean>
- 测试类
/** * 测试ApplicationContext工厂怎么创建对象的 */ @Test public void test1(){ ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml"); Person person = (Person) ctx.getBean("person"); System.out.println("person = " + person); }
6、细节分析
- 名词解释
- Spring工厂创建的对象,叫做bean或者组件Component(实际上严格来说,不能称为对象,对象不等于bean,后面分析源码我再详解,这里先做为对象)
- Spring工厂相关的方法
- getBean家庭
- getBeanDefinitionNames
- getBeanNamesForType
- containsBeanDefinition
- containsBean
- getBean(String, Class)
Person person = (Person) ctx.getBean("person"); System.out.println("person = " + person); //通过这种⽅式获得对象,就不需要强制类型转换 Person person = ctx.getBean("person", Person.class); System.out.println("person = " + person);
- getBean(Class)
//当前Spring的配置⽂件中 只能有⼀个<bean class是Person类型 Person person = ctx.getBean(Person.class); System.out.println("person = " + person);
<bean id="person" class="com.doctchen.spring5.entity.Person"> </bean> <!-- 对于getBean(Class),只能有一个class,id也唯一,这里多写一个Person1后,这个api会报错 --> <bean id="person1" class="com.doctchen.spring5.entity.Person"> </bean>
//getBean(Class)报错 org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.doctchen.spring5.entity.Person' available: expected single matching bean but found 2: person,person1 at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1137) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:407) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:341) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:335) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1101)
很明显报错解释,要找一个确定的Person Bean,但是发现有两个Bean,所以Spring懵逼了!!!
- getBeanDefinitionNames
//获取的是Spring⼯⼚配置⽂件中所有bean标签的id值person person1 people String[] beanDefinitionNames = ctx.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println("beanDefinitionName = " + beanDefinitionName); }
<bean id="person" class="com.doctchen.spring5.entity.Person"> </bean> <bean id="person1" class="com.doctchen.spring5.entity.Person"> </bean> <bean id="people" class="com.doctchen.spring5.entity.People"> </bean>
- getBeanNamesForType
//根据类型获得Spring配置⽂件中对应的id值 String[] beanNamesForType = ctx.getBeanNamesForType(Person.class); for (String id : beanNamesForType) { System.out.println("id = " + id); }
<bean id="person" class="com.doctchen.spring5.entity.Person"> </bean> <bean id="person1" class="com.doctchen.spring5.entity.Person"> </bean>
- containsBeanDefinition
//⽤于判断是否存在指定id值的bean,不能判断name的值 if (ctx.containsBeanDefinition("person")) { System.out.println("true = " + true); } else { System.out.println("false = " + false); }
<!-- 判断是否存在指定id为person的bean,但是无法判断是否存在指定name为p的bean --> <bean id="person" name="p" class="com.doctchen.spring5.entity.Person"> </bean>
- containsBean
<!-- 判断是否存在指定id为person的bean,也可以判断是否存在指定name为p的bean --> <bean id="person" name="p" class="com.doctchen.spring5.entity.Person"> </bean>
这两个有细微的差别,就是id都可以判断,后者连name的值也可以判断,对于name和id的区别,我后面再说!
- 配置文件中的细节注意
- 只配置class属性
<bean class="com.doctchen.spring5.entity.Person">
当我们不写id的时候,spring会不会给我们一个id值?答案是肯定的,我们只需要用上面的api--getBeanDefinitionNames
//打印结果 beanDefinitionName = com.doctchen.spring5.entity.Person#0 String[] beanDefinitionNames = ctx.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println("beanDefinitionName = " + beanDefinitionName); }
所以这个名字是有的,如果没有命名的id对应的class有多个,并且我们id没有写,那么spring会默认以此类推1、2、...
<!-- beanDefinitionName = com.doctchen.spring5.entity.Person#0 beanDefinitionName = com.doctchen.spring5.entity.Person#1 --> <bean class="com.doctchen.spring5.entity.Person"> </bean> <bean class="com.doctchen.spring5.entity.Person"> </bean>
这个应用场景在:如果这个bean只需要使用一次,那么就可以省略id的值,如果这个bean被使用多次,或者被其他的bean引用则需要设置id的值
- name属性
- 作用在Spring配置文件中,为bean的对象定义别名(小名)
相同:ctx.getBean("id|name")--->object
配置上:<bean id="" class=""/> == <bean name="" class=""/>- 区别是
1. 别名可以定义多个,但是id只有一个
2. xml的id属性,命名要求,必须以字母,数字,下划线,连字符开头,不能以特殊字符开头,比如/person
3. 对于name属性命名,没有要求
但是spring发展到了现在,xml中的bean id,已经没有了限制,读者可以自行去试试
7、Spring工厂底层实现原理(简易版)
Spring是可以调用私有的构造方法来创建对象的
8、思考
- 未来开发过程中,是不是所有的对象都要交给Spring工厂来创建?我们下一章见分晓!!!
有不对的地方,或者需要补充的,直接评论,我把这个文章更加完善下去!!!