axios案例应用

1、Spring概述

Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control: 反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring MVC 和持久层。Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多 著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架

2、spring发展历程

1997 年 IBM 提出了 EJB 的思想

1998 年,SUN 制定开发标准规范 EJB1.0

1999 年,EJB1.1 发布

2001 年,EJB2.0 发布

2003 年,EJB2.1 发布

2006 年,EJB3.0 发布

Rod Johnson(spring之父) ,Expert One-to-One J2EE Design and Development(2002) 阐述了 J2EE 使用 EJB 开发设计的优点及解决方案。Expert One-to-One J2EE Development without EJB(2004) 。阐述了 J2EE 开发不使用 EJB 的解决方式(Spring 雏形) ,2017年9月份发布了spring的最新版本spring 5.0通用版(GA)

3、spring的优势

				(1)、方便解耦,简化开发:IOC

					通过 Spring 提供的 IoC容器,可以将对象间的依赖关系交由 Spring进行控制,避免硬编码所造 成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这					些很底层的需求编写代码,可以更专注于上层的应用。

				(2)、AOP编程的支持

					通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP实现的功能可以。通过 AOP轻松应付。

				(3)、声明式事务的支持

					可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理, 提高开发效率和质量。

				(4)、方便程序的测试

					可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可 做的事情。

				(5)、方便集成各种优秀框架

					Spring 可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持

				(6)、降低 JavaEE API 的使用难度

					Spring 对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的 使用难度大为降低。

				(7)、Java 源码是经典学习范例

					Spring 的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对 Java设计模式灵活运用以 及对 Java技术的高深造诣。它的源代码无疑是 Java技术的最佳实践的范例。

4、spring体系结构

二、Spring入门案例のHelloWorld

和大多数框架一样,使用第三方的框架,基本步骤:

1.引入框架使用需要的依赖******

2.配置文件(格式大多数xml格式或yaml、properties)

3.spring提供的API完成对象的获取和实例化

案例代码

  • pom.xml导入spring依赖

      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.3.28</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.28</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.28</version>
    </dependency>
    
  • spring配置文件

    <?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"
           xmlns:c="http://www.springframework.org/schema/c"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                               https://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/context
                               https://www.springframework.org/schema/context/spring-context.xsd
                               http://www.springframework.org/schema/aop
                               https://www.springframework.org/schema/aop/spring-aop.xsd
                               http://www.springframework.org/schema/tx
                               https://www.springframework.org/schema/tx/spring-tx.xsd">
        <!--  spring配置文件中配置对象的基本信息 -->
        <bean id="myUserDaoImpl1" class="com.woniu.dao.impl.UserDaoImpl" scope="prototype"></bean>
    </beans>
    
  • 创建Person类型,利用spring获取Person对象

    通过案例代码的运行,我们会发现,不用写new UserDaoImpl()也能得到UserDaoImpl对象。解决了传统自己new对象的"硬编码"问题。

思考1:Spring中获取的对象是单例的吗?

案例分析

  • UserDaoImpl.java

    public class UserDaoImpl implements UserDao {
    public UserDaoImpl() {
        System.out.println("UserDaoImpl构造器被调用");
    }

    @Override
    public int insert() {
        System.out.println("链接mysql数据库,完成用户信息");
        return 0;
    }

}

  • 测试类

    @Test  
  public void  mt01(){
   	ApplicationContext factory=new ClassPathXmlApplicationContext("ApplicationContext.xml");

    //获取特定的对象 spring容器装的对象,默认:单例的!!!
    Object obj1 = factory.getBean("myUserDaoImpl1");
    Object obj2 = factory.getBean("myUserDaoImpl1");
    System.out.println("obj1内存地址:"+obj1);
    System.out.println("obj2内存地址:"+obj2);

    System.out.println(obj1==obj2);

}

  • 测试结果

观察测试代码运行结果,可以得出结论:默认情况下,所有由Spring构建的对象都是单例对象。

思考2:如何从Spring容器得到一个多例对象呢?

我们可以通过设置bean标签的scope属性来改变对象在Spring容器中的创建方式:

