java从入门到精通二十六(Spring框架篇章一)

2022-07-16 12:11:39 浏览数 (1)

java从入门到精通二十六(Spring框架篇章一)

  • Spring 架构
  • 分析一个项目存在的问题
  • IOC控制反转(IOC)
  • 依赖注入(DI)
  • IOC的一个简单案例
  • DI(依赖注入)案例
  • 有关bean
    • bean 基础配置
    • bean的实例化
      • 无参构造方法实例化
      • 静态工厂实例化
      • 非静态工厂实例化
      • FactoryBean的简化使用
    • bean的生命周期
  • DI 依赖注入方式
    • setter注入
    • 构造器注入
    • 自动装配
    • 集合注入

Spring 架构

按照这个图的说明(4版本架构图)

核心层 Core Container:核心容器,这个模块是Spring最核心的模块,其他的都需要依赖该模块 AOP层 AOP:面向切面编程,它依赖核心层容器,目的是在不改变原有代码的前提下对其进行功能增强 数据层 Data Access:数据访问,Spring全家桶中有对数据访问的具体实现技术 Data Integration:数据集成,Spring支持整合其他的数据层解决方案,比如Mybatis Transactions:事务,Spring中事务管理是Spring AOP的一个具体实现 web层 在SpringMVC框架体现 Test层 那就是测试,集合Junit完成单元测试和集成测试。

一般我们都说这个轻量级框架是为企业级开发而生的,开源,并且几乎可以支持主流的所有框架,简化了代码操作。所提供的的两个核心就是就是IOC(控制反转),AOP(面向切面编程)。我们在这里的介绍首先先知道一下。

首先还有就是这里我们主要还是先说明的就是Spring是一个大的家族,并不是说Spring就是单指一个Spring技术。

以后我们还需要注意

目前本文介绍的还是==Spring Framework这边。因为这个是比较核心的。是基础。 SpringCloud:这个是用来做分布式之微服务架构的相关开发。 SpringCloud:这个是用来做分布式之微服务架构的相关开发

按照黑马给出的条例的这个框架的学习路线图

那么我们的起点就从核心容器这边开始,包括控制反转(IOC),依赖注入(DI),Bean(其实是对象)等这些开始。 我们大体了解到这些。 我们应该从分析项目存在的一些问题引入。

分析一个项目存在的问题

现在我们先给出几部分简单的代码

层次是这样的。

在dao层,接口

代码语言:javascript复制
package jgdabc.dao;

public interface BookDao {
    public void save();
}

接口实现

代码语言:javascript复制
package jgdabc.dao.impl;

import jgdabc.dao.BookDao;

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
}

service层 接口

代码语言:javascript复制
package jgdabc.service;

public interface BookService {
    public void save();
}

接口实现

代码语言:javascript复制
package jgdabc.service.impl;

import jgdabc.dao.BookDao;
import jgdabc.dao.impl.BookDaoImpl;
import jgdabc.service.BookService;

public class BookServiceImpl implements BookService {

        private BookDao bookDao = new BookDaoImpl();
        public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }

}

我们看的这个service调用了dao层的方法。存在的问题就是业务层需要调用数据层方法,然后又我们看到了这里也new了数据层的接口实现类。 但是呢如果数据层得实现类需要做出优化,发生变化,那么业务层是一定要改变的。起码得new出对象,但是这样的话其实在将来的项目中,我们需要重新编译和重新部署。这样如果项目庞大的话,将带来一些代价成本。

这里按照黑马的教程里面讲到一个词叫做耦合度。代码飞耦合度。 代码的耦合度,是指代码中的单元代码的紧密程度,其中一个单元代码的更改对其它单元代码的影响力与作用

也就是说。如果我们代码之前存在太多的相互关联,当一部分代码需要优化或者改进的时候,我们其他的代码段也面临着改动。如果项目庞大的话,这将付出巨大的代价。

所以我们先从对象的问题上尝试解决。 如何在业务层中不在new出数据层的实现对象呢?这样就降低了耦合度。而这个问题,Spring给出了我们的解决方案。

IOC控制反转(IOC)

首先我们要明白什么是控制反转。 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转。

