百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Spring 零基础入门(spring入门书)

ccwgpt 2024-11-06 09:41 24 浏览 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

相关推荐

如何基于Spring Security框架实现权限管理

SpringSecurity是一个功能强大且高度可定制的身份验证和访问控制框架,用于保护基于Spring的应用程序。SpringSecurity主要是从两个方面解决安全性问题:web请求级别:使...

一个轻量级 Java 权限认证框架,Sa-Token 让鉴权变得简单、优雅!

一、框架介绍Sa-Token是一个轻量级Java权限认证框架,主要解决:登录认证、权限认证、单点登录、OAuth2.0、分布式Session会话、微服务网关鉴权等一系列权限相关问题。官网文档:...

16.3K Star!简洁高效的Java权限认证与会话管理框架——Sa-Token

简介今天给大家推荐一个轻量级的Java权限认证框架——Sa-Token。它可以为JavaWeb应用同完整的权限认证解决方案,它的目标是简化权限管理和登录认证的流程,具备高度灵活性和简单易用的特点。S...

从Shiro迁移到Sa-Token:老版JeecgBoot项目权限框架平滑升级方案

背景介绍对于许多维护老版JeecgBoot项目的开发者来说,权限框架的升级一直是个棘手问题。这篇文章分享一种实用的方案,用于将老版JeecgBoot中的ApacheShiro替换为更现代的Sa-To...

刑法框架体系,对照着框架体系学习可以事半功倍哦

有了它,妈妈再也不用担心我司考过不啦!有了它,妈妈再也不用担心我司考过不了啦!其他部门法正在陆续整理制作中哦看不清的话请戳http://mp.weixin.qq.com/s?__biz=MzA3NDE...

全新体验版Windows QQ上线,实现三端统一

7月3日,全新体验版WindowsQQ正式上线官网,面向用户开放官方下载渠道。记者从腾讯获悉,继QQ对macOS、Linux版本进行升级后,本次Windows版本的更新,标志QQ基于NT技术架构...

农村自建房造价多少?包工头教你怎么算

通常我们在找专业人士设计农村自建房设计图时,不管你是打算建独栋一层别墅还是独栋二层别墅或是独栋三层别墅,你是否也找他们打听过相应的房屋工程造价呢?下面简单介绍一下农村自建房的傻瓜式造价估算:1、砖混结...

QQ大会员品牌运营策划与设计(qq大会员有哪些个性装扮)

编辑导语:在互联网产品越来越同质化的今天,做出有差异性和符合品牌调性的设计是品牌运营过程中需要重视的问题。本文作者从QQ大会员品牌项目实践出发,分享了品牌运营设计过程中遇到的一些问题以及具体操作方案,...

支持鸿蒙平台,腾讯视频ovCompose跨平台框架发布

IT之家6月3日消息,腾讯开源今日官宣发布腾讯视频ovCompose跨平台框架,其是腾讯大前端领域Oteam中,腾讯视频团队基于ComposeMultiplatform生态推出的...

腾讯 QQ Mac 版推倒重做,全新 1.0 版本开启内测

IT之家9月1日消息,据多位IT之家小伙伴投稿,腾讯QQMac版近日迎来了全新通用版内测,版本号重新由1.0开始,目前放出的体验版为1.0.4-305。从下图可以看到,该版本在U...

全新体验版Windows QQ正式上线官网,实现三端统一

7月3日,全新体验版WindowsQQ正式上线官网,面向用户开放官方下载渠道。记者从腾讯获悉,继QQ对macOS、Linux版本进行升级后,本次Windows版本的更新,标志QQ基于NT技术架构...

QQ,到了不能不变的境地(怎么发qq邮件到别人邮箱)

相比微信一个小更新,乃至一个小动作,都能上热搜的顶级热度。隔壁的老大哥QQ,显得有些冷清。即使更新再快,功能再激情,都很难引起用户们的集体讨论。机友们细想一番,咱们的老朋友QQ,有多久没上过热搜啦?真...

基于Electron框架全面重做:全新Linux版QQ开启公测

来源:快科技不久前,腾讯QQ项目组曾发布预告,宣布QQforLinux新版本即将开启公测。现在,新的Linux版QQ已经开启公测,不过目前仅支持x86架构,arm64架构还仍在适配中。与此前极为简...

QQ全面升级?基于Electron技术的Windows内测版本预计将于明年推出

在2022年,作为经典的聊天软件的QQ在经历多次的功能调整后,正式选择基于新技术开发新版本的QQ。今日,据相关媒体报道,腾讯QQ项目组发布预告:QQforLinux将在本周迎来公测,全新的QQf...

跨平台三端重构正式统一,QQ Windows全新体验版上架官网

7月3日,全新体验版WindowsQQ正式上线官网,面向用户开放官方下载渠道。继QQ对macOS、Linux版本进行升级后,本次Windows版本的更新,标志QQ基于NT技术架构,实现了桌面端Q...

取消回复欢迎 发表评论: