Spring进阶篇

一、spring复习

1、什么是spring,对spring的认识理解?

spring是一个轻量级开源的一站式的Java开发框架,目的是为了简化企业级开发。

2、spring的优缺点

优点:轻量级、开源的、简单、IOC(控制反转)和AOP面向切面编程

缺点:配置多,很多都是模板化的配置,管理很多依赖

3、理解IOC和AOP

IOC:控制反转,把项目中创建对象的权力交给Spring框架,由spring框架统一管理项目中的对象,由spring框架生成的对象,称为一个bean对象,spring可以对bean对象进行功能上的增强。

AOP:面向切面编程,使用动态代理的方式,为目标对象提供代理对象,在不修改目标类中的代码时,为目标类添加额外的功能,将额外的功能横切到目标类中。

4、IOC和DI的区别

IOC是一种设计原则,强调控制权的反转,是一种宏观的概念

DI:依赖注入,是实现IOC的具体技术手段,侧重于依赖对象的注入过程。在IOC的基础上把对象注入到我们需要的地方

5、spring注入对象的方式

基于xml配置方式:

属性注入:是使用setter方法来实现

java 复制代码
package com.example;

public class DataSource {
    private String url;
    private String username;
    private String password;

    public void setUrl(String url) {
        this.url = url;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    // Getter 方法可按需添加
} 
java 复制代码
package com.example;

public class UserService {
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    // 业务方法
    public void doSomething() {
        // 使用 dataSource 进行操作
    }
}
java 复制代码
<?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 -->
    <bean id="dataSource" class="com.example.DataSource">
        <property name="url" value="jdbc:mysql://localhost:3306/test"/>
        <property name="username" value="root"/>
        <property name="password" value="password"/>
    </bean>

    <!-- 定义服务 Bean,注入数据源 -->
    <bean id="userService" class="com.example.UserService">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>  

构造方法注入:通过构造函数来实现的

java 复制代码
package com.example;

public class DataSource {
    private String url;
    private String username;
    private String password;

    public DataSource(String url, String username, String password) {
        this.url = url;
        this.username = username;
        this.password = password;
    }

    // Getter 方法可按需添加
}
java 复制代码
package com.example;

public class UserService {
    private DataSource dataSource;

    public UserService(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    // 业务方法
    public void doSomething() {
        // 使用 dataSource 进行操作
    }
} 
java 复制代码
<?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 -->
    <bean id="dataSource" class="com.example.DataSource">
        <constructor-arg value="jdbc:mysql://localhost:3306/test"/>
        <constructor-arg value="root"/>
        <constructor-arg value="password"/>
    </bean>

    <!-- 定义服务 Bean,注入数据源 -->
    <bean id="userService" class="com.example.UserService">
        <constructor-arg ref="dataSource"/>
    </bean>
</beans>

注解方式:

属性注入:是使用@Autowired 注解来实现属性注入

java 复制代码
package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    @Autowired
    private DataSource dataSource;

    // 业务方法
    public void doSomething() {
        // 使用 dataSource 进行操作
    }
}

构造方法注入:同样可以使用 @Autowired 注解实现构造方法注入

java 复制代码
package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    private DataSource dataSource;

    @Autowired
    public UserService(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    // 业务方法
    public void doSomething() {
        // 使用 dataSource 进行操作
    }
}

6、自动注入有哪些注解

1、使用spring框架中提供的**@Autowired注解**,可以添加到要注入的属性上面,或者属性的set方法上,如果直接添加到属性上面,那么set方法可以不需要添加。

默认情况下,要注入的属性对象不能空@Autowired(required=true)

注入时,查找bean的方式有两种:

1、当只有一个匹配的Bean时默认是通过属性的类型查询

2、当出现多个匹配的Bean时,需要使用@Qualifier(value=对象ming),或者使用@Primary,Spring 会优先选择被 @Primary 注解标记的 Bean 进行注入。

2、使用jdk中提供的注解**@Resource**

查找bean也是有两种方式:

1、通过名称查询:会根据字段类型在容器中查找实现了该接口的 Bean

2、如果按类型查找时存在多个匹配的 Bean,@Resource 会抛出 NoUniqueBeanDefinitionException 异常。此时,可以通过指定 name 属性来明确要注入的 Bean。@Resource(name = "adminDao") 通过对象名查询

@Resource和@Autowried的区别?

1、来源不同:@Resource 是jdk提供的,@Autowired是spring提供的

2、查找方式不同::@Resource 默认按名称( @Component("userDaoImpl"))查找 Bean,@Autowired默认按类型(类名)查找 Bean。

3、支持的属性不同:@Resource(name=Bean的名称,type=Bean的类型),@Autowired(required=true) 表示注入的依赖必须存在,默认是true

java 复制代码
import javax.annotation.Resource;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    // 指定名称和类型
    @Resource(name = "userDaoImpl", type = UserDaoImpl.class)
    private UserDao userDao;
}
java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    // 依赖可以为 null
    @Autowired(required = false)
    private UserDao userDao;
}