在上诉程序的具体提现就是我们的service实现类对数据层的实现方法对象的创建由外部提供。

业务层要用数据层的类对象,以前是自己new 的现在自己不new了,交给别人[外部] 来创建对象别人[外部] 就反转控制了数据层对象的创建权这种思想就是控制反转

Spring和IOC之间的关系是什么呢? Spring技术对IOC思想进行了实现 Spring提供了一个容器,称为IOC容器,用来充当IOC思想中的"外部" IOC思想中的别人[外部] 指的就是Spring的IOC容器 IOC容器的作用以及内部存放的是什么? IOC容器负责对象的创建、初始化等一系列工作,其中包含了数据层和业务层的类对象被创建或被管理的对象在IOC容器中统称为Bean IOC容器中放的就是一个个的Bean对象 当IOC容器中创建好service和dao对象后,程序能正确执行么? 不行,因为service运行需要依赖dao对象 IOC容器中虽然有service和dao对象 但是service对象和dao对象没有任何关系 需要把dao对象交给service,也就是说要绑定service和dao对象之间的关系 像这种在容器中建立对象与对象之间的绑定关系就要用到DI:

依赖注入(DI)

(1) 什么是依赖注入呢? 在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入业务层要用数据层的类对象,以前是自己new 的现在自己不new了,靠别人[外部其实指的就是IOC容器] 来给注入进来,这种思想就是依赖注入

(2) IOC容器中哪些bean之间要建立依赖关系呢? 这个需要程序员根据业务需求提前建立好关系,如业务层需要依赖数据层,service就要和dao建立依赖关系 介绍完Spring的IOC和DI的概念后,我们会发现这两个概念的最终目标就是:充分解耦,具体实现靠: 使用IOC容器管理bean(IOC) 在IOC容器内将有依赖关系的bean进行关系绑定(DI) 最终结果为:使用对象时不仅可以直接从IOC容器中获取,并且获取到的bean已经绑定了所有的依赖关系.

总结下来就是

(1) 什么IOC/DI思想? IOC:控制反转,控制反转的是对象的创建权DI:依赖注入,绑定对象与对象之间的依赖关系 (2) 什么是IOC容器? Spring创建了一个容器用来存放所创建的对象,这个容器就叫IOC容器 (3)什么是Bean? 容器中所存放的一个个对象就叫Bean或Bean对象

IOC的一个简单案例

直接来看一下简单的操作吧!理论思想太多就很烦人了。

那我们可以先创建一个简单的maven 模块。 我们要用Spring 的话,就需要去在依赖中导入模块。

我们来看pom中需要导入什么

代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>SpringPratice</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
    </dependencies>

</project>

我们的项目的java代码。

代码语言:javascript复制
package jgdabc.dao;

public interface BookDao {
    public void save();
}
代码语言:javascript复制
package jgdabc.dao.impl;

import jgdabc.dao.BookDao;

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
}
代码语言:javascript复制
package jgdabc.service;

public interface BookService {
    public void save();
}
代码语言:javascript复制
package jgdabc.service.impl;

import jgdabc.dao.BookDao;
import jgdabc.dao.impl.BookDaoImpl;
import jgdabc.service.BookService;

public class BookServiceImpl implements BookService {
//    删除业务层当中使用new的方式创建的对象
        private BookDao bookDao = new BookDaoImpl();
        


        public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }

}

其实在这之前我们需要配置一下spring的配置文件。

代码语言:javascript复制
<?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标签表示配置配置bean-->
<!--    id属性表示给bean起名字-->
<!--    class属性表示给bean定义类型-->
    <bean id="bookDao" class="jgdabc.dao.impl.BookDaoImpl"></bean>
    <bean id="bookService" class="jgdabc.service.impl.BookServiceImpl">


    </bean>
</beans>

至于相关标签上面注释说的很明白了。我们需要记住的是bean一定是和对象相关联的。

我们先使用这一部分。

我们这样去获取一个ioc容器,然后获取到对象,我们就可以实现对对象的调用。

