Spring——初识Spring,用通俗易懂的语言解释IOC

目录

一、从Spring到IOC

二、初识IOC(对比新建一个对象三种方式——新建方式、工厂方式、依赖注入)

2.1 新建方式

2.2 工厂方式

2.3 Spring依赖注入

2.4 Spring相关问题

三、IOC原理——关于Spring容器是如何完成依赖注入的整个过程的

四、IOC进阶——ref的使用

五、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的关系是:

控制反转有两种实现方式,分别是依赖查找和依赖注入,其中,依赖查找已经被抛弃,因为他需要用户自己去是使用 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 容器注册 AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessorPersistenceAnnotationBeanPostProcessor 以及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;最后附上用注解的方式实现上述过程(使用注解提供了另一个方式)。

天天打码,天天进步!

发布了177 篇原创文章 · 获赞 31 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/qq_36963950/article/details/103323960