四、SSM学习 | Spring【2】

文章详细介绍了Spring框架的核心特性,包括IOC(控制反转)的概念、优点和实现方式,如构造器注入和Set注入,以及AOP(面向切面编程)的应用。同时,提到了SpringBoot和SpringCloud在微服务开发中的角色。此外,文章还讨论了Spring中的事务管理,强调了声明式事务的重要性,并简单回顾了Mybatis的整合过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.Spring框架

1.1简介

在这里插入图片描述

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.18</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.18</version>
</dependency>


1.2 优点

  • 开源, 免费的框架(容器)!
  • 轻量级的非入侵式框架!
  • 控制翻转(IOC), 面向切面编程(AOP)
  • 支持事务的处理, 对框架整合的支持

总结: Spring是一个轻量级的IOC和AOP框架.
1.3 组成

在这里插入图片描述
1.4 拓展

构建一切, 协调一切, 连接一切

  • Spring Boot
    • 一个快速开发的脚手架
    • 基于它可以快速开发单个微服务
    • 约定大于配置
  • Spring Cloud
    • SpringCloud是基于Spring Boot实现的

学习SpringBoot, 必须完全掌握Spring和SpringMVC

弊端: 发展太久后, 配置地狱了,

2.IOC理论推导

在这里插入图片描述
在我们之前的业务中, 用户的需求可能会影响我们原来的代码, 我们需要根据用户的需求去修改源代码, 如果代码量十分大,修改一次的成本代价比较昂贵.
我们使用一个Set接口实现, 已经发生了革命性的变化.

  • 之前, 程序是主动创建对象, 控制权在程序员手上.
  • 使用set注入后, 程序不再具有主动性, 而是变成了被动的接受对象.

这种思想, 从本质上解决了问题, 我们不需要去管理对象的创建了. 系统耦合性大大降低.可以更专注于业务的实现, 这是IOC的原型.

package com.chen.service;

import com.chen.dao.UserDao;
import com.chen.dao.UserDaoImpl;
import com.chen.dao.UserDaoOracleImpl;
import com.chen.dao.UserDaoMysqlImpl;

public class UserServiceImpl implements UserService{
    // 导入Dao, 业务层调用Dao层的方法.
    // 使用private 对用户隐藏调用机制
//    private UserDao userDao = new UserDaoMysqlImpl();
    // 只有修改 UserDaoImpl 才能调用对应的方法.
    // 既要修改业务层, 也要增加Dao层的代码
    // 所以要将业务层这里的选择交给用户来, 而不是由程序员来完成.
    private UserDao userDao;

    // 利用set进行动态实现值的注入.
    public void setUserDao(UserDao userDao){
        // this.userDao 指向上方已经创建的userDao
        // userDao指向形参.
        this.userDao = userDao;
    }
    @Override
    public void getUser() {
        userDao.getUser(); // 方法名是一样的, 方便业务层调用.
    }
}

在这里插入图片描述
在这里插入图片描述
IoC是Spring的核心内容.
在这里插入图片描述
在这里插入图片描述
3.HelloSpring

对象由Spring来创建管理装配

4.IOC创建对象的方式

  • 使用无参构造创建对象,默认
  • 假设我们要使用有参构造赋值
    <bean id="user" class="com.chen.pojo.User">
        <!--  第零种, 无参赋值-->
<!--        <property name="name" value="陈浩"/>-->
        <!--  第一种, 下标赋值  -->
<!--        <constructor-arg index="0" value="chenhao"/>-->
        <!--  第二种, 类型赋值    -->
<!--        <constructor-arg type="java.lang.String" value="chenhao"/>-->
        <!--  第三种, 参数名赋值   这和默认的很像啊 比较推荐这种 --> 
        <constructor-arg name="name" value="chenhao"/>
    </bean>

总结: 在xml加载的时候, 容器中管理的对象就已经初始化了.

5.Spring配置

5.1 别名

    <!--  如果添加了别名, 我们也可以用它  -->
    <alias name="user" alias="user2"/>

5.2 Bean的配置

<!--  id: bean的唯一标识符, 也就是相当于对象名字  -->
<!--  class: bean 对象所对应的全限定名: 包名+类型  -->
<!--  name: 也是别名,而且可以取多个名字-->
    <bean id="user2" class="com.chen.pojo.UserT" name="t,diff t2;df"> <!-- 这里的name也是别名 -->
        <property name="name" value="陈浩"/>
    </bean>

5.3 import

一般用于团队开发使用,他可以将多个配置文件,导入合并为一个
假设现在团队中多人开发, 这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的.
使用的时候, 使用总的配置就可以了.

  • applicationContext.xml
  • beans1.xml
  • beans2.xml
  • beans3.xml

6.依赖注入

6.1 构造器注入

