Spring动力节点老杜课程学习笔记

没有Spring的时候我们如何进行开发的?

没有Spring为我们服务的,写代码一定是这样的

调用层:

 public static void main(String[] args) {
   
  		//创建视图层对象
        TestController testController = new TestController();
         //调用视图层方法
        testController.test();
    }

视图层:

  //视图层是这么写的
  public class TestController {
   
	DataBaseService testService = new DataBaseServiceImpl();
    public void test(){
   
        testService.testMethod();
    }

  }

业务层:

public class DataBaseServiceImpl implements DataBaseService {
   
     DataBaseDao mysqlDao= new MysqlDaoImpl();
    @Override
      public void testMethod() {
   
        mysqlDao.testMethodDao();
    }
}

持久层:

public class MysqlDaoImpl implements DataBaseDao {
   
    @Override
    public void testMethodDao() {
   
        System.out.println("mysql方法被调用了");
    }
}

这样的流程虽然能够正常调用到方法,如下
在这里插入图片描述

但是存在一个问题:

如果现在除了MySQL的数据源,业务上面要扩展一个Oracle库,只能如下操作
在业务层中多一种实现方式,修改原来的代码

public class DataBaseServiceImpl implements DataBaseService {
   

    DataBaseDao mysqlDao = new MysqlDaoImpl();
    DataBaseDao oracleDao = new OracleDaoImpl();
    
    @Override
    public void testMethod() {
   
        mysqlDao.testMethodDao();
    }

    @Override
    public void testMethod2() {
   
        oracleDao.testMethodDao();
    }
}

OCP原则

开闭原则,英文缩写OCP,全称Open Closed Principle。

原始定义:
Software entities (classes, modules, functions) should be open for extension but closed for modification。

字面翻译:
软件实体(包括类、模块、功能等)应该对扩展开放,但是对修改关闭。

为什么要“开”和“闭”
一般情况,我们接到需求变更的通知,通常方式可能就是修改模块的源代码,然而修改已经存在的源代码是存在很大风险的,尤其是项目上线运行一段时间后,开发人员发生变化,这种风险可能就更大。所以,为了避免这种风险,在面对需求变更时,我们一般不修改源代码,即所谓的对修改关闭。不允许修改源代码,我们如何应对需求变更呢?答案就是我们下面要说的对扩展开放。

通过扩展去应对需求变化,就要求我们必须要面向接口编程,或者说面向抽象编程。所有参数类型、引用传递的对象必须使用抽象(接口或者抽象类)的方式定义,不能使用实现类的方式定义;通过抽象去界定扩展,比如我们定义了一个接口A的参数,那么我们的扩展只能是接口A的实现类。总的来说,开闭原则提高系统的可维护性和代码的重用性。

明显,上述代码就不符合开闭原则。

DIP原则

依赖倒置原则(Dependence Inversion Principle),简称DIP。
高层模块不应该依赖于低层模块,两者都应该依赖其抽象。
抽象不应该依赖细节。细节应该依赖抽象。
这里的低层模块就是不可分割的原子逻辑,原子逻辑再组装就成了高级模块。
抽象是指接口或者抽象类,两者都是不能直接被实例化;
细节就是实现类,实现接口或继承抽象类而产生的类,可以直接被直接实例化。
所谓的能否直接实例化也就在于是否可以通过关键字new产生的一个对象。
简单来说就是模块之间的依赖都是通过抽象发生,实现类之间并不直接产生依赖关系,其依赖关系都是通过接口或者抽象类产生的。
实现类依赖接口或抽象类,但接口或抽象类不依赖实现类。这也正好对应面向接口编程。

为了符合DIP原则,我们刚才的代码就应该这么写,不能引入Service的实现类了,要引入service的接口:

public class DataBaseController {
   
	//但是这时候有另一个问题,就是testService现在是null
	//因为还没有用Spring框架进行对象管理,它替我们管理以后就不会是null了
    DataBaseService testService;
    public void test(){
   
        testService.testMethod();
        testService.testMethod2();
    }
}

其实,这种写法就是控制反转的思想。

控制反转

控制反转: IoC (Inversion of Control)
反转是什么呢?
反转的是两件事:
第一件事:
我不在程序中采用硬编码的方式来new对象了。
(new对象我不管了,new对象的权利交出去了。)
第二件事:
我不在程序中采用硬编码的方式来维护对象的关系了。
(对象之间关系的维护权,我也不管了,交出去了。)
控制反转:是一种编程思想。或者叫做一种新型的设计模式。
由于出现的比较新,没有被纳入GOF23种设计模式范围内。

