2.1 创建Scope注解
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来判断是单例还是原型
2.2 beanDefination对象
我们需要创建一个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;
}
2.3 扫描并且创建单例池和beanDefinationMap
上一节中,我们已经通过扫描获取了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
哈希值相同