项目目录
-
Autowired
-
Component
-
ComponentScan 扫描路径配置在配置类上方
-
Lazy 是否懒加载
-
Scope 原型模式|单例模式
-
InitializingBean
Spring自带类用于解决Bean在定义时需要通过特殊途径(数据库)得到对象
实际是在创建完成bean后会执行, 一个接口实现接口后重写方法就可以了
-
BeanPostProcessor
一个很重要的类, Spring的关键类, 用于被实现AOP操作 这里可以先创建好bean注意注解类都需要加上
java
@Retention(RetentionPolicy.RUNTIME)
加上这个注解就代表我们编写的注解是一个运行作用域的注解,这样才可以被反射方法访问到
applicationContext
这个类就是Spring的启动类,我们在实例化的时候会执行这个类的构造方法。所以我们的代码需要写在这个类中。但是,并不是代表着所有方法都编写其中,需要注意方法的抽取!!!
java
public applicationContext(Class 配置文件){
// 类的装载
}
编写装载方法
Spring的bean有一个叫做beanDefinitionMap
的集合它是用来存放bean定义的,然后在需要创建对应的情况去找对应的bean定义来实例化对象并返回,在Spring中有两种类的创建模式: 单例模式、原型模式。下面的两种创建模式都是通过获取到这个Map集合中指定bean的值,并将这个定义去实例化
java
// bean定义对象beanDefinition
public class BeanDefinition {
private Class type;
private String scope;
private boolean isLazy;
// 省略get、set
}
单例模式:单例bean是在加载Spring配置的时候就会实例化并且存放于singletonObjects
的一个Map集合,这个集合就是存放我们创建单例bean的地方()
原型模式:获取bean定义map对应的value后再实例化这个bean并放回结果。
这里我大概讲解一下他是依靠获取到我们对应配置类中的@ComponentScan注解中的值,将这个值转换为对应的相对路径,再使用我们当前程序的appClassloader获取到再当前操作系统的绝对路径(编译Class文件)。 找到对应的绝对路径就代表着我们找到了我们需要去扫描的包。接下来的操作就很简单了,我们去循环遍历这个目录中的所有class文件,然后使用appClassloader加载到程序中,再去判断是否有@Component注解如果有那么就把他封装为一个BeanDefinition对象
java
// 扫描路径(将需要装载的对象作为BeanDefinition存入到集合中等待实例化)
public void scan(Class confClass){
// 如果没有配置扫描包注解便不会执行
if(!confClass.isAnnotationPresent(ComponentScan.class)) return;
// 获取需要扫描的路径
ComponentScan componentScan = (ComponentScan) confClass.getAnnotation(ComponentScan.class);
String path= componentScan.value();
path = path.replace(".","/"); // 将.换位对应操作系统的分隔符
// 将设置的扫描包路径变为对应的绝对路径,便于后续操作
ClassLoader appClassLoader = confClass.getClassLoader();
URL resource = appClassLoader.getResource(path);
String filePath = resource.getFile();
// 找到需要扫描的路径文件夹,注意这里我们拿到的是target项目编译运行的路径
// (拿到一个.java文件是没有的这个文件是给我们编写人员看的,JVM所获取的是)
// (在我们@ComponentScan注解传递的是一个包,是没有办法直接拿到其中的所有类文件的所以这个我们通过io拿到所有文件
// ,再对文件数据进行转化为我们的类路径)
File file = new File(filePath);
// 将文件夹中的所有问题给拿出了,这里就不考虑相互嵌套的问题了
ArrayList<File> loadFile = new ArrayList<>();
// 如果是一个非文件夹对象接下来的代码就不必执行了,也可以判断为参数错误并非一个包路径
if(!file.isDirectory()) return;
for (File item : file.listFiles()) {
if (item.isDirectory()){
for (File item2 : item.listFiles()) {
loadFile.add(item2);
}
}else{
loadFile.add(item);
}
}
// 拿到文件的file对象后, 遍历使用类加载器加载得到class对象
for (File loadFileItem : loadFile) {
// 通过先获取对应的class文件绝对路径,再将其操作为对应的路径
String absolutePath = loadFileItem.getAbsolutePath();
// 将绝对路径转化为对应的类路径
String className = absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class")).replace("/",".").replace("\",".");
try {
// 加载这个类
Class clazz = appClassLoader.loadClass(className);
if (clazz.getAnnotation(Component.class)==null) continue;
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setType(clazz);
if (clazz.getAnnotation(Scope.class)!=null){
beanDefinition.setScope(((Scope)clazz.getAnnotation(Scope.class)).value());
}else { // 默认情况
beanDefinition.setScope("singleton");
}
// 添加BeanDefinition中
String beanName = ((Component)clazz.getAnnotation(Component.class)).value();
beanDefinitionMap.put(beanName,beanDefinition);
// 判断是否是BeanPostProcessor, class之间的判断类型
if(BeanPostProcessor.class.isAssignableFrom(clazz)){
BeanPostProcessor beanPostProcessor = (BeanPostProcessor) createBean(beanName,beanDefinition);
beanPostProcessorList.add(beanPostProcessor);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
创建bean对象
这里面的概念均会在下文讲解 这里其实就是创建对象以及注入bean。
java
// 创建bean
public Object createBean(String beanName, BeanDefinition beanDefinition){
Class clazz = beanDefinition.getType();
try {
Object bean = null;
// 初始化并注入对象
bean = clazz.newInstance();
bean = assemble(bean);
// 执行BeanPostProcessor方法(前)
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessBeforeInitialization(bean,beanName);
}
// 判断是否执行initialzingBean中的方法
if(bean instanceof InitializingBean){
try {
((InitializingBean)bean).afterPropertiesSet(); // 将当前Object对象转换后调用对应方法
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// 执行BeanPostProcessor方法(后)
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessAfterInitialization(bean,beanName);
}
return bean;
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
// 装配bean,如Autowired注解等
public Object assemble(Object bean){
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
if(field.isAnnotationPresent(Autowired.class)){
Object aurowiredBean = getBean(field.getName());
try {
field.setAccessible(true); // 关闭访问检查
field.set(bean,aurowiredBean); // 赋值属性
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
return bean;
}
获取bean
判断是在单例对象池中获取(单例模式)还是创建bean返回(原型模式)
java
// 获取bean1(泛型方法)
public<T> T getBean(String beanName,Class<T> t){
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
// 如果在bean定义中没有找到就代表这个对象并没有注册到Spring中
if(beanDefinition==null){
throw new RuntimeException(new ClassNotFoundException());
}
if("singleton".equals(beanDefinition.getScope())){ //单例的情况直接从单例bean中读取
Object singletonBean = singletonObjects.get(beanName);
if(singletonBean == null){
singletonBean = createBean(beanName,beanDefinition);
}
return (T)singletonBean;
}else{ // 原型的情况直接使用createBean创建
Object protoType = createBean(beanName,beanDefinition);
return (T)protoType;
}
}
现在方法都有了那么完善构造方法
java
// 构造器初始化
public ApplicationContext(Class confClass){
// 扫描包设置的包路径
scan(confClass);
//(单例对象(singleton)在创建ApplicationContext后就会创建,而prototype是获取的时候创建的)
// 创建设置为singleton的对象,单例对象
// 获取bean定义中的数据,查看来创建
for (String beanName : beanDefinitionMap.keySet()) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if("singleton".equals(beanDefinition.getScope())){
// 通过crateBean方法创建同时实现依赖注入
Object o = createBean(beanName,beanDefinition);
singletonObjects.put(beanName,o);
}
}
}
InitializingBean
在我们工作中会不会出现Spring注入bean的时候属性需要通过数据库获取的情况
这种情况就可以使用我们的InitializingBean接口了,重写方法。它会在我们创建完毕bean的时候调用,使用Bean对象来调用,所以这个方法是可以改变我们bean对象的内部值的
java
void afterPropertiesSet() throws Exception;
为我们需要注入的bean实现接口以及接口的方法,这个方法其实就是穿插在Spring创建过程中的,在上面的创建Bean对象标题的模块就可以看到了
BeanPostProcessor
这个对象是Spring的一个关键对象,它的主要作用是可以在创建Spring的bean的前方或者后方来改变bean对象的值
执行方法完毕后返回的就是处理后的bean对象,返回回去就可以直接对SpringBean进行重新的赋值了
java
/**
* postProcessBeforeInitialization 在spring创建bean之前执行
*
* postProcessAfterInitialization
* 在spring创建bean之后执行,可以对bean进行一些修改,就比如把bean变为一个代理对象返回会去
*
* 在扫描包时就直接创建,并在类的类的内部存储起来在创建bean的时间就直接调用
*
* 执行方法完毕后返回的就是处理后的bean对象需要重新赋值
*/
public interface BeanPostProcessor {
//创建bean之前执行的对象
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
//创建bean之后执行的对象
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
这个对象主要是继承使用,我们在继承这个类后并装配到Spring中Spring就会识别我们的BeanPostProcessor对象并将其存储在一个集合中BeanPostProcessList
在创建bean的时候Spring就会循环调用这里面的方法,而AOP就是在方法体里面做了判断在我们创建bean的时候就会对其实现动态代理
贴一个代码
java
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if("userService".equals(beanName)){
Object proxy = Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("切面逻辑");
return method.invoke(bean,args);
}
});
return proxy;
}
return bean;
}
gitee地址:mySpring: 手写简单Spring (gitee.com)