spring基础
对spring的理解
spring
如何管理Bean
(核心思想:依赖注入)
注册Bean
- 注册
Bean
: 相当于new
一个java类—–独立抽出依赖 - 常用注释:
@Bean
、@Component
、@Controller
、@Service
、@Repository
装配Bean
- 装配
Bean
: 相当于调用new
得到的java类,来使用它;按类型装配由于Java多态性实现IOC
—–注入依赖 - 常用注解:
@Autowired
理解
注册过的Bean
会存放在spring
容器中,当装配Bean
时,会从spring
容器中(按类型、按名字)去取出适合的Bean
new
成实例,提供使用。
而按类型装配Bean
,则会灵活的利用java
多态性,实现IOC
。
MVC
思想
Controller
控制器
- 返回体定制:
ResponseEntity
常用注释
spring内置常用注释列表
项目主入口注解
@SpringBootApplication
相当于以下三个注解:
-
@EnableAutoConfiguration
: 启用Spring Boot
的自动配置机制;所以在Spring Boot
中以下@EnableXXX
可写可不写:a. 包括了:
@EnableAspectJAutoProxy
;容器中注册的Bean
可以使用AOP
这样被注册的Bean
,就可以使用@Aspect
、@Pointcut
、@Around
等实现AOP了。
[为什么spring boot
直接可以使用AOP
](java – Spring AOP works without @EnableAspectJAutoProxy? – Stack Overflow)b. 包括了:
@EnableWebMvc
:容器中注册的Bean
可以使用成为拦截器
这样被注册的Bean
,就可以使用implements WebMvcConfigurer
注入拦截器了c. 包括了:
@EnableAsync
:容器中注册的Bean
可以使用异步多线程
这样被注册的
Bean
,就可以使用@Async
执行异步函数了d. 包括了:
@EnableScheduling
:容器中注册的Bean
可以使用定时任务
这样被注册的
Bean
,就可以使用@Scheduled
执行定时任务函数了e. 包括了:
@EnableTransactionManagement
:容器中注册的Bean
可以使用事务
这样被注册的
Bean
,就可以使用@Transactional
执行事务了 -
@ComponentScan
: 扫描被@Component
注解的 bean,注解默认会扫描该类所在的包下所有的类包括了:
@MapperScan
、@Mapper
、@Repository
都可以不用写,因为在application.yml
中配置了mapper的接口路径 -
@Configuration
: 允许在 Spring 上下文中注册额外的 bean 或导入其他配置类
注册Bean
、装配Bean
-
注册
Bean
两种方式:@Component
:修饰类;里面的每个@Bean
每次调用都是新实例
@Configuration
:修饰类是特殊的@Component
;里面的每个@Bean
都是单例模式
@Bean
:修饰被@Component、@Configuration
标注的类中的成员方法
三层架构的类的注释(默认单例模式):@Controller
(表现层)、@Service
(业务层)、@Repository
(持久层)@RestController
相当于@Controller
+@ResponseBody
Spring
@Configuration
和@Component
区别)@Scope(···)
:
singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
prototype : 每次请求都会创建一个新的 bean 实例。
request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
session : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。@Profile(···)
: 指定环境变量下注册Bean@Mapper
:Mybatis
的注解,向Spring
容器中注册Bean
;等价于在启动类上扫描文件路径@MapperScan
; 配置文件注意:需要把写
Mapper层的XML文件
路径写在配置文件中application.yml
中mybatis mapper-locations
、配置Entity类
路径mybatis typeAliasesPackage
持久层注册装配过程:
Spring
会根据配置文件中的mybatis mapper-locations
扫描所有的Mapper层的XML
并根据在Mapper层的XML
中使用namespace的全限定包名
找到对应的DAO
接口,并实现该DAO
接口里面的每个成员方法生成一个新的DAO类Bean
;且这个新的DAO类Bean
的类型为DAO接口名
,命名为首字母小写的DAO接口名
;最后把这个新的DAO类Bean
注册到Spring
容器中;当Service层
通过@Resource
即可找到那个新的DAO类Bean
并注入进来,至此整个持久层就串起来了。 扫描接口路径:
每个DAO接口类上都加上@Mapper
<====等价于====>在启动类或自定义Mybatis配置类
上使用@MapperScan
。二者选一个即可、也可以都不加;因为只要Spring容器
扫描XML文件
,XML文件
又能通过namespace
找到DAO
层接口。 配置
每个DAO类的成员方法
实现类XML
路径:在配置文件中application.yml
中mybatis mapper-locations
配置
XML
文件中简写返回类型的包路径:在配置文件中application.yml
中mybatis typeAliasesPackage
;推荐使用全限定名不要用alias -
装配
Bean
: ——- 按名字装配性能最优
源自spring库:@Autowired
默认按类型装配、@Autowired
+@Qualifier(···)
按名字装配;
源自java标准:@Resource(name="···")
默认按名字装配、@Resource(type="···")
注意:通常注解式装配
Bean
直接是注入到成员变量
中,相当于setter注入
。
Spring boot
将含有多个构造器的类注入到bean容器,并实现区分对同一个类不同bean名称的 bean的区分的四种方法
Spring Boot
注解装配只能使用无参构造器,如果需要注入有参构造器的Bean则可以通过new getApplicationContext().getBean(clazz)(··参数··)
请求相关
- 请求方法注解:
@GetMapping(···)
、@PostMapping(···)
、@PutMapping(···)
、@DeleteMapping(···)
、@PatchMapping(···)
;
@RequestMapping(value="···",method=RequestMethod.POST)
2. 请求参数传值注解:
@PathVariable
、@RequestParam
、@RequestBody
(替换@ModelAttribute
)
请求参数校验
读取配置文件
@EnableConfigurationProperties
@PropertySource
: 指定使用的配置文件
@ConfigurationProperties
: @ConfigurationProperties(prefix = "···")
全局异常处理
@ControllerAdvice
@RestControllerAdvice
(定义全局处理异常类): 相当于 @ControllerAdvice
+ @ResponseBody
@ExceptionHandler
(写你想要拦截的异常类型)
解释: 被 @RestControllerAdvice
注解的类会自动注入到 控制器 的 请求方法注解(@RequestMapping
)中
事务处理
ServiceImpl的方法中使用: @Transactional(rollbackFor = Exception.class)
json相关注解
其他Spring
相关注解
-
@EnableXXX
:
配合@Configuration
使用,包括@EnableAsync
,@EnableScheduling
,@EnableTransactionManagement
,@EnableAspectJAutoProxy
,@EnableWebMvc
。
通常Spring boot
不需要配置,框架已经配置好了 -
@Lazy
:
spring启动时不先实例化bean,等到使用时才开始实例化
Lombok库的注解
疑问
@Bean
用于第三方组件的注册
Spring MVC相关
Spring的生命周期
实例化Bean启动时 BeanPostProcess
- Spring对Bean进行实例化(相当于程序中的new Xx())
- Spring将值和Bean的引用注入进Bean对应的属性中
- 如果Bean实现了BeanNameAware接口,Spring将Bean的ID传递给setBeanName()方法(实现BeanNameAware清主要是为了通过Bean的引用来获得Bean的ID,一般业务中是很少有用到Bean的ID的)
- 如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanDactory(BeanFactory bf)方法并把BeanFactory容器实例作为参数传入。(实现BeanFactoryAware 主要目的是为了获取Spring容器,如Bean通过Spring容器发布事件等)
- 如果Bean实现了ApplicationContextAwaer接口,Spring容器将调用setApplicationContext(ApplicationContext ctx)方法,把y应用上下文作为参数传入.(作用与BeanFactory类似都是为了获取Spring容器,不同的是Spring容器在调用setApplicationContext方法时会把它自己作为setApplicationContext 的参数传入,而Spring容器在调用setBeanDactory前需要程序员自己指定(注入)setBeanDactory里的参数BeanFactory )
- 如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessBeforeInitialization(预初始化)方法(作用是在Bean实例创建成功后对进行增强处理,如对Bean进行修改,增加某个功能)
- 如果Bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet方法,作用与在配置文件中对Bean使用init-method声明初始化的作用一样,都是在Bean的全部属性设置成功后执行的初始化方法。
- 如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessAfterInitialization(后初始化)方法(作用与6的一样,只不过6是在Bean初始化前执行的,而这个是在Bean初始化后执行的,时机不同 )
- 经过以上的工作后,Bean将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁10.如果Bean实现了DispostbleBean接口,Spring将调用它的destory方法,作用与在配置文件中对Bean使用destory-method属性的作用一样,都是在Bean实例销毁前执行的方法。
监听器
- 实现
javax.servlet.ServletRequestListener
,javax.servlet.http.HttpSessionListener, javax.servlet.ServletContextListener
等等接口 - 主要用来监听对象的创建与销毁的发生, 比如 session 的创建销毁, request 的创建销毁, ServletContext 创建销毁
@Slf4j
@Component
@Conditional(JeecgCloudCondition.class)
public class SystemInitListener implements ApplicationListener<ApplicationReadyEvent>, Ordered {
@Autowired
private ISysGatewayRouteService sysGatewayRouteService;
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
log.info(" 服务已启动,初始化路由配置 ###################");
if (applicationReadyEvent.getApplicationContext().getDisplayName().indexOf("AnnotationConfigServletWebServerApplicationContext") > -1) {
sysGatewayRouteService.addRoute2Redis(CacheConstant.GATEWAY_ROUTES);
}
}
@Override
public int getOrder() {
return 1;
}
}
复制代码
Spring监听器—ApplicationListener
与主动触发监听器
过滤器Filter
基于回调实现、属于web
层,依赖于Servlet
适用于: 静态资源请求
// 首先 不需要任何@EnableXXX,只需要框架里有Servlet即可
// 第一步:自定义过滤器 ··· implements Filter
//重写方法 init、doFilter、destroy
//第二步:注入过滤器到spring容器中的2种方式
//1. 在自定义过滤器上直接使用注解
@Order
@WebFilter // 功能类似于FilterRegistrationBean注册过滤器
@WebInitParam
//2. 在注册了的Bean中代码形式注入过滤器
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean registFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean(); //属于Servlet中的一个方法,用于注册过滤器
registration.setFilter(new CostTimeFilter());
registration.addUrlPatterns("/*");
registration.setName("CostTimeFilter");
registration.setOrder(1);
return registration;
}
}
复制代码
拦截器
基于反射
和AOP思想
,拦截控制器,依赖于Spring框架
适用于: 统一权限控制、动态请求
// 首先开启 @EnableWebMvc,Spring Boot默认已经开启
// 第一步:自定义拦截器 ··· extends HandlerInterceptor/HandlerInterceptorAdapter
// 重写方法 preHandle、postHandle、afterCompletion
// 第二步:注入拦截器 ··· implements WebMvcConfigurer
复制代码
Aspet
—AOP
拦截Service
实际开发中,AOP
常和事务结合
AOP
的实现原理—-本质是反射
服务器定位模式(Service Locator
) < == == > 委托模式(delegate
)< == == > 反射、动态代理 < == ==> 类的方法不是由自己的实例调用,而是委托给用户自主调用
Spring
默认使用cglib库的反射
实现,没有用jdk自带的反射
控制器 servlet、controller
解析器 ··· implements HandlerMethodArgumentResolver
方法参数解析器:适用于使用注解使控制器方法参数的运行时修改
视图解析器(ViewResolver
)
语言解析器
// 首先开启 @EnableWebMvc,Spring Boot默认已经开启
// 第一步:自定义解析器 ··· implements HandlerMethodArgumentResolver/··· extends AbstractNamedValueMethodArgumentResolver
// 重写方法 createNamedValueInfo、resolveName、supportsParameter、handleMissingValue
// 第二步:注入解析器 ··· implements WebMvcConfigurer
复制代码
spring发起一个请求的过程
容器启动、销毁等事件的监听器(ApplicationListener
或自定义事件监听) ->
前端控制器(DispatcherServlet
) -> 请求到处理器映射(HandlerMapping
) -> 过滤器(Filter
) -> Servlet
容器(Tomcat
) -> 拦截器(Interceptor
) -> 解析器(Resolver
) -> AOP
-> 控制器(Controller
) -> 验证器(Validator
) -> 命令对象(Command
请求参数绑定到的对象就叫命令对象)-> 表单对象(Form Object
提供给表单展示和提交到的对象就叫表单对象;就是需要返回给前端的值)
Spring过滤器与拦截器-代码实现
过滤器、拦截器区别
拦截器、过滤器区别—知乎
spring生命周期
Spring架构
spring boot常用组件
监控模块
Spring Boot Actuator
与 Micrometer
Spring MVC
模型(Model
)中注入数据
请求参数校验
使用方式
- 在controller中的参数中使用
@Validated
- 在
entity
文件中使用@valid
注解标注 以校验 集合中的元素 和 自定义数据类型 - 自定义校验器,统一错误信息返回格式
Spring中的零散知识点
路径匹配
**
: 表示任意多级目录
*
: 表示任意名称的目录
classpath*:
与classpath:
:打包速度前者慢些
classpath*:org/jeecg/modules/**/xml/*Mapper.xml
: 此种方式能匹配到与输出目录classes同级的jar包内部的文件
classpath*:/mapper/**/*.xml
:输出目录中的静态资源
Spring中的Bean默认是单例模式,为什么可以并发请求
单例模式的成员变量必须保证线程安全,如用ThreadLocal
,单例的方法及其局部变量每次调用都会创建在自己的方法栈上下文中,因此可以保证并发而互不影响。
所以Spring中的所有注册的类的成员变量有两种情况:
- 成员变量是使用
@Autowired
等注解,且这个成员变量的类定义中成员方法也满足条件1、条件2 - 成员变量是其他普通类型,如
int、List、Set、Map
等,则必须手动保证线程安全ThreadLocal
,
spring boot
web层基于servlet
,servlet
的每个request是一个线程;而且数据库连接也要保证线程隔离,因为临时表
、绑定变量
等都只在当前会话有效,且不同会话互相隔离,才有意义;当有要求单次请求中需要多次操作数据库,必须把多次操作封装在一个事务中。
在同一个方法中,Mybatis多次请求数据库,是否要创建多个SqlSession会话? – 简书 (jianshu.com)
spring中的mybatis的sqlSession是如何做到线程隔离的? – 等你归去来 – 博客园 (cnblogs.com)