目录
1、什么是IOC
2、java实现创建对象的方式有哪些
3、基于配置文件的di实现
3.1、什么是di
3.2、入门案例
3.3、环境搭建
接口和实现类
ioc配置文件
测试程序
3.4、案例总结
3.5、简单类型属性的赋值(set注入)
set注入要求
JavaBean
spring配置文件
3.6、非简单类型属性的赋值(set注入)
3.7、构造注入
byName形式
byType形式
3.8、基于注解的di实现
3.8.1、简单类型的注解di实现
3.8.2、引用类型的注解di实现
学生类
School类
1、什么是IOC
IoC (Inversion of Control) : 控制反转, 是一个理论,概念,思想。把对象的创建,赋值,管理工作都交给代码之外的容器实现, 也就是对象的创建是有其它外部资源完成,这样做实现了与解耦合。
正转:对象的创建、赋值等操作交由程序员手动完成,即使用类似new Xxx(Xxx Xxx)、Xxx.setXxx()语句完成对象的创建与赋值,缺点是一旦程序功能发生改变,涉及到的类就要修改代理,耦合度高,不便于维护和管理。
反转:对象的创建、赋值等操作交由代码之外的容器实现,有容器代替程序员完成对象的创建、赋值;且当程序功能发生变化时,只需要修改容器的配置文件即可。
2、java实现创建对象的方式有哪些
1、构造方法:new student()
2、反射
3、序列化
4、动态代理
5、容器:tomcat容器、ioc容器
其实在以前我们已经接触过了容器创建对象的场景,还记得tomcat服务器吗,在tomcat启动时会实例化servletContext上下文对象;在发出请求时,相应的servlet对象也不是由开发人员进行实例化的,而是在tomcat内部由tomcat容器实例化的,回忆一下在学习javaweb的时候,我们有写过类似new XxxServlet()这样的代码吗,现在想必大家对容器有一个大概的概念了吧。
3、基于配置文件的di实现
3.1、什么是di
DI(Dependency Injection) :依赖注入, 只需要在程序中提供要使用的对象名称就可以, 至于对象如何在容器中创建, 赋值,查找都由容器内部实现。
DI是ioc技术的实现方式(即容器如何创建对象这一问题的实现方式)
3.2、入门案例
使用ioc容器创建对象,调用对象的方法
3.3、环境搭建
创建maven项目,目前都是javase项目,推荐使用骨架,选择quickstart
加入maven依赖:分别是spring依赖、junit依赖
创建类(接口和它的实现类)
创建spring需要使用的配置文件
测试
接口和实现类
//接口
public interface SomeService {
void doSome();
}
//实现类
public class SomeServiceImpl implements SomeService {
//无参构造
public SomeServiceImpl() {
System.out.println("SomeServiceImpl类的无参构造执行了...");
}
@Override
public void doSome() {
System.out.println("执行了SomeServiceImpl的doSome()方法");
}
}
ioc配置文件
<?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(告诉spring要创建某个类的对象)
1、id:自定义名称,唯一值,spring通过该id的属性值找到对象
2、class:要创建类的全限定类名
3、下述的声明语句在spring底层类似与执行了以下代码:
SomeService service = new SomeServiceImpl();
4、对象的保存:
spring将对象保存到内部的map中,map.put(id值,对象)
map.put("someService",new SomeServiceImpl())
5、一个bean标签声明一个java对象
6、spring容器根据bean标签创建对象,尽管存在class属性相同的bean标签,只要是id值不同,
spring容器就会创建该class的对象
-->
<bean id="someService" class="com.mms.service.impl.SomeServiceImpl"/>
<bean id="someService2" class="com.mms.service.impl.SomeServiceImpl"/>
<!--
spring容器也可以创建非自定义类的对象,例如java.lang.String类的对象,只要指定了
class属性,spring容器就可以创建该类的对象
-->
<bean id="myString" class="java.lang.String"/>
</beans>
测试程序
//使用spring容器创建对象
@Test
public void test02() {
//1、指定spring配置文件的名称
String config = "beans.xml";
//2、创建表示spring容器的对象 ApplicationContext
//ClassPathXmlApplicationContext:表示从类路径中加载spring配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//3、从容器中获取对象
SomeService service = (SomeService)ac.getBean("someService");
//4、调用方法
service.doSome();
}
/**
* 测试spring容器创建对象的时机
* 在创建spring容器时,会创建配置文件中的所有对象
*/
@Test
public void test03() {
//1、指定spring配置文件路径
String config = "beans.xml";
//2、创建spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
/**
* 测试输出结果:
* SomeServiceImpl类的无参构造执行了...
* SomeServiceImpl类的无参构造执行了...
* 验证了spring调用类的无参构造完成对象的创建
*/
}
//获取spring容器中java对象的信息
@Test
public void test04() {
String config = "beans.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//获取spring容器中对象的个数
int beansCount = ac.getBeanDefinitionCount();
System.out.println("spring容器中的对象个数="+beansCount);
//获取spring容器中对象的名称(即bean标签的id值)
String[] beansNames = ac.getBeanDefinitionNames();
for (String beanName : beansNames) {
System.out.println(beanName);
}
}
3.4、案例总结
spring配置文件中一个bean标签就代表一个对象,该对象有bean标签的id值唯一标识,从spring拿对象是使用getBean(“bean标签的id值”)
spring默认是使用类的无参构造来创建对象的
3.5、简单类型属性的赋值(set注入)
在入门案例的总结我们说过了spring容器默认是使用无参构造构造来实例化对象的,那么对象的属性必定为初始值,例如int类型为0,boolean类型为false等,那么当我们想使用相关属性进行操作时必然要手动使用set方法给属性赋值,那么有没有办法让容器帮我们完成对象属性的赋值呢?让我们直接就能够从容器中拿到有属性值的对象?答案是肯定的,下面就通过代码演示简单类型的属性赋值。
set注入要求
JavaBean必须要有set方法,因为ioc容器是使用javabean的set方法进行属性赋值的
spring容器调用的是setXxx()方法,而不管对象是否具有Xxx属性(即对象没有的属性只要有set方法也可以实现注入),Xxx不区分大小写
看看代码:
JavaBean
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) {
this.school = school;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", school=" + school +
'}';
}
}
spring配置文件
<!--声明Student对象-->
<bean id="student" class="com.mms.component.Student">
<!--
1、简单类型使用property和value标签给对象属性赋值
2、简单类型:8个基本类型+String
3、当spring容器加载到这一行时会在创建完对象的同时使用对象的set方法给属性赋值,底层
调用的是对象的set方法
4、spring容器调用的是setXxx()方法,而不管对象是否具有Xxx属性,Xxx不区分大小写
-->
<property name="name" value="张三"/>
<property name="age" value="23"/>
<!--测试对象没有属性的set方法-->
<property name="graName" value="s1"/>
</bean>
测试类
//使用set注入给对象属性赋值
@Test
public void test01() {
String config = "ba01/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//执行完14行此时Student对象的属性已被赋值,获取对象进行验证
Student stu = (Student) ac.getBean("student");
System.out.println(stu); //Student{name='张三', age=23}
}
//验证set注入调用的是对象的set方法
@Test
public void test02() {
String config = "ba01/applicationContext.xml";
/*
* 此时会调用set方法进行赋值
* setName...
* setAge...
*/
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
}
//验证没有属性的setXxx方法是否报错
@Test
public void test03() {
String config = "ba01/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//获取对象
Student stu = (Student) ac.getBean("student");
}
3.6、非简单类型属性的赋值(set注入)
上文中的set注入使用property标签的name和value属性给对象属性赋值,但是value知识给简单类型属性赋值,对于非简单类型我们是使用property标签的name和ref属性给对象属性赋值。我们现在给Student类增加一个属性address,该属性是一个引用类型,那当ioc容器创建Student对象时如何给address属性赋值呢?
Student类:别的地方与上文的student类一致,这里只给出address属性和其set方法
//引用类型属性
private Address address;
public void setAddress(Address address) {
System.out.println("引用类型address的set方法执行了...");
this.address = address;
}
Address类
public class Address {
private String homeAddress;
private String schoolAddress;
public void setHomeAddress(String homeAddress) {
this.homeAddress = homeAddress;
}
public void setSchoolAddress(String schoolAddress) {
this.schoolAddress = schoolAddress;
}
@Override
public String toString() {
return "Address{" +
"homeAddress='" + homeAddress + '\'' +
", schoolAddress='" + schoolAddress + '\'' +
'}';
}
}
applicationContext.xml配置文件
<!--声明Student对象-->
<bean id="student" class="com.mms.component.Student">
<property name="name" value="张三"/>
<property name="age" value="23"/>
<!--测试对象没有属性的set方法-->
<property name="graName" value="s1"/>
<!--
引用类型属性的set注入
property标签属性
name:属性名
ref:引用对象的id值
-->
<property name="address" ref="address"/>
</bean>
<!--Student对象的引用属性Address-->
<bean id="address" class="com.mms.component.Address">
<!--set注入-->
<property name="homeAddress" value="新疆"/>
<property name="schoolAddress" value="西安"/>
</bean>
上文执行流程分析:当ioc容器创建id为student的对象时,会进行set注入,当执行到最后一个propert标签时发现使用了ref属性,则ioc容器知道了name为address的属性是非简单类型,它就会暂时跳过address属性的赋值以及Student对象的创建,转而去配置文件的下文去找bean标签id值等于ref属性值的对象,现将该对象创建,再将该对象赋值给之前的address属性并将Student对象创建。
3.7、构造注入
顾名思义,构造注入是使用javabean的构造方法进行属性的赋值的。与set注入一样,构造注入要求javabean必须提供构造方法,且必须是有参构造(如果是无参构造还怎么给属性赋值,对吧),构造注入使用较少,了解就可以了,我们一般使用set注入。看看代码吧,将Student类的set方法注释,加入构造方法,别的地方不用改变,只需要改变spring配置文件即可(这里就可以看出ioc容器与程序的解耦合的好处了)。
<!--
构造注入
1、使用constructor-arg标签完成构造注入
2、构造注入方式一:根据形参名字
3、构造注入方式二:根据形参顺序,默认下标从0开始递增
-->
<!--根据形参名构造注入,形参的出现顺序不是必须的-->
<bean id="student" class="com.mms.value.Student">
<constructor-arg name="name" value="李四"/>
<constructor-arg name="age" value="24"/>
<constructor-arg name="address" ref="address"/>
</bean>
<bean id="address" class="com.mms.value.Address">
<constructor-arg name="homeAddress" value="新疆"/>
<constructor-arg name="schoolAddress" value="西安"/>
</bean>
<!--构造注入,使用下标,出现的顺序没要求,因为已经通过下标绑定起来了-->
<bean id="diByContructor" class="com.mms.value.Student">
<constructor-arg index="0" value="赵六"/>
<constructor-arg index="1" value="26"/>
<constructor-arg index="2" ref="address"/>
</bean>
3.8 非简单类型的自动注入
对于非简单类型,我们在上面是使用ref属性指向一个非简单类型的对象来完成赋值的,那么当ioc容器每次给一个对象的非简单类型属性赋值时,就要在bean标签内部写一行ref这样的代码,这样会造成重复代码的大量堆积,可以使用引用类型的自动注入。
有两种方式的引用类型自动注入
byName形式的引用类型自动注入:
通过java对象引用类型的属性名与spring容器中bean标签对象的id值一样且数据类型是一致的,这样能够实现引用类型的自动注入
byType形式的引用类型自动注入
通过java对象引用类型属性的数据类型和spring容器中 bean标签的class属性值是同源关系;
常见的同源关系:
1)java引用类型属性数据类型和bean标签的class属性值数据类型一样
2)java引用类型属性数据类型和bean标签的class属性值数据类型是父子关系
3)java引用类型属性数据类型和bean标签的class属性值数据类型是接口和实现类关系
注意:在一个配置文件中,符合条件的同源关系只能有一个
下面通过配置文件来详细说明两种形式的实现,在这里还是以Student类的address属性为例来说明。
byName形式
<bean id="student" class="com.mms.ba03.Student" autowire="byName">
<!--简单类型赋值-->
<property name="name" value="张三"/>
<property name="age" value="23"/>
</bean>
<!--引用类型-->
<bean id="school" class="com.mms.ba03.School">
<property name="schoolName" value="石河子大学"/>
<property name="schoolAddress" value="石河子市"/>
</bean>
匹配详解: 当ioc容器在创建Student对象时,发现使用了autowire属性且属性值为byName,ioc容器就会去Student类中去拿引用类型的属性名与和spring配置文件中的bean标签的id值进行比对,若发现有一致的且数据类型一致,则将该对象赋值给引用类型属性。
byType形式
<!--使用byType实现引用类型自动注入-->
<bean id="student2" class="com.mms.ba03.Student" autowire="byType">
<!--简单类型赋值-->
<property name="name" value="李四"/>
<property name="age" value="24"/>
</bean>
<!--引用类型
<bean id="school2" class="com.mms.ba03.School">
<property name="schoolName" value="西南大学"/>
<property name="schoolAddress" value="重庆市"/>
</bean>-->
<!--声明School的子类-->
<bean id="primarySchool" class="com.mms.ba03.PrimarySchool">
<property name="schoolName" value="西北大学"/>
<property name="schoolAddress" value="西安"/>
</bean>
3.8、基于注解的di实现
除了使用配置文件实现ioc创建对象的功能外,使用spring提供的注解也可以实现di。下面来介绍注解方式的di实现,下面是spring提供的di实现的常用注解。
@Component:该注解的功能是使用spring容器创建对象
1)、在要创建对象的类的声明上方加入该注解,该注解有一个属性value,value为spring创建的该类对象的id值
2)、开发中使用将value省略,直接使用双引号将值键入即可
3)、该注解使用类的无参构造创建对象
@Repository 创建dao类对象,访问数据库的对象
@Service 创建service类对象,业务层对象
@Controller 创建控制器对象,用于分发用户的请求和显示处理结果
下面通过代码来看看@Component注解是怎么实现di的。
@Component(value = "student")
public class Student {
...
}
该语句就等价为在spring配置文件中进行了以下声明
<bean id = "student" class = "com.mms.component.Student"/>
但是怎么让配置文件知道哪些类是使用注解进行创建对象的呢?需要在配置文件中声明组件扫描器
<context:component-scan base-package="com.mms.component"/>
当spring读取配置文件时,读取到组件扫描器声明语句时,就会去base-package指定的包和其子包下去递归的寻找有注解修饰的类,并根据注解的功能去执行相应的动作
3.8.1、简单类型的注解di实现
简单类型的注入使用@Value注解实现,哪些简单类型要设置属性值,直接在简单类型属性声明语句的上面加入注解@Value即可,并在@Value的括号内键入属性值,注意不论简单类型属性的数据类型,均由双引号将属性值括起来。例如之前的Student类使用注解注入如下。
@Component("student")
public class Student {
@Value("张三")
private String name;
@Value("23")
private int age;
}
注意别忘了该类要加注解@Component注解,因为要创建该类对象。
3.8.2、引用类型的注解di实现
引用类型的注入使用@Autowired注解完成。
@Autowired
@Autowired是spring提供的属性赋值,用于给引用类型赋值,有byName和byType两种方式,默认使用byType方式自动注入
若是要强制至于byName方式,要在@Autowired注解下面加入 @Qualifier(value = “bean的id”)注解,若程序在给引用类型注入时在xml文件中找不到 该id的bean标签或者手找不到该id的@Component注解,则报错;若不想让程序在赋值失败时报错,可以在@Autowired注解的required属性值置为false
还是拿Student类的school属性的赋值来举例。
学生类
@Component("student")
public class Student {
/*引用类型注入(byType方式)
@Autowired
private School school;*/
//引用类型赋值(byName方式)
@Autowired(required = false)
@Qualifier(value = "mySchool")
private School school;
}
School类
@Component("mySchool")
public class School {
//注入值
@Value("西南大学")
private String schoolAddress;
@Value("新疆")
private String homeAddress;
@Override
public String toString() {
return "School{" +
"schoolAddress='" + schoolAddress + '\'' +
", homeAddress='" + homeAddress + '\'' +
'}';
}
}