spring-1-spring介绍和IOC容器开发
阅读原文时间:2023年07月10日阅读:1

一、介绍

1、版本

2、下载(jar包依赖)

下载

文件分配

maven添加jar包依赖

https://www.cnblogs.com/nwu-edu/p/9542074.html

注意:如果出错可能是因为没有logging日志包

3、入门案例

用spring的方式创建一个对象实例

package com.wang.Spring5;

public class User {
    public void add(){
        System.out.println("add>>>>>>");
    }
}

<?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">

    <!--配置User对象创建-->
    <!--这里创建了一个user的实例对象-->
    <bean id="user" class="com.wang.Spring5.User"></bean>

</beans>
  • 测试

    @Test
    public void testAdd(){
    //1、加载配置文件
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    //2、获取配置创建的对象
    //getBean()第一个参数为这个类的别名,第二个参数为返回类型
    User user = context.getBean("user", User.class);
    System.out.println(user);
    user.add();
    }

二、IOC容器

1、IOC底层原理

IOC是什么,能做什么,怎么做到的

2、IOC接口(BeanFactory)

3、IOC具体操作_Bean管理(基于xml)

什么是Bean管理:(两个操作)

1、Spring创建对象

2、Spring注入属性

(1)基于xml方式创建对象
<!--配置User对象创建-->
<!--这里的意思是给com.wang.Spring5.User起了一个别名-->
<bean id="user" class="com.wang.Spring5.User"></bean>

//1、在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建
//2、在bean标签有很多属性,介绍常用的属性
*id属性:唯一标识(别名)
*class属性:类的全路径
*name属性:很少用,类似于id
//3、创建对象时,默认使用无参构造函数创建(没有的话就会报错)
(2)在java代码中获取xml中实现的对象
@Test
public void test$(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
    UserService userService = context.getBean("userService", UserService.class);
    userService.add();
}
(3)基于xml注入属性

DI:依赖注入(注入属性)(Dependency Insert)

IOC和DI的区别:DI是IOC的一种具体实现,是在创建了对象的基础上操作的

set注入(必须要有set方法、而且必须在创建了对象的基础上)
<bean id="book" class="com.wang.Spring5.Book">
    <property name="bname" value="书名1"></property>
    <property name="time" value="2001"></property>
</bean>
有参构造注入(注意:类中必须要有相应的构造函数)
<bean id="order" class="com.wang.Spring5.Order">
    <constructor-arg name="oname" value="12"></constructor-arg>
    <constructor-arg name="address" value="江西"></constructor-arg>
</bean>
    ——另一种方式(通过索引)
<bean id="order" class="com.wang.Spring5.Order">
    <constructor-arg index="0" value="34"></constructor-arg>
    <constructor-arg index="1" value="北京"></constructor-arg>
</bean>
set注入简化,使用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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans                     http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="book" class="com.wang.Spring5.Book" p:bname="jiu" p:time="无名氏"></bean>
注入特殊类型的数据(null,集合等)
-- 含null
<constructor-arg name="oname">
    <null></null>
</constructor-arg>
-- 含<>符号
<constructor-arg name="address">
    <value><![CDATA[<<北京>>]]></value>
</constructor-arg>
注入内部bean和级联赋值

可以理解为一个bean对象的属性指向另一个bean对象

1、创建service类和dao类

2、在service中调用dao里的方法

- 外部bean
<bean id="userService" class="com.wang.service.UserService">
    <!--注入userDao对象
        name属性:类里面属性名称
        ref属性:创建userDao对象bean标签id
    -->
    <property name="userDao" ref="userDao"></property>
</bean>
<bean id="userDao" class="com.wang.dao.UserDaoImp"></bean>

- 内部bean:和上面那一种方法达到的效果是一样的
<bean id="emp" class="com.wang.company.Employee">
    <property name="ename" value="wangwang"></property>
    <property name="eage" value="18"></property>
    <property name="dept">
        <bean id="dept" class="com.wang.company.Department">
            <property name="dname" value="安保部门"></property>
        </bean>
    </property>
</bean>

- 级联复制(一般不用)
<bean id="emp" class="com.wang.company.Employee">
    <property name="eage" value="18"></property>
    <property name="ename" value="wang"></property>
    <property name="dept" ref="dept"></property>
    <property name="dept.dname" value="人力资源部"></property>
