Spring容器的初步实现
- 构造方法
- 使用componentScan注解扫描对应路径下的类,判断哪些需要装配到容器,并装配;
- 使用beanMap封装 <bean的名字,对应的bean属性(哪个类 和 单例/多例)>
- 对装配到容器中的单例对象调用createBean 进行创建
- 并加入到单例bean池中(singlotenMap)
- getBean方法
- 通过输入 bean的名字 来获取对应的bean对象;
- 如果是单例,从单例池中取;
- 如果是多例,则调用 createBean 创建bean对象;
package com.aguang.spring;
import java.beans.Introspector;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;
/*
* spring容器
*/
public class AguangApplicationContext {
private Class configClass;
//存放bean对象的名字,以及对应的bean对象属性
private ConcurrentHashMap<String, BeanDefinition> beanMap = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, Object> singletonMap = new ConcurrentHashMap<>();
//构造方法;传入一个配置类:扫描bean对象的配置类
public AguangApplicationContext(Class configClass) {
this.configClass = configClass;
//第一步:扫描bean对象--需要扫描 .Class文件
//首先判断传入的类是否是 可以有扫描功能的类,即是否有ComponentScan注解
if (configClass.isAnnotationPresent(ComponentScan.class)) {
//获取类路径---类路径是ComponentScan注解中的属性
//获得ComponentScan注解对象
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
//得到了相对路径; com.aguang.service,此路径下的文件是,原代码,需要的是 .Class文件
String path = componentScanAnnotation.value();
//对相对路径进行格式上的调整
path = path.replace(".", "/");
//通过类加载器,获取对应的.Class文件
//获取类加载器对象
ClassLoader classLoader = AguangApplicationContext.class.getClassLoader();
//通过类加载器,以及相对路径path,可以获取到对应的.class文件的路径
URL resource = classLoader.getResource(path);
//获得对应的文件
File file = new File(resource.getFile());
//判断该文件是否是一个目录
if (file.isDirectory()) {
//取出其中的文件
File[] files = file.listFiles();
//遍历每一个文件,即遍历每一个类判断哪个是 Bean对象,哪个有component注解;需要使用反射拿到这个类
for (File f : files) {
String fileName = f.getAbsolutePath();
//判断文件是否是.class文件
if (fileName.endsWith(".class")) {
//使用反射拿到这个类; 反射需要类名
String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
//对路径进行调整
className = className.replace("\\", ".");
try {
//获得对应的类对象
Class<?> clazz = classLoader.loadClass(className);
//判断是否包含Component注解,即是否是 Bean对象
if (clazz.isAnnotationPresent(Component.class)) {
//获取对应bean对象的名字
Component component = clazz.getAnnotation(Component.class);
String beanName = component.value();
if (beanName.equals("")) {
//如果没有传入beanName,默认值为类的首字母小写
beanName = Introspector.decapitalize(clazz.getSimpleName());
}
//定义bean对象的属性
BeanDefinition beanDefinition = new BeanDefinition();
//设置bean的类型
beanDefinition.setType(clazz);
//设置是多例,还是单例
if (clazz.isAnnotationPresent(Scope.class)) {
Scope scope = clazz.getAnnotation(Scope.class);
//将获取到的scope类型,设置到bean属性中
beanDefinition.setScope(scope.value());
} else {
//默认生成单例bean
beanDefinition.setScope("singleton");
}
beanMap.put(beanName, beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
//第二部:实例化bean;将单例bean放入单例bean池中;
//遍历beanMap集合
for (String beanName : beanMap.keySet()) {
BeanDefinition beanDefinition = beanMap.get(beanName);
//判断是否是单例bean
if (beanDefinition.getScope().equals("singleton")) {
//直接创建单例bean(因为只需要创建一次,则在构造方法中就创建出来)
Object bean = creatBean(beanName, beanDefinition);
//将单例bean放入单例bean池中
singletonMap.put(beanName, bean);
}
}
}
private Object creatBean(String beanName, BeanDefinition beanDefinition) {
Class clazz = beanDefinition.getType();
try {
Object instance = clazz.getConstructor().newInstance();
//依赖注入(简单版--依靠名字)
//遍历类的属性值,判断是否有需要 注入bean 的属性值,如果有需要的,则注入
for (Field field : clazz.getDeclaredFields()) {//getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
if (field.isAnnotationPresent(Autowired.class)) {
//值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查
field.setAccessible(true);
//将clazz类的属性,设置为对应的bean对象,即使用属性名字来获取bean对象;
field.set(instance,getBean(field.getName()));
}
}
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对象
*/
public Object getBean(String beanName) {
//通过beanMap,输入bean的名字,获取对应的bean属性
BeanDefinition beanDefinition = beanMap.get(beanName);
if (beanDefinition == null) {
throw new RuntimeException();
} else {
//beanMap中存在该beanName
String scope = beanDefinition.getScope();
//如果是单例bean,则从单例bean池中获取
if (scope.equals("singleton")) {
Object bean = singletonMap.get(beanName);
//当前bean内包含
if (bean == null) {
bean = creatBean(beanName, beanDefinition);
singletonMap.put(beanName, bean);
}
return bean;
} else {
//多例(只考虑单例和多例)
//创建一个bean对象,并返回
return creatBean(beanName, beanDefinition);
}
}
}
}
相关注解
@component
将类标记为 bean对象,可以被扫描到,并将该类添加到spring容器中
- 属性:
- bean的名字;如果未指定,默认为类名首字母小写;对应代码在spring容器中构造方法中实现;使用了Introspector.decapitalize(clazz.getSimpleName());
package com.aguang.spring;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
* 将类声明为bean对象,即需要装配到spring容器中
*/
@Retention(RetentionPolicy.RUNTIME) //注解的生效时间;RetentionPolicy.RUNTIME —> 运行时生效
@Target(ElementType.TYPE) //该ComponentScan注解;只能写在什么位置,ElementType.TYPE -> 只能写在类上
public @interface Component {
//注解的属性:指定bean的名字,默认值为 ""
String value() default "";
}
@componentScan
扫描对应路径下的文件,装配到spring容器中
- 属性
- 路径:spring容器会根据该路径找到对应的 .class文件的路径,进行bean对象的装配
package com.aguang.spring;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
* 从扫描路径扫描 需要装配 的类,自动装配到spring容器中
*/
@Retention(RetentionPolicy.RUNTIME) //注解的生效时间;RetentionPolicy.RUNTIME —> 运行时生效
@Target(ElementType.TYPE) //该ComponentScan注解;只能写在什么位置,ElementType.TYPE -> 只能写在类上
public @interface ComponentScan {
//注解的属性:指定扫描路径,默认值为 ""
String value() default "";
}
@Scope
对bean对象是单例还是多例,进行控制
- 属性
- 类型:多例 / 单例 ;spring容器会对单例bean在构造方法中就 加入到单例bean池中
package com.aguang.spring;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
* 将类声明为bean对象,即需要装配到spring容器中
*/
@Retention(RetentionPolicy.RUNTIME) //注解的生效时间;RetentionPolicy.RUNTIME —> 运行时生效
@Target(ElementType.TYPE) //该ComponentScan注解;只能写在什么位置,ElementType.TYPE -> 只能写在类上
public @interface Scope {
//注解的属性:指定是bean的类型, ;默认值为 ""
//多例bean ----> prototype
//单例bean ----> singleton
String value() default "";
}
@Autowired
作用在属性值上,将该属性的bean对象注入,
- 在spring容器的creatBean方法中,实现了简单的依赖注入功能;通过反射判断当前类中的属性值是否有被@Autowired注解标记;如果有则通过getBean方法通过 属性值的名称注入;
package com.aguang.spring;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
* 将类声明为bean对象,即需要装配到spring容器中
*/
@Retention(RetentionPolicy.RUNTIME) //注解的生效时间;RetentionPolicy.RUNTIME —> 运行时生效
@Target(ElementType.FIELD) //该ComponentScan注解;只能写在什么位置,ElementType.FIELD -> 写在属性值上
public @interface Autowired {
}
- createBean中的部分代码
Class clazz = beanDefinition.getType();
try {
Object instance = clazz.getConstructor().newInstance();
//依赖注入(简单版--依靠名字)
//遍历类的属性值,判断是否有需要 注入bean 的属性值,如果有需要的,则注入
for (Field field : clazz.getDeclaredFields()) {//getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
if (field.isAnnotationPresent(Autowired.class)) {
//值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查
field.setAccessible(true);
//将clazz类的属性,设置为对应的bean对象,即使用属性名字来获取bean对象;
field.set(instance,getBean(field.getName()));
}
}
return instance;
}
测试方法
Test类
- 通过 Appconfig类 输入扫描路径 ,创建一个spring容器
package com.aguang.service;
import com.aguang.spring.AguangApplicationContext;
/*
* 测试类
*/
public class Test {
public static void main(String[] args) {
//创建了spring容器;使用了config的方式
AguangApplicationContext applicationContext = new AguangApplicationContext(AppConfig.class);
//使用容器中的 getBean 方法 传入bean对象名字,获取一个bean对象(类型由该名字对应的对象决定);
/*
System.out.println(applicationContext.getBean("orderService"));
System.out.println(applicationContext.getBean("userService"));
System.out.println(applicationContext.getBean("userService"));*/
UserService userService = (UserService) applicationContext.getBean("userService");
System.out.println(userService);
/*OrderService orderService = (OrderService) applicationContext.getBean("orderService");
System.out.println(orderService);*/
//userService.test();
}
}
Appconfig类
定义一个扫描配置类
package com.aguang.service;
import com.aguang.spring.ComponentScan;
/*
* 配置类
* 扫描的功能:ComponentScan,通过扫描路径,将需要装配的 类,自动装配到 spring 容器中
*/
//扫描com.aguang.service路径下的类
@ComponentScan("com.aguang.service")
public class AppConfig {
}
UserService类
package com.aguang.service;
/*
* Bean对象(默认单例bean)
* 对于多例bean,使用到才创建
*/
import com.aguang.spring.Autowired;
import com.aguang.spring.Component;
import com.aguang.spring.Scope;
//通过Component注解声明的bean对象(名字为userService)
@Component("userService")
//@Scope("prototype")
public class UserService {
@Autowired
private OrderService orderService;
public void test(){
System.out.println(orderService);
}
}
OrderService类
package com.aguang.service;
import com.aguang.spring.Autowired;
import com.aguang.spring.Component;
@Component
public class OrderService {
//@Autowired
private UserService userService;
}