Spring中的IoC

  • Spring框架实现了控制反转IOC这种思想
    Spring框架可以帮你new对象。
    Spring框架可以帮你维护对象和对象之间的关系。
    *Spring是一个实现了IoC思想的容器。
    *控制反转的实现方式有多种,
    其中比较重要的叫做: 依赖注入(Dependency Injection,简称DI).
    依赖注入DI,又包括常见的两种方式:
    第一种: set注入(执行set方法给属性赋值)
    第二种:构造方法注入(执行构造方法给属性赋值)
    依赖注入 中“依赖"是什么意思?"注入"是什么意思?
    依赖:A对象和B对象的关系。
    注入:是一种手段,通过这种手段,可以让A对象和B对象产生关系
    依赖注入:对象A和对象B之间的关系,靠注入的手段来维护。而注入包括: set注入和构造注入。

Spring中八大模块

在这里插入图片描述

Spring特点

轻量

1.从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有 1MB多的JAR文件里发布,并Spring所需的处理开销也是微不足道的
2.Spring是非侵入式的: Spring应用中的对象不依赖于Spring的特定类

控制反转

Spring通过一种称作控制反转 (loC) 的技术促进了松耦合。当应用了loC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为loC与JND相反一不是对象从容器中查找依赖,而是容器在对象初始化时不等对象清求就主动将依赖传递给它。

面向切面

Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻与系统级服务(例审计(auditing)和事务transaction)管理)进行内聚性的开发。
应用对象只实现它们应该做的一一完成业务逻辑一仅此而已。它们并不负责甚至是意识)其它的系统级关注点,例如日志或事务支持.

容器

Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每bean何被创建一基于一个可面置原型(prototype),你的oean可以创建一个单独的实例或者每次需要时都生成一个新的实例-以及它们是如何相与关联的。然而,Sring不应该被混同于传统的重量级的EJB容器,它价经常是庞大与笨重的,难以使用。

框架

Spring可以将简单的组件雷置、组合成为复杂的应用,在Soring中,应用对象被声明式地组合,典型地是在一个XML文件里,Spring也提供了很多基础功能(事务管理、持久化框架集成等等) ,将应用逻辑的开发留给了你。
所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持

Spring的下载

一般在开发中,都直接用Idea自动引入Spring,下面演示如何自己去下载Spring
首先进入下面这个网站

Spring中文网

https://spring.p2hp.com/

项目下的Spring Framework就是Spring框架
在这里插入图片描述
点击GitHub
在这里插入图片描述
往下滑找到二进制文件,点击链接
在这里插入图片描述
进来之后点击仓库地址
在这里插入图片描述
依次点击文件目录
在这里插入图片描述
在这里插入图片描述
找到对应版本Spring,进行下载
在这里插入图片描述

Spring项目中常用的依赖

如果使用的是里程碑版本的Spring,如6.0.0,需要引入如下仓库依赖

<!--配置多个仓库-->
<repositories>
	<!--spring里程碑版本的仓库-->
	<repository>
		<id>repository.spring.milestone</id>
		<name>Spring Milestone Repository</name>
		<url>https://repo.spring.io/milestone</url>
	</repository>
</repositories>

Spring中最基础的依赖,SpringContext依赖

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>6.0.0-M2</version>
</dependency>

引入该依赖后会自动引入IOC与AOP相关的jar包
在这里插入图片描述

Spring如何管理对象的

比如我们现在有个User对象

public classUser{
   
}

那么我们可以新建一个Spring.xml配置文件来管理它

 <?xml version="1.0" encoding="UTF-8"?>
 <beans 
 xmins="http://www.springframework.org/schema/beans"
xmins:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemalocation="ttp://www.sorinaframerork.org/schemabeans http://ww.springframework.org/schemabeans spring-beans.xsd">
<!--这就是Spring的配智文件-->

	<bean id="userBean" class="com.powernode.spring6.bean.User">
</beans>

注意
1.IDEA工具为我们提供了这个文件的桃板,一定要使用这个模板来创建
2.这个文件名不一定叫做spring.xml,可以是其它名字
同时,这个配置文件也可以有多个,在不同的地方可以分别进行调用。
3.这个文件最好是放在类路径当中,方便后期的移植
4.放在resources根目录下,就相当于是放到了类的根路径下
5.配bean,这样spring才可以帮助我们管理这个对象
6.bean标签的两个重要属性:
id:是这个bean的身份证号,不能重复,是唯一的标识。
cLass: 必须城写类的全路径,全限定类名。(带包名的类名)

设置好配置文件之后,可以像如下代码这样来使用配置文件读取对象

public void testFistSpringCode(){
   
	ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
	Object userBean = applicationContext.getBean(name:"userBean");
	System.out.println(userBean):
}

步骤:
第一步:获取Spring容器对象。
ApplicationContext 翻泽为:应用上下文。其实就是Spring容器。
ApplicationContext 是一个接口
ApplicationContext 接口下有很多实现类。
其中有一个实现类叫做: CLassPathXmlApplicationContext
cLassPathXmLApplicationContext 是专门从类路径当中加载spring配置文件的一个Spring上下文对象。
这行代码只要执行:就相当于启动了Spring容器,解析spring.xml文件,并且实例化所有的ean对象,放到spring容器当中。
第二步:根据bean前id从Spring容器中获取这个对象。