</bean>
<bean id="dept" class="com.wang.company.Department"></bean>
注入集合类型属性
  • 在对象里设置集合类型

    public class Person {

    //1.数组类型属性
    private String[] array;
    
    //2.list集合类型属性
    private List<String> list;
    
    //3.map集合类型属性
    private Map<String, String> map;
    
    //4.set集合类型属性
    private Set<String> set;

    }



    arr1 arr2

        <!--list类型属性注入-->
        <property name="list">
            <list>
                <value>list1</value>
                <value>list2</value>
            </list>
        </property>
    &lt;!--map类型属性注入--&gt;
    &lt;property name="map"&gt;
        &lt;map&gt;
            &lt;entry key="map-key1" value="map-value1"&gt;&lt;/entry&gt;
            &lt;entry key="map-key2" value="map-value2"&gt;&lt;/entry&gt;
        &lt;/map&gt;
    &lt;/property&gt;
    
    &lt;!--set类型属性注入--&gt;
    &lt;property name="set"&gt;
        &lt;set&gt;
            &lt;value&gt;set1&lt;/value&gt;
            &lt;value&gt;set2&lt;/value&gt;
        &lt;/set&gt;
    &lt;/property&gt;
    </bean>
  • 在集合里面设置对象类型值

理解:即处理下面这种数据,集合里面是示例对象的情况

private List<Family> familyList;

public void setFamilyList(List<Family> familyList) {
    this.familyList = familyList;
}

操作:使用了ref标签里的bean属性

<bean>
    <property name="familyList">
        <list>
            <ref bean="family"></ref>
            <ref bean="family"></ref>
            <ref bean="family"></ref>
        </list>
    </property>
</bean>

<bean id="family" class="com.wang.gather.Family">
    <property name="f" value="13"></property>
</bean>
  • 把集合注入部分提取出来

理解:即把下面这个部分独立出来,便于多次调用,有点类似于创建一个公用数组,这样大家都可以调用了

<property name="array">
    <array>
        <value>arr1</value>
        <value>arr2</value>
        <value>arr3</value>
    </array>
</property>

操作

<!--这个地方类似于一个公开数组-->
<util:list id="list1">
    <value>张辽</value>
    <value>hello</value>
    <value>太死</value>
</util:list>

<bean id="person" class="com.wang.gather.Person">
    <property name="list" ref="list1"></property>
</bean>

4、bean管理

(1)工厂bean(Factory bean)

工厂bean的作用:用java代码来代替一些比较赋值比较麻烦的bean,通过在xml文件中配置我们的工厂bean,然后在工厂bean中实现返回配置好的我们需要的bean,然后我们接收这个bean时只需要引入这个工厂bean的id即可

Spring有两种类型的bean, 一种普通bean, 一种工厂bean(FactoryBean)

普通Bean: 在配置文件中定义的bean类型就是返回类型;

工厂bean: 在配置文件中定义的bean类型可以和返回类型不一样,

有点意思是传入一个参数得到另外一个参数

  • 怎么创建Factorybean

    • (1)创建类, 实现接口FactoryBean;
    • (2) 实现接口里面的方法, 在实现的方法中定义返回的bean类型.
  • Mybean

    package com.wang.Factorybean;

    public class Mybean {
    //只要一个名字
    private String name;

    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "Mybean{" +
                "name='" + name + '\'' +
                '}';
    }

    }

  • MybeanImp

    public class MybeanImp implements FactoryBean {
    public Mybean getObject() throws Exception {
    //在这个地方完成对mybean的配置
    Mybean mybean = new Mybean();
    mybean.setName("dashazi");
    return mybean;
    }

    public Class<?> getObjectType() {
        return null;
    }
    
    public boolean isSingleton() {
        return false;
    }

    }

  • xml文件

  • 实现

    @Test
    public void test7(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
    Mybean mybean = context.getBean("mybean", Mybean.class);
    System.out.println(mybean);
    }

(2)bean作用域(单例和多例)

 单例就是所有的请求都用一个对象来处理,比如我们常用的service和dao层的对象通常都是单例的;

 多例则指每个请求用一个新的对象来处理,比如action;

  • 如何设置单例和多例

可以用Spring配置文件bean标签里面的scope属性, 来设置单实例/多实例.

Spring默认单实例, 即scope=singleton

可以通过设置scope=prototype来设置多实例

如:

<bean id="testBean" class="com.ryan.spring5.testScope.TestBean" scope="prototype"></bean>

这就是一个多例对象

  • singleton和prototype区别

    • singleton单例,prototype多例
    • 设置scope值是单例时,加载spring配置文件时就会创建单实例对象,而scope值时多例对象时,加载spring配置文件时不会创建对象,使用getbean方法时才会调用
  • 另外还有:request(一次请求),session(一此对话),不做解释

