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层,接口
package jgdabc.dao;
public interface BookDao {
public void save();
}
接口实现
package jgdabc.dao.impl;
import jgdabc.dao.BookDao;
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
service层 接口
package jgdabc.service;
public interface BookService {
public void save();
}
接口实现
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中需要导入什么
<?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代码。
package jgdabc.dao;
public interface BookDao {
public void save();
}
package jgdabc.dao.impl;
import jgdabc.dao.BookDao;
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
package jgdabc.service;
public interface BookService {
public void save();
}
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();
}
}
其实在这之前我们需要配置一下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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="jgdabc.dao.impl.BookDaoImpl"></bean>
<bean id="bookService" class="jgdabc.service.impl.BookServiceImpl">
</bean>
</beans>
至于相关标签上面注释说的很明白了。我们需要记住的是bean一定是和对象相关联的。
我们先使用这一部分。
我们这样去获取一个ioc容器,然后获取到对象,我们就可以实现对对象的调用。
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) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
当然你可以先运行这一段
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) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}
其实这里主要说明了,我可以获取到这个对象。当然这和配置是紧密相连的。另外从这里我们也可以知道ioc容器可以管理我们的bean对象。
我们还有新的问题就是,现在的service层,还是需要进行new对象。好下面我们对其解耦。
DI(依赖注入)案例
对的,这样是有道理的。但是我们如何给它传呢?依赖注入!
所以这里的改变在于service层实现类和配置的依赖注入。 具体如下。
<?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="bookDao" class="jgdabc.dao.impl.BookDaoImpl"></bean>
<bean id="bookService" class="jgdabc.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"></property>
</bean>
</beans>
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;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
然后测试
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) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");
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层接口类
package jgdabc.dao;
public interface BookDao {
public void save();
}
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层的接口和实现类
package jgdabc.service;
public interface BookService {
public void save();
}
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;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
然后我们在配置文件当中,还是那样写。 然后测试,注意我们这里一共有两个bean,我们一定要明白的是如果构造方法执行的话,一定会执行两次。我们来验证。
我们看到构造方法被调用了,由此可见,实例化的时候,这个无惨构造方法被调用了。
如果我们把这个构造方法
—未完续更。
|