代码语言:javascript复制
import jgdabc.dao.BookDao;
import jgdabc.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App2 {
    public static void main(String[] args) {
//        获取ioc容器(控制反转的)
     ApplicationContext ctx =  new ClassPathXmlApplicationContext("ApplicationContext.xml");
//     获取bean
//        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
//        bookDao.save();
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }

}

当然你可以先运行这一段

代码语言:javascript复制
import jgdabc.dao.BookDao;
import jgdabc.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App2 {
    public static void main(String[] args) {
//        获取ioc容器(控制反转的)
     ApplicationContext ctx =  new ClassPathXmlApplicationContext("ApplicationContext.xml");
//     获取bean
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();
//        BookService bookService = (BookService) ctx.getBean("bookService");
//        bookService.save();
    }

}

其实这里主要说明了,我可以获取到这个对象。当然这和配置是紧密相连的。另外从这里我们也可以知道ioc容器可以管理我们的bean对象。

我们还有新的问题就是,现在的service层,还是需要进行new对象。好下面我们对其解耦。

DI(依赖注入)案例

对的,这样是有道理的。但是我们如何给它传呢?依赖注入!

所以这里的改变在于service层实现类和配置的依赖注入。 具体如下。

代码语言:javascript复制
<?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标签表示配置配置bean-->
<!--    id属性表示给bean起名字-->
<!--    class属性表示给bean定义类型-->
    <bean id="bookDao" class="jgdabc.dao.impl.BookDaoImpl"></bean>
    <bean id="bookService" class="jgdabc.service.impl.BookServiceImpl">
<!--        配置server与dao的关系-->
        <property name="bookDao" ref="bookDao"></property>
<!--        property 表示配置当前bean的属性-->
<!--        name配置配置哪一个具体的属性-->
<!--        ref属性表示参照哪一个bean-->

    </bean>
</beans>
代码语言:javascript复制
package jgdabc.service.impl;

import jgdabc.dao.BookDao;
import jgdabc.dao.impl.BookDaoImpl;
import jgdabc.service.BookService;

public class BookServiceImpl implements BookService {
//    删除业务层当中使用new的方式创建的对象
//        private BookDao bookDao = new BookDaoImpl();
          private BookDao bookDao;
//          生成一个set方法

//
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }

}

然后测试

代码语言:javascript复制
import jgdabc.dao.BookDao;
import jgdabc.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App2 {
    public static void main(String[] args) {
//        获取ioc容器(控制反转的)
     ApplicationContext ctx =  new ClassPathXmlApplicationContext("ApplicationContext.xml");
     获取bean
//        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
//        bookDao.save();
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }

}

有关bean

bean 基础配置

首先关注一下class属性。我们之前一般都是写接口的实现类类名。来让其创建出,并管理对象。但是我们可能有一个疑惑。class 指定为什么不能直接写接口名呢?

hh,当然是因为接口是不能创建对象的,创建对象的工作,我们在以前也学过,一定是需要其实现类来完成。所以在配置这里,同样也是不能指定接口的全类名来进行配置。

还有就是bean的id。id一般是一定是唯一的。有没有可以重命名的办法。有的,办法就是进行指定name属性。向下面这样。

那么在获取bean的时候你也可以通过这个来获取。

bean的scope配置

scope是用来指定是单例还是非单例。单例就是你通过IOC来造的对象在每次创建都是同一个对象,也就是地址是一样的。非单例则反之。

在这里指定就好。

当然你可以在代码中验证一下IOC中的bean是单例还是非单例。 现在不指定scope,看默认是单例还是非单例。

可以看的出是一样的。 那么我们现在指定。

你看地址是不一样了。 关于中间那句是我对bean实例的一次测试。

那么后面我们来看bean如何实例以及其相关的特点。

bean的实例化

无参构造方法实例化

我们明白的是Spring 的IOC容器为我们创建了对象。我们需要了解到的是它是如何创建的对象。

而我们通常把创建对象的过程叫做对象的实例化,bean的本质就是对象,那么一定会调用构造方法。至于无参还是有参,我们可以简单验证。 我们需要写几个类来测试一下。 dao层接口类

代码语言:javascript复制
package jgdabc.dao;

