spring框架的IOC容器和Bean配置

IOC

IOC全名是inversion of control ,就是反转控制,也叫控制反转,其实它也不是听得那么高达上,其实就是一种思想,
之前我们创建类后需要new对象,对象的创建的控制权是在我们程序员的手上,现在我们将创建对象的权力交给spring,我们不需要知道对象是如何创建的,只知道需要向spring要对象使用就行,这样提高了开发的效率
这种将创建对象的权力交给spring的思想就是反转控制

DI

DI全名是dependency injection ,也叫依赖注入
这里就是顾名思义,依赖就是一个对象的创建需要什么对象,比如上面的例子,如果一个person的创建必须要id和name两个属性,那么在创建person对象前就必须先new出id和name两个对象,然后在这两个对象的基础上创建person对象,这就是依赖
而注入就是spring根据对象的创建需要什么就注入什么,比如上面需要id和name,那么就会给你id和name
其实IOC就是一种反转控制的思想,而DI就是IOC这种思想的具体实现

package jane;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test
{
	public static void main(String[] args)
	{
		/*
		 * 初始化容器,ApplicationContext是一个接口,我们需要用它的实现类来创建容器
		 * ClassPathXmlApplicationContext里面的参数是配置文件的路径,所以conf里面的
		 * 配置文件名是可以改的,但是在以后的ssm框架里面是自动初始化容器,会自动找
		 * applicationContext.xml文件,所以建议还是写成applicationContext.xml
		 * 在配置文件里面有一个唯一标识的id,这里使用id来得到bean
		 */
		ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
		/*
		 * 这个方法是直接通过bean的id属性进行查找的,
		 * 因为bean的id是不能重复的,所以这里不会冲突
		 */
//		Person bean = (Person)ac.getBean("Person");
		
		/*
		 * 这个方法是通过类来得到bean的,如果使用这个方法,
		 * 那么确保配置文件里面这个class对应的bean只能有一个,
		 * 如果这个class有多个bean的时候,就会报错
		 * Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [jane.Person] is defined: expected single matching bean but found 2: Person1,Person2
			at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:312)
			at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:985)
			at jane.Test.main(Test.java:19)
		 */
//		Person bean = ac.getBean(Person.class);
		
		/*
		 * 这个重载的方法就是使用id和类确定bean的位置,这个一般不会有太多问题
		 * 所以一般我们使用这个
		 */
		Person bean = ac.getBean("Person1", Person.class);
		System.out.println(bean);
		
		/*
		 * spring能帮我们管理对象,但是spring到底是怎么创建对象的呢?
		 * 其实我们可以大胆地尝试,之前学过反射,反射创建对象就是先加载
		 * 这个类,使用class.forname()的方法加载,然后newInstance()创建对象
		 * 其实spring就是根据反射进行创建对象的,前面配置的bean的class属性
		 * 就是类的全限定名,就是用来加载类的
		 * 测试验证:
		 * 我们如果在person类中写出有参的构造方法,那么无参的构造方法就会被覆盖
		 * 那么如果spring是反射创建对象的应该会出现错误
		 * 如我们所愿出现了错误
		 * Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [jane.Person]: No default constructor found; nested exception is java.lang.NoSuchMethodException: jane.Person.<init>()
				at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:85)
				at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1069)
				... 13 more
			Caused by: java.lang.NoSuchMethodException: jane.Person.<init>()
				at java.base/java.lang.Class.getConstructor0(Class.java:3354)
				at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2558)
				at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:80)
				... 14 more
			所以spring就是使用反射创建对象的
		 */
	}
}

IOC容器在spring中的实现

spring中最重要的是IOC思想,IOC思想必须基于IOC容器来实现,IOC的最底层实际上就是一个对象工厂,我们来看一下里面的继承关系
在这里插入图片描述
最上面的接口就是BeanFactory,就是最基础的东西,而最下面的就是
ClassPathXmlApplicationContext和FileSystemXmlApplicationContext类,
其实这两个类都是一样效果的,
ClassPathXmlApplicationContext是从当前项目的文件中读取配置文件,写的是相对的路径
FileSystemXmlApplicationContext是从系统上读取配置文件,写的是绝对路径,因为有些
项目的配置文件独立地放在一个服务器里面,所以就可以使用这种方法,使用绝对路径

