目录
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/beanshttp://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容器创建对象@Testpublic 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容器时,会创建配置文件中的所有对象*/@Testpublic void test03() {//1、指定spring配置文件路径String config = "beans.xml";//2、创建spring容器ApplicationContext ac = new ClassPathXmlApplicationContext(config);/*** 测试输出结果:* SomeServiceImpl类的无参构造执行了...* SomeServiceImpl类的无参构造执行了...* 验证了spring调用类的无参构造完成对象的创建*/}//获取spring容器中java对象的信息@Testpublic 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;}@Overridepublic 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个基本类型+String3、当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注入给对象属性赋值@Testpublic 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方法@Testpublic void test02() {String config = "ba01/applicationContext.xml";/** 此时会调用set方法进行赋值* setName...* setAge...*/ApplicationContext ac = new ClassPathXmlApplicationContext(config);}//验证没有属性的setXxx方法是否报错@Testpublic 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;}@Overridepublic 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方式)@Autowiredprivate 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;@Overridepublic String toString() {return "School{" +"schoolAddress='" + schoolAddress + '\'' +", homeAddress='" + homeAddress + '\'' +'}';}
}