Spring5学习记录(一)之IOC容器管理(基于XML方式)

IOC容器管理

一、IOC概述

1、什么是IOC(控制反转)

​ a)把对象创建和对象之间的调用过程,交给Spring进行管理

​ b)使用IOC目的:为了降低耦合度

2、IOC底层

​xml解析、工厂模式、反射

3、Spring提供的IOC容器实现的两种方式(两个接口)

​ a)BeanFactory接口:IOC容器基本实现是Spring内部接口的使用接口,不提供给开发人员进行使用(加载配置文件时候不会创建对象,在获取对象时才会创建对象。属于预加载或者是懒加载。)

​ b)ApplicationContext接口:BeanFactory接口的子接口,提供更多更强大的功能,提供给开发人员使用(加载配置文件时候就会把在配置文件对象进行创建。一般采用监听器,监听服务,一旦开启tomcat服务就会加载配置文件,并且创建配置文件中所有的对象。虽然降低了启动服务的效率,但是提高了运行效率。)

二、IOC容器-Bean管理

1、Bean管理

Bean管理的本质就是实现两个操作:

(1)Spring创建对象;

(2)Spring注入属性(DI ,依赖注入)。

2、基于XML配置文件创建对象

加入把配置文件命名为bean.xml,向文件中添加:

xml 复制代码
<!--1 配置User对象创建-->
<bean id="user" class="com.atguigu.spring5.User"></bean>

其中,id为这条Bean的唯一标识,class表示对应的实体对象。

尝试在Test中创建对象:

java 复制代码
public void test(){
        //1 加载spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        //2 获取配置创建的对象
        User user = context.getBean("user", UserService.class);   //getBean中的第一个参数表示要获取的配置文件中的对象的id
        user.add(); //调用user中的add方法
    }

3、基于XML方式注入属性

set方式注入

1.在实体类中添加属性的set方法。

java 复制代码
//(1)传统方式: 创建类,定义属性和对应的set方法
public class Book {
        //创建属性
        private String bname;

        //创建属性对应的set方法
        public void setBname(String bname) {
            this.bname = bname;
        }
   }

2.在xml配置文件中进行属性注入

xml 复制代码
<!--(2)spring方式: set方法注入属性-->
<bean id="book" class="com.atguigu.spring5.Book">
    <!--使用property完成属性注入
        name:类里面属性名称
        value:向属性注入的值
    -->
    <property name="bname" value="Hello"></property>
    <property name="bauthor" value="World"></property>
</bean>

有参构造函数注入

1.在实体类中添加有参构造函数。

java 复制代码
//(1)传统方式:创建类,构建有参函数
public class Orders {
    //属性
    private String oname;
    private String address;
    //有参数构造
    public Orders(String oname,String address) {
        this.oname = oname;
        this.address = address;
    }
  }

2.在xml配置文件中进行属性注入

xml 复制代码
<!--(2)spring方式:有参数构造注入属性-->
<bean id="orders" class="com.atguigu.spring5.Orders">
    <constructor-arg name="oname" value="Hello"></constructor-arg>
    <constructor-arg name="address" value="China!"></constructor-arg>
</bean>

p名称空间注入

xml 复制代码
<!--1、添加p名称空间在配置文件头部-->
<?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:p="http://www.springframework.org/schema/p"		<!--在这里添加一行p-->

<!--2、在bean标签进行属性注入(算是set方式注入的简化操作)-->
    <bean id="book" class="com.atguigu.spring5.Book" p:bname="very" p:bauthor="good">
    </bean>

4、注入空值和特殊符号

1.注入NULL。

xml 复制代码
<bean id="book" class="com.atguigu.spring5.Book">
    <!--(1)null值-->
    <property name="address">
        <null/><!--属性里边添加一个null标签-->
    </property>

此时,book中的address属性为null。

2.注入特殊符号。

xml 复制代码
<bean id="book" class="com.atguigu.spring5.Book">
 <!--(2)特殊符号赋值-->
     <!--属性值包含特殊符号
       1. 把<>进行转义 &lt; &gt;
       2. 把带特殊符号内容写到CDATA
      -->
        <property name="address">
            <value><![CDATA[<<南京>>]]></value>
        </property>