在通过IOC容器读取Bean的实例之前,需要先将IOC容器本身实例化。
Spring提供了IOC容器的两种实现方式

  • BeanFactory:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的。
  • ApplicationContext:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。

里面有个onfigurableApplicationContext是ApplicationContext的子接口,包含一些扩展方法:refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文的能力。

Bean的属性赋值

依赖注入的方式

通过bean的setXXX()方法赋值
	<bean id="Person1" class="jane.Person">
		<property name="id" value="1"></property>
		<property name="name" value="张三"></property>
	</bean>

比如这里的bean里面,<property name="id" value="1"></property>是如何给person对象赋值的,
在person类里面,所有的属性都是私有化的,我们访问不了,那么spring是如何赋值
通过追踪查看,原来是调用里面的get和set方法进行赋值

通过bean的构造器进行赋值
	<bean>
		<constructor-arg value="3"></constructor-arg>
		<constructor-arg value="小红" index="1" type="java.lang.String"></constructor-arg>
	</bean>

通过构造方法注入是有个小问题,比如类中的构造方法有多个,例如person类中有两个构造方法public person(Integer id,String name)和public person(Double id,String name)
那么通过这种方法就出现赋值的对象不是你想要的或者报错,那么就需要在constructor-arg
标签里面增加index索引和type类型属性,索引是说明是第几个参数,类型说明它属于什么类型,这种方法不常用,通过set方法注入常用

p命名空间
这也不是什么高达上的东西,就是简化给属性赋值的快捷方式 如果你是使用STS的话,很方便直接在namesspace里面将p勾上就行 STS会自动在命名空间里面增加p命名空间的约束 `xmlns:p="http://www.springframework.org/schema/p"`

在这里插入图片描述
设置好后这样bean的设置就简单了
直接设置成这样就行<bean id="4" class="jane.Person" p:id="4" p:name="小明"></bean>

属性的值
字面量
字面量就是spring管理的对象的属性是基本数据类型或基本数据类型的封装类还有string类,可以通过value属性或者value子节点的方式给属性赋值, 如果字面量中包含特殊的字符,可以使用<![CDATA[]]>把字面量包裹起来
<property name="name" value="李四"></property>
这个可以改成
<property name="name">
	<value>李四</value>
</property>
引用外部声明的bean
字面量我们可以直接赋值,但是如果这个属性不是字面量,而是一个我们自己定义的类或者其他,那么我们就是不可以通过value进行赋值了,那么就得需要使用ref进行引用赋值了,ref的值就是引用的bean的id
	<bean id="Person2" class="jane.Person">
		<property name="id" value="2"></property>
		<property name="name" value="李四"></property>
		<property name= "eat" ref ="eat"/>
	</bean>
//其中person里面有一个eat类的属性
级联属性赋值
级联就是将属性分成一层一层,就好比如淘宝的收货地址,一个省下面对应的是这个省的市,绝不会访问到其他省的市,那么属性赋值的时候也是可以使用这种级联,比如
	<bean id="Person2" class="jane.Person">
		<property name="id" value="2"></property>
		<property name="name" value="李四"></property>
		<property name= "eat" ref ="eat"/>
		<property name= "eat.run" value="用筷子"/>
	</bean>
//这里是在eat类里面有个run的属性,将这个属性赋值为"用筷子"
内部bean
当bean实例仅仅给一个特定的属性使用的时候,可以将它声明成为内部bean,声明直接包含在< property>或< constructor-arg>元素里面,因为只给这个bean使用,不需要设置id或name,内部bean也不能使用在其他的任何地方,例如
	<bean id="Person2" class="jane.Person">
		<property name="id" value="2"></property>
		<property name="name" value="李四"></property>
		<property name= "eat">
			<bean class="jane.run">
				<property name="run" value="用筷子"/>
			</bean>
		</property>
	</bean>