<bean id="userDao" class="cn.woniu.dao.UserDao" scope="prototype"></bean>
  • 作用:
    用于注册对象信息,让 spring 来创建。
    默认情况下 它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。
  • 属性:
    id:给对象在容器中提供一个唯一标识。用于获取对象。
    class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
    scope:指定对象的作用范围。
    1、singleton :默认值,单例的.
    2、prototype :多例的.
    3、request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中
    4、session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
    5、global session :WEB 项目中,应用在 Portlet 环境 .如果没有 Portlet 环境那么 globalSession 相当于 session.

三、解读Helloworld案例代码

1、ApplicationContext是什么?

我们可以把ApplicationContext理解成是一个装载对象的"大容器",其本质就是一个对象工厂。借助IDEA快捷键Alt+Ctrl+U可以看到ApplicationContext实现过的所有父接口:

2、BeanFactory和ApplicationContext的区别【常见面试题】

ApplicationContext底层就是对象工厂(即BeanFactory)。Spring提供两种创建对象工厂的方式(BeanFactory和ApplicationContext两个接口)。这种方式创建对象工厂时有什么区别呢?

案例演示ApplicationContext

  • 在UserDaoImpl类中添加无参构造方法:代码略

  • 测试方法

    //1.创建spring ioc容器 ApplicationContext可以理解成是Spring容器,本质其实就是BeanFactory,职责就是负责获取对象

    //ApplicationContext:工厂建好,立马将需要工厂管理对象也一起建好

    ApplicationContext cnt=new ClassPathXmlApplicationContext("spring-config.xml");

  • 结果如下:

UserDaoImpl对象的构造器被调用,意味着UserDaoImpl对象在创建ApplicationContext时就被创建

案例演示BeanFactory

  • 修改测试方法

    //1.创建spring ioc容器 ApplicationContext可以理解成是Spring容器,本质其实就是BeanFactory,职责就是负责获取对象

    //BeanFactory延迟加载(也称为懒加载)创建工厂对象时,不会帮我们把工厂内部对象创建

    Resource resource=new ClassPathResource("spring-config.xml");

    BeanFactory cnt=new XmlBeanFactory(resource);

  • 结果如下:

    控制台什么也没输出,也就是UserDaoImpl对象的构造器没有调用,意味着UserDaoImpl对象在创建BeanFactory时没有被创建

通过以上案例对比,我们可以得出以下结论:

BeanFactory:SpringIoc容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用。

BeanFactory对象工厂实现的特点是:

构建核心容器时,创建对象采取的策略是延迟加载的方式,什么时候调用getBean根据id获取对象了,什么时候才真正创建对象。适用于多例模式

ApplicationContext:ApplicationContext是BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。

ApplicationContext实现的特点是:

在构建核心容器时,创建对象采取的策略是立即加载的方式,只要一读取完配置文件就马上创建配置文件中的配置对象。适用于单例模式

3、ApplicationContext的三个实现类【理解】

在入门Helloworld案例中,我们实例化spring的工厂对象时,使用的是ClassPathXmlApplicationContext对象,其实在ApplicationContext的继承体系中,除了ClassPathXmlApplicationContext这个实现类以外,还有另外两个子类也可以完成ApplicationContext对象工厂的实例化。

提示:idea中通过选中类,通过ctrl+H可以查看向下派生的继承体现,通过ctrl+alt+u可以查看该类向上的继承体系结构。

		在ApplicationContext对象上按ctrl+h查看该类的结构
ClassPathXmlApplicationContext:加载类路径下的配置文件,要求配置文件必须在类路径下(常用)*****
FileSystemXmlApplicationContext:加载磁盘任意路径下的配置文件(必须有访问权限) 
AnnotationConfigApplicationContext:读取注解配置容器

4、bean 的作用范围和生命周期 【面试题】

1、单例对象:scope="singleton"
  一个应用只有一个对象的实例。它的作用范围就是整个应用。
  生命周期:
   对象出生:当应用加载,创建容器时,对象就被创建了。
   对象活着:只要容器在,对象一直活着。 
   对象死亡:当应用卸载,销毁容器时,对象就被销毁了。

2、多例对象:scope="prototype"
  每次访问对象时,都会重新创建对象实例。
  生命周期:
   对象出生:当使用对象时,创建新的对象实例。不使用就不创建
   对象活着:只要对象在使用中,就一直活着。
   对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了。 

案例演示:bean的生命 周期

		创建StudentService类,在该类中添加:一个构造方法、一个init方法、一个destory方法

