spring是使用的di实现了ioc的功能, spring底层建立对象,使用的是反射机制。java
spring是一个容器,管理对象,给属性赋值, 底层是反射建立对象spring
bean 实例在调用无参构造器建立对象后,就要对 bean 对象的属性进行初始化。app
初始化是由容器自动完成的,称为注入
根据注入方式的不一样,经常使用的有两类:set 注入、构造注入ide
set 注入也叫设值注入,是指经过 setter 方法传入被调用者的实例,这种注入方式简单、直观,于是在 Spring 的依赖注入中大量使用测试
项目的具体建立看上一篇就能够了,这里直接写重点this
首先声明一个Studnet的类spa
package com.md.b1; /** * @author MD * @create 2020-08-07 19:55 */ public class Student { private String name; private int age; public Student() { System.out.println("我是Student类的无参构造方法"); } public void setName(String name) { System.out.println("setName:"+name); this.name = name; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
而后写对应的配置文件code
<?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"> <!-- 声明Student的对象 简单类型:spring中java的基本数据类型和String都是简单数据类型 di:给属性赋值也就是注入 1. set注入 :spring来调用类的set方法,在set方法中完成属性的赋值 1. 简单类型的注入 <bean id="xx" class="yyy"> <property name="属性名字" value="此属性的值"/> 一个property只能给一个属性赋值 <property....> </bean> 必需要有属性对应的set方法,没有的话就报错 可是set方法里面的内容是你能控制,除了赋值,你还能够在set里多写几条java语句 --> <bean id="student" class="com.md.b1.Student"> <property name="name" value="张三" /> <!-- setName("张三")--> <property name="age" value="20"/> </bean> </beans>
结构图xml
测试类对象
注意:此时因为这个文件不是直接在resources下面,而是在下面的b1包的下面,因此指定的路径得加上
@Test public void test01(){ String config = "b1/applicationContext.xml"; ApplicationContext ac = new ClassPathXmlApplicationContext(config); // 从容器中获取Student的对象 Student student = (Student) ac.getBean("student"); System.out.println(student); // 我是Student类的无参构造方法 // setName:张三 // Student{name='张三', age=20} }
注意一:没有属性但有set方法
还能够在Student的类中加入这个方法,
public void setEmail(String eamil) { System.out.println("setEmail:"+eamil); }
对应的配置文件
<bean id="student" class="com.md.b1.Student"> <property name="name" value="张三" /> <!-- setName("张三")--> <property name="age" value="20"/> <property name="email" value="zs@qq.com"/> </bean>
此时在Student类中没有email属性,可是有setEmail方法,能顺利执行不?
能,只要有对应的set方法都是正确的,不管属性名是否存在
测试:
@Test public void test01(){ // 注意:此时因为这个文件不是直接在resources下面,而是在下面的b1包的下面,因此指定的路径得加上 String config = "b1/applicationContext.xml"; ApplicationContext ac = new ClassPathXmlApplicationContext(config); // 从容器中获取Student的对象 Student student = (Student) ac.getBean("student"); System.out.println(student); // 我是Student类的无参构造方法 // setName:张三 // setEmail:zs@qq.com // Student{name='张三', age=20} }
注意二:对于非自定义的类
在配置文件中
<!-- 非自定义类设置属性 只有这个类中有setXXX(),就能够 --> <bean id="mydate" class="java.util.Date"> <!--setTime(993462034956)--> <property name="time" value="9348362034" /> </bean>
测试:
@Test public void test02(){ String config = "b1/applicationContext.xml"; ApplicationContext ac = new ClassPathXmlApplicationContext(config); Date mydate = (Date) ac.getBean("mydate"); System.out.println(mydate); }
当指定 bean 的某属性值为另外一 bean 的实例时,经过 ref 指定它们间的引用关系
ref的值必须为某 bean 的 id 值
以下:
先建立一个School类
package com.md.b2; /** * @author MD * @create 2020-08-07 20:40 */ public class School { private String name; private String address; public void setName(String name) { this.name = name; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "School{" + "name='" + name + '\'' + ", address='" + address + '\'' + '}'; } }
再建立一个Student类,在里面引用School的类的对象
package com.md.b2; /** * @author MD * @create 2020-08-07 19:55 */ public class Student { private String name; private int age; // 声明一个引用数据类型 private School school; public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public void setSchool(School school) { System.out.println("setSchool:"+school); this.school = school; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", school=" + school + '}'; } }
写对应的配置文件
<?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"> <!-- 2. 引用数据类型的注入:spring来调用类的set方法, <property name="属性名称" ref="bean的id也就是对象的名称"/> --> <!-- 声明school对象 --> <bean id="school" class="com.md.b2.School"> <property name="name" value="清华"/> <property name="address" value="北京"/> </bean> <bean id="student" class="com.md.b2.Student"> <property name="name" value="张三"/> <property name="age" value="40"/> <!-- 引用数据类型,调用的是setSchool(school),就是上面的 --> <property name="school" ref="school"/> </bean> </beans>
测试:
@Test public void test02(){ // 注意:此时因为这个文件不是直接在resources下面,而是在下面的b2包的下面,因此指定的路径得加上 String config = "b2/applicationContext.xml"; ApplicationContext ac = new ClassPathXmlApplicationContext(config); Student student = (Student) ac.getBean("student"); System.out.println(student); // setSchool:School{name='清华', address='北京'} // Student{name='张三', age=40, school=School{name='清华', address='北京'}} }
构造注入是指,spring在调用类的有参构造方法,在建立对象的同时,在构造方法中进行属性的赋值
语法:使用<constructor-arg />标签,具体看下面的使用
首先还在Student类中写有参构造器
public Student(String name, int age, School school) { System.out.println("我是Student类的有参构造方法"); this.name = name; this.age = age; this.school = school; }
而后在配置文件中
<?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"> <!-- 构造注入 spring在调用类的有参构造方法,在建立对象的同时,在构造方法中进行属性的赋值 构造注入使用 <constructor-arg> 标签 <constructor-arg> 标签:一个<constructor-arg>表示构造方法一个参数。 <constructor-arg> 标签属性: name:表示构造方法的形参名 index:表示构造方法的参数的位置,参数从左往右位置是 0 , 1 ,2的顺序 value:构造方法的形参类型是简单类型的,使用value ref:构造方法的形参类型是引用类型的,使用ref --> <!-- 声明school对象 --> <bean id="school" class="com.md.b3.School"> <property name="name" value="清华"/> <property name="address" value="北京"/> </bean> <!--推荐用name--> <!--调用类的有参构造方法--> <!--<bean id="student" class="com.md.b3.Student">--> <!--<constructor-arg name="name" value="张三"/>--> <!--<constructor-arg name="age" value="30"/>--> <!--<constructor-arg name="school" ref="school"/>--> <!--</bean>--> <!-- 或者这样也是能够的,根据参数的位置--> <!--<bean id="student" class="com.md.b3.Student">--> <!--<constructor-arg index="0" value="张三"/>--> <!--<constructor-arg index="1" value="30"/>--> <!--<constructor-arg index="2" ref="school"/>--> <!--</bean>--> <!----> <!--或者直接省略--> <bean id="student" class="com.md.b3.Student"> <constructor-arg value="张三"/> <constructor-arg value="30"/> <constructor-arg ref="school"/> </bean> </beans>
测试:
@Test public void test01(){ // 注意:此时因为这个文件不是直接在resources下面,而是在下面的b3包的下面,因此指定的路径得加上 String config = "b3/applicationContext.xml"; ApplicationContext ac = new ClassPathXmlApplicationContext(config); Student student = (Student) ac.getBean("student"); System.out.println(student); // 我是Student类的有参构造方法 // Student{name='张三', age=30, school=School{name='清华', address='北京'}} }
对于引用类型属性的注入,也可不在配置文件中显示的注入。能够经过为<bean/>标签设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属性)
根据自动注入判断标准的不一样,能够分为两种:
当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可以使用byName 方式,让容器自动将被调用者 bean 注入给调用者 bean。容器是经过调用者的 bean类的属性名与配置文件的被调用者 bean 的 id 进行比较而实现自动注入的
语法:
byName(按名称注入) : java类中引用类型的属性名和spring容器中(配置文件)<bean>的id名称同样, 且数据类型是一致的,这样的容器中的bean,spring可以赋值给引用类型。 语法: <bean id="xx" class="yyy" autowire="byName"> 简单类型属性赋值 </bean>
例子:
public class School { private String name; private String address; // 省略set } //--------------------------------- public class Student { private String name; private int age; // 声明一个引用数据类型 private School school; // 省略set }
在配置文件中
<bean id="school" class="com.md.b4.School"> <property name="name" value="北大"/> <property name="address" value="北京"/> </bean> <!--/////////////////////////////--> <bean id="student" class="com.md.b4.Student" autowire="byName"> <property name="name" value="张三"/> <property name="age" value="20"/> <!--自动赋值引用数据类型--> </bean>
如上:
java类中引用类型的属性名school 和 spring容器中(配置文件)<bean>的id名称同样,且数据类型一致,这样就能自动注入
使用 byType 方式自动注入,要求:配置文件中被调用者 bean 的 class 属性指定的类,要与代码中调用者 bean 类的某引用类型属性类型同源。即要么相同,要么有 is-a 关系(子类,或是实现类)
但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配哪个了
语法:
byType(按类型注入) : java类中引用类型的数据类型和spring容器中(配置文件)<bean>的class属性 是同源关系的,这样的bean可以赋值给引用类型 同源就是一类的意思: 1.java类中引用类型的数据类型和bean的class的值是同样的。 2.java类中引用类型的数据类型和bean的class的值父子类关系的。 3.java类中引用类型的数据类型和bean的class的值接口和实现类关系的 语法: <bean id="xx" class="yyy" autowire="byType"> 简单类型属性赋值 </bean> 注意:在byType中, 在xml配置文件中声明bean只能有一个符合条件的, 多余一个是错误的
仍是上面的例子:
在实际应用里,随着应用规模的增长,系统中 Bean 数量也大量增长,致使配置文件变得很是庞大、臃肿。为了不这种状况的产生,提升配置文件的可读性与可维护性,能够将Spring 配置文件分解成多个配置文件,其中一个为主配置文件
total.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"> <!-- 包含关系的配置文件: total表示主配置文件 : 包含其余的配置文件的,主配置文件通常是不定义对象的。 语法:<import resource="其余配置文件的路径" /> 关键字:"classpath:" 表示类路径(class文件所在的目录), 在spring的配置文件中要指定其余文件的位置, 须要使用classpath,告诉spring到哪去加载读取文件。 --> <!--加载的是文件列表--> <!-- <import resource="classpath:b4/spring-school.xml" /> <import resource="classpath:b4/spring-student.xml" /> --> <!--/////////////////////////////--> <!-- 在包含关系的配置文件中,能够通配符(*:表示任意字符) 注意: 主的配置文件名称不能包含在通配符的范围内(不能叫作spring-total.xml) --> <import resource="classpath:b4/spring-*.xml" /> </beans>
spring调用类的set方法实现属性赋值
<property name="属性名" value="属性的值"/>
<property name="属性名" ref="bean的id"/>
spring调用有参数的构造方法
<constructor-arg>的name属性,name表示构造方法的形参名
<constructor-arg>的index属性,表示构造方法形参的位置,从0开始
由spring根据某些规则,给引用类型完成赋值,有byName、byType
java类中引用类型的属性名和spring容器中bean的id同样,数据类型同样
java类中引用类型的数据类型和spring容器中bean的class是同源关系