public interface BookDao {
    public void save();
}
代码语言:javascript复制
package jgdabc.dao.impl;

import jgdabc.dao.BookDao;

public class BookDaoImpl implements BookDao {
    public BookDaoImpl() {
        System.out.println("book dao constructor is running...");
    }

    public void save() {
        System.out.println("book dao save ...");
    }
}

我们在dao层实现类中写一个构造方法。因为我们将来在service层调用这个属于这个类的bean。

现在我们写service层的接口和实现类

代码语言:javascript复制
package jgdabc.service;

public interface BookService {
    public void save();
}
代码语言:javascript复制
package jgdabc.service.impl;

import jgdabc.dao.BookDao;
import jgdabc.dao.impl.BookDaoImpl;
import jgdabc.service.BookService;

public class BookServiceImpl implements BookService {
//    删除业务层当中使用new的方式创建的对象
//        private BookDao bookDao = new BookDaoImpl();
          private BookDao bookDao;
//          生成一个set方法

//
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }

}

然后我们在配置文件当中,还是那样写。

然后测试,注意我们这里一共有两个bean,我们一定要明白的是如果构造方法执行的话,一定会执行两次。我们来验证。

我们看到构造方法被调用了,由此可见,实例化的时候,这个无惨构造方法被调用了。

如果我们把这个构造方法私有化的话,他还能不能调用到或者实例化成功呢?

代码语言:javascript复制
package jgdabc.dao.impl;

import jgdabc.dao.BookDao;

public class BookDaoImpl implements BookDao {
   private BookDaoImpl() {
        System.out.println("book dao constructor is running...");
    }

    public void save() {
        System.out.println("book dao save ...");
    }
}

然后我们看测试代码

代码语言:javascript复制
import jgdabc.dao.BookDao;
//import jgdabc.factory.BookDaoFactory;
//import jgdabc.factory.BookDaoFactory01;
import jgdabc.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App2 {
    public static void main(String[] args) {
//        获取ioc容器(控制反转的)
     ApplicationContext ctx =  new ClassPathXmlApplicationContext("ApplicationContext.xml");
     获取bean
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        System.out.println(bookDao);
        bookDao.save();
//        BookDao bookDao1 = (BookDao) ctx.getBean("bookDao");
//        System.out.println(bookDao1);
//        BookService bookService = (BookService) ctx.getBean("bookService");
//        bookService.save();
//        自己是使用工厂
//        BookDao bookDao = BookDaoFactory.getBookDao();
//        bookDao.save();
//        BookDao bookDao2 = (BookDao)ctx.getBean("bookDao2");
//        bookDao2.save();
//        BookDaoFactory01 bookDaoFactory01 = new BookDaoFactory01();
//        BookDao bookdao = bookDaoFactory01.getBookdao();
//        bookdao.save();
    }

}

我们可以这个构造方法还是可以被调用到,既然私有了为什么还可以调用到?原因就是Spring底层用的是反射。

我们注意到我们这个构造方法是无参构造的。 那么我们尝试传入一个参数,那么就是一个有参构造构造方法,那么有参构造方法能参与到实例化吗?

我们就验证一下。

你看这样运行就爆红了,说明有参构造方法是无法进行实例化造出对象的。所以我们的Spring所用的是无参构造方法进行实例化。

但是这并不是唯一的实例化的方式。

静态工厂实例化

所谓的静态工厂实例化是怎么一回事呢?

我们分开层次。先创建一个目录结构。

在这里写我们的工厂类。 在这里造对象。

代码语言:javascript复制
package jgdabc.factory;

import jgdabc.dao.BookDao;
import jgdabc.dao.impl.BookDaoImpl;

//自己创建静态一个工厂来创造对象
public class BookDaoFactory {
    public static BookDao getBookDao()
    {
        return new BookDaoImpl();
    }
}

我们dao实现类这样写就可以。

代码语言:javascript复制
package jgdabc.dao.impl;

import jgdabc.dao.BookDao;

public class BookDaoImpl implements BookDao {
//   public BookDaoImpl() {
//        System.out.println("book dao constructor is running...");
//    }

    public void save() {
        System.out.println("book dao save ...");
    }
}

