控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理。使用IOC目的:降低耦合度
底层原理:
xml解析、工厂模式、反射
我们先来看看关联两个类最直接的实现方式是怎样的:
public class UserService {
public void execute(){
UserDao userDao = new UserDao();
userDao.add();
}
}
class UserDao{
public void add(){
//这里是方法体部分
}
}
说明:通过在UserService 类当中new一个关于UserDao的对象,并且调用其方法,很明显这种方式是可以实现关联的,但是这两个类之间的耦合度太高,当UserDao内部代码修改之后,UserService相应的调用也需要修改。
接下来使用工厂模式进行解耦操作:
public class UserService {
public void execute(){
//UserDao userDao = new UserDao();
//userDao.add();
UserDao userDao = UserFactory.getDao();
userDao.add();
}
}
class UserDao{
public void add(){
//这里是方法体部分
}
}
class UserFactory{
public static UserDao getDao(){
return new UserDao();
}
}
说明:对于工厂设计模式来说,对象的new操作不需要自己直接来进行,只需要跟第三方的Fantory进行交涉就行了。工厂设计模式确实是降低耦合度的一种方式,但并不是最好的一种方式,那么下面就需要介绍IOC解耦过程了。
IOC解耦过程:
第一步:xml配置文件,配置创建的对象
<bean id="user" class="com.spring.User"></bean>
//这个class字段内容是你的项目种User类的路径(包名.类名)
第二步:有UserService类和UserDao类,同样创建一个工厂类
class UserFactory{
public static UserDao getDao(){
//1.xml解析
//String classValue = class属性值;
//2.通过反射创建对象(得到相应类的字节码文件)
//Class clazz = Class.forName(classValue);
//return (UserDao)clazz.newInstance();
}
}
说明:IOC可以进一步降低代码间的耦合度,将对象的创建以及对象之间的调用都交给Spring来管理。
什么是Bean管理?
Bean管理指的是两个操作,1.Spring创建对象;2.Spring注入属性
基于xml方式实现过程:
1.在Spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建。
<bean id="user" class="com.spring.User"></bean>
bean标签里面有很多属性:
id:类的唯一标识
class:类全路径(包+类路径)
name:和id属性类似,可以加特殊符号(几乎不使用这个属性)
注意:bean标签在创建对象的时候,默认也是执行无参构造方法。
2.基于xml方式注入属性
public class UserService {
private String name;
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
UserService userService = new UserService();
userService.setName("张三");
}
}
上面的过程可以使用xml配置文件的形式来实现:
<!--对象创建-->
<bean id="user" class="com.spring.User">
<!-- 设置字段属性值-->
<property name="userName" value="张三"></property>
</bean>
public class UserService {
private String name;
public UserService(String name) {
this.name = name;
}
public static void main(String[] args) {
UserService userService = new UserService("张三");
}
}
上面的过程可以使用xml配置文件的形式来实现:
<bean id="user" class="com.spring.User">
<constructor-arg name="userName" value="张三"></constructor-arg>
</bean>
XML注入其它类型属性
<!--对象创建-->
<bean id="user" class="com.spring.User">
<!-- 设置字段属性值-->
<property name="userName">
<null></null>
</property>
</bean>
<!-- 设置字段属性值-->
<property name="userName">
<value><![CDATA[<<张三>>]]></value>
</property>
import dao.UserDao;
public class UserService {
//1.创建UserDao类型属性,生成set方法
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add(){
System.out.println("添加方法");
userDao.updata();
//原始方式:new对象+调用方法
/*UserDao userDao = new UserDaoImpl();
userDao.updata();*/
}
}
public class UserDaoImpl implements UserDao{
@Override
public void updata() {
System.out.println("dao updata");
}
}
<!-- 1.service和dao对象创建-->
<bean id="userService" class="service.UserService">
<!--注入UserDao对象
name属性:类里面的属性名称
ref属性:创建UserDao对象bean标签id值
-->
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="dao.UserDaoImpl"></bean>
public class School {
private String sch_name;
public void setSch_name(String sch_name) {
this.sch_name = sch_name;
}
//重写toString()方法
public String toString(){
return sch_name;
}
}
public class Student {
//设置学生属性
private String stu_name;
private String stu_gender;
private School stu_school;
//生成每个属性对应的set方法
public void setStu_name(String stu_name) {
this.stu_name = stu_name;
}
public void setStu_gender(String stu_gender) {
this.stu_gender = stu_gender;
}
public void setStu_school(School stu_school) {
this.stu_school = stu_school;
}
//设置一个方法
public void print(){
System.out.println(stu_name+" "+stu_gender+" "+stu_school.toString());
}
}
<!--1.创建对象-->
<bean id="student" class="bean.Student">
<!-- 2.给该对象设置其属性-->
<property name="stu_name" value="张三"></property>
<property name="stu_gender" value="男"></property>
<property name="stu_school">
<bean id="school" class="bean.School">
<property name="sch_name" value="某大学"></property>
</bean>
</property>
</bean>
上面的xml配置文件也可以使用级联赋值方式:
(1)第一种方式:也即是外部bean方法
<!--1.创建对象-->
<bean id="student" class="bean.Student">
<!-- 2.给该对象设置其属性-->
<property name="stu_name" value="张三"></property>
<property name="stu_gender" value="男"></property>
<property name="stu_school" ref="school"></property>
</bean>
<bean id="school" class="bean.School">
<property name="sch_name" value="某大学"></property>
</bean>
(2)第二种方式:
<!--1.创建对象-->
<bean id="student" class="bean.Student">
<!-- 2.给该对象设置其属性-->
<property name="stu_name" value="张三"></property>
<property name="stu_gender" value="男"></property>
<property name="stu_school" ref="school"></property>
<property name="stu_school.sch_name" value="某小学"></property>
</bean>
<bean id="school" class="bean.School">
<!--<property name="sch_name" value="某大学"></property>-->
</bean>
Tips:这个似乎是有优先级内味儿了,某大学那一栏的属性值可以去掉,只需要创建school对象即可。注意哈,需要生成Student类当中属性stu_school的get方法。
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Student {
private String[] names;
private List<String> list;
private Map<String,String> map;
private Set<String> set;
public void setNames(String[] names) {
this.names = names;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void print(){
System.out.println(Arrays.toString(names));
System.out.println(list);
System.out.println(map);
System.out.println(set);
}
}
<!--1.创建对象-->
<bean id="student" class="collectiontype.Student">
<!--2.设置属性-->
<property name="names">
<array>
<value>张三</value>
<value>李四</value>
</array>
</property>
<property name="list">
<list>
<value>张三</value>
<value>李四</value>
</list>
</property>
<property name="map">
<map>
<entry key="张三" value="张三"></entry>
<entry key="李四" value="李四"></entry>
</map>
</property>
<property name="set">
<set>
<value>张三</value>
<value>李四</value>
</set>
</property>
</bean>
</beans>
对集合提取公共代码部分
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- 1.提取list集合类型属性注入-->
<util:list id="studentList">
<value>张三</value>
<value>李四</value>
</util:list>
<!-- 2.提取list集合类型属性注入使用-->
<bean id="student" class="collectiontype.Student">
<property name="list" ref="studentList"></property>
</bean>
</beans>
Spring有两种类型的bean,一种普通bean,另外一种是工厂bean (FactoryBean)。
(1)普通bean:在配置文件中定义的bean类型就是返回类型
(2)工厂bean:在配置文件中定义的bean类型可以和返回类型不一样
public class MyBean implements FactoryBean<Course> {
@Override
public Course getObject() throws Exception {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean3.xml");
Course course = context.getBean("course",Course.class);
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
}
<!-- 1.创建对象-->
<bean id="mybean" class="bean.MyBean"></bean>
<bean id="course" class="collectiontype.Course">
<property name="course_name" value="语文"></property>
</bean>
有关bean作用域问题:
1.在Spring框架当中,需要明确设置创建的bean实例是单实例还是多实例。默认情况下,bean是单实例对象。
2.如何设置单实例还是多实例?
在Spring配置文件bean标签里面有属性(scope)用于设置单实例还是多实例。
scope属性值:
默认值singleton:表示是单实例对象
prototype:表示是多实例对象
两者的区别是啥呢:
scope的属性值是singleton的时候,加载Spring配置文件的时候就会创建单实例对象;当scope的属性值是prototype的时候,不是在加载Spring配置文件的时候创建对象,在调用getBean方法的时候创建多实例对象。
有关bean生命周期问题:
生命周期:一个对象从创建到销毁的过程
bean的生命周期阶段:
(1)通过构造器创建bean实例(无参构造方法)
(2)为bean的属性设置值和对其他bean引用(调用set方法)
→→把bean实例传递到bean后置处理器的方法
(3)调用bean的初始化方法(需要进行配置初始化的方法)
→→把bean实例传递到bean后置处理器的方法
(4)bean可以使用了(对 象获取到了)
(5)当容器关闭的时候,调用bean的销毁的方法(需要进行配置销毁的方法)
xml自动装配问题
上面讨论的有关使用value设置属性值等等的xml文件配置问题都是基于手动配置的,接下来我要讲的就是xml自动装配问题。
根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入。
bean标签属性autowire用来自动装配,autowire属性常用的两个值:
(1)byName:根据属性名称注入,要求是注入值bean的id值和类属性名称一样。
(2)byType:根据属性类型注入。
<bean id="student" class="autowire.Student" autowire="byName">
<!-- <property name="school_name" ref="school"></property>-->
</bean>
<bean id="school_name" class="autowire.School"></bean>
<bean id="student" class="autowire.Student" autowire="byType">
<!-- <property name="school_name" ref="school"></property>-->
</bean>
<bean id="school_name" class="autowire.School"></bean>
引入外部属性文件
(1)直接配置数据库信息(配置德鲁伊连接池)
<?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="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/userDb"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
(2)通过引入外部属性文件配置数据库连接池
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClassName}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</bean>
</beans>
Tips:需要使用名称空间context
下期博客接着讲解IOC操作Bean管理(基于注解)