帮我们创建当前的对象,把对象交给spring,工厂模式工厂可以帮我们生产这些对象,工厂的这些类依旧需要手动去定义
技术的更新迭代最重要的一个目的就是 偷懒
springIOC容器可以放入bean对象,还可以放集合框架,在spring当中选择的是Map,因为当我们创建完成的时候,map是一个k-v键值对的方式,我们可以根据我们的key很方便的获取到value.
容器的底层原理是反射
spring当中用的最多的一个注解 @Autowire
将userService注入userController中
package com.example.demo.basejavalearn.reflect.case1;
/**
* @author fancc
* @date 2022年01月28日 15:56
*/
public class UserController {
private UserService userService;
//如果没有@Autowire最简单的一种方式通过对userService进行get set方法
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
}
userService
package com.example.demo.basejavalearn.reflect.case1;
/**
* @author fancc
* @date 2022年01月28日 15:56
*/
public class UserService {
}
注入
package com.example.demo.basejavalearn.reflect.case1;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author fancc
* @date 2022年01月28日 15:59
*/
public class Test {
public static void main(String[] args) throws Exception{
//1 先要获取大写的class类
UserController userController = new UserController();
//2 因为要注入useService对象,所以先把userService创建出来
UserService userService = new UserService();
System.out.println(userService);
// 获取到class对象,因为反射的时候必然用到class
/***
* 获取class有三种方式:
* 第一种: class.forName
* 第二种:类.class
* 第三种: 对象.getClass
*/
Class<? extends UserController> clazz = userController.getClass();
//获取想要注入的成员变量或者说属性的名称
Field serviceField = clazz.getDeclaredField("userService");
//当我拿到当前的这个field之后,可以通过set方法,往里边写入,这意味着要获取当前的set方法,
// 而这个set方法是根据我们的当前的属性的名字加上一个set得到的
//先把名字给拿到 获取属性的名称
String name = serviceField.getName();
//获取set方法的名称
name = "set" + name.substring(0,1).toUpperCase()+name.substring(1,name.length());
//获取set方法对象 传递对应的参数
Method method = clazz.getDeclaredMethod(name, UserService.class);
//执行set方法 放入对应的类,放入对应的类对象
method.invoke(userController,userService);
//执行成功 打印
System.out.println(userController.getUserService());
}
}
输出结果
com.example.demo.basejavalearn.reflect.case1.UserService@1c20c684
com.example.demo.basejavalearn.reflect.case1.UserService@1c20c684
1、创建一个自定义注解
package com.example.demo.basejavalearn.reflect.case2;
import java.lang.annotation.*;
/**
* @author fancc
* @date 2022年02月07日 10:40
*/
//注解需要实现最基本的四个元注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)//作用范围是什么
@Inherited //是否可以被继承实现
@Documented //是否在文档里面展示
public @interface Autowired {
}
userController
package com.example.demo.basejavalearn.reflect.case2;
/**
* @author fancc
* @date 2022年02月07日 10:44
*/
public class UserController {
@Autowired
private UserService userService;
public UserService getUserService(){
return userService;
}
}
userService
package com.example.demo.basejavalearn.reflect.case2;
/**
* @author fancc
* @date 2022年02月07日 10:45
*/
public class UserService {
}
实现
package com.example.demo.basejavalearn.reflect.case2;
import java.lang.reflect.Field;
import java.util.stream.Stream;
/**
* @author fancc
* @date 2022年02月07日 10:45
*/
//用反射的机制进行最基本的一个实现
public class Test2 {
public static void main(String[] args) {
UserController userController = new UserController();
//获取userController里面对应的属性都有哪些
Class<? extends UserController> clazz = userController.getClass();
//获取所有的属性值
//Field[] fields = clazz.getDeclaredFields();
Stream.of(clazz.getDeclaredFields()).forEach(field -> {
//开始遍历每一个属性值,当发现其中的属性值被Autowire修饰的时候,需要进行注入
Autowired annotation = field.getAnnotation(Autowired.class);
if (annotation != null){
//当前是可访问的对象
field.setAccessible(true);
//获取到类型,方便进行具体对象的创建
Class<?> type = field.getType();
try {
Object o = type.newInstance();
//当有了对象的时候,传入具体的对象和值
field.set(userController,o);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
});
System.out.println(userController.getUserService());
}
}
beanDefination描述一个bean实例,描述有哪些属性值,构造器描述更多的实现
先把定义信息读取到容器当中,beanDefinationRegistry就是存放beanDefination,由当前接口的一个实现子类beanDefinationReader,读取完毕后放入beanDefination
IOC已经包含了bean的定义信息,该进行当前对象的一个实例化操作
BeanFactoryPostProcessor进行一些增强或者说扩展操作,例如:当读取这些bean时,还未实例化,只想把bean的名字打印出来,或者进行一些操作
下面是代码实例:
看一个springboot的注解实现
错误的查看方法,不应该从注解入手
正确的方法:
getCandidateConfigurations()这个方法,是什么时候开始调用的?从这个方法往上面看,spring有一个reflash方法里面有一个执行beanFactoryPostProcessor的类,这个类里面会解析import注解。
整个的注解从外面根注解开始,是怎么取到这些注解,怎么取到这些类,通过这个类是如何导入进去,为什么要执行get方法,需要把这个东西讲清楚。
有了beanFactory可以进行一个反射实现,有了反射实现正常情况下应该进行当前bean的一个实例化和初始化,在进行的这个过程引入了一个新的接口方法BeanPostProcessor(AOP也是在这块实现的BeanPostProcessor)
1、FactoryBean和BeanFactory的区别:FactoryBean实现了三个方法 isSingleton(是否是单例),getObject,getObjectType 就是生产一个特殊的对象相当于一个扩展bean。
创建对象的时候有两种方式一般情况下常规对象就是BeanFactory,但是有某些特殊对象可以用FactoryBean来实现,当时用FactoryBean的时候可以在getObject的地方可以做想要的操作。
2、如果想要在spring运行的不同阶段做不同的事情,应该怎么处理?
观察者模式:监听器(监听事件)
super(parent);调用父类的对应构造方法,父类构造方法并不是白创建的,(看源码时要看这个类对应的类图)一层一层的继承,是在往父类的构造方法里设置属性值
设置配置文件的路径
1)先创建一个标准环境变量 standardEnvironment
2)调用父类的一个AbstractEnvironment构造方法
3)调用父类构造方法时候,会调用一个customizePropertySource方法,这个时候这个方法是空的实际上调用的是子类的方法
该方法是 Spring Bean 加载的核心,它是 ClassPathXmlApplicationContext 的父类 AbstractApplicationContext 的一个方法 , 顾名思义,用于刷新整个Spring 上下文信息,定义了整个 Spring 上下文加载的流程。
先看下refresh()方法总体
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备预处理:记录容器的启动时间startupDate, 标记容器为激活,初始化上下文环境如文件路径信息,验证必填属性是否填写
this.prepareRefresh();
// **告诉子类去刷新bean工厂,此方法解析配置文件并将bean信息存储到beanDefinition中,注册到BeanFactory(但是未被初始化,仅将信息写到了beanDefination的map中)**重点方法,下面的操作都基于这个beanFactory进行的
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
// 设置beanFactory的基本属性:类加载器,添加多个beanPostProcesser
this.prepareBeanFactory(beanFactory);
try {
// 空实现:允许子类上下文中对beanFactory做后期处理
this.postProcessBeanFactory(beanFactory);
/**************************以上是BeanFactory的创建及预准备工作 ****************/
// 调用BeanFactoryPostProcessor各个实现类的方法
this.invokeBeanFactoryPostProcessors(beanFactory);
// 注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别
// 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
// 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
this.registerBeanPostProcessors(beanFactory);
//初始化ApplicationContext的MessageSource组件(资源文件),如国际化文件,消息解析,绑定等
this.initMessageSource();
//初始化ApplicationContext事件广播器
this.initApplicationEventMulticaster();
// 初始化子类特殊bean(钩子方法)
this.onRefresh();
// 获取所有的事件监听器,并将监听器注册到事件广播器
this.registerListeners();
//** 初始化所有singleton bean;**重点方法
this.finishBeanFactoryInitialization(beanFactory);
// 广播事件:ApplicationContext初始化完成
this.finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex);
}
// 销毁bean
this.destroyBeans();
// 重置 'active' 标志.
this.cancelRefresh(ex);
throw ex;
}
}
后面需要用到earlyApplicationEvents这个集合,作为保存容器的一些早起事件
判断是否有工厂,有的话清空并销毁,没有工厂进行创建,返回DefaultListableBeanFactory(后续会用到)指的是默认创建bean工厂的类,创建的工厂相当于在堆里面开辟了一块新空间,里面的属性值还未进行写;下面是进行具体地属性值,bean的定义信息的写入loadBeanDefination,写入xml bean定义信息,这个地方可以看到解析过程
loadBeanDefinitions可以看到xml的解析过程,
该方法主要负责对BeanFactory的预准备工作,配置beanFactory的基础属性,比如ClassLoader和一些PostProcessor等。
这个方法主要是给BeanFactory设置一些基本的属性,比如类加载器、表达式解析器、属性编辑器,注册几个单例、添加一些不用注入的接口、添加解析依赖项等。
主要负责在BeanFactory准备工作完成之后,beanFactory的后置处理工作;
这段代码重复处理逻辑很多
processBean用来记录已经执行过的bean
initMessageSource();
创建priorityOrderedPostProcessor、internalPostProcessors、orderedPostProcessorNames、nonOrderedPostProcessorNames的list集合,下面进行每一个集合的循环,循环的时候进行注册
初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
模板设计模式;该方法属于钩子方法;子类重写该方法并在容器刷新的时候自定义逻辑;
注册监听器分为两部分:
1、向事件分发器注册硬编码设置的applicationListener
2、向事件分发器注册一个IOC中的事件监听器(并不实例化)
finishBeanFactoryInitialization主要是负责初始化单实例的bean;该方法是重点方法,bean的生命周期基本调用getBean()方法完成。
初始化所有非懒加载得对象
循环引用
A里面有B的属性,B的里面有A的属性
构造器的循环依赖是没办法解决的。因为创建A的时候的构造方法需要给B赋值,B对象创建的时候要给A赋值,A、B谁先赋值谁后赋值是解决不了问题的
当前对象为空不是正在创建的状态
这个集合当中包含了正在创建的所有对象
三级缓存就是三个map结构
一级缓存跳到二级缓存有两个条件:1、一次缓存没有 2、当前对象是一个正在创建的状态
创建对象
相当于往三级缓存放入一个实例化的A对象
属性填充的过程
beanName:a名称; mbd:bean定义信息; bw:包装类; pvs:A里面对B的填充;
这个时候b是没有的
判断B是否属于这个类型
在走一遍getSingleton方法的从一级缓存当中获取,是没有的
之后在进行一个applyPropertyValues 在进行一遍这样的过程,这个时候pvs为a
先获取类型,然后去bean工厂拿a
拿不到a然后进行getBean
然后doGetBean再次进行getSingleton,这是一级缓存当中没有,a这时候是在创建中,进入判断体内,去二级缓存当中取a,没有;allowEarlyReference恒为true进入判断体,去三级缓存当中拿到了;取到了之后getObject执行内部类,返回一个Object;a 放入二级缓存;移除三级缓存中的a
执行内部类
执行完后返回
return bean
b取a已取到
value这个时候已经有a了,剩下的是将a的属性给b的成员变量
b的初始化完成,会调用一些beanFactory的postProcessor如before、after、init method
对象暴露出去
b放入一级缓存,三级缓存把b移除,二级缓存放的是a,但是a正在初始化还未完成初始化
返回b
返回b之后,把b给到a
把a对象移植到一级缓存中
这个时候循环依赖问题已解决
实例化和初始化分开来操作,将实例化好的对象提前暴露出去之后,可以拿到当前对象的引用,只不过当前对象不是一个完整状态,是实例化完成但不是初始化完成。
这时候 a b已经创建完成
完成bean创建和初始化过程,通知生命周期处理器 lifecycleProcessor 刷新过程,同时发出 ContextRefreshEvent 通知。
缓存当中放的是半成品,成品
如果当前多线程环境,都从一级缓存获取数据,有可能获取到半成品,导致属性都是空的,只有一级缓存不能解决问题
只有二级缓存能否解决该问题?
能解决一级缓存中存在的问题
如果没有三级缓存,刚开始放置的是实例化好的对象,然后缓存中有了,后面进行代理处理,生成了一个代理类。没有三级缓存会导致有的人用的是实例化好的对象有的是代理对象。这就是为什么三级缓存要用ObjectFactory,因为传递的匿名内部类最终是用来生成一个统一的对象。
如果创建的类是一个普通类,二级缓存足以,如果创建的是一个代理类,那么就会出问题!!
一级、二级放的是String、Object,三级缓存放的是String、ObjectFactory
BeanPostProcessor用来对这个类进行一些初始化的前置后置的处理
bean和beanName传进来
createProxy动态代理传入的是bean.getClass字节码文件,beanName bean的名称,specificInterceptors拦截器,new SingletonTargetSource(bean)把它包装成一个对象
对proxyFactory进行一个重新的代理,生成一个新的对象和刚才的对象已经没有关系
找到切入点
Aop一定是要生成代理对象的,这一定会进到postProcessAfterInitialization这个方法
进入到wrapIfNecessary里会找到createProxy
看懂下面,需要看懂jdbk动态代理和cglib动态代理
cglibAopProxy里面的getProxy方法的createEnhancer往里面设置属性值,然后设置回调函数,然后看createProxyClassAndInstance