4、用法场景不同:@Resource适合明确指定了Bean名称进行注入,其可以保持代码的一致性。

@Autowried:在spring项目中主要依据类型进行注入,并结合@Qulifier注解处理多个匹配Bean的情况,使用其更加方便

7、spring中的bean和new的对象有什么区别?

bean对象是由spring框架创建的,根据我们的配置(事务、日志、统一异常处理),可以进行功能上的增强。

new对象是最原始的对象,没有任何功能的增强。

8、AOP中的术语有哪些,通知有哪些?

连接点:类中可以增强的方法

切入点:类中实际被增强的方法

通知:给切入点添加的功能

目标:被增强的类

代理:代理对象

通知类型:

1、前置通知:@Before,在方法执行前执行

2、后置通知:@After,在方法执行后执行

3、环绕通知:@Around,可以实现前置、后置、返回、异常通知

4、异常通知:@AfterThrowing,在出现异常时执行

5、返回通知:@AfterReturning,在目标方法正常执行并返回结果后执行,一旦出现异常就不执行

9、spring AOP实现的几种方式

xml配置方式

java 复制代码
<!-- 开启 AOP 自动代理 -->
    <aop:aspectj-autoproxy/>

    <!-- 定义目标对象 -->
    <bean id="userService" class="com.example.UserServiceImpl"/>

    <!-- 定义切面 -->
    <bean id="loggingAspect" class="com.example.LoggingAspect"/>

    <!-- 配置 AOP -->
    <aop:config>
        <!-- 定义切入点 -->
        <aop:pointcut id="userServiceMethods" expression="execution(* com.example.UserService.*(..))"/>
        <!-- 定义切面和通知 -->
        <aop:aspect ref="loggingAspect">
            <!-- 前置通知 -->
            <aop:before method="beforeAdvice" pointcut-ref="userServiceMethods"/>
            <!-- 后置通知 -->
            <aop:after method="afterAdvice" pointcut-ref="userServiceMethods"/>
        </aop:aspect>
    </aop:config>

注解方式

java 复制代码
 @Before("execution(* com.example.UserService.*(..))")
    public void beforeAdvice() {
        System.out.println("Before method execution");
    }

    @After("execution(* com.example.UserService.*(..))")
    public void afterAdvice() {
        System.out.println("After method execution");
    }

10、spring中事务的管理,实现方式有几种,原来是什么?

首先事务是数据库中的特性,在spring中,spring框架对事务开启,提交,回滚进行管理。

**编程式事务:**通过编写代码来管理事务的开始、提交、回滚,这种方式比较灵活,但代码量比较大,适用于需要精细控制事务边界的场景。

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private TransactionTemplate transactionTemplate;

    @Override
    public void transferMoney(int fromUserId, int toUserId, double amount) {
        transactionTemplate.execute(new TransactionCallback<Void>() {
            @Override
            public Void doInTransaction(TransactionStatus status) {
                try {
                    // 扣除转出用户的金额
                    jdbcTemplate.update("UPDATE users SET balance = balance - ? WHERE id = ?", amount, fromUserId);
                    // 模拟异常
                    if (true) {
                        throw new RuntimeException("Simulated exception");
                    }
                    // 增加转入用户的金额
                    jdbcTemplate.update("UPDATE users SET balance = balance + ? WHERE id = ?", amount, toUserId);
                } catch (Exception e) {
                    status.setRollbackOnly();
                    throw e;
                }
                return null;
            }
        });
    }
}    

声明式事务: 通过配置文件注解来定义事务的属性,Spring 会自动管理事务的开始、提交和回滚,这种方式代码侵入性小,适用于大多数场景。

java 复制代码
    <!-- 配置事务通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="transferMoney" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Transactional
    @Override
    public void transferMoney(int fromUserId, int toUserId, double amount) {
        // 扣除转出用户的金额
        jdbcTemplate.update("UPDATE users SET balance = balance - ? WHERE id = ?", amount, fromUserId);
        // 增加转入用户的金额
        jdbcTemplate.update("UPDATE users SET balance = balance + ? WHERE id = ?", amount, toUserId);
    }
}

