自研Spring框架之IoC
手写Spring框架第一篇博客(必读):自研简易版Spring框架
本博客主要实现Spring中的Bean容器和SpringIoC,对应于框架中core
、inject
包
Bean容器
Spring 容器是 Spring 框架的核心,是用来管理对象的。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。容器的载体就是Map映射,里面存储了应用中所有bean的实例, key为该bean实例的Class对象,bean的框架中
BeanContainer
类中定义了容器的实现,封装了与容器相应的方法。
实现思路
- 创建一个基于线程安全的Map映射
ConcurrentHashMap
作为容器,且是单例的 - 遍历特定package下文件,提取所有
.class
文件 - 将被特定注解标记的bean存入到Map映射中, key为该bean实例的Class对象,value为bean的实例
IOC
Inversion of Control
:控制反转,由容器控制程序对象间的依赖关系,而不是基于传统实现的,由程序代码直接控制,控制权由程序代码转移到外部容器,控制权的转移就是所谓的反转。对于spring而言,就是spring容器控制对象的生命周期和对象之间的依赖关系。IoC还有另外一个名字DI(Dependency Injection) 依赖注入,所谓依赖注入就是,组件间的依赖关系由容器在运行期决定,即容器动态的将某种依赖关系添加到组件中。
实现思路
- 遍历Bean容器中所有的Class对象
- 遍历Class对象中所有的成员变量
- 找出被Autowired注解标记的成员变量
- 获取这些成员变量的类型
- 获取这些成员变量的类型在容器里对应的实例
- 通过反射将对应的成员变量实例注入到成员变量所在的类的实例里面
代码实现
定义注解
(1) 组件注解@Component
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}
(2)控制器@controller
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
(3)服务@service
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
}
(4)dao层注解@repository
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {
}
(5)依赖注入@Autowired
/**
* @Description: Autowired目前仅支持成员变量注入
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
String value() default "";
}
核心代码
(1)BeanContainer类 bean容器的创建和相应方法实现
/**
* @program: simpleframework
* @description: bean容器
* @author: 十字街头的守候
**/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
/**
* 存放所有被配置标记的目标对象的Map
*/
private final Map<Class<?>,Object> beanMap = new ConcurrentHashMap<>();
/**
* 加载bean的注解列表
*/
private static final List<Class<? extends Annotation>> BEAN_ANNOTATION= Arrays.asList(Component.class, Service.class, Repository.class, Controller.class, Aspect.class);
/**
* 容器是否已经加载过
*/
private boolean loaded=false;
/**
* @Description: 是否被加载过
* @Param: []
* @return: boolean
* @Date: 2021/1/18
*/
public boolean isLoaded(){
return loaded;
}
/**
* @Description: 获取bean容器实例
* @Param: []
* @return: org.simpleframework.core.BeanContainer
* @Date: 2021/1/18
*/
public static BeanContainer getInstance(){
return ContainerHolder.HOLDER.instance;
}
/**
* @Description: 获取bean实例的数量
* @Param: []
* @return: int
* @Date: 2021/1/19
*/
public int size() {
return beanMap.size();
}
/**
* @Description:使用内部枚举类单列模式模式创建bean容器对象
* @return: null
*/
private enum ContainerHolder{
HOLDER;
private BeanContainer instance;
ContainerHolder(){
instance=new BeanContainer();
}
}
/**
* @Description: 扫描加载所有的bean
* @Param: [packageName]
* @return: void
* @Date: 2021/1/18
*/
public synchronized void loadBeans(String packageName){
//判断bean是否被加载过
if(isLoaded()){
log.warn("bean has been loaded");
return;
}
Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
//类是否为空
if(ValidationUtil.isEmpty(classSet)){
log.warn("extract nothing from packageName"+packageName);
return;
}
for(Class<?> clazz:classSet){
for(Class<? extends Annotation> annotation:BEAN_ANNOTATION){
//如果类上面标记了定义的注解
if(clazz.isAnnotationPresent(annotation)){
//将目标类身作为键,目标类的实例作为值,放入到beanMap中
beanMap.put(clazz,ClassUtil.newInstance(clazz,true));
}
}
}
loaded=true;
}
/**
* @Description: 添加一个class对象及其bean实例
* @Param: [clazz, bean]
* @return: java.lang.Object
* @Date: 2021/1/20
*/
public Object addBean(Class<?> clazz,Object bean){
return beanMap.put(clazz, bean);
}
/**
* @Description: 移除一个IOC容器管理的对象
* @Param: [clazz]
* @return: java.lang.Object
* @Date: 2021/1/20
*/
public Object removeBean(Class<?> clazz){
return beanMap.remove(clazz);
}
/**
* @Description: 根据class对象获取bean实例
* @Param: [clazz]
* @return: java.lang.Object
* @Date: 2021/1/20
*/
public Object getBean(Class<?> clazz){
return beanMap.get(clazz);
}
/**
* @Description: 获取容器中所有的class对象
* @Param: []
* @return: java.util.Set<java.lang.Class<?>>
* @Date: 2021/1/20
*/
public Set<Class<?>> getClasses(){
return beanMap.keySet();
}
/**
* @Description: 获取所有的bean集合
* @Param: []
* @return: java.lang.Object
* @Date: 2021/1/20
*/
public Set<Object> getBeans(){
return new HashSet<>(beanMap.values());
}
/**
* @Description: 根据注解筛选出bean的class集合
* @Param: [annotation]
* @return: java.util.Set<java.lang.Class<?>>
* @Date: 2021/1/20
*/
public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation){
//1.获取beanMap的所有class对象
Set<Class<?>> keySet = getClasses();
if(ValidationUtil.isEmpty(keySet)){
log.warn("nothing in beanMap");
return null;
}
//2.通过注解筛选被注解标记的class对象,并添加到classSet里
Set<Class<?>> classSet=new HashSet<>();
for(Class<?> clazz:keySet){
//类是否被相应注解标记
if(clazz.isAnnotationPresent(annotation)){
classSet.add(clazz);
}
}
return classSet.size()>0?classSet:null;
}
/**
* @Description:通过接口或者父类获取实现类或者子类的class集合,不包括其本身
* @Param: [annotation]
* @return: java.util.Set<java.lang.Class<?>>
* @Date: 2021/1/20
*/
public Set<Class<?>> getClassesBySupper(Class<?> interfaceOrClass){
//1.获取beanMap的所有class对象
Set<Class<?>> keySet = getClasses();
if(ValidationUtil.isEmpty(keySet)){
log.warn("nothing in beanMap");
return null;
}
//2.通过判断keySet里的元素是否传入的接口或者父类的子类,如果是,将其并添加到classSet里
Set<Class<?>> classSet=new HashSet<>();
for(Class<?> clazz:keySet){
//判断keySet里的元素是否是传入的接口或者类的子类
if(interfaceOrClass.isAssignableFrom(clazz)&&!clazz.equals(interfaceOrClass)){
classSet.add(clazz);
}
}
return classSet.size()>0?classSet:null;
}
}
(2)DependencyInjection类 实现依赖注入
/**
* @program: simpleframework
* @description: 依赖注入
* @author: 十字街头的守候
* @create: 2021-01-20 15:26
**/
@Slf4j
public class DependencyInjector {
private BeanContainer beanContainer;
public DependencyInjector() {
beanContainer = BeanContainer.getInstance();
}
public void doIoc(){
//1.遍历Bean容器中所有的Class对象
if(ValidationUtil.isEmpty(beanContainer.getClasses())){
log.warn("empty classset in BeanContainer");
return;
}
for(Class<?> clazz:beanContainer.getClasses()){
//2.遍历Class对象中所有的成员变量
Field[] fields = clazz.getDeclaredFields();
if(ValidationUtil.isEmpty(fields)){
//跳出当前循环
continue;
}
for(Field field: fields){
//3.找出被Autowired标记的成员变量
if(field.isAnnotationPresent(Autowired.class)){
Autowired autowired = field.getAnnotation(Autowired.class);
String autowiredValue = autowired.value();
//4.获取这些成员变量的类型
Class<?> fieldClass = field.getType();
//5.获取这些成员变量的类型在容器里对应的实例
Object fieldValue = getFieldInstanct(fieldClass,autowiredValue);
if(fieldValue==null){
throw new RuntimeException("unable to inject relevant type, target fieldClass is:"+fieldClass.getName()+"autowriedValue:"+autowiredValue);
}else{
//6.通过反射将对应的成员变量实例注入到成员变量所在的类的实例里面
Object targetBean = beanContainer.getBean(clazz);
ClassUtil.setField(field,targetBean,fieldValue,true);
}
}
}
}
}
/**
* @Description:根据Class在beanContainer里面获取其实例或者实现类
* @Param: [fieldClass]
* @return: java.lang.Object
*/
private Object getFieldInstanct(Class<?> fieldClass,String autowriedValue) {
Object fieldValue=beanContainer.getBean(fieldClass);
if(fieldValue!=null){
return fieldValue;
}else{
Class<?> implementedClass= getImplementClass(fieldClass,autowriedValue);
if(implementedClass!=null){
return beanContainer.getBean(implementedClass);
}else {
return null;
}
}
}
/**
* @Description: 获取接口的实现类
* @Param: [fieldClass, autowriedValue]
* @return: java.lang.Class<?>
* @Date: 2021/1/20
*/
private Class<?> getImplementClass(Class<?> fieldClass,String autowriedValue) {
Set<Class<?>> classSet = beanContainer.getClassesBySupper(fieldClass);
if(!ValidationUtil.isEmpty(classSet)){
if(ValidationUtil.isEmpty(autowriedValue)){
if(classSet.size()==1){
return classSet.iterator().next();
}else{
//如果多与两个实现类且用户未指定其中一个实现类,则抛出异常
throw new RuntimeException("multiple implemented classes for"+fieldClass.getName()+"pleanse set @Autowired's value to pick one ");
}
}else{
for(Class<?> clazz:classSet){
if(autowriedValue.equals(clazz.getSimpleName())){
return clazz;
}
}
}
}
return null;
}
}
(3)ClassUtil工具类 封装了对Class相关的操作,包括提取指定路径下的Class文件
/**
* @program: simpleframework
* @description: 类相关通用方法通过类加载器获取资源信息
* @author: 十字街头的守候
**/
@Slf4j
public class ClassUtil {
public static final String FILE_PROTOCOL = "file";
/**
* @Description: 设置类的属性值
* @Param: [field, target, value, accessible]
* @return: void
* @Date: 2021/1/20
*/
public static void setField(Field field,Object target,Object value,boolean accessible){
try {
field.setAccessible(accessible);
field.set(target,value);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/**
* 获取包下类集合
* @param packageName
* @return 类集合
*/
public static Set<Class<?>> extractPackageClass(String packageName){
//1.获取类的加载器
ClassLoader classLoader = getClassLoader();
//2.通过类加载器获取加载的资源
URL url = classLoader.getResource(packageName.replace(".", "/"));
if(url==null){
log.warn("unable to retrieve anything from package:"+packageName);
return null;
}
//3.依据不同的资源类型,采用不同的方式获取资源的集合
Set<Class<?>> classSet=null;
//过滤出文件类型的资源
if(url.getProtocol().equalsIgnoreCase(FILE_PROTOCOL)){
classSet=new HashSet<>();
File packageDirectory = new File(url.getPath());
extractClassFile(classSet,packageDirectory,packageName);
}
return classSet;
}
/**
* @Description: 实例化class
* @Param: [clazz,accessible:是否支持私有class对象的实例]
* @return: T
* @Date: 2021/1/18
*/
public static <T> T newInstance(Class<?> clazz,boolean accessible){
try {
Constructor constructor= clazz.getDeclaredConstructor();
constructor.setAccessible(accessible);
return (T) constructor.newInstance();
} catch (Exception e) {
log.error("newInstance error",e);
throw new RuntimeException(e);
}
}
/**
* @Description: 递归获取目标package里面的所有class文件(包括子package里的class文件)
* @Param: [classSet, packgaeDirectory, packageName]
* @return: void
* @Date: 2021/1/18
*/
private static void extractClassFile(Set<Class<?>> emptyClassSet, File fileSource, String packageName) {
if(!fileSource.isDirectory()){
return;
}
//如果是一个文件夹,则调用器listFiles方法获取文件夹下的文件或者文件夹
File[] files = fileSource.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
if(file.isDirectory()){
return true;
}else{
//获取文件的绝对值路径
String absoluteFilePath = file.getAbsolutePath();
if(absoluteFilePath.endsWith(".class")){
//若是class文件则直接加载
addToClassSet(absoluteFilePath);
}
}
return false;
}
//根据class文件的绝对路径,获取并生成class对象,并放入classSet中
private void addToClassSet(String absoluteFilePath) {
//1.从class文件的绝对值路径里面提取出包含了package的类名
absoluteFilePath = absoluteFilePath.replace(File.separator, ".");
String className=absoluteFilePath.substring(absoluteFilePath.indexOf(packageName));
className= className.substring(0,className.lastIndexOf("."));
//2.通过反射机制获取对应的class对象并加入到classSet里
Class<?> targerClass = loadClass(className);
emptyClassSet.add(targerClass);
}
});
if(files!=null){
for(File f:files){
//递归调用
extractClassFile(emptyClassSet,f,packageName);
}
}
}
/**
* @Description: 加载class对象
* @Param: [className]
* @return: java.lang.Class<?>
* @Date: 2021/1/18
*/
private static Class<?> loadClass(String className) {
try {
Class<?> clazz = Class.forName(className);
return clazz;
} catch (ClassNotFoundException e) {
log.error("load class error",e);
throw new RuntimeException(e);
}
}
/**
* @Description:
* @Param: [id]
* @return: java.lang.ClassLoader
* @Date: 2021/1/18
*/
public static ClassLoader getClassLoader(){
return Thread.currentThread().getContextClassLoader();
}
}
到此,Spring容器和IoC相关的代码已经完成了,是不是迫不及待的单元测试下,那我们添加一个BeanContainer测试类
测试我们需要引入junit5相关的 jar包
class BeanContainerTest {
private static BeanContainer beanContainer;
@BeforeAll
static void init(){
beanContainer=BeanContainer.getInstance();
}
@DisplayName("加载目标对象及其实例到BeanContainer:loadBeansTest")
@Test
public void loadBeansTest(){
//在没有加载bean时查看容器是否被加载
Assertions.assertEquals(false,beanContainer.isLoaded());
//加载自己指定包下的bean,我这里是“tjoker”包
//所在的包下的类应该被定义的注解标记
beanContainer.loadBeans("tjoker");
//测试当前bean容量是否也我们预期的6是否一样
Assertions.assertEquals(6,beanContainer.size());
//测试当前bean容器是否被加载
Assertions.assertEquals(true,beanContainer.isLoaded());
}
启动测试类,结果如下
恭喜你,测试通过。哦,不要高兴的太早,这只是做了一个简单测试,其他功能还没有测试,那你可以按照我上面的方法对其他功能进行测试,这里就不在演示了。
经过上面的学习,相信大家对Spring容器和IoC已经有了一定了解,当应用启动后, 就会生成Bean容器, 将特定注解的类加入到容器中。好了,那让我们继续攻克SpringAOP的内容吧
自研框架之AOP:自研框架之AOP