public class StudentService {
    //构造方法
    public StudentService(){
        System.out.println("创建对象实例");
    }

    //初始化方法
    public void init(){
        System.out.println("对象被创建了");
    }

    //销毁
    public void destory(){
        System.out.println("对象被销毁了");
    }
}

配置spring-config.xml

<!--  实例化StudentService  -->
    <bean id = "studentService"
          class="cn.woniu.service.StudentService"
          scope="singleton"
          init-method="init"
          destroy-method="destory">
    </bean>

创建测试方法

	我们发现,在===========上面时,对象就已经被创建了,这是单例对象的特点,立即加载,而且执行了studnetService类的init方法,但是我们并没有看到执行destory方法。原因很简单:当测试方法执行完时,线程结束,此时容器销毁,容器销毁意味着创建的单例对象也要销毁,只是此时没来得及打印。要想看到效果,我们需要手动让容器销毁,调用容器的close方法,此时对象就会销毁,即执行destory方法

如果我们把配置文件的对象改成多例模式呢?其它的代码不变

<!--  实例化StudentService  -->
    <bean id = "studentService"
          class="cn.woniu.service.StudentService"
          scope="prototype"
          init-method="init"
          destroy-method="destory">
    </bean>

改为多例模式后发现对象是懒加载的方式,即在用到的时候才会创建,而且我们手动关闭容器的时候也不会调用destory方法,原因很简单,多例对象的死亡是由java垃圾回收器回收的,不受容器管理。

四 IOC

IOC容器负责bean管理。什么是bean管理?从两个方面来理解:

  1. Spring容器负责为java项目创建对象(即IOC)
  2. Spring容器负责为创建的对象注入属性值(即DI)

Bean管理操作的方式有:

  • 基于xml配置文件方式实现
  • 基于注解方式实现
  • 通过JavaConfig配置bean

IOC概念

IOC: Inversion Of Control 控制反转。

以前:java程序使用对象:开发人员 new 对象 正向控制

spring:java程序使用对象:找Spring的ApplicationContext对象拿对象

翻译:new对象称为开发人员对对象的控制,将以前程序员自己new对象"权利" 交个Spring的spring容器统一管理的现象就是控制的权利反转了。所以,在spring中IOC的主要作用就是利用spring容器完成不同类对象的创建【实体类不会通过spring的ioc完成创建】进而解决程序中new对象时硬编码的问题。

IOC只解决程序间的依赖关系,除此之外没有任何功能

如何使用ioc完成对象创建呢?通常步骤有:

1.配置文件利用<bean></bean>配置你要spring容器管理的对象
2.获取spring工厂对象【或spring容器】
3.调用getBean根据id获取对象即可。

五 DI

DI:Dependency Injection 依赖注入

翻译:IOC只管对象创建,DI负责对象属性赋值

DI常用方式

  • setter注入方式
  • 构造器注入方式

setter注入方式

Set注入就是在类中提供需要注入成员的 setter方法,通过调用setter完成属性赋值

案例

Person.java代码

@ToString
public class Person {
    private Integer id;//1
    private String name;//张三丰