11、声明式事务失效的场景

1、非public方法

2、异常没有抛出

3、数据库不支持事务(myIsam)

4、未指定异常回滚类型:默认情况下,Spring 的声明式事务仅在遇到运行时异常 (RuntimeException)和错误(Error)时回滚,对于受检查异常(如 IOExceptionSQLException 等)则不会回滚。

5、传播行为设置错误。

6、同类方法调用:当一个类中的非事务方法调用同一个类中的事务方法时,事务注解会失效。因为这种调用是通过this引用进行的,而非通过代理对象,所以事务注解不会生效。

7、异步方法调用:若在异步方法中使用事务注解,由于异步方法是在新线程中执行的,事务上下文无法传递,从而导致事务失效

12、springWeb的运行流程

1.用户发送出请求到前端控制器 DispatcherServlet。

  1. DispatcherServlet 收到请求调用 HandlerMapping(处理器映射器)。

  2. HandlerMapping 找到具体的处理器(可查找 xml 配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给 DispatcherServlet。

  3. DispatcherServlet 调用 HandlerAdapter(处理器适配器)。

  4. HandlerAdapter 经过适配调用具体的处理器(Handler/Controller)。

6.Controller 执行完成向前端响应结果。

相关组件

DispatcherServlet:前端控制器,不需要程序员开发,由框架提供,在web.xml 中配置。

作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求.
HandlerMapping:处理器映射器,不需要程序员开发)由框架提供。

作用:根据请求的 url 查找 Handler(处理器/Controller)

HandleAdapter:处理器适配器,不需要程序员开发,由框架提供。

作用:按照特定规则(HandlerAdapter 要求的规则)去执行 Handler
Handler:处理器,也称之为 Controller,需要程序员去开发

13、servlet过滤器和spring拦截器

首先过滤器是servlet中规范定义的,拦截器是spring中定义的。

过滤器只可以拦截所有进入到Java后端的请求,拦截器只能拦截进入到处理器(controller,web层)的请求。

14、spring和springboot的关系

springboot是对spring框架的搭建进行了封装,不是代替spring的,底层依然还是spring

15、spring常用的注解

声明Bean的注解

@RestController、@Service、@Repository都可以称为@Component,都是@component的衍生,这些注解在语义上更加明确,能让代码的可读性和可维护性得到提升。

@Component:基本组件

@RestController:控制层

@Service:业务层

@Repository:数据访问层

Bean的声明周期属性

@Scop设置类型:spring容器如何创建新建Bean的实例 @Scop(" ")

singleton:单例,一个spring容器中只有一个Bean的实例,默认的

protetype:每次调用创建一个新的对象

request:web项目中,给每个http request创建一个新Bean

注入Bean的注解

@Autowried、出现多个匹配的Bean与@Qualifier配合使用,@Resource

AOP的相关注解

@Aspect:声明一个切面类(包含通知的类)

@After:后置通知

@AfterReturning 返回通知

@Before:在方法执行之前执行

@AfterThrowing 异常通知

@Around:在方法执行之前与之后执行

SpringWeb的常用注解

@RequestMapping: 为类,方法定义访问请求地址@PostMapping:定义只允许 post 请求方式访问地址

@GetMapping:定义只允许 get 请求方式访问地址

@RequestBody:允许 request 的参数在 request 体中以 json 格式传递.

其他注解

@DateTimeFormat:此注解用于属性上,可以方便的把 Date 类型直接转化为我

们想要的格式.

@Transactional 注解放在类级别时,表示所有该类的公共方法都配置相同的事务属性信息。

SpringBoot 注解

@SpringBootApplication:

包含@SpringBootConfiguration、@EnableAutoConfiguration、

@ComponentScan 通常用在主类上;

统一异常处理注解

@RestControllerAdvice,@ExceptionHandler 用于统一异常处理,捕获指定的

异常.

配置类相关注解

@Configuration:声明当前类为配置类

@Bean:注解在方法上,声明当前方法的返回值为一个 bean,替代 xml 中的

方式

Spring 定时器

在启动类上添加 @EnableScheduling

在任务方法上添加 @Scheduled(cron = "*/6 * * * * ?")

16、spring中bean的声明周期

从宏观上来讲,可以分为5个阶段:

1、实例化 Instantiation

2、属性赋值 Populate