</bean>

此时,book中的address属性为<<南京>>

5、注入属性-外部bean

1.创建dao类和service类

java 复制代码
public class UserDaoImpl implements UserDao {//dao类

    @Override
    public void update() {
        System.out.println("dao update...........");
    }
}
java 复制代码
public class UserService {//service类
    //创建UserDao类型属性,生成set方法
    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    public void add() {
        System.out.println("service add...............");
        userDao.update();//调用dao方法
    }
}

2.在spring配置文件中进行配置

xml 复制代码
<!--1 service和dao对象创建-->
<bean id="userService" class="com.atguigu.spring5.service.UserService">
    <!--注入userDao对象
        name属性:类里面属性名称,对应java代码中我们想要定义的类名。
        ref属性:创建userDao对象bean标签id值,对应xml文件中我们想找到的对应的bean的id。
    -->
    <property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>

6、基于XML方式注入内部bean和级联赋值

对于一对多、多对一的实体关系,假设:

部门Dept 和员工Emp ,一个部门有多个员工,一个员工属于一个部门(部门是一,员工是多),在实体类之间表示一对多关系,那么在员工的属性中想要表示所属部门,就需要使用对象类型属性dept进行表示部门。
部门类

java 复制代码
public class Dept {
    private String dname;
    public void setDname(String dname) {
        this.dname = dname;
    }
}

员工类

java 复制代码
public class Emp {
    private String ename;
    private String gender;
    //员工属于某一个部门,使用对象形式表示
    private Dept dept;
    
    public void setDept(Dept dept) {
        this.dept = dept;
    }
    public void setEname(String ename) {
        this.ename = ename;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
}

a)注入属性-内部bean(嵌套bean)

xml 复制代码
<!--内部bean-->
    <bean id="emp" class="com.atguigu.spring5.bean.Emp">
        <!--设置两个普通属性-->
        <property name="ename" value="Andy"></property>
        <property name="gender" value="女"></property>
        <!--设置对象类型属性-->
        <property name="dept">
            <bean id="dept" class="com.atguigu.spring5.bean.Dept"><!--内部bean赋值-->
                <property name="dname" value="宣传部门"></property>
            </bean>
        </property>
    </bean>

b)注入属性-级联赋值-方式一

xml 复制代码
<!--级联赋值-->
    <bean id="emp" class="com.atguigu.spring5.bean.Emp">
        <!--设置两个普通属性-->
        <property name="ename" value="Andy"></property>
        <property name="gender" value="女"></property>
        <!--级联赋值-->
        <property name="dept" ref="dept"></property>
    </bean>
    <bean id="dept" class="com.atguigu.spring5.bean.Dept">
        <property name="dname" value="公关部门"></property>
    </bean>

c)注入属性-级联赋值-方式二

xml 复制代码
 <!--级联赋值-->
    <bean id="emp" class="com.atguigu.spring5.bean.Emp">
        <!--设置两个普通属性-->
        <property name="ename" value="jams"></property>
        <property name="gender" value="男"></property>
        <!--级联赋值-->
        <property name="dept" ref="dept"></property>
        <property name="dept.dname" value="技术部门"></property>
    </bean>
    <bean id="dept" class="com.atguigu.spring5.bean.Dept">
    </bean>

但是要注意,方式二中,想要使用dept.dname直接表示dept的name,必须在emp实体中定义getDept方法,否则无法找到dept对应的属性。

java 复制代码
 //方式二:生成dept的get方法(get方法必须有!!)
    public Dept getDept() {
        return dept;
    }

7、IOC 操作 Bean 管理------xml 注入集合属性

注入数组Array类型、 List 集合类型 、Map 集合类型属性

java 复制代码
//(1)创建类,定义数组、list、map、set 类型属性,生成对应 set 方法
public class Stu {
    //1 数组类型属性
    private String[] courses;
    //2 list集合类型属性
    private List<String> list;
    //3 map集合类型属性
    private Map<String,String> maps;
    //4 set集合类型属性
    private Set<String> sets;
    