Spring是怎么实例化对象的

默认情况下Spring 会通过反射机制,调用类的无参数构造方法来实例化对象

实现原理如下:
CLass clazz = Class.forName(“com.powernode.spring6.bean.User”);
clazz.newInstance();
注意
如果定义了有参构造方法那么JVM就不会在提供无参数构造了,
所以如果我们定义了有参构造那么无参构造也要显示的定义出来,
否则就无法通过反射创建对象了。

实例化对象之后存到什么数据结构中?

实际存到了一个map中
在这里插入图片描述

Spring配置文件可以同时存在多个吗?

可以的,ClassPathXmlApplicationContext可以传参多个配置文件,同时使用

ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("spring6.xml","beans.xml");

Spring在配置文件中配置的类必须是自定义的吗,可以使用JDK中的类吗,例如: java.util.Date?

可以,使用方法都是一样的

Spring配置文件中getBean()方法调用时,如果指定的id不存在会怎样?

会报错,而不是返回空对象
在这里插入图片描述

Spring配置文件中getBean()方法返回的对象类型是什么?如何处理?

返回类型是Object

可以强制类型转换
Date nowTime = (Date) applicationContext.getBean(“nowTime”);

也可以在第二个参数传入要获取的类型
Date nowTime = applicationContext.getBean(“nowTime”, Date,class);

ClassPathXmlApplicationContext是从类路径中加载配置文件,如果没有在类路径当中,又应该如何加载配置文件呢?

比如Spring的配置文件现在没有在类的路径下,在D盘根路径中,如下所示
在这里插入图片描述

可以采取这种方式引入

ApplicationContext applicationContext = new FileSystemXmlApplicationContext("d:/spring6.xml");

ApplicationContext接口的来源

它的超级父接口是:BeanFactory
(翻译Bean工厂,就是能够生产Bean对象的一个工厂对象。)
BeanFactory.是IoC容器的顶级接口。
Spring的IoC容器底层实际上使用了:工厂模式。
Spring底层的IoC是怎么实现的?XML解析+工厂模式+反射机制

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring6.xml")

依赖注入方式一:set方法注入

采用xml文件配置对象之后,能够创建对象了,但是如下这样调用

public class UserService {
   
	private UserDao userDao;
	public void saveUser(){
   
		userDao.insert();
	}
}

会报错空指针异常,因为xml 文件目前仅能够创建对象,而没有实现对象注入的功能。
可以通过如下set方法方式进行注入

	<!--配置dao-->
	<bean id="userDaoBean"class="com.powernode.spring6.dao.UserDao"/>
	<--配置service-->
	<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
		<property name="mySQLUserDao" ref="userDaoBean"/>
	</bean>

注意

  1. 想让Spring调用对应的set方法,需要配置property标签
  2. name属性怎么指定值:set方法的方法名,去掉set,然后把剩下的单词首字母变小写,写到这里
  3. ref翻译为引用。英语单词:refrences。ref后面指定的是要注入的bean的id
  4. 需要存在对应的set方法才能生效,如下所示
public class UserService {
   
	private UserDao userDao;
	public void setMySQLUserDao(UserDao xyz){
   
		this.userDao xyz;
	}
}

依赖注入方式二:构造方法注入

index属性指定参数下标。
第一个参数是0,第二个参数是1,第三个参数是2,以此类排。
ref属性用来指定注入bean的id

<bean id="userDaBean"class="com.powernode.spring6.dao.UserDao"/>
<bean id="vipDaoBean"class="com.powernode.spring6.dao.VipDao"/>
<bean id="csBean"class="com.powernode.spring6.service.CustomerService">
	<!--构造注入-->
	//index属性指定参数下标,第一个参数是0,第二个参数是1,第三个参数是2,以此类排。ref属性用来指定注入bean的id
	//指定构造方法的第一个参数,下标是0
	<constructor-arg index="0" ref="userDaoBean"/>
	//指定构渣方法的第二个参数,下标是1
	<constructor-arg index="1" ref="vipDaoBean"/>
</bean>

需要有指定的构造方法

public class CustomerService
	private UserDao userDao;
	private VipDao vipDao;
	//指定构造方法
	public CustomerService(UserDaouserDao,VipDao vipDao){
   
		this.userDao userDao;
		this.vipDao vipDao;
	}
	public void save(){
   
		userDao.insert();
		vipDao.insert();
	}
}

注意
1.这里面的index属性也可以换成name,同样会生效

<constructor-arg name="0"ref="userDaoBean"/>