(3)bean生命周期
  • 具体步骤
  1. 通过构造器创建bean实例(无参数构造);
  2. 设置bean的属性值和对其他bean引用(调用set方法);
  3. *(可以不进行)**把bean实例传递给bean后置处理器的方法postProcessBeforeInitialization
  4. 调用bean的初始化的方法(需要配置初始化方法);
  5. *(可以不进行)**把bean实例传递给bean后置处理器的方法postProcessAfterInitialization
  6. bean可以使用了(获取到了对象);
  7. 当容器关闭的时候, 调用bean的销毁方法(需要配置销毁方法)
  • 演示

    public class Orders {

    private String name;
    
    public Orders() {
        System.out.println("1、无参数构建");
    }
    
    public void setName(String name) {
        this.name = name;
        System.out.println("2、调用set方法");
    }
    
    public void add(){
        System.out.println("6、获取到了对象");
    }
    
    //初始化方法,对应initMethod属性
    public void initMethods(){
        System.out.println("4、调用初始化方法");
    }
    //销毁方法,对应destroyMethod属性
    public void destroyMethods(){
        System.out.println("5、调用销毁方法");
    }

    }

    public class MybeanPost implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("3、初始化前的操作");
        return bean;
    }
    
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("5、初始化之后的操作");
        return bean;
    }

    }

    @Test
    public void test8(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
    Orders orders = context.getBean("orders", Orders.class);
    orders.add();
    context.close();
    }

(4)xml自动装配

自动装配: 根据指定装配规则(属性名称或属性类型), Spring自动将匹配的属性值进行注入

  • 根据名字匹配

    <!--原来的配置方式-->
    <!--后来的配置方式-->
    <bean id="emp" class="com.wang.autowire.Emp" autowire="byName"></bean>
    <!--它会根据emp里的属性的名称自动匹配id也式同样名字的属性-->
    <bean id="dep" class="com.wang.autowire.Dep"></bean>
  • 根据类型匹配

注意点:一旦自动装配选择了bytype,那么我们需要的那个属性的类就只能创建一个实例

<bean id="emp" class="com.wang.autowire.Emp" autowire="byType"></bean>

<bean id="dep" class="com.wang.autowire.Dep"></bean>
(5)引入外部属性的文件

5、IOC具体操作_Bean管理(基于注解)

注解是代码标记,格式:@注解名称(属性名称=属性值,属性名称=属性值)

使用注解配置的目的:简化xml配置

  • spring针对bean对象管理有四个注解

(1)注解管理基本操作
  • 开启组件扫描:分两步: 引入context名称空间 -> 设置要扫描的包


    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:component-scan base-package="com.wang.dao,com.wang.service"></context:component-scan>

  • 创建类调用注解

    //这个等价于
    //可以默认不写:不写的话value值就是类的名称首字母小写
    @Component(value = "userservice")
    public class UserService {
    public void add(){
    System.out.println("add________________");
    }
    }

  • 测试调用

    @Test
    public void test1(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean_1.xml");
    UserService userservice = context.getBean("userservice", UserService.class);
    System.out.println(userservice);
    }

  • 经过测试调用,发现事实上注解的作用就是把那个对应的类帮助我们在xml文件里简化了开发而已,并不是另起炉灶,所以我们获取实例对象仍然是在使用原来的方式获取

(2)扫描配置装置

可以令一些配置组件不被扫描,而另外一些配置组件扫描

  • 只扫描部分组件

  • 设置不扫描部分组件

(3)基于注解进行属性注入
  • 属性注入的注解

  • @Autowired和@Qualifier注解

    //先创建一个实例
    @Component(value = "userDao")
    public class UserDaoImp implements UserDao {
    }

    //在另外一个类中
    @Component(value = "userservice")
    public class UserService {
    //这个会自动识别到我们在UserService中添加的注解,并添加到这个属性来
    //这个时根据属性名称自动装配(必须要实例的名称一样才能注入)
    @Autowired
    //当找不到时就会去寻找教userdao的实例对象
    @Qualifier(value = "userDao")
    private UserDao userDao;

    }

  • @resource注解

    //根据属性注入
    @Resource
    //根据名称注入
    @Resource(name = "userService")
    private UserDao userDao;

  • @Value注解

    @Value(value = "13")
    private int age;

(4)完全注解开发

可以使用配置类代替配置文件, 从而使用纯注解来写代码(一般使用在SpringBoot中)

  • 新建SpringConfig类并添加注解, 以代替原来的xml文件:

    //作为配置类,替代xml配置文件
    @Configuration
    //扫描包的注解
    @ComponentScan(basePackages = {"com.wang"})
    //其他地方完全一致,但获取实例对象的地方变化了
    public class springconfig {
    }

  • 改写测试程序

    @Test
    public void est2(){
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(springconfig.class);
    UserService userservice = context.getBean("userservice", UserService.class);
    userservice.add();
    }