我们直接在测试类去通过这个工厂new出来的对象来调用方法。

其实这样奖的是工厂的话,其实还是简单许多的。但凡学过面向对象的就不会觉得这是什么非常什么的操作。但是这是一种思维模式。

我们需要注意的是这种思维模式创建的对象我们还是可以交给Spring管理。

我们还是需要在配置文件中进行操作。

代码语言:javascript复制
 <bean id="bookDao2" class="jgdabc.factory.BookDaoFactory" factory-method="getBookDao"></bean>

class:工厂类的类全名 factory-mehod:具体工厂类中创建对象的方法名

然后我们再次从ioc容器获取到这个对象。 不相关的代码已经注释掉了。

可以看到成功运行。

但是这样其实你会发现可能会存在多余的操作。这样的操作似乎很多余,但是可能存在的问题就是这里在工厂类中可能我们需要做一些必要的操作。比如将来会在实体中反复调用的代码段,我们需要用到工厂。

非静态工厂实例化

我们写一个非静态工厂

代码语言:javascript复制
package jgdabc.factory;

import jgdabc.dao.BookDao;
import jgdabc.dao.impl.BookDaoImpl;
//非静态
public class BookDaoFactory01 {
   public BookDao getBookdao()
   {
       return new BookDaoImpl();
   }
}

非静态的话,我们是不能直接调用方法的。我们需要new出来这个类,然后又通过这个类的实例来调用方法。

代码语言:javascript复制
   BookDaoFactory01 bookDaoFactory01 = new BookDaoFactory01();
   BookDao bookdao = bookDaoFactory01.getBookdao();
   bookdao.save();

然后我们同样来让Spring管理。 这回我们和上面有一些不同的是,我们需要这样配置。

代码语言:javascript复制
<!--    非静态工厂-->
    <bean id="daoFactory" class="jgdabc.factory.BookDaoFactory01"></bean>
    <bean id="bookDao3" factory-bean="daoFactory" factory-method="getBookdao"></bean>
代码语言:javascript复制
  BookDao bookDao2 = (BookDao)ctx.getBean("bookDao2");
  bookDao2.save();

FactoryBean的简化使用

我们可以看到工厂实例化其实并不是很方便。于是我们提供FactoryBean方式来简化这种操作。

我们这样写

代码语言:javascript复制
package jgdabc.factory;

import jgdabc.dao.BookDao;
import jgdabc.dao.impl.BookDaoImpl;
import org.springframework.beans.factory.FactoryBean;

public class BookDaoFactoryBean implements FactoryBean<BookDao> {

//代替原始实例工厂中创建对象的方法
    @Override
    public BookDao getObject() throws Exception {
       return new BookDaoImpl();
    }
// 这里我们返回所创建类的class对象
    @Override
//    类型
    public Class<?> getObjectType() {
        return BookDao.class;
    }
}

然后我们在配置文件中这样写。

代码语言:javascript复制
  <bean id="bookDao4" class="jgdabc.factory.BookDaoFactoryBean"></bean>
代码语言:javascript复制
   BookDao bookDao2 = (BookDao)ctx.getBean("bookDao4");
        bookDao2.save();

另外还有这个方法

代码语言:javascript复制
 public boolean isSingleton() {

        return true;
    }

另外两个 getObject(),被重写后,在方法中进行对象的创建并返回 getObjectType(),被重写后,主要返回的是被创建类的Class对象

总结 bean是通过无参构造方法创建的。 Spring的IOC实例化对象的三种方式分别是: 构造方法(常用) 静态工厂(了解) 实例工厂

bean的生命周期

生命周期值得就是一个对象从创建到销毁的过程。 我们首先在dao层实现类写这样两个方法

代码语言:javascript复制
package jgdabc.dao.impl;

import jgdabc.dao.BookDao;

public class BookDaoImpl implements BookDao {
//   public BookDaoImpl() {
//        System.out.println("book dao constructor is running...");
//    }
//    代表bean初始化的操作
    public void init()
    {
        System.out.println("init---");
    }
//    表示bean销毁前的操作
    public void destory()
    {
        System.out.println("destory---");
    }