2.不用name也不用index,只用ref也可以
这种方式实际上是根据类型进行注入的。
spring会自动根据类型来判断把ref注入给哪个参数。

<constructor-arg ref="userDaoBean"/>

Set注入:Spring配置文件中的外部bean和内部bean是什么

内部bean,property标签中使用嵌套bean标签

<bean id="orderServiceBean2" class="com.powernode.spring6.service.OrderService">
	<property name="orderDao">
		<bean class="com.powernode.spring6.dao.OrderDao">
		</bean>
	</property>
</bean>

外部bean,通过ref来引用其他的Bean

<bean id="userDaoBean"class="com.powernode.spring6.dao.UserDao"/>
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
	<property name="mySQLUserDao" ref="userDaoBean"/>
</bean>

Set注入: 如何注入简单类型

如果是给简单类型赋值,就不能使ref了。就需要使value了

<bean id="userBean"class="com.powernode.spring6.bean.User">
	<property name:="username" valve="张三"/>
	<property name="password"  value="123"/>
	<property name="age"       value="20"/>
</bean>

Set注入: 简单类型都包括哪些

可以进入BeanUtil类中的这个方法,里面列举了简单类型

public static boolean isSimpleValueType(Class<?>type){
   
	return (Void.class !type &void.class !type &
			(Classutils.isPrimitiveorWrapper(type)
			Enum.class.isAssignableFrom(type)
			CharSequence.class.isAssignableFrom(type)
			Number.class.isAssignableFrom(type)
			Date.class.isAssignableFrom(type)
			Temporal.class.isAssignableFrom(type)
			URI.class =type
			URL.class =type
			Locale.class =type
			Class.class =type));
}

对于这些简单类型,都可以通过value来进行对象赋值

<bean id="svt"class="com.powernode.spring6.bean.SimpleValueType">
	<property name="age"value="20"/>
	<property name="age2"value="20"/>
	<property name="username"value="zhangsan"/>
	<property name="season"value="SPRING"/>
	<property name="flag"value="false"/>
	<property name="flag2"value="true"/>
	<property name="c"value=""/>
	<property name="c2"value=""/>
	<property name="clazz" value="java.Lang.String"/>
</bean>

但是上述类型中有一个比较特殊的类型,Date类型直接用value会失败

<property name="birth" value="1970-10-11"/>

报错信息如下
在这里插入图片描述
这个报错是因为时间的格式不对
我们可以发现,时间的默认格式是这样的
在这里插入图片描述
经测试,这种格式是正确的

<property name="birth"value="Wed Oct 19 16:28:13 CST 2022"/>

但是实际开发中这种格式很麻烦,所以日期格式一般都用ref来引入

Set注入: 级联属性赋值

比如现在有一个班级类

package com.powernode.spring6.beans;

/**
 * @author 动力节点
 * @version 1.0
 * @className Clazz
 * @since 1.0
 **/
public class Clazz {
   
    private String name;

    public Clazz() {
   
    }

    public Clazz(String name) {
   
        this.name = name;
    }

    public String getName() {
   
        return name;
    }

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

    @Override
    public String toString() {
   
        return "Clazz{" +
                "name='" + name + '\'' +
                '}';
    }
}

还有一个学生类

package com.powernode.spring6.beans;

/**
 * @author 动力节点
 * @version 1.0
 * @className Student
 * @since 1.0
 **/
public class Student {
   
    private String name;
    private Clazz clazz;

    public Student() {
   
    }

    public Student(String name, Clazz clazz) {
   
        this.name = name;
        this.clazz = clazz;
    }

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

    public void setClazz(Clazz clazz) {
   
        this.clazz = clazz;
    }

    public Clazz getClazz() {
   
        return clazz;
    }

    @Override
    public String toString() {
   
        return "Student{" +
                "name='" + name + '\'' +
                ", clazz=" + clazz +
                '}';
    }
}

可以如下这样,在学生类中直接给班级类的属性赋值

<?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="clazzBean" class="com.powernode.spring6.beans.Clazz"/>

    <bean id="student" class="com.powernode.spring6.beans.Student">
        <property name="name" value="张三"/>

        <!--要点1:以下两行配置的顺序不能颠倒-->
        <property name="clazz" ref="clazzBean"/>
        <!--要点2:clazz属性必须有getter方法-->
        <property name="clazz.name" value="高三一班"/>
    </bean>
</beans>

测试程序

@Test
public void testCascade(){
   
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-cascade.xml");
    Student student = applicationContext.getBean("student", Student.class);
    System.out.println(student);
}

运行结果

要点:
● 在spring配置文件中,如上,注意顺序。
● 在spring配置文件中,clazz属性必须提供getter方法。

Set注入: 注入数组

当数组中的元素是简单类型:

package com.powernode.spring6.beans;

import java.util.Arrays;

public class Person {
   
    private String[] favariteFoods;

    public void 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值