    public void setSets(Set<String> sets) {
        this.sets = sets;
    }
    public void setCourses(String[] courses) {
        this.courses = courses;
    }
    public void setList(List<String> list) {
        this.list = list;
    }
    public void setMaps(Map<String, String> maps) {
        this.maps = maps;
    }
xml 复制代码
<!--(2)在 spring 配置文件进行配置-->
    <bean id="stu" class="com.atguigu.spring5.collectiontype.Stu">
        <!--数组类型属性注入-->
        <property name="courses">
            <array>
                <value>java课程</value>
                <value>数据库课程</value>
            </array>
        </property>
        <!--list类型属性注入-->
        <property name="list">
            <list>
                <value>张三</value>
                <value>小三</value>
            </list>
        </property>
        <!--map类型属性注入-->
        <property name="maps">
            <map>
                <entry key="JAVA" value="java"></entry>
                <entry key="PHP" value="php"></entry>
            </map>
        </property>
        <!--set类型属性注入-->
        <property name="sets">
            <set>
                <value>MySQL</value>
                <value>Redis</value>
            </set>
        </property>
</bean>

8、在集合里面设置对象类型值

java 复制代码
  //学生所学多门课程
    private List<Course> courseList;//创建集合
    public void setCourseList(List<Course> courseList) {
        this.courseList = courseList;
    }
xml 复制代码
    <!--创建多个course对象-->
    <bean id="course1" class="com.atguigu.spring5.collectiontype.Course">
        <property name="cname" value="Spring5框架"></property>
    </bean>
    <bean id="course2" class="com.atguigu.spring5.collectiontype.Course">
        <property name="cname" value="MyBatis框架"></property>
    </bean>
    
   	<!--注入list集合类型,值是对象-->
       <property name="courseList">
           <list>
               <ref bean="course1"></ref>
               <ref bean="course2"></ref>
           </list>
       </property>

9、工厂Bean(Factory Bean)

Spring 有两种类型 bean,一种是普通 bean,另外一种是工厂 bean(Factory Bean)。

  • 普通 bean:在配置文件中定义 bean 类型就是返回类型(通过IOC创建的对象类型与定义的bean类型一致)
  • 工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样(通过IOC创建的对象类型与定义的bean类型不一致)

如何实现工厂 bean?

  1. 创建某个类,让这个类作为工厂 bean,继承并实现接口 FactoryBean,把泛型设置为想要变更的类型。
  2. 实现接口里面的方法,在实现的方法中定义返回的 bean 类型
java 复制代码
public class MyBean implements FactoryBean<Course> {  //把泛型设置为course

    //定义返回bean
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCname("abc");
        return course;
    }
}
xml 复制代码
<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean"></bean>
java 复制代码
@Test
public void test3() {
 	ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
 	Course course = context.getBean("myBean", Course.class);//返回值类型可以不是定义的bean类型!
 	//这里的myBean返回的是course类型。
 	System.out.println(course);
}

10、Bean 的作用域

在 Spring 里面,默认情况下,bean 是单实例对象,但是也可以设置为多例对象。

  • 在 spring 配置文件 bean 标签里面有属性(scope)用于设置单实例还是多实例
  • scope 属性值:默认值是 singleton,表示是单实例对象。另外一个值是 prototype,表示是多实例对象
xml 复制代码
<bean id="book" class="com.atguigu.spring5.collectiontype.Book" scope="prototype">
																<!--设置为多实例-->
        <property name="list" ref="bookList"></property>
</bean>

singleton 和 prototype 区别:

  • singleton 单实例,prototype 多实例
  • 设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象 ;设置 scope 值是prototype 时候,是在调用getBean 方法时候创建多实例对象。
java 复制代码
public class TestBean {
    @Test
    public void test(){
        //1 加载spring配置文件,scope=singleton 时,在加载配置文件的时候就已经创建对象。
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        //2 获取配置创建的对象,scope=prototype 时,调用getBean 方法时候才会创建多实例对象。
        Book book= context.getBean("book",Book.class);
        book.add();
    }
}

11、Bean 生命周期