    public void save() {
        System.out.println("book dao save ...");
    }
}

然后我们在配置中指定哪个是初始化方法和销毁方法。 像这样。

代码语言:javascript复制
  <bean id="bookDao" name="sima" class="jgdabc.dao.impl.BookDaoImpl"init-method="init" destroy-method="destory"></bean>

然后我们在测试类中运行一下

但是我们发现init方法执行了。但是为什么destory方法没有执行。我们不是配置了吗?

原因如下

Spring的IOC容器是运行在JVM中 运行main方法后,JVM启动,Spring加载配置文件生成IOC容器,从容器获取bean对象,然后调方法执行 main方法执行完后,JVM退出,这个时候IOC容器中的bean还没有来得及销毁就已经结束了所以没有调用对应的destroy方法

既然是容器的话,我们如果将容器关闭的话,所以这个对象bean不也就被销毁了吗?

所以我们尝试关闭容器。 但是我们发现我们之前用的这个类并没有close方法。

需要将ApplicationContext更换成ClassPathXmlApplicationContext

代码语言:javascript复制
   ClassPathXmlApplicationContext ctx01 = new ClassPathXmlApplicationContext("ApplicationContext.xml");
   BookDao bookDao3 = (BookDao)ctx01.getBean("bookDao");
   bookDao3.save();
   ctx01.close();

还有一件事,如果我们在配置里面这样设置

这样的话,销毁方法也是不会执行到的。

这是一种销毁的方式。另外还有一种方法,就是注册钩子。

我们先看如何使用。 我们用到这个方法 ctx.registerShutdownHook();

代码语言:javascript复制
  ClassPathXmlApplicationContext ctx01 = new ClassPathXmlApplicationContext("ApplicationContext.xml");
     ctx01.registerShutdownHook();
     BookDao bookDao3 = (BookDao)ctx01.getBean("sima");
     bookDao3.save();
//     ctx01.close();

这样也是可以做到销毁对象的。

我们比较这两个方法的不同。

close()是在调用的时候关闭,registerShutdownHook()是在JVM退出前调用关闭。

另外Spring提供了两个接口来帮助我们实现对生命周期的控制。 我们尝试将实现加在dao的实现类上。

代码语言:javascript复制
package jgdabc.dao.impl;

import jgdabc.dao.BookDao;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class BookDaoImpl implements BookDao, InitializingBean, DisposableBean {
//   public BookDaoImpl() {
//        System.out.println("book dao constructor is running...");
//    }
//    代表bean初始化的操作
  /*  public void init()
    {
        System.out.println("init---");
    }*/
//    表示bean销毁前的操作
//    public void destory()
//    {
//        System.out.println("destory---");
//    }

    public void save() {
        System.out.println("book dao save ...");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("destory--");

    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("init---");

    }
}

然后我们在测试类中测试一下。看看是什么情况。

代码语言:javascript复制
import jgdabc.dao.BookDao;
import jgdabc.factory.BookDaoFactory;
import jgdabc.factory.BookDaoFactory01;
import jgdabc.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App2 {
    public static void main(String[] args) {
//        获取ioc容器(控制反转的)
//     ApplicationContext ctx =  new ClassPathXmlApplicationContext("ApplicationContext.xml");
     ClassPathXmlApplicationContext ctx01 = new ClassPathXmlApplicationContext("ApplicationContext.xml");
     ctx01.registerShutdownHook();
     BookDao bookDao3 = (BookDao)ctx01.getBean("sima");
     bookDao3.save();
//     ctx01.close();


     
// 获取bean
//        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
//        System.out.println(bookDao);
        bookDao.save();
//        BookDao bookDao1 = (BookDao) ctx.getBean("bookDao");
//        System.out.println(bookDao1);
//        BookService bookService = (BookService) ctx.getBean("bookService");
//        bookService.save();
//        自己是使用工厂
//        BookDao bookDao1 = BookDaoFactory.getBookDao();
//        bookDao1.save();
//        BookDao bookDao2 = (BookDao)ctx.getBean("bookDao4");
//        bookDao2.save();
//        BookDaoFactory01 bookDaoFactory01 = new BookDaoFactory01();
//        BookDao bookdao = bookDaoFactory01.getBookdao();
//        bookdao.save();
    }

}

