Spring笔记
任务一 手写Spring
1.1 Spring的优势
- 方便解耦、简化开发。管理对象间依赖关系、封装了单例、文件解析等
- AOP编程支持 oop不容易实现的,面向切面编程轻松实现
- 声明式事务支持 便捷的事务管理
- 程序测试
- 集成各种框架
- 降低了j2ee api调用难度
- 源码学习
1.2 Spring核心架构
- web、deta access、Aop、core container、test
描述:web层处理浏览器前端的请求及界面渲染,springmvc,与其他应用的交互,远程调用能力;data access为数据持久化提供了大量的模板,简化了操作,只需要调用api即可,配合Aop可以实现事务控制。AOP为日志、事务等面向切面编程的需求服务。核心容器,管理了对象,包括DI实现,以及上下文。test就是测试模块。
1.3 核心思想 IOC/DI、AOP
1.3.1 IOC(对象角度)
Inversion of Control,控制反转。在具体的使用位置,只需要定义变量,不需要new出对象,而是交由容器去做,用autowire标记后,容器就会注入进来。
控制:对象的创建、管理
反转:控制权交由外部容器实现
1.3.2 DI(容器角度)
Dependency Injection 依赖注入。同一问题的两种角度的描述
1.3.3 AOP
面向切面编程
oop是垂直继承体系,而横切重复问题无法解决,即使抽取了代码,也需要有一行来调用,这个重复部分无法避免。
AOP这种面向横切的,就应运而生。可以在方法的前后增强。实际即是代理模式。
切:不改变原有代码逻辑,只操作横切逻辑代码,即横切逻辑
面:不单单是一个方法,多个方法的相同切点,组成一个面
1.4 手写实现IOC、AOP
- 编写配置文件
- 加载配置文件
- 解析配置文件
- 解析bean,读出id作为key,class反射实例化出对象,作为value放进map(IOC容器)中
- 解析property,获得父对象,从上一步的map中,拿出对象,获得方法,遍历找出set方法,invoke方法,把依赖对象注入进去
- 提供getBean出口——至此,IOC完成
- AOP,通过动态代理实现,在方法执行的前后增强
- servlet中从容器中获得代理工厂ProxyFactory,把service交给代理工厂实例化,由他去执行invoke,把service的方法置于事务控制中。
1.5 设计模式
1.5.1 简单工厂模式
对一个接口,通过不同的实现类,在工厂类中通过switch调用,或是重载方法调用。
比如INoodle接口,实现类有各种的Noodle类,工厂类中通过类型,switch调用不同实现类;Mybatis源码中则是通过方法重载,不同的参数列表实现。
1.5.2 工厂方法模式
在简单工厂中进一步封装,抽取一个通用的createNoodle方法,创建INoodleFactory工厂接口,编写不同的Noodle工厂类,每个工厂只生成一种。在create方法中实现特定的Noodle创建。通过使用不同的工厂类,创建实例。类似Mybatis中,SqlsessionFactory,专门创建sqlsession。
1.5.3 饿汉式单例模式
首先,通用部分,单例模式:
-
构造方法私有化,只有该类本身可以实例化自身;
private HungrySingleton(){}
-
调用私有构造方法实例化自身,获得静态常量属性:
private static final HungrySingleton instance = new HungrySingleton();
-
通过静态公共方法返回。
public static HungrySingleton getInstance(){
return instance;
}
不同点:定义私有属性instance时就实例化对象。
1.5.4 懒汉式单例
通用部分一样,不同的是实例化时,需要判断instance是否为空。定义静态常量时,先不实例化。
// 构造方法私有化
private LazySingleton() {}
// 将自身实例化对象设置为一个属性,并用static修饰
private static LazySingleton instance;
// 静态方法返回该实例,加synchronized关键字实现同步
public static synchronized LazySingleton getInstance() {
if(instance == null) {
instance = new LazySingleton();
}
return instance;
}
1.5.5 代理模式
1.5.5.1 静态代理
针对一个实现类,编写代理类,且其只能接收该实现类作为入参。
代理,其实就是把执行方法的工作交给了代理类。如下:
IRentingHouse rentingHouse = new RentingHouseImpl();
// 自己要租用一个一室一厅的房子
// rentingHouse.rentHosue();
RentingHouseProxy rentingHouseProxy = new RentingHouseProxy(rentingHouse);
rentingHouseProxy.rentHosue();
静态跟动态的区别就是:每个代理类只能对应一个场景。动态代理是由jdk或cglib封装了的,通过反射拿到传入的对象,去动态实例化。获得其方法等内容。
为了在代理类中附加一些业务处理,比如在执行原方法的前后。做横向逻辑切入。
1.5.5.2 动态代理
jdk、cglib两种方式实现。
在ProxyFactory中,其实也是一个工厂方法模式,专门用来生成Proxy的。
public Object getJdkProxy(Object obj) {
// 获取代理对象
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
// 写增强逻辑
System.out.println("中介(代理)收取服务费3000元");
// 调用原有业务逻辑
result = method.invoke(obj,args);
System.out.println("客户信息卖了3毛钱");
return result;
}
});
}
调用时,从代理对象中生成对象,调用具体的方法。(故method能匹配上。)
cglib代理:
public Object getCglibProxy(Object obj) {
return Enhancer.create(obj.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = null;
System.out.println("中介(代理)收取服务费3000元");
result = method.invoke(obj,objects);
System.out.println("客户信息卖了3毛钱");
return result;
}
});
}
jdk面向接口,需要有接口才能实现(能通过getInterface拿到接口)。调用时:
IRentingHouse rentingHouse = new RentingHouseImpl();
// JDK 原生动态代理是基于接口实现的,而 cglib 是基于继承当前类的子类实现的(实现类)。传入接口也行
IRentingHouse jdkProxy= (IRentingHouse) ProxyFactory.getInstance().getJdkProxy(rentingHouse);
jdkProxy.rentHouse();
RentingHouseImpl rentingHouse2 = new RentingHouseImpl();
RentingHouseImpl cglibProxy = (RentingHouseImpl) ProxyFactory.getInstance().getCglibProxy(rentingHouse2);
cglibProxy.rentHouse();
任务二 Spring特性及源码
回顾Spring
BeanFactory是顶级接口,ApplicationContext是子接口,常用的,里面加了更多的功能。常用他下面的实现类
ClassPathXmlApplicationContext:从类的根路径下加载配置⽂件(推荐使⽤)
FileSystemXmlApplicationContext:从磁盘路径上加载配置⽂件
AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
启动方式
web.xml中配置,配置类启动、xml启动两种。
实例化bean(3种)
- 无参构造
- 静态方法
- 实例方法
bean作用范围@scope、生命周期
- singleton
- prototype
依赖注入方式
- set注入(使用最多)@autowired
- 构造函数注入
注解详解
@autowired 按照类型注入,当一个类型有多个bean时,如IDAO类型(接口),有多个impl(Mybatis、jdbc等),配合@Qualifier(name=“jdbcDaoImpl”),加个名称做定位即可。
@Resource默认是byname,可以指定name,type
@configuration配置类
@componentscan扫描
@propertysource
@Import
@Value,赋值,读取配置文件信息${}
@Bean 注入容器
XML方式
- 去掉手写的bean加载,及配置文件引入applicationcontext.xml
- getbean由ClassPathXmlApplicationContext这个类加载xml配置文件读取
加上注解
- 配上@autowired
- @configuration加载连接信息SpringConfig类
- 事务控制@transactional,注解扫描,@service,@Repository
延迟加载
lazy-init
BeanFactory和FactoryBean
BeanFactory是容器顶级接口,生产bean的工厂;FactoryBean是bean的一种,工厂bean
后置处理器
BeanPostProcessor,BeanFactoryPostProcessor
工厂初始化后可以用后者
bean实例化后可以使用前者。
任务三 Spring源码部分
代码架构思维、理解框架
原则:定焦原则,抓主线
宏观原则:关注架构、业务流程
源码方法和技巧:
- 断点(观察调用栈)
- 反调(find usage)
- 经验(doXXX真正开始处理)
IOC源码
3.1 bean创建的切入
利用debug,在bean的关键调用位置,查看线程调用栈。
开发工具在断点部分,会显示当前执行代码所处的调用栈,即可知道程序调用了哪些代码。
关键的bean创建,放在构造方法中,调用栈为:AbstractApplicationContext 类 的 refresh ⽅法
debug循环中条件控制
3.2 refresh方法总览创建流程
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.预处理
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 获取BeanFactory,默认实现为DefaultListableBeanFactory
// 加载beandefinition,注册到beandefinitionregistry
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.BeanFactory的预准备工作,类加载器等
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.后置处理工作
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.(国际化功能;消息绑定,解析)
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
/*第⼗⼀步:
初始化所有剩下的⾮懒加载的单例bean
初始化创建⾮懒加载⽅式的单例Bean实例(未设置属性)
填充属性
初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、 init-method⽅法)
调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处
*/
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
3.3 BeanFactory创建子流程
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
3.4 BeanDefinition加载、解析、注册子流程
在obtainBeanFactory流程中
3.5 Bean创建流程
AbstractApplicationContext.refresh()->finishBeanFactoryInitialization()中
3.6 lazy-init延迟加载机制
AbstractApplicationContext.refresh()->finishBeanFactoryInitialization()中
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
DefaultListableBeanFactory.preInstantiateSingletons()
public void preInstantiateSingletons() throws BeansException {
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
3.7 Spring IOC循环依赖问题
3.7.1 构造器注入
(无法解决)BeanCurrentlyInCreationException
3.7.2 Field属性,set注入
spring采⽤的是提前暴露对象的⽅法
单例的可以解决
prototype的无法解决
基于java的引用传递(类似指针?)定义后,可以延后设值。
3.7.3 源码
AbstractApplicationContext.refresh()->finishBeanFactoryInitialization()中->DefaultListableBeanFactory.preInstantiateSingletons()
->AbstractBeanFactory.doGetBean()
作业
思路:
改造前现状
通过bean.xml配置需要管理的属性、及依赖关系,在BeanFactory中实现对bean的管理,以及依赖注入
需要实现的重点
- 采用xml+anno的形式,需要实现@Service、@Repository前面两者可以合一起,bean对象发现及创建,@Autowired维护依赖、@Transactional,Aop的实现,注解的value处理,以及aop实现的方式,jdk、cglib
- 加载配置文件(base-package)->解析配置文件,解析scan包,拼装获得全类名集合->实例化对象->id+对象放到map中->依赖注入->启动调用
- 实现,编写注解类->编写解析处理类->改造原有业务代码,加入注解
- 事务注解开发,区分是jdk还是cglib
- 判断条件:TestStringCal.class.getInterfaces().length==0,在实例化对象的时候判断有无事务注解
…(img-ZpflD8Ic-1631316525590)]
思路:
改造前现状
通过bean.xml配置需要管理的属性、及依赖关系,在BeanFactory中实现对bean的管理,以及依赖注入
需要实现的重点
- 采用xml+anno的形式,需要实现@Service、@Repository前面两者可以合一起,bean对象发现及创建,@Autowired维护依赖、@Transactional,Aop的实现,注解的value处理,以及aop实现的方式,jdk、cglib
- 加载配置文件(base-package)->解析配置文件,解析scan包,拼装获得全类名集合->实例化对象->id+对象放到map中->依赖注入->启动调用
- 实现,编写注解类->编写解析处理类->改造原有业务代码,加入注解
- 事务注解开发,区分是jdk还是cglib
- 判断条件:TestStringCal.class.getInterfaces().length==0,在实例化对象的时候判断有无事务注解