Spring 零基础入门(spring入门书)
ccwgpt 2024-11-06 09:41 29 浏览 0 评论
一 Spring简介
1.1 为什么需要spring?
在软件设计中 ,有一种模式叫做 MVC 。称之为 Model 模型 View 视图 Controller 控制器。
但是我们的javaweb项目并没有完全实现mvc中所有的内容。因为我们没有事件驱动。
而javaweb在发展的时候 ,其实为玩 Model 1 、Model 1 二代 、Model 2
Model1 时期整个请求的流程:
Model 1 二代时期整个请求的流程:
Model2 时期整个请求的流程:
servlet负责 获取请求、处理业务、调用DAO、共享数据、选择视图
JAVAWeb由Model2 演变成 三层架构 :
在原始的三层架构中 ,我们都是使用正转进行操作的,例如:
当我们需要使用某一个类的对象时,直接通过new去创建该类的对象,这种操作就称之为 正转操作。
但是正转操作是一种高耦合(多个类之间关联比较密切,会出现如果一个类修改,会导致一系列的类都需要进行修改)的表现。以前java代码都是比较笨重的,称为诟病的就是因为都在进行正转操作。
此时我们试想一下,如果StudentServiceImpl这个类升级了,换成了StudentServiceImpl2,此时我们需要去所有使用StudentServiceImpl的地方将其换成StudentServiceImpl2。
所以正转操作不是很好,我们可以使用 工厂模式 来解决这个问题。
例如: People Man Women 都使用到了 Food这个类
我们如果直接正转操作使用 food 此时将来如果food变成food2 此时改起来不好改,所以我们使用简单工厂模式。
还有一个核心问题没有解决,当我们将工厂中的food换成food2的时候 ,此时并没有实现一个效果,所有的类都改完成, 而是全部报错
原因在于 工厂此时返回值为 Food2 而我们接受的类型为Food,此时类型不匹配。这个问题怎么解决?
可以创建一个接口,我们使用接口类型去接收实现类对象-----接口多态。
我们就解决了正转的问题;创建对象的时候不再是直接new对象 ----控制正转
而是将创建对象的操作放到工厂中,需要使用对象的时候去工厂中获取----控制反转 IoC
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。使用对象的时候不再是直接new,而是将创建对象的权利交给框架中的核心容器,需要使用对象的时候直接从容器中获取。
IoC的思想早就出现了,但是没有一个具体的实现,大家都是各自根据这个思想去写代码(自己去创建工厂)。
后来有一个大师 Rod Johnson(Spring Framework创始人,著名作者。 Rod在悉尼大学不仅获得了计算机学位,同时还获得了音乐学位。更令人吃惊的是在回到软件开发领域之前,他还获得了音乐学的博士学位。),写了一个框架,将IoC思想具体的实现了,这个框架后来起了个名字叫做 Spring。(因为在这之前 大家都是用hibernate这个框架,这个框架中文名字叫做冬眠,并且这个框架比较笨重,耦合比较高。Rod Johnson写的框架就是用来解决类似于hibernate高耦合的问题 ,所以他将自己框架的名字命名为 Spring 告诉全世界 冬天过去了 春天来了)
1.2 spring产品
spring的官方网站:https://spring.io/
我们可以看到spring有很多产品 spring已经变成了一个生态圈。但是我们平时还是需要将spring理解成Spring Framework
我们看一下spring-framework组成
Test: spring自带的单元测试
Core Container: 核心容器。这是IoC思想的关键,Spring负责创建对象 并且存放到 核心容器中。
AOP:面向切面编程
Web:与javaweb相关的组件
Data Access : 与数据库操作/JDBC操作相关组件
1.3 spring项目的构建
A 创建maven工程
B 在maven工程中添加spring的pom依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.0.RELEASE</version>
</dependency>
C src/main下创建一个文件夹 sources 并且将文件夹的格式改成resources root 。在里面添加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
https://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
D 创建一个测试类 测试spring的使用。
二 Spring的IoC操作
2.1 传统的正转操作
@Test
public void test1() throws Exception{
People people = new People();
people.eat();
}
public class People {
public void eat(){
System.out.println("人类都要吃饭");
}
}
2.2 通过spring核心容器获取
A 在xml中配置当前类的信息(告诉spring 有哪些类的对象 需要spring核心容器去管理)
<bean id="p1" class="com.aaa.test.People"></bean>
B 使用spring的工厂去获取对应的bean 。 BeanFactory
@Test
public void test1(){
ClassPathResource resource = new ClassPathResource("application.xml");
BeanFactory factory = new XmlBeanFactory(resource);
People p1 = (People) factory.getBean("p1");
p1.eat();
}
原理思考:
2.3 ApplicationContext获取bean
@Test
public void test2() {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
People p1 = (People) context.getBean("p1");
p1.eat();
}
面试题: ApplicationContext 和 BeanFactory的区别?
BeanFactory:是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能;
ApplicationContext:应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能;
1) 国际化(MessageSource)
2) 访问资源,如URL和文件(ResourceLoader)
3) 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层
4) 消息发送、响应机制(ApplicationEventPublisher)
5) AOP
并且二者之间对bean的初始化时机(创建对象的时机)是不一样的。
BeanFactory是遵循懒加载模式创建对象,也就是说BeanFactory被创建的时候并没有初始化bean,只有getBean的时候才去创建对象。
ApplicationContext是遵循饿汉模式创建对象,因为ApplicationContext对象被创建的时候,同时会初始化bean。
我们可以通过lazy-init设置成懒加载模式。
2.4 getBean()的三种方式
A 通过 id 或者 name的值获取bean
Object p1 = context.getBean("p1");
B 通过类型去获取
People bean = context.getBean(People.class);
确保bean的类型是唯一的 不然会报错:org.springframework.beans.factory.NoUniqueBeanDefinitionException
C 通过 id + 类型去获取
People bean = context.getBean("p1",People.class);
2.5 bean的属性配置
id : 当前bean的唯一标识符
class:配置类的全限定名
name:配置当前bean的名字,并且可以配置多个。以前使用的比较多,在spring3之前 name可以使用符号例如 : /stu 而id不可以。
后来id也可以添加符号例如/ 所以通常使用id比较多。
lazy-init : 配置bean的初始化时机 是否是懒加载。设置成true代表懒加载。还可以全局设定在beans标签中设定default-lazy-init="true"
scope:作用域 默认是 singleton单例的 prototype多例的 request请求 session会话 global session 全局会话
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
People bean1 = context.getBean("p1",People.class);
People bean2 = context.getBean("p1",People.class);
System.out.println(bean1 == bean2);//true
有的类 对象被创建的时候需要调用初始化函数,对象被回收的时候需要调用销毁函数。现在对象时由spring核心容器去管理的。
所以这些函数的调用应该由spring负责。我们只需要通过配置告诉spring初始化函数是谁,销毁函数是谁。bean的生命周期函数
init-method:
destroy-method:
<bean id="p1" name="aa bb cc" class="com.aaa.test.People" lazy-init="true" scope="singleton" init-method="hehe" destroy-method="haha"></bean>
@Test
public void test2() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
People bean1 = context.getBean("p1",People.class);
context.close();
}
注意,此时scope不能是多例的 否则销毁函数不会调用
三 DI依赖注入
3.1 对象关系----依赖关系
面向对象设计 对象间关系:依赖、关联、聚合和组合。
关联关系: 例如 人类和电脑类
某个对象会长期的持有另一个对象的引用,而二者的关联往往也是相互的。
public class People {
int id;
String name;
String nickname;
String address;
Computer c;
}
public class Computer {
int id;
String name;
String cpu;
}
@Test
public void test1(){
People people = new People();
people.id = 20;
people.name = "张三0";
people.c.name = "外星人";
}
依赖关系: Servlet 和service
所谓依赖就是某个对象的功能依赖于另外的某个对象,而被依赖的对象只是作为一种工具在使用,而并不持有对它的引用。
public class StudentServlet {
StudentService service;
}
3.2 依赖注入---DI Dependency Injection
当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在 传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在Spring里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者 实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为依赖注入。
依赖注入有两种:设值注入、构造注入
所谓依赖注入,是指程序运行过程中,如果需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部的注入。Spring的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对POJO之间依赖关系的管理。
在我们开发中会出现对象依赖的情况 ,例如 StudentServlet 依赖于 StudentService。在以前的时候我们都是直接在Servlet中new业务层Service对象,这是一个正转操作,会有高耦合。我们使用Spring框架,要遵循IoC,让对象的创建都交给Spring的核心容器。此时所有的对象都是Spring管理,也就是说 StudentServlet 是Spring 管理的,StudentService也是Spring管理的。
通过依赖注入 我们在类中需要使用另一个类的对象时,完全不需要自己创建了,而是让spring去帮我们创建并注入
IoC是一种思想,在在Spring中 DI 是这个思想的具体实现方式。
3.3 依赖注入的两种方式
A set方法注入
public class StudentServlet {
StudentService haha;
public void setHaha(StudentService haha) {
this.haha = haha;
}
@Override
public String toString() {
return "StudentServlet{" +
"haha=" + haha +
'}';
}
}
public class StudentService {
}
xml配置DI依赖注入的关系
<bean id="servlet" class="com.aaa.test1.StudentServlet">
<property name="haha" ref="hehe" ></property>
</bean>
<bean id="hehe" class="com.aaa.test1.StudentService"></bean>
调用代码:
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Object servlet = context.getBean("servlet");
System.out.println(servlet);
}
B 构造函数注入
public class StudentServlet {
StudentService haha;
public StudentServlet(StudentService haha) {
this.haha = haha;
}
@Override
public String toString() {
return "StudentServlet{" +
"haha=" + haha +
'}';
}
}
<bean id="servlet" class="com.aaa.test1.StudentServlet">
<constructor-arg name="haha" ref="hehe" ></constructor-arg>
</bean>
<bean id="hehe" class="com.aaa.test1.StudentService"></bean>
3.4 依赖注入的底层思考
反射是框架的灵魂
@Test
public void test2() throws Exception{
/*
<bean id="servlet" class="com.aaa.test1.StudentServlet">
<property name="haha" ref="hehe" ></property>
</bean>
<bean id="hehe" class="com.aaa.test1.StudentService"></bean>
*/
Class<?> aClass = Class.forName("com.aaa.test1.StudentServlet");
Object o = aClass.newInstance();
BeanInfo beanInfo = Introspector.getBeanInfo(aClass);
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor p:descriptors) {
if(p.getName().equals("haha")){
Method writeMethod = p.getWriteMethod();
writeMethod.invoke( o , Class.forName("com.aaa.test1.StudentService").newInstance() );
}
}
System.out.println(o);
}
3.5 注入值的形式
A 注入对象
<bean id="servlet" class="com.aaa.test1.StudentServlet">
<property name="haha" ref="hehe" ></property>
</bean>
<bean id="hehe" class="com.aaa.test1.StudentService"></bean>
注意 注入对象的时候 使用的是 ref
B 注入简单值
注意 注入对象的时候 使用的是 value
<bean id="dao" class="com.aaa.test1.BaseDAO">
<property name="className" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///test"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
public class BaseDAO {
private String className;
private String url;
private String username;
private String password;
public void setClassName(String className) {
this.className = className;
}
public void setUrl(String url) {
this.url = url;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "BaseDAO{" +
"className='" + className + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
代码调用
@Test
public void test3(){
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Object servlet = context.getBean("dao");
System.out.println(servlet);
}
C 注入集合框架
public class People {
List<String> tels;
Map<String,Object> friends;
public void setTels(List<String> tels) {
this.tels = tels;
}
public void setFriends(Map<String, Object> friends) {
this.friends = friends;
}
@Override
public String toString() {
return "People{" +
"tels=" + tels+"friends:"+ friends +
'}';
}
}
<bean id="p1" class="com.aaa.test1.People">
<property name="tels">
<list>
<value>15966663333</value>
<value>15166333333</value>
<value>18966663333</value>
<value>15933336666</value>
</list>
</property>
<property name="friends">
<map>
<entry key="f1" value="张三"></entry>
<entry key="f2" value="李四"></entry>
<entry key="f3" value="王五"></entry>
<entry key="f4" value="赵六"></entry>
</map>
</property>
</bean>
3.6 自动注入的形式
<bean id="haha" class="com.aaa.test1.StudentServlet">
<property name="haha" ref="hehe"></property>
</bean>
<bean id="hehe" class="com.aaa.test1.StudentService"></bean>
这是我们以前注入的形式 ,是手动维护类和类之间的依赖关系,此时没有问题。但是不优秀,因为在一个项目中会有很多的类需要关系的维护,我们都是通过 propertity进行手动操作很麻烦,而是配置文件会很庞大。
所以spring提供了一套自动注入的形式
xml形式:
public class StudentServlet {
StudentService hehe;
public void setHehe(StudentService hehe) {
this.hehe = hehe;
}
@Override
public String toString() {
return "StudentServlet{" +
"haha=" + hehe +
'}';
}
}
<bean id="haha" class="com.aaa.test1.StudentServlet" autowire="byName"></bean>
<bean id="hehe" class="com.aaa.test1.StudentService"></bean>
autowire自动注入,此时设定的是byName按照名字去注入,此时在servlet中有一个成员变量名字是hehe,所以就会去所有的bean中找id为hehe的bean,找到了则注入。没找到则抛异常。
<bean id="haha" class="com.aaa.test1.StudentServlet" autowire="byType"></bean>
<bean id="heihei" class="com.aaa.test1.StudentService"></bean>
autowire自动注入,此时设定的是byType按照类型去注入,此时servlet中成员变量的类型为 com.aaa.test1.StudentService, 所以就会去所有的bean中找class为com.aaa.test1.StudentService,找到了则注入,没有就抛异常,找到多个也抛异常。
注解形式:
A 配置当前spring需要管理哪些bean(类)
B 配置哪些依赖需要spring帮忙注入
第一步: 开启注解扫描
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<!--注解配置-->
<context:annotation-config></context:annotation-config>
<!--注解包扫描-->
<context:component-scan base-package="com.aaa.test1" ></context:component-scan>
</beans>
第二步:通过注解 @Component 配置 哪些类需要spring管理
@Component("haha")
/*<bean id="haha" class="com.aaa.test1.StudentServlet" />*/
public class StudentServlet {
StudentService hehe;
@Override
public String toString() {
return "StudentServlet{" +
"haha=" + hehe +
'}';
}
}
@Component("heihei")
public class StudentService {
}
第三步:通过注解@AutoWired注解告诉spring需要进行依赖注入
@Autowired
StudentService hehe;
3.7 @Autowired 和 @Resource的注入原则/装配顺序
@Autowired:
当自动注入bean的时候 首先根据类型去匹配
如果一个都匹配不到,则查看是否配置(required = true) 如果没有配置或者配置的是true,就代表必须注入,此时则抛出异常。
如果配置的是false,代表不是必须注入,此时如果找不到没救不注入。(注意:如果设定为false,又没有注入成功 ,在使用当前成员变量的时候 会出现空指针异常。)
如果正常匹配到一个 ,则按照类型正常注入。
如果匹配到多个bean的类型都是同一类型。则根据当前变量的名字匹配对应的bean的名字。如果匹配到 则进行注入。
如果匹配不到 则 抛出异常
如果配置了@Qualifier("heihei") 则根据@Qualifier("heihei")的名字匹配对应的bean
@Resource
import javax.annotation.Resource;
是我们JDK自带的一个注解
a. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
b. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
c. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
d. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配(名字没有一样的),则回退为一个原始类(相当于按照类型取查找一个实现类,如果按照类型取查找的实现类有多个 则抛出异常)
3.8 注解配置的语义化注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
String value() default "";
}
我们需要在控制层添加 @Controller
在业务层添加 @Service
在数据持久层添加@Repository
在其他的地方都是用@Component
相关推荐
- 一个基于.Net Core遵循Clean Architecture原则开源架构
-
今天给大家推荐一个遵循CleanArchitecture原则开源架构。项目简介这是基于Asp.netCore6开发的,遵循CleanArchitecture原则,可以高效、快速地构建基于Ra...
- AI写代码翻车无数次,我发现只要提前做好这3步,bug立减80%
-
写十万行全是bug之后终于找到方法了开发"提示词管理助手"新版本那会儿,我差点被bug整崩溃。刚开始两周,全靠AI改代码架构,结果十万行程序漏洞百出。本来以为AI说没问题就稳了,结果...
- OneCode低代码平台的事件驱动设计:架构解析与实践
-
引言:低代码平台的事件驱动范式在现代软件开发中,事件驱动架构(EDA)已成为构建灵活、松耦合系统的核心范式。OneCode低代码平台通过创新性的注解驱动设计,将事件驱动理念深度融入平台架构,实现了业务...
- 国内大厂AI插件评测:根据UI图生成Vue前端代码
-
在IDEA中安装大厂的AI插件,打开ruoyi增强项目:yudao-ui-admin-vue31.CodeBuddy插件登录腾讯的CodeBuddy后,大模型选择deepseek-v3,输入提示语:...
- AI+低代码技术揭秘(二):核心架构
-
本文档介绍了为VTJ低代码平台提供支持的基本架构组件,包括Engine编排层、Provider服务系统、数据模型和代码生成管道。有关UI组件库和widget系统的信息,请参阅UI...
- GitDiagram用AI把代码库变成可视化架构图
-
这是一个名为gitdiagram的开源工具,可将GitHub仓库实时转换为交互式架构图,帮助开发者快速理解代码结构。核心功能一键可视化:替换GitHubURL中的"hub...
- 30天自制操作系统:第六天:代码架构整理与中断处理
-
1.拆开bootpack.c文件。根据设计模式将对应的功能封装成独立的文件。2.初始化pic:pic(可编程中断控制器):在设计上,cpu单独只能处理一个中断。而pic是将8个中断信号集合成一个中断...
- AI写代码越帮越忙?2025年研究揭露惊人真相
-
近年来,AI工具如雨后春笋般涌现,许多人开始幻想程序员的未来就是“对着AI说几句话”,就能轻松写出完美的代码。然而,2025年的一项最新研究却颠覆了这一期待,揭示了一个令人意外的结果。研究邀请了16位...
- 一键理解开源项目:两个自动生成GitHub代码架构图与说明书工具
-
一、GitDiagram可以一键生成github代码仓库的架构图如果想要可视化github开源项目:https://github.com/luler/reflex_ai_fast,也可以直接把域名替换...
- 5分钟掌握 c# 网络通讯架构及代码示例
-
以下是C#网络通讯架构的核心要点及代码示例,按协议类型分类整理:一、TCP协议(可靠连接)1.同步通信//服务器端usingSystem.Net.Sockets;usingTcpListene...
- 从复杂到优雅:用建造者和责任链重塑代码架构
-
引用设计模式是软件开发中的重要工具,它为解决常见问题提供了标准化的解决方案,提高了代码的可维护性和可扩展性,提升了开发效率,促进了团队协作,提高了软件质量,并帮助开发者更好地适应需求变化。通过学习和应...
- 低代码开发当道,我还需要学习LangChain这些框架吗?| IT杂谈
-
专注LLM深度应用,关注我不迷路前两天有位兄弟问了个问题:当然我很能理解这位朋友的担忧:期望效率最大化,时间用在刀刃上,“不要重新发明轮子”嘛。铺天盖地的AI信息轰炸与概念炒作,很容易让人浮躁与迷茫。...
- 框架设计并不是简单粗暴地写代码,而是要先弄清逻辑
-
3.框架设计3.框架设计本节我们要开发一个UI框架,底层以白鹭引擎为例。框架设计的第一步并不是直接撸代码,而是先想清楚设计思想,抽象。一个一个的UI窗口是独立的吗?不是的,...
- 大佬用 Avalonia 框架开发的 C# 代码 IDE
-
AvalonStudioAvalonStudio是一个开源的跨平台的开发编辑器(IDE),AvalonStudio的目标是成为一个功能齐全,并且可以让开发者快速使用的IDE,提高开发的生产力。A...
- 轻量级框架Lagent 仅需20行代码即可构建自己的智能代理
-
站长之家(ChinaZ.com)8月30日消息:Lagent是一个专注于基于LLM模型的代理开发的轻量级框架。它的设计旨在简化和提高这种模型下代理的开发效率。LLM模型是一种强大的工具,可以...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 框架图 (58)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- springmvc框架 (49)
- 分布式事务框架 (65)
- scrapy框架 (56)
- shiro框架 (61)
- 定时任务框架 (56)
- java日志框架 (61)
- mfc框架 (52)
- abb框架断路器 (48)
- beego框架 (52)
- java框架spring (58)
- grpc框架 (65)
- tornado框架 (48)
- 前端框架bootstrap (54)
- orm框架有哪些 (51)
- 知识框架图 (52)
- ppt框架 (55)
- 框架图模板 (59)
- 内联框架 (52)
- cad怎么画框架 (58)
- ssm框架实现登录注册 (49)
- oracle字符串长度 (48)