前面已经说过了

6.2 Set方式注入(重点)

  • 依赖注入: Set注入!
    • 依赖: bean对象的创建依赖于容器(spring)
    • 注入: bean对象中的所有属性由容器来注入!

[环境搭建]
1.复杂类型

package com.chen.pojo;

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

2.真实测试对象

    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;

3.beans.xml

    <bean id="student" class="com.chen.pojo.Student" name="st"> <!-- 这里的name也是别名 -->
        <property name="name" value="陈豪"/>
    <!--   如何一次性大量注册 -->
    </bean>

4.测试类

public class MyTest {
    public static void main(String[] args) {
         ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student.getAddress());
    }
}

5.完善注入信息

<?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="address" class="com.chen.pojo.Address">
        <property name="address" value="Fuzhou"/>
    </bean>

    <bean id="student" class="com.chen.pojo.Student" name="st"> <!-- 这里的name也是别名 -->
        <!--普通值注入, value-->
        <property name="name" value="陈豪"/>
        <!--Bean注入, ref-->
        <property name="address" ref="address"/>
        <!--数组注入, ref-->
        <!--数组-->
        <property name="books" >
            <array>
                <value>红楼梦</value>
                <value>西游记</value>
                <value>水浒传</value>
                <value>三国演义</value>
            </array>
        </property>
        <!--list-->
        <property name="hobbys" >
            <list>
                <value>听歌</value>
                <value>敲代码</value>
                <value>看电影</value>
            </list>
        </property>
        <!--map-->
        <property name="card" >
            <map>
                <entry key="身份证" value="111111111122223333"/>
                <entry key="银行卡" value="666611122233445"/>
            </map>
        </property>
        <!--set-->
        <property name="games" >
            <set>
                <value>CSGO</value>
                <value>RA2</value>
                <value>PUBG</value>
            </set>
        </property>
        <!--null-->
        <property name="wife">
            <null/>
        </property>
        <!--properties-->
        <!--key=value-->
        <property name="info">
            <props>
                <prop key="学号">20220115</prop>
                <prop key="性别"></prop>
                <prop key="姓名">小明</prop>
            </props>
        </property>
    </bean>
</beans>

6.3其他方式注入(拓展)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       
       <!--c命名空间注入-->
       xmlns:p="http://www.springframework.org/schema/p"
       <!--c命名空间注入-->
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--p命名空间注入: 即Properties, 可以直接注入属性的值-->
    <bean id="user" class="com.chen.pojo.User" p:name="陈浩" p:age="12"/>
    <!--c命名空间注入: 即Constructor-args, 可以通过构造器注入-->
    <bean id="user2" class="com.chen.pojo.User" c:age="18" c:name="小俊俊"/>


</beans>

测试:

    public void testUser() {
        ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
        User user = context.getBean("user2", User.class); // 增加后面一个参数就不用强转了
        System.out.println(user.toString());

    }

注意点: P命名和C命名空间不能直接使用, 需要导入xml约束.

6.4 bean的作用域

在这里插入图片描述
重点掌握singleton和prototype

  • 单例模式(Spring默认机制)
    在这里插入图片描述
    无论怎样, 都是拿的同一个实例.在这里插入图片描述

  • 原型模式(每次从容器中get的时候都会产生新对象)
    在这里插入图片描述

<bean id="user2" class="com.chen.pojo.User" c:age="18" c:name="小俊俊" scope="prototype"/>
  • 其余的request session application这些只能在web开发中使用

7.Bean的自动装配

  • 自动装配是Spring满足bean依赖的一种方式!
  • Spring会在上下文中自动寻找,并自动给bean装配属性.

在Spring中, 有三种装配的方式

  1. 在xml中显式配置
  2. 在java中显式配置
  3. 隐式的自动装配bean [重要]

7.1测试

环境搭建 一个人有两只宠物!

7.2 ByName自动装配

    <bean id="cat" class="com.chen.pojo.Cat"/>
    <bean id="dog" class="com.chen.pojo.Dog"/>
    <bean id="people" class="com.chen.pojo.People" autowire="byName">
        <property name="name" value="chenhao"/>
<!--        <property name="dog" ref="dog"/>-->
<!--        <property name="cat" ref="cat"/>-->
    </bean>

7.3 ByType自动装配

    <bean class="com.chen.pojo.Cat"/>
    <bean class="com.chen.pojo.Dog"/>
    <bean id="people" class="com.chen.pojo.People" autowire="byType">
        <property name="name" value="chenhao"/>
<!--        <property name="dog" ref="dog"/>-->
<!--        <property name="cat" ref="cat"/>-->
    </bean>

小结:

  • byName的时候, 需要保证所有bean的ID唯一, 并且这个bean需要和自动注入的属性的set方法的值一致
  • byType的时候, 需要保证所有bean的Class唯一, 并且这个bean需要和自动注入的属性的类型一致