    public Person() {
        System.out.println("Person的构造器被调用了");
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

构造器注入

注入属性值的对象提供了带参构造器,而且还保持类有无参构造器

案例

Person.java添加构造器

public class Person {
    private Integer id;//1
    private String name;//张三丰

    public Person(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Person() {
    }
}

使用c命名空间和p命名空间简化构造器赋值和setter赋值【了解】

使用步骤:

  • xml文件根标签中引入p和c命名空间
  • 使用c:和p:作为前缀实现赋值

不同数据类型的属性注入

Spring不管entity类型,主要对三层架构对象进行ioc和di管理

8种基本类型及对应的包装类及String类型

赋值方式都是通过value属性赋值

使用setter赋值,完整语法
<property name="属性名" value="属性值"/>
使用构造器赋值,完整语法
<constructor-arg name/index/type="形参名" value="属性值"/>

自定义对象类型

使用setter赋值,完整语法
<property name="属性名" ref="引用的对象ID"/>
使用构造器赋值,完整语法
<constructor-arg name/index/type="形参名" ref="引用的对象ID"/>

集合和数组类型【了解】

常见类型:数组、List集合、Set集合、Map集合、Properties集合

利用c命名空间和p命名空间简化属性赋值【了解】

c命名空间简化构造器注入方式,举例:

p命名空间简化setter注入方式

使用步骤:

  • xml文件根标签中引入p和c命名空间
  • 使用c:和p:作为前缀实现赋值

六 DI自动装配机制【理解】

自动装配是根据自动的装配规则(byName属性名称或byType属性类型),Spring自动将匹配的属性值进行注入的方式。自动装配是spring DI的一种方式。

在Spring中有三种自动装配的方式:

  • 在xml中显式配置【理解】
  • 在javal类中显示配置【Spring5+的新特性】【重点】
  • 隐式的自动装配【注解方式,重点】

利用XML配置完成自动装配【理解】

自动装配常见异常

使用byType进行装配时,如果一个类型可以找到多个Bean对象,就会出现以下异常:

解决方案:利用byName进行装配

七 spring提供注解完成IOC和DI功能【实际开发都是注解完成ioc和di 重点】

IOC注解

@Component,@Repository,@Service,@Controller标记在类上,实现这个类由spring容器负责对象管理

作用类似xml配置

@Component:标注三层架构以外的类,比如全局异常、工具类、redis或minio工具类

spring为@Component衍生了三个注解,三个注解作用跟@Component一模一样,但是从词意来看,阅读性比@Component更好

@Repository:一般用在持久层
@Service:一般用在业务层
@Controller:一般用在控制器层

DI注解

@Value

完成8种基本类型和String类型的属性值注入。@Value的作用等价于以下代码:

<property name="" value='属性值'/> 
或
<constructor-arg name/index/type="" value="属性值"/>

@Autowired

完成自定义类型属性注入。@Autowired的作用等价于以下代码:

<bean autowired="byName/byType"/>    

@Autowired默认先根据byType,如果byType装配失败,退而其次,使用byName再装配,如果byName也失败,直接抛出异常

跟@Autowired注解一样,也可以为引用类型的对象属性注入值的的注解还有以下两个:

@Resource

@Resource注解与@Autowired注解的作用是一样的,都是用来为bean对象注入值的,唯一的区别是@Resource注解要根据实例化的bean名称为bean对象注入值。@Resource是javax包下注解,不是spring官方注解。

@Resource(value="byName属性名")

@Quanifier

byName注入,是spring提供的byName注解,它在给字段注入时不能独立使用,必须和@Autowire 一起使用,表示在自动按照类型注入的基础之上,再按照 Bean 的id 注入;@Quanifier也给方法形参注入,注入时可以独立使用。

语法:

@Quanifier(value="byName的名称")

小结注解开发

spring提供的注解,依据注解作用和使用位置不同,划分为三类:
1.注解在类上,作用IOC创建对象的作用:
  @Component   @Repository  @Service  @Controller

2.注解在属性上,作用DI属性注入值的作用:
  @Resource:byName
  @Quanifier:默认byName,但是不能独立使用在属性上,必须@Autowired结合使用
  @AutoWired:默认先byType,byType失败,再byName

  @Value:注入八种基本类型和String类型

3.注解在类上,设置对象单例模式还是多例模式,设置对象使用范围:[了解]
  @Scope(设置对象使用范围:propotype  singleton  session  request)

使用案例

  • UserDaoImpl.java

    //这个类是否会被别的层的类使用,如果会,这个类交给spring容器管理

    @Repository

    public class UserDaoImpl implements UserDao {

    @Override

    public int insert() {

    System.out.println("连接mysql数据库,执行insert操作...");

    return 0;

    }

    }

  • UserServiceImpl.java

    @Service

    public class UserServiceImpl implements UserService {

    //DI注入对象,注入方式

    @Autowired

    private UserDao userDao;

    @Override

    public void add() {

    userDao.insert();

    }

    }

  • UserController.java

    /**

    • 注解开发里面,如果使用setter注入方式,类中不需要提供setter都可以注入成功
      */
      @Controller
      public class UserController {
      @Autowired
      private UserService userService;
      public void service(){
      userService.add();
      }
      }
  • 单元测试

    @Test

    public void service() {

    ApplicationContext cnt=new ClassPathXmlApplicationContext("spring-config.xml");

    UserController controller = cnt.getBean("userController", UserController.class);

    controller.service();

    }

当我们再为UserDao接口提供一个新的实现类,代码如下所示:

  • UserDaoImpl2.java

    @Repository

    public class UserDaoImpl2 implements UserDao {

    public UserDaoImpl2() {

    System.out.println("UserDaoImpl2");

    }

      @Override
      public int insert() {
          System.out.println("连接oracle数据库,执行insert操作...");
          return 0;
      }
    

    }

重新执行单元测试,会发现控制台爆出异常:

如何解决呢?可以借助@Resource或@Qualifier指定注入对象的名称,代码如下所示:

  • UserServiceImpl.java

    @Service

    public class UserServiceImpl implements UserService {

    @Resource(name = "userDaoImpl2")//name属性设置注入对象的属性名,如果属性名和name注入的名称一致的,name可以不赋值

    private UserDao userDao;

    @Override

    public void add() {

    userDao.insert();

    }

    }

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    @Qualifier("userDaoImpl") //默认byName,但是注入属性时不能独立使用,必须和@Autowired一起结合使用
    private UserDao userDao;
    @Override
    public void add() {
        userDao.insert();
    }
}

如果,我们希望UserDaoImpl2是一个多例对象,也可以在类上通过@Scope注解注解对象的使用范围:

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Repository
public class UserDaoImpl2 implements UserDao {
    public UserDaoImpl2() {
        System.out.println("UserDaoImpl2");
    }

    @Override
    public int insert() {
        System.out.println("连接oracle数据库,执行insert操作...");
        return 0;
    }
}

测试多例模式

@Test
    public void test02() {
        ApplicationContext cnt=new ClassPathXmlApplicationContext("spring-config.xml");
        Object o1 = cnt.getBean("userDaoImpl2");
        Object o2 = cnt.getBean("userDaoImpl2");
        Object o3 = cnt.getBean("userDaoImpl2");

    }

控制台输出结果如下图所示:

扩展了解:程序的耦合和解耦

1、藕合和解藕的思路

			藕合就是程序间的依赖关系。解藕就是降低程序间的依赖关系。在开发过程中应做到编译期不依赖,运行时再依赖

			解藕思路:

				使用反射来创建对象,而不使用new关键字

				 通过读取配置文件来获取要创建的对象全限定名

2、曾经代码的问题

	创建java控制台程序

创建Dao类

创建service并调用Dao

创建测试类并调用service

层与层之间的关系通过new来实现调用,并且new对象时需要导入该对象所在的正确的包名,否则报错。这种关系称为藕合关系。

3、工厂类和配置文件解决藕合问题

a、创建properties属性文件

b、创建工厂类

package cn.woniu.utils;

import java.io.InputStream;
import java.util.Properties;

/**
 * 对象工厂
 */
public class BeanFactory {
    //定义一个Properties对象
    private static Properties properties;
    //使用静态代码块为Properties对象赋值
    static {
        try {
            properties = new Properties();
            //获取Properties文件流对象
            InputStream input = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            properties.load(input);

        } catch (Exception e) {
            throw new ExceptionInInitializerError("初始化Properties属性出错"+e.getMessage());
        }
    }

    /**
     * 根据Bean名称获取Bean对象
     *
     * @param beanName
     */
    public static Object getBean(String beanName) {
        Object bean = null;
        try {
            String beanPath = properties.getProperty(beanName);
            bean = Class.forName(beanPath).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }
}

c、使用工厂类调用各层

 service调用dao

测试调用service

相关推荐
2401_8576100315 分钟前
Spring Boot框架:电商系统的技术优势
java·spring boot·后端
希忘auto31 分钟前
详解MySQL安装
java·mysql
娅娅梨34 分钟前
C++ 错题本--not found for architecture x86_64 问题
开发语言·c++
汤米粥39 分钟前
小皮PHP连接数据库提示could not find driver
开发语言·php
冰淇淋烤布蕾42 分钟前
EasyExcel使用
java·开发语言·excel
拾荒的小海螺1 小时前
JAVA:探索 EasyExcel 的技术指南
java·开发语言
Jakarta EE1 小时前
正确使用primefaces的process和update
java·primefaces·jakarta ee
马剑威(威哥爱编程)1 小时前
哇喔!20种单例模式的实现与变异总结
java·开发语言·单例模式
白-胖-子1 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
好睡凯1 小时前
c++写一个死锁并且自己解锁
开发语言·c++·算法