context.getBean("userService");
在创建bean对象的时候,会传入一个beanName,根据这个beanName,首先框架会去判断这个beanName是单例bean还是原型bean
在实体类中被componen注解的类,我们需要一个Scope注解,来表示这个类是单例还是原型,如果没有Scope注解默认是单例
@Component("userService") @Scope("prototype") public class UserService { }
首先我们来创建一个Scope注解
package com.rainwood.spring; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Scope { String value(); }
value并不是缺省的
那么如何通过简单得context.getBean("userService")
就可以知道这个beanName是单例还是原型呢
单例代表着,每次getBean取出来的是同一个bean对象,而原型每次取出来的都是新创建的bean对象
我们可以通过map (key value)存取的方式来存bean对象,这样getBean的时候,传进一个beanName,就可以通过map取出这个bean对象
//定义一个map,来存放单例的bean对象 private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();//单例池
可以创建一个单例池来存放单例bean对象
那么如何根据beanName来判断是单例还是原型
我们需要创建一个beanDefination对象来实现
package com.rainwood.spring; public class BeanDefination { private Class clazz; 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; } }
这个对象存储了bean所对应的class类还有它的scope
在扫描包的时候,创建该beanDefination对象,然后将beanDefination对象存在map里
private ConcurrentHashMap<String, BeanDefination> beanDefinationMap = new ConcurrentHashMap<>();//存储所有扫描到的bean的定义
调用getBean时候:
public Object getBean(String beanName) { if(beanDefinationMap.containsKey(beanName)) { BeanDefination beanDefination = beanDefinationMap.get(beanName); String scope = beanDefination.getScope(); Class clazz = beanDefination.getClazz(); System.out.println(clazz); if(scope == "singleton") { Object o = singletonObjects.get(beanName); return o; } else { Object bean = createBean(beanDefination); return bean; } } else { //不存在对应的Bean throw new NullPointerException(); } }
先是根据beanName取到对应的class和scope,判断scope是单例还是原型
如果是单例,从单例池里拿到对应的bean对象,singletonObjects.get(beanName);
如果是原型,我们需要创建一个createBean方法,将beanDefination传进去进一步处理(下一节再学)
public Object createBean(BeanDefination beanDefination) { Class clazz = beanDefination.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; }
上一节中,我们已经通过扫描获取了Component注解的类
扫描:
try { //这个地方不是放的class文件的url,而是这个类的全限定名 Class<?> aClass = classLoader.loadClass(className); //判断这个类是不是有Component注解 if(aClass.isAnnotationPresent(Component.class)) { //表示当前这个类是一个Bean //解析类,判断当前bean是单例bean还是prototype的bean 解析之后会生成一个BeanDefination对象 //BeanDefination //拿到Component 对应的beanName Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class); String beanName = componentAnnotation.value(); BeanDefination beanDefination = new BeanDefination(); beanDefination.setClazz(aClass); //如果可以拿到Scope注解 //如果拿不到表示这个类默认是一个单例 if(aClass.isAnnotationPresent(Scope.class)) { Scope scopeAnnotation = aClass.getDeclaredAnnotation(Scope.class); String scope = scopeAnnotation.value(); beanDefination.setScope(scope); } else { beanDefination.setScope("singleton"); } beanDefinationMap.put(beanName, beanDefination); } } catch (Exception e) { e.printStackTrace(); }
调用scan方法扫描后,已经将beanDefinationMap中放入了所有的beanDefination对象和beanName
还需要将bean单例依次放入单例池中:
for (Map.Entry<String, BeanDefination> entry : beanDefinationMap.entrySet()) { String beanName = entry.getKey(); BeanDefination beanDefination = entry.getValue(); if(beanDefination.getScope().equals("singleton")) { Object bean = createBean(beanDefination); // 创建单例bean对象 singletonObjects.put(beanName, bean); } }
下面是limingApplicationContext.java:
package com.rainwood.spring; import java.io.File; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class limingApplicationContext { private Class configClass; //定义一个map,来存放单例的bean对象 private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();//单例池 private ConcurrentHashMap<String, BeanDefination> beanDefinationMap = new ConcurrentHashMap<>();//存储所有扫描到的bean的定义 public limingApplicationContext(Class configClass) throws ClassNotFoundException { this.configClass = configClass; //解析配置类 //ComponentScan注解--》扫描路径 -->扫描-->Beandefination--->BeanDefinationMap scan(configClass); for (Map.Entry<String, BeanDefination> entry : beanDefinationMap.entrySet()) { String beanName = entry.getKey(); BeanDefination beanDefination = entry.getValue(); if(beanDefination.getScope().equals("singleton")) { Object bean = createBean(beanDefination); // 创建单例bean对象 singletonObjects.put(beanName, bean); } } } public Object createBean(BeanDefination beanDefination) { Class clazz = beanDefination.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; } private void scan(Class configClass) { //解析配置类 //ComponentScan注解--》扫描路径 ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class); String path = componentScanAnnotation.value(); //扫描路径 path = path.replace(".","/"); System.out.println(path); //扫描 //类加载器: //Bootstrap -->jre/lib //Ext----->jre/ext/lib //App---->classpath ClassLoader classLoader = limingApplicationContext.class.getClassLoader(); //app 取得的是classpath对应的类加载器 //resource对应的是一个目录 file:/D:/spring%e6%ba%90%e7%a0%81%e5%ad%a6%e4%b9%a0/target/classes/com/rainwood/liming/service URL resource = classLoader.getResource(path); File file = new File(resource.getFile()); //判断是不是一个目录 if(file.isDirectory()) { //可以获取到这个目录下所有的编译好的class文件路径 File[] files = file.listFiles(); for (File file1 : files) { System.out.println(file1); //D:\springResourceLearn\target\classes\com\rainwood\liming\service\UserService.class 转化成com.rainwood.liming.service.UserService String fileName = file1.getAbsolutePath(); //判断是不是class文件再进一步处理 if(fileName.endsWith(".class")) { String className = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class")); className = className.replace("\\","."); try { //这个地方不是放的class文件的url,而是这个类的全限定名 Class<?> aClass = classLoader.loadClass(className); //判断这个类是不是有Component注解 if(aClass.isAnnotationPresent(Component.class)) { //表示当前这个类是一个Bean //解析类,判断当前bean是单例bean还是prototype的bean 解析之后会生成一个BeanDefination对象 //BeanDefination //拿到Component 对应的beanName Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class); String beanName = componentAnnotation.value(); BeanDefination beanDefination = new BeanDefination(); beanDefination.setClazz(aClass); //如果可以拿到Scope注解 //如果拿不到表示这个类默认是一个单例 if(aClass.isAnnotationPresent(Scope.class)) { Scope scopeAnnotation = aClass.getDeclaredAnnotation(Scope.class); String scope = scopeAnnotation.value(); beanDefination.setScope(scope); } else { beanDefination.setScope("singleton"); } beanDefinationMap.put(beanName, beanDefination); } } catch (Exception e) { e.printStackTrace(); } } } } } public Object getBean(String beanName) { if(beanDefinationMap.containsKey(beanName)) { BeanDefination beanDefination = beanDefinationMap.get(beanName); String scope = beanDefination.getScope(); Class clazz = beanDefination.getClazz(); System.out.println(clazz); if(scope == "singleton") { Object o = singletonObjects.get(beanName); return o; } else { Object bean = createBean(beanDefination); return bean; } } else { //不存在对应的Bean throw new NullPointerException(); } } }
test
package com.rainwood.liming; import com.rainwood.spring.limingApplicationContext; public class test { public static void main(String[] args) throws ClassNotFoundException { limingApplicationContext context = new limingApplicationContext(AppConfig.class); //如果是单例bean getBean调用多次获得的都是同一个bean对象 (可以通过map来实现, key就是beanName, 对应的value就是bean对象) 单例池 //如果是原型bean 会返回不同的bean对象 // Object bean = context.getBean("userService"); System.out.println(context.getBean("userService")); System.out.println(context.getBean("userService")); System.out.println(context.getBean("userService")); // System.out.println(bean); } }
打印出getBean,如果是原型的话运行结果:
可见哈希值不同
com/rainwood/liming/service D:\springResourceLearn\target\classes\com\rainwood\liming\service\UserService.class D:\springResourceLearn\target\classes\com\rainwood\liming\service\XxUtil.class class com.rainwood.liming.service.UserService com.rainwood.liming.service.UserService@5e2de80c class com.rainwood.liming.service.UserService com.rainwood.liming.service.UserService@1d44bcfa class com.rainwood.liming.service.UserService com.rainwood.liming.service.UserService@266474c2
如果是单例则:
com/rainwood/liming/service D:\springResourceLearn\target\classes\com\rainwood\liming\service\UserService.class D:\springResourceLearn\target\classes\com\rainwood\liming\service\XxUtil.class class com.rainwood.liming.service.UserService com.rainwood.liming.service.UserService@60e53b93 class com.rainwood.liming.service.UserService com.rainwood.liming.service.UserService@60e53b93 class com.rainwood.liming.service.UserService com.rainwood.liming.service.UserService@60e53b93
哈希值相同