IOC是什么
- 控制反转,自己吃饭,告诉仆人自己喜欢吃什么菜,被人喂饭
- 本质就是一个工厂模式
IOC的优点
没有IOC
依赖StudentService,需要自己创建出来
StudentService studentService = new StudentService();
常规的开发中有大量的依赖,手动创建麻烦且不宜管理
有了IOC
通过配置文件配置所有对象的信息,对象都可以通过加载入IOC来托管
非特殊逻辑的可以使用scan直接扫描包
<?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="studentService" class="com.vission.service.StudentService" init-method="init" destroy-method="destroy" scope="prototype"> <constructor-arg name="studentDao" ref="studentDao"/> </bean> <bean id="studentDao" class="com.vission.dao.StudentDao"/> </beans>
依赖StudentService,直接从IOC容器里面取
StudentService studentService = (StudentService)beanFactory.getBean("StudentService");
封装对象的一些常用且易冗余的代码
- 单例模式
- 生命周期
- init
- destory
对象创建时自动注入所需的依赖
通过上述分析,可以看出,IOC,只要一次配置,剩下可以托管给IOC,开发者再无需关心对象的创建、销毁的细节,也无需担心原型模式浪费内存,甚至所需对象的依赖也可以隐藏在工厂模式下无需关心
什么是依赖注入
- 描述所需依赖,无需自己手动创建,由IOC自动创建组装
- 告诉仆人喜欢吃的菜需要怎么做
Spring Bean装配
根据依赖关系将Bean通过构造器注入、setter注入进行装配,并放入IOC容器中
Spring Bean自动装配
根据依赖关系,自动搜寻IOC容器中的Bean进行装配,搜寻的方式有两种
- ByName
- ByType
再通过构造函数或是set注入
@Required的作用
初始化必须setter方法,否则抛出bean初始化异常
已弃用无需深究
自动装配的局限性
不知道,平时都用lombok配合构造函数注入,idea弹警告就很少用这玩意了
依赖注入的方式
构造器
手动写构造器
@lombok
@RequiredArgsConstructor
- final修饰
- 无初始值
@AllArgsConstructor
- 所有属性的构造函数
bean.xml
constructor-arg
<?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="studentService" class="com.vission.service.StudentService"> <constructor-arg name="studentDao" ref="studentDao"/> </bean> <bean id="studentDao" class="com.vission.dao.StudentDao"/> </beans>
setter
手动写Set方法
@lombok
- @Setter
- @Data
bean.xml
property
<?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="studentService" class="com.vission.service.StudentService"> <property name="studentDao" ref="studentDao"/> </bean> <bean id="studentDao" class="com.vission.dao.StudentDao"/> </beans>
Spring中常用的IOC容器
- BeanFactory
- Application
BeanFactory与ApplicationContext的关系
Application继承自BeanFactory且扩展了很多方法,简而言之就是增强版的BeanFactroy
- 国际化
Spring IOC实现机制
- 工厂模式
- 隐藏构造细节
- 反射
- 通过全限定路径反射,可以做到弱依赖
JavaBean、POJO、SpringBean区别
- POJO
- 具有所有属性皆为private
- 所有属性都具有public的get、set方法
- 无继承、 实现
- JavaBean
- 无参构造函数
- 实现序列化接口 Serializable
- SpringBean
- SpringIOC管理的对象即为SpringBean
BeanWrapper
SpringFramework提供的一个用于操作JavaBean属性的工具
提供Spring内部统一使用get,set方法
更新-拷贝场景
- 将一个对象存储a的值赋值到另一个对象b,且a中属性为null的不进行赋值
- BeanUtils#copy方法中可以使用BeanWrapper获取全部的字段并判空过滤
- Spring提供的copy方法是可以忽略属性为空的字段,将上个步骤处理的结果带入即可
/** * 拷贝source属性覆盖target属性(忽略null) * * @param <S> the type parameter * @param <T> the type parameter * @param source 源对象 * @param target 目标对象类型 */ public static <S, T> void copyIgnoreNull(S source, T target) { try { String[] nullPropertyNames = ObjectUtils.getPropertyNamesByValueNull(source).toArray(new String[0]); org.springframework.beans.BeanUtils.copyProperties(source, target, nullPropertyNames); } catch (Exception e) { throw new RuntimeException(e); } } /** * 获取对象为空的属性名集合 * * @param source 对象 * @return 属性集合 string [ ] */ public static List<String> getPropertyNamesByValueNull(Object source) { return ObjectUtils.traverseAllPropertyNames(source, Objects::isNull); } /** * 获取对象为空的属性名集合 * * @param source 对象 * @return 属性集合 string [ ] */ private static List<String> traverseAllPropertyNames(Object source, Function<Object, Boolean> filterStrategy) { BeanWrapper beanWrapper = new BeanWrapperImpl(source); PropertyDescriptor[] properties = beanWrapper.getPropertyDescriptors(); return Arrays.stream(properties).map(PropertyDescriptor::getName) .map(beanWrapper::getPropertyValue) .filter(Objects::nonNull) .map(Object::toString) .filter(filterStrategy::apply) .collect(Collectors.toList()); }
Spring的配置方式
XML
注解声明
- @Controller、@Service、@Component、@Repository…
基于Java的配置类
package com.vission.config; import com.vission.dao.StudentDao; import com.vission.service.StudentService; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class BeanConfig implements ApplicationContextAware { private ApplicationContext context; @Bean(name = "studentService") public StudentService studentService() { return new StudentService((StudentDao) context.getBean("studentDao")); } @Bean(name = "studentDao") public StudentDao studentDao() { return new StudentDao(); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { context = applicationContext; } }
如何在Spring中启动注解装配
XML
annotation-config用的较少,更加专注于@Component注解的类
- 默认扫描项目所在的根路径比如
com.vission.study.*
context-component-scan会扫描指定路径下的类,并将使用了@Component注解的类注册为SpringBean,实际上会默认开启了annotation-config,这也是在实际开发中最通用的办法
@ComponentScan(basePackages = “”)
- @ComponentScan可以不指定路径使用默认路径
SpringBean的Scope
- Singleton
- 单例
- Prototype
- 原型
- Request
- 每次请求产生一个新实例 并且只在此HTTP中生效
- Session
- 每次请求产生一个新示例 并且只在此HTTP Session中生效
- Global-session
- 不太了解chatGPT说不存在这个东西
SpringBean的生命周期
- 实例化
- 属性注入
- 依赖注入
- 各种回调函数注入
- 初始化
- @PostConstruct注解标注初始方法
- 实现InitializingBean接口的初始化方法
- 配置Bean的Init-method
- 销毁
- @PreDestroy注解标注销毁方法
- 实现DisposableBean接口的销毁方法
- 配置Bean的destroy-method
- 原型类型的Bean无法通过IOC管理销毁,因为他是由JVM管理销毁
Spring中单例Bean是否线程安全
- 单例非线程安全
- 无状态Bean
- bean中不存数据
- 大部分丢给IOC管理的Bean多半是service controller等,这些只是调用方法,对象内部不涉及存储数据的改变,所以不存在考虑线程安全
- 有状态Bean
- bean中存放数据
- 如果硬要存放数据考虑安全性。需要将作用域改为原型模式,这样每次都会新建实例
Spring支持的事务管理类型
- 编程式事务管理
- 通过使用事务API ,手写事务代码来实现事务功能
- 业务代码与事务代码耦合
- 声明式事务管理
- 使用@Transaction注解
静态代理与动态代理的区别
静态代理
- 手写的代理模式
动态代理
- 顾名思义动态生成代理类实现代理模式
- 动态生成一个proxyXXX.class实现代理的功能
- 常用的思路有CGLIB,JDK代理,SpringAop也是通过这两种方式实现,具体底层逻辑我也不是很了解
Spring是如何集成MyBatis
注解配置
package com.vission.config; import javax.sql.DataSource; import lombok.AllArgsConstructor; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; @Configuration @AllArgsConstructor @MapperScan("com.vission.mapper") public class MyBatisConfig { private DataSource dataSource; @Bean(name = "sqlSessionFactory") public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); Resource[] mapperResources = new PathMatchingResourcePatternResolver().getResources( "classpath:mapper/**/*.xml"); sqlSessionFactoryBean.setMapperLocations(mapperResources); sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml")); return sqlSessionFactoryBean.getObject(); } }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="autoMappingBehavior" value="PARTIAL"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="25000"/> <setting name="mapUnderscoreToCamelCase" value="false"/> </settings> </configuration>
Spring如何将Mybatis的mapper扫描成为Bean的
@MapperScan(“com.vission.mapper”)