7.4 注解自动装配

基于注释的配置的引入提出了这样一个问题:这种方法是否比XML“更好”。
要使用注解须知:

  • 导入约束 context约束
  • 配置注解的支持 context:annotation-config
<?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:context="http://www.springframework.org/schema/context"
	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">

	<context:annotation-config/>

</beans>

@Autowired
加入该注解后,该属性可以不需要添加set方法
直接在属性上或者set方法上使用
默认通过byType注入, 找不到再通过byName注入
可以忽略set方法
如果显式的定义了Autowired的default为False, 那么说明这个对象可以为Null

@Nullable 说明这个字段可以为Null

    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;

如果自动装配环境比较复杂, 自动装配无法只通过一个注解完成, 要借助@Qualified: 若Autowired按byName匹配,则可以配合@Qualifier 注解指定实现类id

@Resource 是一个组合注解, 先byName再byType

    @Resource // 是一个组合注解, 先byName再byType
    private Cat cat;
//    @Autowired
//    @Qualifier(value = "dog")
    @Resource(name="dog")
    private Dog dog;

小结:
@Resource 和 @Autowired的区别:

  • 都是用来自动装配的, 都可以放在属性字段上的
  • @Resource 是一个组合注解, 先byName再byType
  • @Autowired默认通过byType注入, 找不到再通过byName注入
  • 执行顺序不同

8.使用注解开发

使用注解开发, 必须导入AOP包
使用注解后需导入context约束, 增加注解支持

@Component 说明这个属性被spring托管了

1.bean
2.属性如何注入

@Component //组件, 等价于 <bean id="user" class="com.chen.pojo.User"/>

public class User {

    public String name;
    
    // 相当于 <property name="name" value="chenhao"/>
    @Value("chenhao")
    public void setName(String name) {
        this.name = name;
    }

    //    public String name = "chenhao";
}

3.衍生注解
@component 有几个衍生注解, 我们在web开发中, 会按照mvc三层架构分层!

  • dao @Repository
  • service @Service
  • controller @Controller
    这四个注解功能一样, 都是代表将某个类注册到Spring中, 装配Bean

4.自动装配(has been introduced)
5.作用域
@Scope
可实现单例和原型模式

@Component //组件, 等价于 <bean id="user" class="com.chen.pojo.User"/>
@Scope("prototype")
public class User {

    public String name;

    // 相当于 <property name="name" value="chenhao"/>
    @Value("chenhao")
    public void setName(String name) {
        this.name = name;
    }

    //    public String name = "chenhao";
}

6.小结
xml和注解:

  • xml 更万能, 适合任何场合, 维护简单方便
  • 注解不是自己的类使用不了, 维护相对复杂
    最佳实践:
  • xml用来管理bean
  • 注解只负责属性的注入
  • 我们在使用中,只需要注意一个问题: 必须让注解生效, 就需要开启注解的支持:

9.JavaConfig

在这里插入图片描述
使用java的方式配置spring
将xml配置交给java来做.
JavaConfig是Spring的一个子项目, 是核心子功能.

实体类

package com.chen.pojo;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


// 这个注解的意思, 说明这个类被Spring托管了, 注册到容器里面/
@Component
public class User {
    private String name;

    public String getName() {
        return name;
    }

    @Value("chenhao") // 属性注入
    public void setName(String name) {
        this.name = name;
    }

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

配置类

package com.chen.config;

import com.chen.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration // 这个也会被Spring容器托管, 注册到容器中, 因为他本来就是一个Component. 代表这是一个配置类,
@ComponentScan("com.chen.pojo") // 不加这个扫描也可以是因为 他在这个类里面加了@Bean注解 已经把这个注入到spring了 不需要通过扫描了 扫描是配合@Component使用的 在User类上面加入了这个注解
@Import(ChenConfig.class) // 合并
public class ChenConfig2 {

    // 这个方法的名字就相当于bean中的id属性
    // 返回值, 就相当于bean中的class属性
    @Bean
    public User getUser(){
        return new User();
    }
}

测试类

import com.chen.config.ChenConfig;
import com.chen.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {
    @Test
    public void test1() {
        // 如果完全使用了配置类方式来做, 那只能通过AnnotationConfig 上下文来获取容器, 通过配置类的class对象加载.
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ChenConfig.class);
        User getUser = context.getBean("getUser", User.class);
        System.out.println(getUser.toString());

    }
}

这种纯java的配置方式, 在SpringBoot中随处可见.

10.代理模式

为什么有代理模式? 因为这是Spring中AOP的底层
代理模式的分类:

  • 静态代理
  • 动态代理

10.1 静态代理