//这里是在eat类里面有个run的属性,将这个属性赋值为"用筷子"
集合属性
现在就是来说我们经常用的数组,List,Map集合的实现
List集合
直接使用< list>标签就可以给list属性赋值,list标签里面可以包含字面量< value>实现,引用数据类型< ref > 实现或者内部bean实现,例如
	<bean id="t1" class="jane.Teacher">
		<property name="tid" value="1001"></property>
		<property name="tname" value="jane1"></property>
		<property name="clas">
			<list>
				<value>A</value>
				<value>B</value>
				<value>C</value>
			</list>
		</property>
	</bean>
	</bean>
	
		<bean id="t2" class="jane.Teacher">
		<property name="tid" value="1002"></property>
		<property name="tname" value="jane2"></property>
		<property name="students">
			<list>
				<ref bean="s1"/>
			</list>
		</property>
	</bean>

而数组和list一样,用< array>进行注入,其实数组也可以使用list进行赋值,因为List的底层就是数组,Set使用< set>标签进行注入,定义方法和list一样
这里说下要交给spring管理的类的属性的命名规范,比如sName,不能这样命名属性,因为这样生成的get和set方法不符合set和get方法的规范,生成的get和set方法的属性的名字会弄成大写的,但是sName这样命名就不会变成大写,就直接说没有get和set方法的

Map
通过< map>标签进行注入,< map>标签里面可以使用多个< entry>作为子标签,每一个< entry>就包含一个键和一个值,例如
	<bean id="t3" class="jane.Teacher">
		<property name="tid" value="1003"></property>
		<property name="tname" value="jane2"></property>
		<property name="bossMap">
			<map>
				<entry>
					<key>
						<value>1001</value>
					</key>
					<value>jane1</value>
				</entry>
			</map>
		</property>
	</bean>
util命名空间对集合属性的注入
也是直接在Namespaces里面直接勾选上util就行 util命名空间里面有对集合的简单创建,比如一个< util:list>就是一个list集合
	<bean id="t4" class="jane.Teacher">
		<property name="tid" value="10042"></property>
		<property name="tname" value="jane4"></property>
		<property name="students" ref="list"></property>
	</bean>
	
	<util:list id="list">
		<ref bean="s1"/>
	</util:list>

FactoryBean

Spring中有两种类型的bean,一种是普通bean,另一种是工厂bean,就是FactoryBean,不多说,先上代码

//Car类
package factorybean;
public class Car
{
	private String brand;
	private Double price;
	//下面的get和set方法就在这里写了
}

//Myfactory类
package factorybean;

import org.springframework.beans.factory.FactoryBean;

public class Myfactory implements FactoryBean<Car>
{
	@Override
	public Car getObject() throws Exception
	{
		Car car=new Car();
		car.setBrand("法拉利");
		car.setPrice(800000.0);
		return car;
	}
	@Override
	public Class<?> getObjectType()
	{
		return Car.class;
	}
	@Override
	public boolean isSingleton()
	{
		return false;
	}
}
//对应的factory-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 id="Myfactory" class="factorybean.Myfactory"></bean>
</beans>
//测试类
package factorybean;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test
{
	public static void main(String[] args)
	{
		ApplicationContext ac=new ClassPathXmlApplicationContext("factory-bean.xml");
		Object bean = ac.getBean("Myfactory");
		System.out.println(bean);
	}
}
//结果
Car [brand=法拉利, price=800000.0]

Factory Bean就是对象工厂,就是用来产生对象的,我们在bean里面配置工厂的bean,最后运行返回的就是这个工厂对应的的生产的类的对象,就是通过getObject方法得到的

发布了141 篇原创文章 · 获赞 37 · 访问量 6316

猜你喜欢

转载自blog.csdn.net/qq_43416157/article/details/104978997