3、初始化 Initialization(最关键的,根据各种注解配置,在这里进一步落地实现)

4、将Bean对象放入到容器中使用

5、销毁 Destruction

17、spring中的Bean是线程安全的吗?

spring中的bean如果是单例,始终创建一个对象,所有请求公用一个对象,那么对该单例对象中的成员变量也就只有一份,所有请求共享。

在多用户访问时,可能出现问题,还有种情况,就是像每个请求中,都有属于自己的成员变量使用。所以单例Bean使用时,就有可能出现线程安全问题。

原型Bean每次请求都会创建一个新的对象,不会存在线程安全问题。

单例Bean线程安全问题解决:

如果成员变量是共享的,多线程 操作时,如**++**等操作,进行加锁控制,如果每一个请求中,都需要一个属于自己的成员变量:

1、单例bean修改为原型bean

2、使用ThreadLocal线程变量,为每一次的请求提供一个变量的副本。

在这里,单例Bean又分为:有状态Bean和无状态Bean

有状态Bean:

就是有成员变量,而且成员变量可以存储数据,就有可能多线程操作时出现问题。

无状态Bean:

注入对象,对象不进行数据存储,只是调用方法,每次请求中的参数数据都是相互隔离的

18、Bean的循环依赖

java 复制代码
class A{
B b;
}
class B{
A a;
}
public static void main(String[] args){
A a=new A();//创建A对象,关联对象b为null
B b=new B();//创建B对象,关联对象a为null
}
//修然A,B之间相互关联,但是创建对象时,没有任何问题

在spring中存在循环依赖的问题(spring已经解决了)

在spring中我们使用@Autowired注解,那么在创建A对象时,需要关联的B对象要注入,需要去创建B对象,创建B对象时,需要为关联的A注入值,但是此时A对象还没有创建完成,形成了一种死循环。

spring中是如何解决循环依赖的问题?

采用三级缓存来解决循环依赖问题

三级缓存就是三个map(ConcurrentHashMap)对象,来存储不同的对象。

一级缓存:singletonObjiects,一级缓存对象,主要存储创建、初始化完成的bean对象

二级缓存:earlysingletonObjects,二级缓存,主要存储实例化完成,但没有初始化完成的半成品对象。

三级缓存:singletonFactories:主要存储创建对象的工厂对象,创建A对象时还有一个创建A的工厂对象来创建A对象。

过程:

创建A对象时,需要用到B,A创建了一半,把他存储到二级缓存(earlysingletonobiects),把创建A的工厂对象存储到三级缓存中(singletonFactories),把半成品A注入到B中,B完成了创建,存储到一级缓存中(singletonObjects),把B注入到A中,A完成了创建,存储到一级缓存中。

19、spring boot自动装配原理

基本流程:

springboot启动时,首先对application.yml和pom.xml文件进行读取,获取到项目中使用到的第三方组件,然后读取spring.factories中的spring支持的配置类,最后加载项目中所使用到的组件配置类。
注解层面
启动类:

@SpringBootApplication 注解是一个复合注解


在@EnableAutoConfiguration 中有一个 @Import({AutoConfigurationImportSelector.class})注解,其中包含AutoConfigurationImportSelector 类.
根据pom.xml中的配置的相关依赖,进行选择性的加载,读取相关依赖的配置类

AutoConfigurationImportSelector 类中关键方法

​​​​​​​

相关推荐
摇滚侠1 天前
Spring Boot 3零基础教程,WEB 开发 HttpMessageConverter @ResponseBody 注解实现内容协商源码分析 笔记33
java·spring boot·笔记
计算机毕业设计小帅1 天前
【2026计算机毕业设计】基于Springboot的校园电动车短租平台
spring boot·后端·课程设计
调试人生的显微镜1 天前
Web前端开发工具实战指南 从开发到调试的完整提效方案
后端
静心观复1 天前
drawio画java的uml的类图时,class和interface的区别是什么
java·uml·draw.io
Java水解1 天前
【SQL】MySQL中空值处理COALESCE函数
后端·mysql
Laplaces Demon1 天前
Spring 源码学习(十四)—— HandlerMethodArgumentResolver
java·开发语言·学习
guygg881 天前
Java 无锁方式实现高性能线程
java·开发语言
ss2731 天前
手写Spring第7弹:Spring IoC容器深度解析:XML配置的完整指南
java·前端·数据库
Python私教1 天前
DRF:Django REST Framework框架介绍
后端·python·django
间彧1 天前
Java HashMap如何合理指定初始容量
后端