Bean的生命周期指的是从对象创建到对象销毁的过程。(正常生命周期为五步,而配置后置处理器后为七步)

  1. 通过构造器创建 bean 实例(无参数构造)
  2. 为 bean 的属性设置值(调用 set 方法)和对其他 bean 引用
  3. 把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
  4. 调用 bean 的初始化的方法(需要在配置文件中配置初始化的方法)
  5. 把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
  6. bean 可以使用了(对象获取到了)
  7. 当容器关闭时候,调用 bean 的销毁的方法(需要在配置文件中配置销毁的方法)
java 复制代码
 public class Orders {
         //无参数构造
       public Orders() {
       		System.out.println("第一步 执行无参数构造创建 bean 实例");
       }
       //定义属性
       private String oname;
       //set方法
       public void setOname(String oname) {
       		this.oname = oname;
       		System.out.println("第二步 调用 set 方法设置属性值");
       }
       //创建执行的初始化的方法
       public void initMethod() {
       		System.out.println("第四步 执行初始化的方法");
       }
       //创建执行的销毁的方法
       public void destroyMethod() {
       		System.out.println("第七步 执行销毁的方法");
       }
}
java 复制代码
public class MyBeanPost implements BeanPostProcessor {//创建后置处理器实现类
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("第三步 在初始化之前执行的方法");
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("第五步 在初始化之后执行的方法");
        return bean;
    }
}
xml 复制代码
<!--配置文件的bean参数配置-->
<bean id="orders" class="com.atguigu.spring5.bean.Orders" 
								init-method="initMethod" destroy-method="destroyMethod">	
											<!--配置初始化方法和销毁方法-->
    <property name="oname" value="手机"></property><!--这里就是通过set方式(注入属性)赋值-->
</bean>

<!--配置后置处理器-->
<bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>
java 复制代码
 @Test
 public void testBean3() {
// ApplicationContext context =
// new ClassPathXmlApplicationContext("bean4.xml");
   ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
   Orders orders = context.getBean("orders", Orders.class);
   System.out.println("第四步 获取创建 bean 实例对象");
   System.out.println(orders);
   //手动让 bean 实例销毁
   context.close();
 }

12、配置外部属性文件

方式一:直接配置数据库信息

(1)配置Druid(德鲁伊)连接池

(2)引入Druid(德鲁伊)连接池依赖 jar 包

xml 复制代码
<!--直接配置连接池-->
    <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>

方式二:引入外部属性文件配置数据库连接池

(1)创建外部属性文件,properties 格式文件,写数据库信息
jdbc.properties

bash 复制代码
	prop.driverClass=com.mysql.jdbc.Driver
	prop.url=jdbc:mysql://localhost:3306/userDb
	prop.userName=root
	prop.password=root

(2)把外部 properties 属性文件引入到 spring 配置文件中 ------ 引入 context 名称空间

xml 复制代码
<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名称空间-->
    
        <!--引入外部属性文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--配置连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${prop.driverClass}"></property>
        <property name="url" value="${prop.url}"></property>
        <property name="username" value="${prop.userName}"></property>
        <property name="password" value="${prop.password}"></property>
    </bean> 
</beans>
相关推荐
萧瑟余晖8 小时前
Java深入解析篇十七之Spring Security
java·开发语言·spring
Flittly13 小时前
【雕虫大技】Agent 动态 Skill 供应链安全加固(二):执行隔离沙箱实战
java·spring boot·spring
BUG指挥官1 天前
Sa-Token和Spring Security对比
java·后端·spring
geminigoth1 天前
Spring AI Alibaba 入门开发一(备份)
java·人工智能·spring
xbgRS2 天前
spring整合mybaits
java·spring·mybatis
ruleslol2 天前
静态依赖注入 VS 动态依赖查询
spring
墨雨晨曦882 天前
Spring AI总结
java·人工智能·spring
用户3126874877202 天前
Spring Boot 异常处理到底怎么玩的?从 DispatcherServlet 到全局兜底的全链路拆解
spring
不才不才不不才2 天前
Spring 源码系列(17): HandlerMapping 与 HandlerAdapter 两大体系
java·后端·spring
(轻舟已过万重山)2 天前
第40章 Spring AI 实战:企业级 AI 应用架构
人工智能·spring·架构