Spring框架教程(二):IOC控制反转概述、基于XML配置的Bean管理

一、IOC的概念和原理

1.什么是IOC

  IOC,翻译过来就是控制反转,就是把对象创建和对象之间的调用过程都交给Spring进行管理。 目的是为了降低耦合。

2.IOC的底层原理

  技术支持:xml解析、工厂模式(设计模式的一种)、反射。

(1)工厂模式

  假设有两个类,UserService类和UserDao类,我们想要在UserService中调用一个UserDao的方法,传统做法如下。这样的弊端就是,类之间的耦合度高。

	class UserService{
    
    
		execute(){
    
    
		UserDao userDao=new UserDao();
		userDao.add();
	}

	class UserDao{
    
    
		add(){
    
    
		}
	}

  如果采用工厂模式,如下,耦合度有所降低,但工厂类和UserDao类依旧存在耦合。

	class UserService{
    
    
		execute(){
    
    
			UserDao userDao=UserFactory.getDao();
			userDao.add();
	}

	class UserDao{
    
    
		add(){
    
    
		}
	}

	class UserFactory{
    
    //用这个类统一创建对象
		public static UserDao getDao(){
    
    
			return new UserDao();
		}
	}

(2)IOC的解耦过程

  为了进一步将耦合度降到最低限度,我们在利用了xml解析、工厂模式、反射的原理基础上,引入了IOC技术。

  步骤如下:

  • 在xml文件中配置创建的对象
	<bean id="dao" class="com.wang.UserDao"></bean>
  • 已有Service类和Dao类,现在要创建工厂类
	class UserFactory{
    
    
		public static UserDao getDao(){
    
    
			String classValue=class属性值;//通过xml解析获取class属性值,这一步还需要展开
			Class class=Class.forName(classValue);//通过反射创建对象
			return (UserDao)class.newInstance();//完成了反射创建对象
		}
	}

(3)IOC接口

  IOC思想基于IOC容器完成,IOC容器底层就是对象工厂。

  Spring中提供IOC容器两种实现方式:

  • BeanFactory接口:IOC容器最基本的方式(Spring自带的),一般只内部使用,不提供我们这些开发人员使用。特点是:加载配置文件时不会创建对象,而是获取对象、使用对象时才创建对象。
	BeanFactory context=new ClassPathXmlApplicationContext("bean1.xml");
  • ApplicationContext接口:是BeanFactory的子接口,功能更强大,是推荐使用的。特点是:加载配置文件时(启动服务器时)就创建对象。
	ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");
  • BeanFactory接口下面还有一个ConfigurableApplicationContext子接口,有一些扩展的功能,可以了解一下。

(4)ApplicationContext接口的实现类

  • FileSystemXmlApplicationContext类:参数为xml文件的绝对路径
  • ClassPathXmlApplicationContext类:参数为xml文件的项目相对路径

二、IOC操作Bean管理(基于XML)

1.什么是Bean管理

  • 两个操作:

  Spring创建对象
  Spring注入属性

  • 两种实现方式:

  基于xml配置文件
  基于注解方式

2.基于xml方式操作Bean管理

(1)创建对象

	<bean id="user" class="com.wang.User">	</bean>
  • 在spring配置文件中使用bean标签,在标签中添加对应属性,就可以实现对象创建。
  • bean标签中常用属性:id属性(唯一标识)、class属性(类全路径)。
  • 创建对象时,默认也是执行无参数构造方法。

(2)注入属性

  DI(依赖注入),说白了就是注入属性的一种实现。可通过set方法注入、类的有参数的构造器注入。

  • 通过Set方法实现

第一步:在src下新建Book类,并写好相应的代码,如下:

	package com.wang;
	
	public class Book {
    
    
	    private String bname;
	    private String bauthor;
	
	    public void setBname(String bname) {
    
    
	        this.bname = bname;
	    }
	
	    public void setBauthor(String bauthor) {
    
    
	        this.bauthor = bauthor;
	    }
	
	    public void printMessage(){
    
    
	        System.out.println("《"+bname+"》 "+bauthor);
	    }
	}

第二步:在xml配置文件中配置对象,配置属性注入,如下:

	//在beans标签内写bean子标签
	<!--配置book类-->
	<bean id="book" class="com.wang.Book">
		<!--使用property标签完成属性注入
	    name属性是类属性名,
	    value是要注入的属性值-->
	    <property name="bname" value="喵王八"></property>
	    <property name="bauthor" value="狗兔子"></property>
	</bean>

第三步:我们在TestSpring5类中来测试一下,来看看效果是什么,TestSpring5类中测试方法代码及运行结果如下:

    @Test
    public void testBook1(){
    
    
        //加载spring配置文件
        ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");
        //获取对象
        Book book = context.getBean("book",Book.class);
        System.out.println(book);
        book.printMessage();
    }

在这里插入图片描述

  • 通过有参构造方法实现

第一步:在src下面创建Order类、

	package com.wang;
	
	public class Order {
    
    
	    private String oname;
	    private String address;
	
	    public Order(String oname,String address){
    
    
	        this.address=address;
	        this.oname=oname;
	    }
	
	    public void orderTest(){
    
    
	        System.out.println("从"+address+"买"+oname);
	    }
	}

第二步:在xml配置文件中配置对象,配置属性注入,如下:

	<!--配置Order类-->
	<bean id="order" class="com.wang.Order">
		<!--如果bean内部没有任何子标签,将通过无参构造器创建对象,但我们现在要通过有参构造器创建-->
	    <!--通过constractor-arg子标签完成属性注入-->
	    <constructor-arg name="oname" value="兔兔"></constructor-arg>
	    <constructor-arg name="address" value="China"></constructor-arg>
	</bean>

第三步:在TestSpring5类中来测试一下,方法代码及运行效果如下:

	@Test
    public void testOrder1(){
    
    
        //加载spring配置文件
        ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");
        //获取对象
        Order order=context.getBean("order",Order.class);
        System.out.println(order);
        order.orderTest();
    }

在这里插入图片描述

  • 通过p名称空间来简化基于set方法注入属性的操作

  和基于set方法唯一的区别就是xml配置更简化了,首先要在xml的beans头标签中添加属性:

	xmlns:p="http://www.springframework.org/schema/p"

  然后Book类的配置为:

	<bean id="book" class="com.wang.Book" p:bname="喵王八" p:bauthor="狗兔子"></bean>

3.注入属性-其他类型属性

(1)null值

  在注入属性的时候,这么写:

	<!--null值-->
    <property name="bauthor">
    	<null></null>
    </property>

  测试结果如下:

在这里插入图片描述

(2)属性值中包含了特殊符号

  • 方法一:实体引用。

在这里插入图片描述

  • 方法二:CDATE
	<property name="address">
		<value><![CDATE[<<南京>>]]></value>
	</property>

4.注入属性-外部bean

  创建Service类和Dao类,在Service里面调用Dao里面的方法

  • 在src下 新建两个包service和dao包,然后在service包下新建UserService类,在dao包下新建UserDao接口和UserDaoImpl实现类,代码如下:
	public interface UserDao {
    
    
	    void update();
	}
	
	public class UserDaoImpl implements UserDao {
    
    
	    @Override
	    public void update() {
    
    
	        System.out.println("dao update...");
	    }
	}
	
	public class UserService {
    
    
	    private UserDao userdao;
	
	    public void setUserdao(UserDao userdao) {
    
    
	        this.userdao = userdao;
	    }
	
	    public void add(){
    
    
	        System.out.println("service add...");
	        userdao.update();
	    }
	}

  通过在xml中进行配置

  • 在xml文件中基于set方法注入属性,然后就可以在UserService中调用UserDao的方法了。此处我重新建立了一个xml文件,命名为bean2.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">
	
	    <!--配置UserService类-->
	    <bean id="userService" class="com.wang.service.UserService">
	        <!--注入属性,外部依赖要使用ref属性-->
	        <property name="userdao" ref="userDaoImpl"> </property>
	    </bean>
	
	    <!--配置UserDaoImpl类-->
	    <bean id="userDaoImpl" class="com.wang.dao.UserDaoImpl">
	    </bean>
	</beans>

  测试及运行结果:

	@Test
    public void testUserService(){
    
    
        ApplicationContext context=new ClassPathXmlApplicationContext("bean2.xml");
        UserService userService=context.getBean("userService", UserService.class);

        System.out.println(userService);
        userService.add();
    }

在这里插入图片描述

5.注入属性-内部bean

  一对多关系:部门对员工。

  • 新建部门类Dept类和员工类Employee类
	package com.wang;
	//部门类
	public class Dept {
    
    
	    private String dname;
	    public void setDname(String dname) {
    
    
	        this.dname = dname;
	    }
	    @Override
	    public String toString() {
    
    
	        return "Dept{" +
	                "dname='" + dname + '\'' +
	                '}';
	    }
	}
	
	//员工类
	public class Employee {
    
    
	    private String ename;
	    private String gender;
	    //员工属于某一个部门
	    private Dept dept;
	    public void setEname(String ename) {
    
    
	        this.ename = ename;
	    }

	    public void setGender(String gender) {
    
    
	        this.gender = gender;
	    }
	
	    public void setDept(Dept dept) {
    
    
	        this.dept = dept;
	    }
	    
	    public void add(){
    
    
	        System.out.println(dept.toString());
	    }
	}
  • 配置xml文件
	<!--配置Employee类-->
    <bean id="employee" class="com.wang.Employee">
        <property name="ename" value="tracy"> </property>
        <property name="gender" value="female"> </property>
        <!--内部嵌套配置Dept类-->
        <property name="dept">
            <bean id="dept" class="com.wang.Dept">
                <property name="dname" value="保安部"> </property>
            </bean>
        </property>
    </bean>
  • 单元测试
	@Test
    public void testEmployee(){
    
    
        ApplicationContext context=new ClassPathXmlApplicationContext("bean2.xml");
        Employee employee=context.getBean("employee",Employee.class);

        System.out.println(employee);
        employee.add();
    }

在这里插入图片描述

6.注入属性-级联赋值

  在5的两个类的基础上,除了内部bean,可以有几种不同的xml配置方式:

  • 方式一:在bean标签内部用ref属性
  • 方式二:……略

7.注入属性-集合属性

(1)一般做法

  为了更清楚地展示这部分内容,我们在src下面的com.wang包中新建一个collectionType包。

  • 在collectionType包下创建Student类,如下:
	package com.wang.collectionType;
	
	import java.util.Arrays;
	import java.util.List;
	import java.util.Map;
	import java.util.Set;
	
	public class Student {
    
    
	    //数组类型
	    private String[] course;
	    //List集合类型
	    private List<String> list;
	    //Map集合类型
	    private Map<String,String> map;
	    //Set集合类型
	    private Set<String> set;
	
	    public void setCourse(String[] course) {
    
    
	        this.course = course;
	    }
	    public void setList(List<String> list) {
    
    
	        this.list = list;
	    }
	    public void setMap(Map<String, String> map) {
    
    
	        this.map = map;
	    }
	    public Student setSet(Set<String> set) {
    
    
	        this.set = set;
	        return this;
	    }
	
	    public void testCollection(){
    
    
	        System.out.println(Arrays.toString(course));
	        System.out.println(list);
	        System.out.println(map);
	        System.out.println(set);
	    }
	}
  • 在src下新建bean3.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">
	
	    <bean id="student" class="com.wang.collectionType.Student">
	        <!--数组类型属性-->
	        <property name="course">
	            <array>
	                <value>java课程</value>
	                <value>数据库课程</value>
	            </array>
	        </property>
	        
	        <!--list类型属性-->
	        <property name="list">
	            <list>
	                <value>小团团</value>
	                <value>小坨坨</value>
	            </list>
	        </property>
	        
	        <!--map类型属性-->
	        <property name="map">
	            <map>
	                <entry key="JAVA" value="java"> </entry>
	                <entry key="PHP" value="php"> </entry>
	            </map>
	        </property>
	        
	        <!--set类型属性-->
	        <property name="set">
	            <set>
	                <value>MySQL</value>
	                <value>Redis</value>
	            </set>
	        </property>
	    </bean>
	</beans>
  • 测试代码及运行结果:
	@Test
    public void testStudent(){
    
    
        ApplicationContext context=new ClassPathXmlApplicationContext("bean3.xml");
        Student student=context.getBean("student",Student.class);

        System.out.println(student);
        student.testCollection();
    }

在这里插入图片描述

(2)当集合属性为自定义类

  • 在Student类中添加属性和对应的set方法
	private List<Course> courses;
    public void setCourses(List<Course> courses) {
    
    
        this.courses = courses;
    }
  • 在CollectionType包下新建Course类:
	package com.wang.collectionType;
	public class Course {
    
    
	    private String cname;
	    public Course setCname(String cname) {
    
    
	        this.cname = cname;
	        return this;
	    }
	}
  • 配置xml文件,配置Course类的对象:
	<!--创建多个course对象-->
    <bean id="course1" class="com.wang.collectionType.Course">
        <property name="cname" value="Spring5框架"> </property>
    </bean>
    <bean id="course2" class="com.wang.collectionType.Course">
        <property name="cname" value="Redis教程"> </property>
    </bean>
  • 配置xml文件,配置Student类的新增属性:
	<property name="courses">
		<list>
			<ref bean="course1"></ref>
			<ref bean="course2"></ref>
		</list>
	</property>
  • 测试:略

(3)把集合注入部分提取出来

  • 在collectionType包下新建Books类,如下:
	package com.wang.collectionType;
	import java.util.List;
	public class Books {
    
    
	    private List<String> bookList;
	    public void setBookList(List<String> bookList) {
    
    
	        this.bookList = bookList;
	    }
	    public void test(){
    
    
	        System.out.println(bookList);
	    }
	}
  • 配置xml文件:

  首先在beans头标签中需要添加一些代码:

	<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"//添加1
	       xsi:schemaLocation="http://www.springframework.org/schema/beans
	           http://www.springframework.org/schema/beans/spring-beans.xsd
	    http://www.springframework.org/schema/util//添加2
	    http://www.springframework.org/schema/util/spring-util.xsd">//添加3

  具体配置:

	<!--提取list集合类型属性注入-->
    <util:list id="bookList">
        <value>三字经</value>
        <value>养兔秘籍</value>
        <value>红楼梦</value>
    </util:list>

    <!--使用上面的集合类型属性注入-->
    <bean id="books" class="com.wang.collectionType.Books">
        <property name="bookList" ref="bookList">
        </property>
    </bean>
  • 测试,代码及运行效果:
	@Test
    public void testBooks(){
    
    
        ApplicationContext context=new ClassPathXmlApplicationContext("bean3.xml");
        Books books=context.getBean("books", Books.class);

        System.out.println(books);
        books.test();
    }

在这里插入图片描述

8.工厂Bean

  spring中有两种Bean,前面全都是普通Bean(在配置文件中配置的Bean类型就是返回类型),另外一种是工厂Bean(在配置文件中配置的Bean类型可以和返回类型不同)。实现过程为:

  • 创建一个类,让这个类作为工厂Bean,实现接口FactoryBean。
  • 实现接口中的方法,定义返回的Bean类型,可以和原本类的类型不同,也就是和配置的Bean类型不同。

9.Bean的作用域和生命周期

(1)作用域

  在spring里面,可以设置bean实例是单实例(只创建一个对象,供多次使用)和多实例(每次使用都创建一个新对象)。默认情况下,bean是单实例对象。

  如何设置单实例和多实例?在bean标签中使用属性scope,值为singleton表明为单实例,prototype为多实例。

  除了单实例和多实例的区别singleton和prototy的区别:若设置为scope时,加载配置文件时就自动创建了对象;若设置为prototype时,在调用getBean方法时才创建多实例对象。

(2)生命周期

  从对象创建到对象销毁的基本过程:

  • ①通过构造器创建bean实例(无参构造器)。
  • ②为bean的属性设置值和其他bean引用(调用set方法)。
  • ③调用bean的初始化方法(需要进行配置):先在类中定义初始化方法,然后在xml文件中的对应bean标签中添加属性init-method=“方法名”。
  • ④bean的获取和使用。
  • ⑤当容器在关闭时,会调用bean的销毁方法(需要进行配置):首先在类中定义销毁时能起提示作用的方法,然后在xml文件中对应的bean标签中添加属性destory-method=“方法名”,最后在使用bean的java代码中调用ClassPathXmlApplicationContext类中的close方法(即context.close()),就可以销毁bean了。

  另外,关于bean的后置处理器的部分此处不作说明。

10.自动装配

  通过在xml配置文件的bean标签中使用属性autowire来实现,值为byName表明是根据属性名称自动装配(属性名称要和要注入的bean的id相同),属性值为byType表明是根据属性类型自动装配(根据属性的类型自动注入,其属性对应类型的bean不能注入多个)。

11.外部属性文件

  以配置数据库连接池为例:

  • 在src目录下创建外部属性文件,properties格式文件,写数据库信息。比如命名为jdbc.properties。

  • 在xml配置文件中的beans头标签中引入context名称空间:

	<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"
	       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/util
	    http://www.springframework.org/schema/util/spring-util.xsd
	    http://www.springframework.org/schema/context	//增加的部分
	    http://www.springframework.org/schema/context/spring-context.xsd">//增加的部分
  • 在spring的xml配置文件中使用标签引入外部配置文件:
	<context:property-placeholder location="classpath:jdbc.properties" />//classpath表明文件在src路径下
  • 在xml配置文件中配置连接池:
	<property name="url" value="&{properties文件中对应属性key的名称}"><property>

猜你喜欢

转载自blog.csdn.net/Tracycoder/article/details/112549311