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方法来实现
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 方法可按需添加
}
package com.example;public class UserService {private DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}// 业务方法public void doSomething() {// 使用 dataSource 进行操作}
}
<?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/beanshttp://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>
构造方法注入:通过构造函数来实现的
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 方法可按需添加
}
package com.example;public class UserService {private DataSource dataSource;public UserService(DataSource dataSource) {this.dataSource = dataSource;}// 业务方法public void doSomething() {// 使用 dataSource 进行操作}
}
<?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/beanshttp://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
注解来实现属性注入
package com.example;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate DataSource dataSource;// 业务方法public void doSomething() {// 使用 dataSource 进行操作}
}
构造方法注入:同样可以使用 @Autowired
注解实现构造方法注入
package com.example;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService {private DataSource dataSource;@Autowiredpublic 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
import javax.annotation.Resource;
import org.springframework.stereotype.Service;@Service
public class UserService {// 指定名称和类型@Resource(name = "userDaoImpl", type = UserDaoImpl.class)private UserDao userDao;
}
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配置方式
<!-- 开启 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>
注解方式
@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框架对事务开启,提交,回滚进行管理。
编程式事务:通过编写代码来管理事务的开始、提交、回滚,这种方式比较灵活,但代码量比较大,适用于需要精细控制事务边界的场景。
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 {@Autowiredprivate JdbcTemplate jdbcTemplate;@Autowiredprivate TransactionTemplate transactionTemplate;@Overridepublic void transferMoney(int fromUserId, int toUserId, double amount) {transactionTemplate.execute(new TransactionCallback<Void>() {@Overridepublic 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 会自动管理事务的开始、提交和回滚,这种方式代码侵入性小,适用于大多数场景。
<!-- 配置事务通知 --><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="transferMoney" propagation="REQUIRED"/></tx:attributes></tx:advice>
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 {@Autowiredprivate JdbcTemplate jdbcTemplate;@Transactional@Overridepublic 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
)时回滚,对于受检查异常(如 IOException
、SQLException
等)则不会回滚。
5、传播行为设置错误。
6、同类方法调用:当一个类中的非事务方法调用同一个类中的事务方法时,事务注解会失效。因为这种调用是通过this
引用进行的,而非通过代理对象,所以事务注解不会生效。
7、异步方法调用:若在异步方法中使用事务注解,由于异步方法是在新线程中执行的,事务上下文无法传递,从而导致事务失效
12、springWeb的运行流程
1.用户发送出请求到前端控制器 DispatcherServlet。
2. DispatcherServlet 收到请求调用 HandlerMapping(处理器映射器)。
3. HandlerMapping 找到具体的处理器(可查找 xml 配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给 DispatcherServlet。
4. DispatcherServlet 调用 HandlerAdapter(处理器适配器)。
5. 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的循环依赖
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 注解是一个复合注解
AutoConfigurationImportSelector 类中关键方法