角色分析:

  • 抽象角色: 共同做的事情, 一般使用接口或抽象类解决
  • 真实角色: 被代理的角色
  • 代理角色: 代理真实角色, 代理真实角色后, 一般会做一些附属操作
  • 客户: 访问代理对象的人.

代码步骤:

  1. 接口
public interface Rent {
    public void rent();
}
  1. 真实角色
// 房东
public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("房东出租房子");
    }
}

  1. 代理角色
// 代理 帮房东租房子
public class Proxy implements Rent{
    private Host host; // 组合范式

    public Proxy() {
    }

    public Proxy(Host host) {
        this.host = host;
    }

    @Override
    public void rent() {
        host.rent();
    }

    // 看房
    public void seeHouse(){
        System.out.println("中介带你看房");
    }
    // 合同
    public void makeContract(){
        System.out.println("签租赁合同");
    }
    // 收费用
    public void collectFare(){
        System.out.println("中介收中介费");
    }
}

  1. 客户端访问代理角色
public class Client {
    public static void main(String[] args) {
        // 房东要租房子
        Host host = new Host();
        // 代理, 中介帮房东租房子, 但是代理角色会有一些附属操作
        Proxy proxy = new Proxy(host);
        // 你不用面对房东, 直接找中介租房即可.
        proxy.rent();
    }
}

代理模式的好处:

  • 可以使真实角色的操作更加纯粹, 不用关注一些公共业务
  • 公共业务交给代理色, 实现业务分工.
  • 公共业务发生扩展时, 方便集中管理!
    缺点:
  • 一个真实角色会产生一个代理角色; 代码量会翻倍, 开发效率变低.

10.2加深理解

代码:对应08-demo02
在这里插入图片描述
10.3 动态代理

可以解决静态代理的缺点

  • 动态代理和静态代理角色一致
  • 动态代理的代理类是动态生成的,不是我们直接写好的.
  • 动态代理分为两大类: 基于接口的动态代理, 基于类的动态代理
    • 基于接口—JDK动态代理[在这里使用]
    • 基于类: cglib
    • java字节码实现:javasist

需要了解两个类: Proxy:代理, InvocationHandler:调用处理程序

InvocationHandler

动态代理的好处: 有静态代理的所有好处
一个动态代理类代理的是一个接口, 一般就是对应的一类业务.
一个动态代理类可以代理多个类, 只要是实现的同一个接口.

11.AOP

11.1什么是?

在这里插入图片描述
在这里插入图片描述
11.2Aop在Spring中的作用

在这里插入图片描述

在这里插入图片描述
11.3使用Spring实现Aop
[重点]使用AOP织入, 需导入一个依赖包在这里插入图片描述

依赖:

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
<!--            <scope>runtime</scope>-->
        </dependency>

方式一: 使用Spring的API接口[主要SpringAPI接口实现]
方式二: 自定义实现AOP[主要是切面定义]
方式三: 使用注解实现!
对应spring09_AOP代码

12.整合Mybatis

在这里插入图片描述
12.1 回忆Mybatis

1.编写实体类
2.编写核心配置文件
3.编写接口
4.编写Mapper.xml
5.测试

12.2 Mybatis-Spring

1.编写数据源配置
2.sqlSessionFactory
3.sqlSessionTemplate
4.需要给接口加实现类
5.将自己写的实现类,注入到Spring中
6,测试即可

13.声明式事务

13.1回顾事务

  • 把一组业务当成一个业务来做;要么都成功,要么都失败
  • 事务在项目开发中, 十分的重要, 涉及到数据的一致性问题,不能马虎
  • 确保完整性和一致性

事务ACID原则:

  • 原子性: 确保要么成功要么失败
  • 一致性:
  • 隔离性: 确保多个程序可以操作同一个资源
  • 持久性: 事务一旦完成, 无论系统发生什么问题, 结果不会再被影响. 被持久化写入存储器中

13.2 Spring中的事务管理

  • 声明式事务: aop
  • 编程式事务: 需要在代码中 进行事务管理

Spring中七种Propagation类的事务属性详解:

  • REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
  • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
  • MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
  • REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
  • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  • NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
    REQUIRED用于增删改, SUPPORTS用于查询,REQUIRES_NEW用于日志等操作,其他不用记!!

execution(* com.chen.mapper..(…))
第一个星号代表 com.chen 下的所有包!
第二个星号代表 com.chen.* 下所有的类!
第三个星号代表 com.chen.* 下所有类中的所有方法,(…)代表无论什么参数都适用

思考:为什么需要事务

  • 如果不配置事务, 可能存在数据提交不一致的情况;
  • 如果我们不再Spring中去配置声明式事务,我们就需要在代码中手动配置事务!
  • 事务在项目的开发中十分重要,涉及到数据的一致性和完整性问题, 不容马虎!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值