这里初始和销毁都是三次。 初始是怎么回事? 我们将其他注释掉,只留下获取IOC容器这段代码。

然后看输出。

于是从这里可以看出,其实在获取IOC容器的时候这里是一定会初始化的,会调用到这个方法。那为什么是三个?

因为我们的配置文件。

有多少初始,销毁的话就会销毁多少。这个是容易理解到的。当前销毁会被执行需要我们去调用到关闭,还有或者就是注册钩子。

这种方法我们实现了Spring给到的类,所以不需要在配置文件中写了。

DI 依赖注入方式

setter注入

我们在之前对DI的部分介绍中介绍过这种注入方法,我们传bean对象的时候。而且这种·注入是注入的引用数据类型。

我们这样注入的是引用类型,如果我们注入的是简单类型呢?

然后在配置文件中 我们这边指定的是value

构造器注入

构造器注入一定是需要构造方法的。我们把setter方法删除掉。提供一个构造方法。

我们先使用构造方法对引用数据类型注入。

我们需要在配置文件这样去写。

然后我们尝试注入简单类型。

那么我们在构造方法中需要添加一个参数

存在的问题就是这种方式其实需要我们执行名字,如果代码中属性的名字发生改变,这里也需要进行改变。这样其实还是耦合度还是很高的。

我们试图尝试其它的办法来降低这种耦合度。

我们按照类型注入。具体方式如下。 简单类型和引用类型我们都可以这样做。

如果构造方法中有相同的参数,这种参数类型的匹配似乎也会出现问题。于是我们增加了索引。我们可以使用索引匹配。

还是会有的问题就是如果参数位置发生变化的话,这种匹配方法也就不合适了。

当然为了精准你可以配合一些操作

代码语言:javascript复制
 <bean ...>
	<constructor-arg name="" index="" type="" ref=""/>
	</bean>

其实推荐的话,相比较构造器注入和setter注入的话还是自己使用setter是比较方便操作的。

自动装配

自动装配注入的话分为按类型注入,按照构造方法注入,不过这个是不常用的。我们常用的就是按照类型注入。 这种装配呢,少不了我们的setter方法。

举个例子。

并且呢!自动装配的局限在智能用于引用类型的装入。

注意点

  1. 自动装配用于引用类型依赖注入,不能对简单类型进行操作
  2. 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
  3. 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
  4. 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效

集合注入

专门针对集合(数组,List,Set,Map,Propertie)这些系列

我们定义这些数据结构

另外我们需要生成setter方法。

自己快捷生成,然后写配置。哦对了,为了测试是否注入成功。我们可以输出他们。

配置的话这样配就好啦

代码语言:javascript复制
<bean id="bookDao" name="sima" class="jgdabc.dao.impl.BookDaoImpl">
        <property name="array">
            <array>
                <value>100</value>
                <value>200</value>
                <value>300</value>
            </array>
        </property>
        <property name="list">
            <list>
                <value>dsdsf2</value>
                <value>dsdsf2</value>
                <value>dsdsf3</value>
            </list>
        </property>

        <property name="map">
          <map>
              <entry key="jgdabc01" value="1"></entry>
              <entry key="jgdabc02" value="2"></entry>
              <entry key="jgdabc03" value="3"></entry>
          </map>
        </property>
        <property name="set">
            <set>
                <value>666</value>
                <value>777</value>
                <value>888</value>
            </set>
        </property>
        <property name="properties">
            <props>
                <prop key="u">china</prop>
                <prop key="v">ksdndsk</prop>
                <prop key="w">nsdaksda</prop>
            </props>
        </property>
    </bean>

然后测试类

代码语言:javascript复制
      ClassPathXmlApplicationContext ctx01 = new ClassPathXmlApplicationContext("ApplicationContext.xml");
//     ctx01.registerShutdownHook()
      BookDao bookDao = (BookDao) ctx01.getBean("bookDao");
      bookDao.save();

注入成功。

配置几乎一样,只有最后一个有点区别。

—未完续更。

0 人点赞