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方法得到的