这段时间有想法去学习手写Spring源码,在此记录一下自己手写的Spring的迭代版本趴。
1. Spring的启动扫描到容器
@Component
@ComponentScan
@Scope // 判断Bean对象是不是单例,值为singleton是单例,
上述注解需要手写出来来实现简单实现Spring中的功能
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
/**
* 容器中类的名称
* @return
*/
String value() default "";
}
/**
* 自定义的注解,默认扫描包的路径
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
String value() ; // 扫描的路径
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
String value();
}
上述三个注解里面没有什么价值性的东西,因为注解在我理解就是一个标识嘛。
接下来就要实现ApplicationContext,这玩意和我们使用Spring的ApplicationContext一样,主要的核心都在里面
直接上代码。
/**
* 容器类
*/
public class ZJHApplicationContext {
/**
* 配置类
*/
private Class configClass;
/**
* 单例池,里面存储的是单例对象
*/
private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<>();
/**
* 存放BeanDefinition对象
*/
private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
/**
* 构造器
*
* @param configClass
* @throws ClassNotFoundException
*/
public ZJHApplicationContext(Class configClass) {
this.configClass = configClass;
// 初始化Bean容器,其实Scan方法并没有将单例Bean对象加入到单例池中,Scan方法的主要作用就是将BeanDefinition对象加入到BeanDefinitionMap中,为了下面的将单例Bean加入到单例池中做准备
scan(configClass);
// 初始化BeanDefinition容器后,通Beandefinition容器里的元素来一一将扫描路径下的对象的Beandefinition对象取出
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : beanDefinitionMap.entrySet()) {
// 获取Beandefinition对象的key,也就是Bean的name
String beanName = beanDefinitionEntry.getKey();
// 获取BeandefinitionEntry的value,也就是BeanDefinition对象
BeanDefinition beanDefinition = beanDefinitionEntry.getValue();
// 接下来就是获取BeanDefinition对象的scope属性来判断对象是单例还是多例
if ("singleton".equals(beanDefinition.getScope())){
// 说明是单例Bean,将单例Bean加入到单例池中
Object bean = createBean(beanDefinition);
singletonObjects.put(beanName,bean);
}
// 如果对象是多例,则不需要加入到单例池中
}
}
/**
* 创建Bean对象的方法,
* @param beanDefinition
* @return
*/
public Object createBean(BeanDefinition beanDefinition){
// 通过BeanDefinition的class属性来通过反射创建出对象
Class clazz = beanDefinition.getClazz();
try {
Object instance = clazz.getDeclaredConstructor().newInstance();
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
/**
* 初始化Bean容器的方法
* @param configClass
*/
private void scan(Class configClass){
// 拿到配置类后会去解析这个配置类
// 1. 解析@ComponentScan注解的信息 -> 获取扫描路径 -> 扫描
// 查看配置类里有没有ComponentScan注解
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
// 获取扫描路径
String path = componentScanAnnotation.value();
System.out.println(path); // test.service
// 扫描路径的类是否加入了Component注解
// 1. 根据扫描路径来得到路径下的所有类,通过类加载器来获取,这里需要应用类加载器,因为Bootstrap,ext加载器不加载我们自定义的类,通过自己的类来获取对应的类加载器
ClassLoader classLoader = ZJHApplicationContext.class.getClassLoader();
path = path.replace(".", "//");
// 这个resource是一个目录
URL resource = classLoader.getResource(path);
// 通过File类来获取目录下的文件
File file = new File(resource.getFile());
// 如果文件是一个文件夹,则获取文件夹下的所有的文件
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
System.out.println(f);
// 将D:\Tools\Spring_ZJH\target\classes\test\service\UserService.class转为 test.service.UserService
String fileName = f.getAbsolutePath();
// 判断文件是不是class文件,如果是的话在开始截取
if (fileName.endsWith(".class")) {
// 从test开始截取,到.class结束
String className = fileName.substring(fileName.indexOf("test"), fileName.indexOf(".class"));
// 将\转换为.
className = className.replace("\\", ".");
// 得到了类的路径
System.out.println(className);
try {
// 通过类加载器来加载类
Class<?> clazz = classLoader.loadClass(className);
// 判断类上是否有Component注解
if (clazz.isAnnotationPresent(Component.class)) {
// 到这一步就说明扫描类中已经有了Component注解,即它是一个Bean对象
// 到了这一步得判断这个Bean是单例Bean还是Prototype的Bean(即多例Bean)
// 如果是单例Bean则将Bean对象加入到singletonObjects单例池中
// 在加入容器时,我们需要判断Bean是单例还是多例,在getBean方法时,我们也需要判断,但其实没有必要判断两次,Spring中有BeanDefinition的概念,即Bean的定义
// 每解析一个类都会生成一个BeanDefinition对象
// 获取Bean的名称
Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
String beanName = componentAnnotation.value();
// 生成BeanDefinitation对象,注意这个不是Bean对象,而是Bean的定义
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
if (clazz.isAnnotationPresent(Scope.class)){
// 设置BeanDefinitation对象的scope属性
Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
String value = scopeAnnotation.value();
beanDefinition.setScope(value);
}else {
// Bean中没有scope注解,则将BeanDefinition的scope属性设置为singleton,即单例
beanDefinition.setScope("singleton");
}
// 将BeanDefinition对象加入到BeanDefinitionMap中
beanDefinitionMap.put(beanName,beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
/**
* getBean方法
*
* @param beanName
* @return
*/
public Object getBean(String beanName) {
if (beanDefinitionMap.containsKey(beanName)){
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if ("singleton".equals(beanDefinition.getScope())){
// 说明这是单例Bean,从单例池子中获取Bean对象返回
Object o = singletonObjects.get(beanName);
return o;
}else {
// 说明这是多例Bean 手动创建Bean对象并返回
return createBean(beanDefinition);
}
}else {
throw new RuntimeException("没有"+beanName+"对象");
}
}
}
在使用Spring时,我们创建ApplicationContext对象,传入配置类即可使用容器。
上述代码精炼如下:
- createBean方法就是用于创建Bean对象
- scan方法的作用就是将读取配置类的ComponentScan注解的扫描路径,然后生成扫描路径下的包含@Component注解的类的BeanDefinition对象(即类定义对象),BeanDefinition对象的作用后面叙述。
- ZJHApplicationContext这是构造器,在得到所有的BeanDefinition对象后,来判断BeanDefinition所对应的对象是不是单例的,即类上是否加入了@Scope(“singleton”)注解,如果是单例,则加入到单例池
- getBean方法就是从容器中获取Bean对象
接下来就要解释一下BeanDefinition对象
package com.spring;
public class BeanDefinition {
/**
* Bean的类型
*/
private Class clazz;
/**
* Bean是单例还是多例
*/
private String scope;
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public BeanDefinition(Class clazz, String scope) {
this.clazz = clazz;
this.scope = scope;
}
public BeanDefinition() {
}
}
这里的BeanDefinition对象就只有两个属性:
- class:来记录Bean对象的类型,为了createBean方法而做准备
- scope:记录Bean对象中Scope注解的值,来判断类是不是单例的
为什么要有这个BeanDefinition对象?
在getBean方法和构造器方法中都需要判断Bean是不是单例,难道每次都要逐一遍历扫描路径的所有类一一判断?这里就用到了BeanDefinition对象来记录Bean对象的class和scope,方便判断Bean对象是不是单例的,也方便了创建Bean对象。
上述代码的流程图:
代码的详细注释在代码中,结合流程图更清晰。
手动测试,看看是否能获得Bean对象
配置类
@ComponentScan("test.service")
public class AppConfig {
}
待加入容器的类
@Component("userService")
@Scope("singleton")
public class UserService {
}
测试类
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
ZJHApplicationContext zjhApplicationContext = new ZJHApplicationContext(AppConfig.class);
Object userService = zjhApplicationContext.getBean("userService");
Object userService2 = zjhApplicationContext.getBean("userService");
System.out.println(userService.toString());
System.out.println(userService2.toString());
}
}
这里的Bean是单例的,结果如图
2. 依赖注入
Spring的依赖注入:先ByType后ByName。上述代码中没有实现依赖注入的功能,因为createBean方法里就直接通过BeanDefinition对象的class属性通过反射直接调用无参构造器进行创建了。那么如何实现简单的依赖注入呢?
在createBean方法通过反射创建一个Bean对象时,里面的属性没有被赋值,我们通过反射查看其所有属性,判断属性上是否含有@Autowired注解,如果有的话,我们就从容器里寻找注入。所以我们只需要修改createBean方法的逻辑来实现依赖注入。
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.METHOD,ElementType.FIELD})
public @interface Autowired {
}
那么如何实现先ByType后ByName呢?先上代码,后解释。
/**
* 创建Bean对象的方法,
* @param beanDefinition
* @return
*/
public Object createBean(BeanDefinition beanDefinition){
// 方法内部的存储BeanDefinition的Map
ConcurrentHashMap<String,BeanDefinition> internalBeanDefinitionMap = new ConcurrentHashMap();
// 通过BeanDefinition的class属性来通过反射创建出对象
Class clazz = beanDefinition.getClazz();
try {
Object instance = clazz.getDeclaredConstructor().newInstance();
// 依赖注入,对象里面有很多属性,那么哪些属性需要赋值?
// 通过反射来获取class的所有的属性
for (Field declaredField : clazz.getDeclaredFields()) {
// 判断属性上是否包含了Autowired注解
if (declaredField.isAnnotationPresent(Autowired.class)){
// 进入到这一步就说明已经包含了AutoWired注解
// 接下来获取包含@Autowired注解的属性的属性
Class<?> type = declaredField.getType();
System.out.println("aclass"+type.toString());
// 从BeanDefinitionMap中遍历,找到和包含@Autowired注解的属性的属性相同的BeanDefinition,并把BeanDefinition存入到internalBeanDefinitionMap
for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
// 进行比较
if (entry.getValue().getClazz().equals(type)){
System.out.println("有的哦");
internalBeanDefinitionMap.put(entry.getKey(), entry.getValue());
}
}
// 判断internalBeanDefinitionMap里有几个元素,如果大于1,就说明相同类型有两个,我们就通过ByName来进行注入
if (internalBeanDefinitionMap.size()>1){
// 先获取有@AutoWired注解的属性的名称
String beanName = declaredField.getName();
// 不断从internalBeanDefinitionMap中进行比较
for (Map.Entry<String, BeanDefinition> entry : internalBeanDefinitionMap.entrySet()) {
if (beanName.equals(entry.getKey())){
// 到这一步就说明Name有相同的了,通过name来调用getBean方法
Object bean = getBean(beanName);
// 属性注入
declaredField.setAccessible(true);
declaredField.set(instance,bean);
// 将内部的internalBeanDefinitionMap置空
internalBeanDefinitionMap.clear();
break;
}
}
}else {
// 这一步就说明容器中类型相同的只有1个
Object bean = getBean(declaredField.getName());
declaredField.setAccessible(true);
declaredField.set(instance, bean);
// 将内部的internalBeanDefinitionMap置空
internalBeanDefinitionMap.clear();
}
}
}
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
- 首先方法内,我定义了一个内部的internalBeanDefinitionMap,这个是用于存储当容器中有多个BeanDefinition的class属性能够匹配的上含@Autowired注解属性的类型,这个是我用于判断是否需要接下来的ByName。
- 首先通过反射创建出instance对象,此时这个对象还没有任何的属性注入
- 通过反射来获取instance对象中包含@Autowired注解的属性
- 获取属性的Type来和BeanDefinitionMap元素里的class进行比较,如果相同,就将这个BeanDefinition加入到自己方法内定义的internalBeanDefinitionMap
- 属性一一比较完毕后,就需要判断internalBeanDefinitionMap里的元素是否>1,如果>1,则说明需要ByName,就获取属性的name,来和BeanDefinitonMap的元素的key进行比较即可,只要有相同就注入
接下来手动测试一下趴
public class UserService {
@Autowired
private OrderService orderService;
@Autowired
private NameService nameService;
}
userService里依赖注入了OrderService和NameService,get,set方法就不贴出来了(占空间)
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
ZJHApplicationContext zjhApplicationContext = new ZJHApplicationContext(AppConfig.class);
UserService userService = (UserService) zjhApplicationContext.getBean("userService");
System.out.println(userService.getOrderService().toString());
System.out.println(userService.getNameService().toString());
}
}
结果
2.2 这里实现一个小功能,如果类有name属性,我们自动将beanName给赋值到这个name属性上
说一下实现思路:
- 创建一个接口,其包含了一个给name赋值的方法
- 类实现这个接口
- 在createBean方法内,依赖注入完毕后判断通过反射创建出来的对象是否实现了这个接口,如果实现了就通过对象调用接口的方法,在这里将Bean的名称赋值到类中
public interface BeanNameAware {
public void setBeanName(String beanName);
}
接口代码如上
@Component("userService")
@Scope("singleton")
public class UserService implements BeanNameAware {
@Autowired
private OrderService orderService;
@Autowired
private NameService nameService;
private String name;
}
测试的类的代码如上
/**
* 创建Bean对象的方法,
* @param beanDefinition
* @return
*/
public Object createBean(String beanNames,BeanDefinition beanDefinition){
// 方法内部的存储BeanDefinition的Map
ConcurrentHashMap<String,BeanDefinition> internalBeanDefinitionMap = new ConcurrentHashMap();
// 通过BeanDefinition的class属性来通过反射创建出对象
Class clazz = beanDefinition.getClazz();
try {
Object instance = clazz.getDeclaredConstructor().newInstance();
// 依赖注入,对象里面有很多属性,那么哪些属性需要赋值?
// 通过反射来获取class的所有的属性
for (Field declaredField : clazz.getDeclaredFields()) {
// 判断属性上是否包含了Autowired注解
if (declaredField.isAnnotationPresent(Autowired.class)){
// 进入到这一步就说明已经包含了AutoWired注解
// 接下来获取包含@Autowired注解的属性的属性
Class<?> type = declaredField.getType();
System.out.println("aclass"+type.toString());
// 从BeanDefinitionMap中遍历,找到和包含@Autowired注解的属性的属性相同的BeanDefinition,并把BeanDefinition存入到internalBeanDefinitionMap
for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
// 进行比较
if (entry.getValue().getClazz().equals(type)){
System.out.println("有的哦");
internalBeanDefinitionMap.put(entry.getKey(), entry.getValue());
}
}
// 判断internalBeanDefinitionMap里有几个元素,如果大于1,就说明相同类型有两个,我们就通过ByName来进行注入
if (internalBeanDefinitionMap.size()>1){
// 先获取有@AutoWired注解的属性的名称
String beanName = declaredField.getName();
// 不断从internalBeanDefinitionMap中进行比较
for (Map.Entry<String, BeanDefinition> entry : internalBeanDefinitionMap.entrySet()) {
if (beanName.equals(entry.getKey())){
// 到这一步就说明Name有相同的了,通过name来调用getBean方法
Object bean = getBean(beanName);
// 属性注入
declaredField.setAccessible(true);
declaredField.set(instance,bean);
// 将内部的internalBeanDefinitionMap置空
internalBeanDefinitionMap.clear();
break;
}
}
}else {
// 这一步就说明容器中类型相同的只有1个
Object bean = getBean(declaredField.getName());
declaredField.setAccessible(true);
declaredField.set(instance, bean);
// 将内部的internalBeanDefinitionMap置空
internalBeanDefinitionMap.clear();
}
}
}
if (instance instanceof BeanNameAware){
((BeanNameAware) instance).setBeanName(beanNames);
}
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
createBean方法如上
2.3 InitializingBean
其实在Spring中,我们知道有一个接口InitializingBean,其有一个方法afterPropertiesSet方法,我们可以自定义类实现这个接口里的这个方法,在方法里写自己的逻辑,Spring就可以在初始化Bean的时候给我们调用这个方法,那么这个应该如何实现呢?其实和上面的那个小功能一样。
public interface InitializingBean {
void afterPropertiesSet();
}
InitializingBean 接口
/**
* 创建Bean对象的方法,
* @param beanDefinition
* @return
*/
public Object createBean(String beanNames,BeanDefinition beanDefinition){
// 方法内部的存储BeanDefinition的Map
ConcurrentHashMap<String,BeanDefinition> internalBeanDefinitionMap = new ConcurrentHashMap();
// 通过BeanDefinition的class属性来通过反射创建出对象
Class clazz = beanDefinition.getClazz();
try {
Object instance = clazz.getDeclaredConstructor().newInstance();
// 依赖注入,对象里面有很多属性,那么哪些属性需要赋值?
// 通过反射来获取class的所有的属性
for (Field declaredField : clazz.getDeclaredFields()) {
// 判断属性上是否包含了Autowired注解
if (declaredField.isAnnotationPresent(Autowired.class)){
// 进入到这一步就说明已经包含了AutoWired注解
// 接下来获取包含@Autowired注解的属性的属性
Class<?> type = declaredField.getType();
System.out.println("aclass"+type.toString());
// 从BeanDefinitionMap中遍历,找到和包含@Autowired注解的属性的属性相同的BeanDefinition,并把BeanDefinition存入到internalBeanDefinitionMap
for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
// 进行比较
if (entry.getValue().getClazz().equals(type)){
System.out.println("有的哦");
internalBeanDefinitionMap.put(entry.getKey(), entry.getValue());
}
}
// 判断internalBeanDefinitionMap里有几个元素,如果大于1,就说明相同类型有两个,我们就通过ByName来进行注入
if (internalBeanDefinitionMap.size()>1){
// 先获取有@AutoWired注解的属性的名称
String beanName = declaredField.getName();
// 不断从internalBeanDefinitionMap中进行比较
for (Map.Entry<String, BeanDefinition> entry : internalBeanDefinitionMap.entrySet()) {
if (beanName.equals(entry.getKey())){
// 到这一步就说明Name有相同的了,通过name来调用getBean方法
Object bean = getBean(beanName);
// 属性注入
declaredField.setAccessible(true);
declaredField.set(instance,bean);
// 将内部的internalBeanDefinitionMap置空
internalBeanDefinitionMap.clear();
break;
}
}
}else {
// 这一步就说明容器中类型相同的只有1个
Object bean = getBean(declaredField.getName());
declaredField.setAccessible(true);
declaredField.set(instance, bean);
// 将内部的internalBeanDefinitionMap置空
internalBeanDefinitionMap.clear();
}
}
}
// Aware回调
if (instance instanceof BeanNameAware){
((BeanNameAware) instance).setBeanName(beanNames);
}
// Bean初始化
if (instance instanceof InitializingBean){
((InitializingBean) instance).afterPropertiesSet();
}
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
实现Bean初始化
@Component("userService")
@Scope("singleton")
public class UserService implements InitializingBean {
@Autowired
private OrderService orderService;
@Autowired
private NameService nameService;
@Override
public void afterPropertiesSet() {
System.out.println("棒棒哒");
}
}
测试
3. BeanPostProcessor
BeanPostProcessor,bean的后置处理器,其实在Spring中Bean的生命周期是有许多步骤,实例化前后,初始化前后等等,在这些环节,Spring能够给你提供方法,你自己编写逻辑,Spring可以帮你把你的逻辑在对应的阶段进行调用。
实现思路:
- 定义接口
- 类实现接口
- 在scan方法,判断类包含@Conpoment注解后判断类是否实现了BeanPostProcessor接口,如果实现了,就实例化,并加入到list中
- 在createBean方法中,调用InitializingBean方法的前后分别调用BeanPostProcessor的postProcessBeforeInitialization方法和postProcessAfterInitialization方法
public interface BeanPostProcessor {
// 初始化前
Object postProcessBeforeInitialization(Object bean, String beanName);
// 初始化后
Object postProcessAfterInitialization(Object bean, String beanName);
}
先定义这个接口
/**
* 初始化Bean容器的方法
* @param configClass
*/
private void scan(Class configClass){
// 拿到配置类后会去解析这个配置类
// 1. 解析@ComponentScan注解的信息 -> 获取扫描路径 -> 扫描
// 查看配置类里有没有ComponentScan注解
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
// 获取扫描路径
String path = componentScanAnnotation.value();
System.out.println(path); // test.service
// 扫描路径的类是否加入了Component注解
// 1. 根据扫描路径来得到路径下的所有类,通过类加载器来获取,这里需要应用类加载器,因为Bootstrap,ext加载器不加载我们自定义的类,通过自己的类来获取对应的类加载器
ClassLoader classLoader = ZJHApplicationContext.class.getClassLoader();
path = path.replace(".", "//");
// 这个resource是一个目录
URL resource = classLoader.getResource(path);
// 通过File类来获取目录下的文件
File file = new File(resource.getFile());
// 如果文件是一个文件夹,则获取文件夹下的所有的文件
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
System.out.println(f);
// 将D:\Tools\Spring_ZJH\target\classes\test\service\UserService.class转为 test.service.UserService
String fileName = f.getAbsolutePath();
// 判断文件是不是class文件,如果是的话在开始截取
if (fileName.endsWith(".class")) {
// 从test开始截取,到.class结束
String className = fileName.substring(fileName.indexOf("test"), fileName.indexOf(".class"));
// 将\转换为.
className = className.replace("\\", ".");
// 得到了类的路径
System.out.println(className);
try {
// 通过类加载器来加载类
Class<?> clazz = classLoader.loadClass(className);
// 判断类上是否有Component注解
if (clazz.isAnnotationPresent(Component.class)) {
// 判断类是否继承了BeanPostProcessor
if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
// 如果继承了,就创建这个BeanPostProcessor对象,并添加到list中
try {
BeanPostProcessor instance = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
beanPostProcessorList.add(instance);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
// 到这一步就说明扫描类中已经有了Component注解,即它是一个Bean对象
// 到了这一步得判断这个Bean是单例Bean还是Prototype的Bean(即多例Bean)
// 如果是单例Bean则将Bean对象加入到singletonObjects单例池中
// 在加入容器时,我们需要判断Bean是单例还是多例,在getBean方法时,我们也需要判断,但其实没有必要判断两次,Spring中有BeanDefinition的概念,即Bean的定义
// 每解析一个类都会生成一个BeanDefinition对象
// 获取Bean的名称
Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
String beanName = componentAnnotation.value();
// 生成BeanDefinitation对象,注意这个不是Bean对象,而是Bean的定义
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
if (clazz.isAnnotationPresent(Scope.class)){
// 设置BeanDefinitation对象的scope属性
Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
String value = scopeAnnotation.value();
beanDefinition.setScope(value);
}else {
// Bean中没有scope注解,则将BeanDefinition的scope属性设置为singleton,即单例
beanDefinition.setScope("singleton");
}
// 将BeanDefinition对象加入到BeanDefinitionMap中
beanDefinitionMap.put(beanName,beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
scan方法
/**
* 创建Bean对象的方法,
* @param beanDefinition
* @return
*/
public Object createBean(String beanNames,BeanDefinition beanDefinition){
// 方法内部的存储BeanDefinition的Map
ConcurrentHashMap<String,BeanDefinition> internalBeanDefinitionMap = new ConcurrentHashMap();
// 通过BeanDefinition的class属性来通过反射创建出对象
Class clazz = beanDefinition.getClazz();
try {
Object instance = clazz.getDeclaredConstructor().newInstance();
// 依赖注入,对象里面有很多属性,那么哪些属性需要赋值?
// 通过反射来获取class的所有的属性
for (Field declaredField : clazz.getDeclaredFields()) {
// 判断属性上是否包含了Autowired注解
if (declaredField.isAnnotationPresent(Autowired.class)){
// 进入到这一步就说明已经包含了AutoWired注解
// 接下来获取包含@Autowired注解的属性的属性
Class<?> type = declaredField.getType();
System.out.println("aclass"+type.toString());
// 从BeanDefinitionMap中遍历,找到和包含@Autowired注解的属性的属性相同的BeanDefinition,并把BeanDefinition存入到internalBeanDefinitionMap
for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
// 进行比较
if (entry.getValue().getClazz().equals(type)){
System.out.println("有的哦");
internalBeanDefinitionMap.put(entry.getKey(), entry.getValue());
}
}
// 判断internalBeanDefinitionMap里有几个元素,如果大于1,就说明相同类型有两个,我们就通过ByName来进行注入
if (internalBeanDefinitionMap.size()>1){
// 先获取有@AutoWired注解的属性的名称
String beanName = declaredField.getName();
// 不断从internalBeanDefinitionMap中进行比较
for (Map.Entry<String, BeanDefinition> entry : internalBeanDefinitionMap.entrySet()) {
if (beanName.equals(entry.getKey())){
// 到这一步就说明Name有相同的了,通过name来调用getBean方法
Object bean = getBean(beanName);
// 属性注入
declaredField.setAccessible(true);
declaredField.set(instance,bean);
// 将内部的internalBeanDefinitionMap置空
internalBeanDefinitionMap.clear();
break;
}
}
}else {
// 这一步就说明容器中类型相同的只有1个
Object bean = getBean(declaredField.getName());
declaredField.setAccessible(true);
declaredField.set(instance, bean);
// 将内部的internalBeanDefinitionMap置空
internalBeanDefinitionMap.clear();
}
}
}
// Aware回调
if (instance instanceof BeanNameAware){
((BeanNameAware) instance).setBeanName(beanNames);
}
// 初始化前
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
instance = beanPostProcessor.postProcessAfterInitialization(instance, beanNames);
}
// Bean初始化
if (instance instanceof InitializingBean){
((InitializingBean) instance).afterPropertiesSet();
}
// 初始化后
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
instance = beanPostProcessor.postProcessAfterInitialization(instance, beanNames);
}
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
createBean方法
主要就是在扫描的时候,将BeanPostProcessor的实现类加入到list,在createBean方法中的调用BeanPostProcessor的方法。
4. AOP
如果了解SpringBean生命周期的话,就知道AOP操作是初始化后这个阶段进行的AOP,上面叙述BeanPostProcessor就是为了AOP做准备。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface EnableAspectJAutoProxy {
}
上述是AOP的注解
@Component("zJHBeanPostProcessor")
@Scope("singleton")
public class ZJHBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
// Spring把Bean对象和BeanName给你,你自己进行逻辑编写
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean.getClass().isAnnotationPresent(EnableAspectJAutoProxy.class)) {
// 类上有AOP的注解
System.out.println("初始化后");
Object proxyInstance = Proxy.newProxyInstance(ZJHBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("这是代理逻辑");
// 调用原对象的方法
return method.invoke(bean,args);
}
});
return proxyInstance;
}
return bean;
}
}
在具体的PostProcessor的实现类里进行实现后置处理来实现AOP
这样子因为PostProcessor实现类是进入容器的且实现了PostProcesser,所以在scan方法中就会将它加入到beanPostProcessorList集合中,在createBean中因为beanPostProcessorList里有元素了,所以每一个bean都会执行PostProcess实现类的前置后置处理器的逻辑,AOP的逻辑就在后置处理器中,首先判断类是否含有AOP注解,如果有就通过JDK的方式来生成代理对象,这里就没有实现方法的切入,只是手动模拟代理逻辑。
@Component("zJHBeanPostProcessor")
@Scope("singleton")
public class ZJHBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
// Spring把Bean对象和BeanName给你,你自己进行逻辑编写
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean.getClass().isAnnotationPresent(EnableAspectJAutoProxy.class)) {
// 类上有AOP的注解
System.out.println("初始化后");
Object proxyInstance = Proxy.newProxyInstance(ZJHBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("这是代理逻辑");
// 调用原对象的方法
return method.invoke(bean,args);
}
});
return proxyInstance;
}
return bean;
}
}
PostProcessor实现类如上,最终返回的是代理对象,测试一下结果
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
ZJHApplicationContext zjhApplicationContext = new ZJHApplicationContext(AppConfig.class);
Object userService = zjhApplicationContext.getBean("userService");
System.out.println("userService的类型"+userService.getClass());
}
}
可以看到最后返回的是代理对象
以上的都只是模拟Spring,真正的Spring核心代码远远不止这些。
代码地址: https://gitee.com/Nov_Zsir/zjh_-spring