这次课我们详细理解一下Spring,理解每一行代码到底是什么意思。
我们在上次的代码上做一点修改:
Persion.java
1 package com.study.entity; 2 3 public class Persion { 4 5 private String name; 6 private Integer age; 7 public String getName() { 8 return name; 9 } 10 public void setName(String name) { 11 System.out.println("setName:"+name); 12 this.name = name; 13 } 14 public Integer getAge() { 15 return age; 16 } 17 public void setAge(Integer age) { 18 this.age = age; 19 } 20 @Override 21 public String toString() { 22 return "Persion [name=" + name + ", age=" + age + "]"; 23 } 24 public Persion(String name, Integer age) { 25 super(); 26 this.name = name; 27 this.age = age; 28 } 29 public Persion() { 30 System.out.println("Persion's Contructor...."); 31 } 32 }
我们在第10行和第30行加了打印。目的就是我想知道Persion是什么时候被创建了并且设置了属性。
我们在TestPersion中修改:
1 package com.study.entity; 2 3 import org.springframework.context.ApplicationContext; 4 import org.springframework.context.support.ClassPathXmlApplicationContext; 5 6 public class TestPersion { 7 8 public static void main(String[] args) { 9 // Persion p1 = new Persion(); 10 // p1.setName("xiaoming"); 11 // System.out.println(p1.getName()); 12 13 //1.创建Spring的IOC容器对象 14 System.out.println("开始创建Spring的IOC容器"); 15 ApplicationContext ext = new ClassPathXmlApplicationContext("applicationContext.xml"); 16 System.out.println("结束创建Spring的IOC容器"); 17 18 //2.从IOC容器中获取Bean实例 19 System.out.println("开始从IOC容器中获取Bean实例"); 20 Persion p2 = (Persion) ext.getBean("persionService"); 21 System.out.println("结束从IOC容器中获取Bean实例"); 22 23 //3.调用实例对象的方法 24 System.out.println("开始调用实例对象的方法"); 25 System.out.println(p2.toString()); 26 System.out.println("结束调用实例对象的方法"); 27 } 28 29 }
我们在之前代码的每一行前后都加了打印,目的就是想看看Spring创建对象的过程。
我们的applicationContext.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"> <!-- 配置Bean class:bean的全类名,通过反射的方式在IOC容器中创建Bean,所以要求Bean中必须有 无参的构造函数 id:表示容器的bean,id是唯一的,即在这个xml文件中要唯一 --> <bean id="persionService" class="com.study.entity.Persion"> <property name="name" value="xiaozhang"></property> </bean> </beans>
输出的结果如下:
开始创建Spring的IOC容器 三月 05, 2020 8:28:51 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1dd92fe2: startup date [Thu Mar 05 20:28:51 CST 2020]; root of context hierarchy 三月 05, 2020 8:28:51 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [applicationContext.xml] Persion's Contructor.... setName:xiaozhang 结束创建Spring的IOC容器 开始从IOC容器中获取Bean实例 结束从IOC容器中获取Bean实例 开始调用实例对象的方法 Persion [name=xiaozhang, age=null] 结束调用实例对象的方法
可以看到,我们在创建Spring的IOC容器的时候,就会创建Persion的实例对象,并且给对象的属性赋值。
也就是说,只要容器启动了,容器内就已经存在xml中配置的实例对象,所以我们可以通过getBean的方式去获取Bean。
我们讲一下IOC和DI。IOC(Inversion of Control):其思想是翻转资源获取的方向,传统的资源查找方式要求组件向容器发起请求查找资源。作为回应,容器适当的时候返回资源。而应用了IOC之后,则是容器主动的将资源推送给它所管理的组件,组件所要做的仅是选择一种合适的方式来接受资源。这种行为也被称为查找的被动形式。
DI(Dependency Injection)组件以一些预先定义好的方式(如setter方法)接受来自容器的资源注入。
package logan.spring.study; public class HelloWorld { private String name; public void setName2(String name){ System.out.println("setName: "+ name); this.name = name; } public void hello(){ System.out.println("hello: " + name); } public HelloWorld(){ System.out.println("HelloWorld's Constructor..."); } }
package logan.spring.study; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { // TODO Auto-generated method stub //1.创建Spring的IOC容器对象 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //2.从 IOC容器中获取Bean实例 HelloWorld helloWorld = (HelloWorld) ctx.getBean("hello"); //3.调用hello方法 System.out.println(helloWorld); } }
applicationContext
<?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"> <!-- 配置bean class:bean的全类名,通过反射的方式在IOC容器中创建bean,所以要求Bean中必须有无参的构造函数 id:表示容器的bean,id唯一 --> <bean id="hello" class="logan.spring.study.HelloWorld"> </bean> </beans>
输出结果
五月 18, 2017 8:51:39 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3eb07fd3: startup date [Thu May 18 20:51:39 CST 2017]; root of context hierarchy 五月 18, 2017 8:51:39 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [applicationContext.xml] HelloWorld's Constructor... logan.spring.study.HelloWorld@48eff760
ApplicationContext本身是一个容器
ApplicationContext的主要实现类:
ClassPathXmlApplicationContext():从类路径下加载配置文件
FileSystemXmlApplicationContext:从文件系统加载配置文件
ConfigurableAppliactionContext扩展于ApplicationContext,新增加两个主要方法,refresh()和close(),让ApplicationContext具有启动,刷新和关闭上下文的功能。
ApplicationContext在初始化上下文时就实例化所有单例的Bean。
WebApplicationContext是专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作。
获取bean还可以用一下方式:
package logan.spring.study; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { // TODO Auto-generated method stub //1.创建Spring的IOC容器对象 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //2.从 IOC容器中获取Bean实例 HelloWorld helloWorld = (HelloWorld) ctx.getBean("hello"); HelloWorld hello = ctx.getBean(HelloWorld.class); //3.调用hello方法 System.out.println(hello); } }
这样做有缺点,就是如果在配置文件里面配置两个Bean,它就会报错。
<?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"> <!-- 配置bean class:bean的全类名,通过反射的方式在IOC容器中创建bean,所以要求Bean中必须有无参的构造函数 id:表示容器的bean,id唯一 --> <bean id="hello" class="logan.spring.study.HelloWorld"> </bean> <bean id="hello2" class="logan.spring.study.HelloWorld"> </bean> </beans>
这样就会报错
Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'logan.spring.study.HelloWorld' available: expected single matching bean but found 2: hello,hello2 at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1041) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:345) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1093) at logan.spring.study.Main.main(Main.java:14)
所以在利用类型返回IOC容器中的Bean,但是要求IOC容器中必须只能有一个该类型的Bean。