目录
二、初识IOC(对比新建一个对象三种方式——新建方式、工厂方式、依赖注入)
三、IOC原理——关于Spring容器是如何完成依赖注入的整个过程的
五、Spring的另一种配置方式——注解配置(从xml配置到注解配置)
一、从Spring到IOC
Spring作为Java开发中使用最广泛的容器(不加之一,就是这么傲娇)。
最重要的两大功能:
1、Spring作为超级大工厂,负责创建、管理所有的Java对象,这些Java对象成为Bean。通常一个Bean=若干属性(必须)+属性的setter-getter方法(必须)+toString()方法(非必须,打印调试)+全参构造函数(非必须,可能用到)+无参构造函数(无其他构造函数时非必须,因为自带无参构造函数;有其他构造函数时必须,因为被隐藏了,Spring要调用无参构造函数)
2、Spring管理容器中Bean之间的依赖关系,Spring使用其特有的"依赖注入"来管理Bean之间的依赖关系。
由上,第二个功能是以第一个功能为基础的,其实可以看成是一个功能,核心是依赖注入,就是本节要演示的IOC.
IOC全称为Inversion of Control,直译过来就是控制反转,即“不用打电话过来,我们会打给你”。
IOC两种实现: 依赖查找(DL)和依赖注入(DI)。IOC、DL、DI的关系是:
IOC、DI、DL三者辨析(比较重要)
控制反转IOC:控制反转并非是一种技术,而是一种思想,在操作方面是指在spring配置文件中创建;
依赖注入DI:依赖注入即为由spring容器为应用程序的某个对象提供资源;
依赖查找DL:用户自己去是使用 API 进行查找资源和组装对象。
控制反转有两种实现方式,分别是依赖查找和依赖注入,其中,依赖查找已经被抛弃,因为他需要用户自己去是使用 API 进行查找资源和组装对象。即有侵入性。所以,现在控制反转基本上就是依赖注入,反之,依赖注入是控制反转仅存的一种方式,所以,Spring容器中,我们基本上就把控制反转和依赖注入划等号了。
二、初识IOC(对比新建一个对象三种方式——新建方式、工厂方式、依赖注入)这里,我们用三种方式来新建对象,最后体会Spring容器的好处。
新建一个对象的三种方式区别:
新建方式 | 工厂方式 | Spring依赖注入 | |
自己是否新建对象 | 是 | 否 | 否 |
自己是否调用别人新建对象 | 否 | 是 | 否 |
小结 | 自己使用new新建对象 | 工厂使用new新建对象,自己调用getProduct()方法获取对象 | 自己既不new对象,也不调用方法获取对象,而是等待Spring容器将对象给它 |
且看代码1、代码2、代码3.
2.1 新建方式
代码1——新建方式:
package mypackage;
public class Student {
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private int id;
private String name;
}
package mypackage;
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.setName("小明");
System.out.println(student.getName());
}
}
输出1:
小明
小结1:基础的方式,没有任何框架的思维,主动使用new新建对象。
2.2 工厂方式
代码2——工厂方式:
package mypackage;
public class Student {
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private int id;
private String name;
}
package mypackage;
public class StudentFactory {
public static Student getStudent() {
return new Student();
}
public static void setStudentName(Student student, String name) {
student.setName(name);
}
public static String getStudentName(Student student) {
return student.getName();
}
}
package mypackage;
public class Test {
public static void main(String[] args) {
Student student =StudentFactory.getStudent();
StudentFactory.setStudentName(student, "小明");
System.out.println(StudentFactory.getStudentName(student));
}
}
输出2:
小明
小结2:工厂方式,调用工厂方法,让工厂方法来new,新建对象。
2.3 Spring依赖注入
代码3——Spring容器依赖注入:
使用Spring容器时,新建对象不用客户端自己动手,只要在applicationContext.xml中配置好,然后Spring容器会自动新建对象并设置好对象的属性值,客户端只要直接拿来用就好了。
给出Student.java TestSpring.java applicationContext.xml代码
package com.pojo;
public class Student {
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private int id;
private String name;
}
package com.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.pojo.Student;
public class TestSpring {
public static void main(String[] args) {
//访问applciationContext.xml
//ApplciatinoContext是Spring中的一个接口,它有两个实现类ClassPathXmlApplicationContext和FileSystemXmlApplicationContext
//Java中一般使用ClassPathXmlApplicationContext类创建Spring容器
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "applicationContext.xml" });
Student c = (Student) context.getBean("c");
System.out.println(c.getName());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean name="c" class="com.pojo.Student">
<property name="name" value="小明" />
</bean>
</beans>
输出3:
小明
小结3:使用Spring容器时,新建对象不用客户端自己动手,只要在applicationContext.xml中配置好,然后Spring容器会自动新建对象并设置好对象的属性值,客户端只要直接拿来用就好了。
2.4 Spring相关问题
Spring相关问题1:Bean类一定要有无参构造函数吗?
回答1:对,Bean类一定要有无参构造函数,否则报错,代码3原封不动,Student类中加上一个有参构造函数,屏蔽掉默认无参构造函数,运行代码报错,Spring容器不能正常工作。如:
运行代码报错,输出为:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'c' defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.pojo.Student]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.pojo.Student.<init>()
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:965)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:911)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:580)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:93)
at com.test.TestSpring.main(TestSpring.java:13)
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.pojo.Student]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.pojo.Student.<init>()
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:72)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:958)
... 13 more
Caused by: java.lang.NoSuchMethodException: com.pojo.Student.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getDeclaredConstructor(Class.java:2178)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:67)
... 14 more
即Spring容器不能正常工作,因为applicationContext.xml中<bean.../>驱动调用无参构造函数,如果没有无参构造函数,Spring不能正常工作。
解决方式:Student类中去掉带参构造函数,这样默认的无参构造函数就可以被调用了,或者在Student类中再加上一个无参构造函数。解决方法很简单这里不演示了。
Spring相关问题2:配置文件是否一定要命名为applicationContext.xml?
回答2:不是,但是建议命名为applicationContext.xml。大多数情况下,我们使用Spring框架时,配置文件总是命名为applicationContext.xml,给人的感觉该配置文件只能命名为applicationContext.xml,实则不然,这个配置文件开发者可以任意命名的,如下:
即使是这样,还是建议使用名称为applciationContext.xml,毕竟是约定俗成的。
三、IOC原理——关于Spring容器是如何完成依赖注入的整个过程的在Spring配置文件中配置Bean时,class属性的值必须是Bean实现类的全限定性类名。这里注意两点:
1、Bean实现类:必须是能实例化对象的类,不能是接口,不能是抽象类(除非有特殊配置);
2、全限定性类名:即包名+类名,保证唯一定位到这个类。
class属性指定的类必须同时满足以上两点,第一,是个实体类,第二,指出全限定性类名,否则Spring无法通过反射创建给类实例。
Spring是如何完成IOC的?本质,通过Java反射机制完成IOC的。
(1)反射的数据源从哪里来?浅析<property.../>标签
可以看到,applicationContect.xml配置文件中存在一个<property.../>标签,这个标签表示的是一个类的属性,它的父标签是<bean../>标签,表示的是一个唯一的类(鼠标移动到class属性的值上面,按住键盘上的ctrl同时点击鼠标左键就可以转到该类的定义(仅限于eclipse,idea不知道)),所以<property.../>就是该类下面的一个属性,其value提供反射的数据源。
(2)反射的执行?浅析<bean.../>、<property.../>标签
每一个<bean../>标签默认驱动Spring调用<bean../>对应类的无参构造函数新建实例对象,并将该实例作为Spring容器的Bean;
每一个<property../>标签默认驱动Spring对<property.../>对应的属性执行一次setter方法,其中<property.../>的name提供属性名,value提供实参。
这样一来,先由<bean.../>驱动Spring调用构造方法新建对象,再由<property.../>驱动Spring执行setter方法设值注入,从而完成Spring的IOC机制。
值得注意的是,对于<property.../>中的参数类型:
1、如果参数类型是基本类型或其包装类型,String等类型,<property.../>中使用value传入参数。
2、如果参数类型是其他Bean类型,<property.../>中使用ref传入参数。
关于ref的使用,且看下面
四、IOC进阶——ref的使用代码:
package com.pojo;
public class Student {
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private int id;
private String name;
private Teacher teacher;
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
package com.pojo;
public class Teacher {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name;
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean name="student" class="com.pojo.Student">
<property name="name" value="小明" />
<property name="teacher" ref="teacher" />
</bean>
<bean name="teacher" class="com.pojo.Teacher">
<property name="name" value="张老师"></property>
</bean>
</beans>
package com.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.pojo.Student;
public class TestSpring {
public static void main(String[] args) {
//访问applciationContext.xml
//ApplciatinoContext是Spring中的一个接口,它有两个实现类ClassPathXmlApplicationContext和FileSystemXmlApplicationContext
//Java中一般使用ClassPathXmlApplicationContext类创建Spring容器
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "applicationContext.xml" });
Student student = (Student) context.getBean("student");
System.out.println(student.getName());
System.out.println(student.getTeacher().getName());
}
}
输出:
小明
张老师
小结:假如说Teacher——Student是一对多,所以要在Student类中加入Teacher引用或TeacherId作为记录外键,当然这是持久化层和数据库的事了,这里为了为了测试ref的使用,在Student类中加入Teacher引用,在配置文件applicationContext.xml中配置好,再到TestSpring中打印。
五、Spring的另一种配置方式——注解配置(从xml配置到注解配置)任务:将第四部分业务代码不动,xml配置改为注解配置。
代码:
package com.pojo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("student")
public class Student {
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private int id;
private String name="小明";
@Autowired
private Teacher teacher; //@Autowired不再需要setter-getter
//这里之所以保留getTeacher()是因为TestSpring打印控制台时要用一下,其实Spring容器已经不再需要setter-getter
public Teacher getTeacher() {
return teacher;
}
}
package com.pojo;
import org.springframework.stereotype.Component;
@Component("teacher")
public class Teacher {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name ="张老师";
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.pojo"/>
</beans>
package com.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.pojo.Student;
public class TestSpring {
public static void main(String[] args) {
//访问applciationContext.xml
//ApplciatinoContext是Spring中的一个接口,它有两个实现类ClassPathXmlApplicationContext和FileSystemXmlApplicationContext
//Java中一般使用ClassPathXmlApplicationContext类创建Spring容器
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "applicationContext.xml" });
Student student = (Student) context.getBean("student");
System.out.println(student.getName());
System.out.println(student.getTeacher().getName());
}
}
输出:
小明
张老师
小结:我们来解释每一个注解的意思,
六、小结<context:annotationconfig/> 将隐式地向 Spring 容器注册
AutowiredAnnotationBeanPostProcessor
、CommonAnnotationBeanPostProcessor
、PersistenceAnnotationBeanPostProcessor
以及equiredAnnotationBeanPostProcessor
这 4 个 BeanPostProcessor。表示告诉Spring要用注解的方式进行配置。然后开始使用注解:@Autowired @Component 都是作用于pojo,Spring的姊妹注解
Spring 2.5 引入了@Autowired注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。
@Component 仅需要在类定义处,使用@Component注释就可以将一个类实例化为 Spring 容器中的 Bean,相当于配置文件中的<bean id="" class=""/>
配合使用:@Component完成将一个类定义成 Spring 容器中的一个Bean,@Autowired完成将自动注入,用在类成员变量上,该成员变量不再需要setter-getter.
从开始对比新建对象的三种方式,新建方式,工厂方式,Spring方式新建Student对象,使用Spring容器时,新建对象不用客户端自己动手,只要在applicationContext.xml中配置好,然后Spring容器会自动新建对象并设置好对象的属性值,客户端只要直接拿来用就好了;再到谈论Spring的IOC是实现原理,是使用反射机制实现的;再谈论IOC关于<property.../>中注入Bean;最后附上用注解的方式实现上述过程(使用注解提供了另一个方式)。
天天打码,天天进步!