01.idea中创建一个maven管理的空项目
02.模拟创建出spring容器类,这里叫wzpApplicationContext,创建的时候会自动加载配置类的数据
public class wzpApplicationContext {private Class configClass;public wzpApplicationContext(Class configClass) {this.configClass = configClass;}
}
模拟出容器类要扫描的配置类Appconfig
public class Appconfig {
}
在test中去测试这些创建的类
public class Mytest {public static void main(String[] args) {wzpApplicationContext wzpApplicationContext=new wzpApplicationContext(Appconfig.class);
}
}
03.在wzpApplicationContext 添加getbean函数,获取spring容器中的bean
public Object getBean(String beanName) {return null;}
创建服务类UserService
public class UserService {
}
在测试test中运行getbean方法
public class Mytest {public static void main(String[] args) {wzpApplicationContext wzpApplicationContext=new wzpApplicationContext(Appconfig.class);UserService userService= (UserService)wzpApplicationContext.getBean("UserService");
}
}
04.在服务类中写一些方法,等会看一下是不是可以运行
public class UserService {public void test(){System.out.println("test");}
}
此时在test中:
public class Mytest {public static void main(String[] args) {wzpApplicationContext wzpApplicationContext=new wzpApplicationContext(Appconfig.class);UserService userService= (UserService)wzpApplicationContext.getBean("UserService");userService.test();}}
05.写几个自定义的注解
比如说,spring容器中的扫描,需要注解@ComponentScan
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {String value() default "";
}
解释一下:
Target 注解是说,这个注解主要用在哪一个区域,TYPE表示类,FIELD表示属性,METHOD表示方法。
Rentention 注解是说,这个注解在那个阶段使用,RUNTIME是在类加载阶段使用
然后模仿spring去用:
@ComponentScan("org.example.wzp.service")
public class Appconfig {}
再创建一个注解:@Component
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {String value() default "";
}
在服务类中去使用:
@Component
public class UserService {public void test(){System.out.println("test");}
}
06.实现扫描的功能
现在只是写了几个注解,具体的功能代码还没有写,接下来就是扫描的代码
回想spring框架,是创建spring容器的时候,就会自动创建bean
所以,扫描的具体实现代码就应该在构造函数中写
//判断类上是不是有注解ComponentScanif(configClass.isAnnotationPresent(ComponentScan.class)){//获取注解对象ComponentScan componentScan = (ComponentScan)configClass.getAnnotation(ComponentScan.class);//获取注解对象上的value,这里也就是pathString path = componentScan.value();//文件的路径是/,而获取到的是带有.的包名path=path.replace(".","/");//获取当前的类加载器ClassLoader classLoader = wzpApplicationContext.class.getClassLoader();//根据类加载器获取编译完成的target的class文件夹的路径URL resource = classLoader.getResource(path);//根据路径,获取文件File file=new File(resource.getFile());//因为扫描的是包,所以大概率是一个目录if (file.isDirectory()){//获取目录中的文件集合for (File f:file.listFiles()){//获取文件的绝对路径String absolutePath = f.getAbsolutePath();//截取从org到classString org = absolutePath.substring(absolutePath.indexOf("org"), absolutePath.indexOf(".class"));//由于是文件路径,要转换回来,把\变成 . 包名,好让类加载器加载得到类的对象String classpath = org.replace("\\", ".");//加载器加载后,得到一个类对象Class<?> aClass = classLoader.loadClass(classpath);//判断是不是类上存在Component注解if(aClass.isAnnotationPresent(Component.class)){//判断是不是单例模式,要看自定义的注解Scopeif (aClass.isAnnotationPresent(Scope.class)){Scope scopeAnnotation = aClass.getAnnotation(Scope.class);String value = scopeAnnotation.value();if (value.equals("singleton")){//创建单例模式的bean}else{//多例模式}}}}}}}
创建一个注解 Scope
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {String value() default "";
}
在服务类上使用,如果是单例,就写singleton,如果是多例,就写prototype
例如:服务类UserService ,这里声明是单例模式
@Component("UserService")
@Scope("singleton")
public class UserService {public void test(){System.out.println("test");}
}
07.优化设计,把类上注解的许多信息都放入到一个对象中的话,直接去读取这个类的属性就好了
创建一个BeanDefinition
public class BeanDefinition {private Class type;private String scope;private Boolean isLazy;public Class getType() {return type;}public void setType(Class type) {this.type = type;}public String getScope() {return scope;}public void setScope(String scope) {this.scope = scope;}public Boolean getLazy() {return isLazy;}public void setLazy(Boolean lazy) {isLazy = lazy;}}
再次优化扫描
//判断是不是类上存在Component注解if(aClass.isAnnotationPresent(Component.class)){Component componentAnnotation = aClass.getAnnotation(Component.class);String name = componentAnnotation.value();BeanDefinition beanDefinition=new BeanDefinition();beanDefinition.setType(aClass);//判断是不是单例模式,要看自定义的注解Scopeif (aClass.isAnnotationPresent(Scope.class)){Scope scopeAnnotation = aClass.getAnnotation(Scope.class);String value = scopeAnnotation.value();beanDefinition.setScope(value);if (value.equals("singleton")){//创建单例模式的beanbeanDefinition.setScope("singleton");}else{//多例模式}}}
在wzpApplicationContext 中添加一个属性,map集合
public class wzpApplicationContext {
//配置类private Class configClass;
//存放BeanDefinitionprivate Map<String,BeanDefinition> BeanDefinitionMap =new HashMap<>();}
把刚刚创建的BeanDefinition放入到map集合中:
if(aClass.isAnnotationPresent(Component.class)){Component componentAnnotation = aClass.getAnnotation(Component.class);String name = componentAnnotation.value();BeanDefinition beanDefinition=new BeanDefinition();beanDefinition.setType(aClass);//判断是不是单例模式,要看自定义的注解Scopeif (aClass.isAnnotationPresent(Scope.class)){Scope scopeAnnotation = aClass.getAnnotation(Scope.class);String value = scopeAnnotation.value();beanDefinition.setScope(value);if (value.equals("singleton")){//创建单例模式的beanbeanDefinition.setScope("singleton");}else{//多例模式}BeanDefinitionMap.put(name,beanDefinition);}}
最后总的代码抽象此外一个方法:
public wzpApplicationContext(Class configClass) throws ClassNotFoundException {this.configClass = configClass;//判断类上是不是有注解ComponentScanscan(configClass);}private void scan(Class configClass) throws ClassNotFoundException {if(configClass.isAnnotationPresent(ComponentScan.class)){//获取注解对象ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);//获取注解对象上的value,这里也就是pathString path = componentScan.value();//文件的路径是/,而获取到的是带有.的包名path.replace(".","/");//获取当前的类加载器ClassLoader classLoader = wzpApplicationContext.class.getClassLoader();//根据类加载器获取编译完成的target的class文件夹的路径URL resource = classLoader.getResource(path);//根据路径,获取文件File file=new File(resource.getFile());//因为扫描的是包,所以大概率是一个目录if (file.isDirectory()){//获取目录中的文件集合for (File f:file.listFiles()){//获取文件的绝对路径String absolutePath = f.getAbsolutePath();//截取从org到classString org = absolutePath.substring(absolutePath.indexOf("org"), absolutePath.indexOf(".class"));//由于是文件路径,要转换回来,把\变成 . 包名,好让类加载器加载得到类的对象String classpath = org.replace("\\", ".");//加载器加载后,得到一个类对象Class<?> aClass = classLoader.loadClass(classpath);//判断是不是类上存在Component注解if(aClass.isAnnotationPresent(Component.class)){Component componentAnnotation = aClass.getAnnotation(Component.class);String name = componentAnnotation.value();BeanDefinition beanDefinition=new BeanDefinition();beanDefinition.setType(aClass);//判断是不是单例模式,要看自定义的注解Scopeif (aClass.isAnnotationPresent(Scope.class)){Scope scopeAnnotation = aClass.getAnnotation(Scope.class);String value = scopeAnnotation.value();beanDefinition.setScope(value);if (value.equals("singleton")){//创建单例模式的beanbeanDefinition.setScope("singleton");}else{//多例模式}BeanDefinitionMap.put(name,beanDefinition);}}}}}}
08.扫描完了,开始写createbean方法了
在扫描之后调用:
用for循环
public class wzpApplicationContext {private Class configClass;private Map<String,BeanDefinition> BeanDefinitionMap =new HashMap<>();public wzpApplicationContext(Class configClass) throws ClassNotFoundException {this.configClass = configClass;//判断类上是不是有注解ComponentScanscan(configClass);for (Map.Entry<String, BeanDefinition> entry : BeanDefinitionMap.entrySet()){BeanDefinition beanDefinition = entry.getValue();String beanName = entry.getKey();if (beanDefinition.getScope().equals("singleton")){//在这里调用createbean,创建单例Object bean= createBean(beanName,beanDefinition);}}
}
09.在wzpApplicationContext 创建createBean
private Object createBean(String beanName, BeanDefinition beanDefinition) {return null;}
10.创建的bean要保存到容器
在wzpApplicationContext 创建一个属性 Map<String,Object> singletonObjectsMap,这个用来保存创建的单例bean
//保存单例的地方,Map集合private Map<String,Object> singletonObjectsMap =new HashMap<>();
此时的for循环
for (Map.Entry<String, BeanDefinition> entry : BeanDefinitionMap.entrySet()){BeanDefinition beanDefinition = entry.getValue();String beanName = entry.getKey();if (beanDefinition.getScope().equals("singleton")){//创建单例Object bean= createBean(beanName,beanDefinition);//保存单例到容器中 singletonObjectsMap.put(beanName,bean);}}
11.什么时候调用这个getbean方法
先从BeanDefinitionMap获取已经扫描完了beanDefinition,如果有的话,那表示在扫描的包中,有这个类,再判断是不是单例模式,如果没有的话,那就说明没有这样的类,抛出异常。
public Object getBean(String beanName) throws Exception {if (!BeanDefinitionMap.containsKey(beanName)){throw new NullPointerException();}BeanDefinition beanDefinition = BeanDefinitionMap.get(beanName);if (beanDefinition.getScope().equals("singleton")){//单例}else{//原型}return null;}
这里是获取bean,单例从容器中获取
singletonObjectsMap.get(beanName)
原型(多例)就马上创建一个bean
createBean(beanName, beanDefinition);
这个时候的getBean方法
public Object getBean(String beanName) throws Exception {if (!BeanDefinitionMap.containsKey(beanName)){throw new NullPointerException();}BeanDefinition beanDefinition = BeanDefinitionMap.get(beanName);if (beanDefinition.getScope().equals("singleton")){//单例return singletonObjectsMap.get(beanName);}else{//原型Object bean = createBean(beanName, beanDefinition);return bean;}}
12.如何创建bean呢,用反射 newInstance()
private Object createBean(String beanName, BeanDefinition beanDefinition) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {Class clazz = beanDefinition.getType();Object o = clazz.getConstructor().newInstance();return o;}
13.测试
14.创建一个多例
@Component("OrderService")
@Scope("prototype")
public class OrderService
{
}
public class Mytest {public static void main(String[] args) throws Exception {wzpApplicationContext wzpApplicationContext=new wzpApplicationContext(Appconfig.class);UserService userService= (UserService)wzpApplicationContext.getBean("UserService");System.out.println((UserService)wzpApplicationContext.getBean("UserService"));System.out.println((UserService)wzpApplicationContext.getBean("UserService"));System.out.println((UserService)wzpApplicationContext.getBean("UserService"));System.out.println((OrderService)wzpApplicationContext.getBean("OrderService"));System.out.println((OrderService)wzpApplicationContext.getBean("OrderService"));System.out.println((OrderService)wzpApplicationContext.getBean("OrderService"));userService.test();}}