手写 Spring 之 Autowired、Component、ComponentScan 注解功能模拟
一、创建一个空的项目
完成后的工程目录如图:
二、代码说明
容器定义
package com.demo.spring;
import com.demo.spring.ann.Autowired;
import com.demo.spring.ann.Component;
import com.demo.spring.ann.ComponentScan;
import com.demo.spring.ann.Scope;
import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
/**
* Description: Spring 容器
*
* @Author: zhx & moon hongxu_1234@163.com
* @Date: 2022-04-13 20:06
* @version: V1.0.0
*/
public class SpringApplicationContext {
/**
* 启动类
*/
private Class appClass;
/**
* 扫描结果池
*/
private static Map<String,BeanDefinition> beanMap = new HashMap<>(16);
/**
* 单例池
*/
private static Map<String,Object> singletonMap = new HashMap<>(16);
/**
* 构造容器 扫描注解并实例化Bean
* @param appClass
*/
public SpringApplicationContext(Class appClass) {
this.appClass = appClass;
//扫描
scan();
//生成
for (String k:beanMap.keySet()){
singletonMap.put(k,createBean(k, beanMap.get(k)));
}
}
/**
* 获取Bean实例 不存在则抛出空指针异常
* @param beanName
* @return
*/
public Object getBean(String beanName){
if (beanMap.containsKey(beanName)){
BeanDefinition beanDefinition = beanMap.get(beanName);
if ("prototype".equals(beanDefinition.getScope())){
return createBean(beanName,beanDefinition);
}else{
return singletonMap.get(beanName);
}
}
throw new NullPointerException();
}
/**
* 扫描需要在启动时生成Bean配置的类
*/
private void scan(){
if (appClass.isAnnotationPresent(ComponentScan.class)){
ComponentScan scan = (ComponentScan) appClass.getAnnotation(ComponentScan.class);
String scanPath = scan.value();
scanPath = scanPath.replace(".","/");
ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();
URL url = classLoader.getResource(scanPath);
File file = new File(url.getFile());
//实际可能需要递归处理
if (file.isDirectory()){
for (File f:file.listFiles()){
String absolutePath = f.getAbsolutePath();
absolutePath = absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class")).replace("\\",".");
try {
Class clazz = classLoader.loadClass(absolutePath);
if (clazz.isAnnotationPresent(Component.class)){
//获取Bean名称
Component component = (Component) clazz.getAnnotation(Component.class);
String beanName = component.value();
if ("".equals(beanName)){
beanName = Introspector.decapitalize(clazz.getSimpleName());
}
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setScope("");
beanDefinition.setType(clazz);
//判断单例还是原型(多例)
if (clazz.isAnnotationPresent(Scope.class)){
Scope scope = (Scope) clazz.getAnnotation(Scope.class);
String model = scope.value();
if ("prototype".equals(model)){
beanDefinition.setScope("prototype");
}
}
beanMap.put(beanName,beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 创建Bean 当前只处理无参构造
* @return
*/
public Object createBean(String beanName,BeanDefinition beanDefinition){
Class clazz = beanDefinition.getType();
try {
Object bean = clazz.getConstructor().newInstance();
for (Field field:clazz.getDeclaredFields()){
if (field.isAnnotationPresent(Autowired.class)){
String autowiredBeanName = field.getName();
field.setAccessible(true);
field.set(bean,getBean(autowiredBeanName));
}
}
return bean;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
注解
1.Autowired
package com.demo.spring.ann;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Description: 依赖注入注解
*
* @Author: zhx & moon hongxu_1234@163.com
* @Date: 2022-04-13 22:32
* @version: V1.0.0
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
2.Component
package com.demo.spring.ann;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Description: 配置类注解
*
* @Author: zhx & moon hongxu_1234@163.com
* @Date: 2022-04-13 20:19
* @version: V1.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
String value() default "";
}
3.ComponentScan
package com.demo.spring.ann;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Description: 注解扫描
*
* @Author: zhx & moon hongxu_1234@163.com
* @Date: 2022-04-13 22:30
* @version: V1.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
String value() default "";
}
4.Scope
package com.demo.spring.ann;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Description: 作用域
*
* @Author: zhx & moon hongxu_1234@163.com
* @Date: 2022-04-13 23:05
* @version: V1.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
String value() default "";
}
测试实体
1.Student
package com.demo.test.entity;
import com.demo.spring.ann.Component;
import com.demo.spring.ann.Scope;
/**
* Description: 学生
*
* @Author: zhx & moon hongxu_1234@163.com
* @Date: 2022-04-13 23:43
* @version: V1.0.0
*/
@Component
@Scope("prototype")
public class Student {
}
2.Teacher
package com.demo.test.entity;
import com.demo.spring.ann.Component;
/**
* Description: 老师
*
* @Author: zhx & moon hongxu_1234@163.com
* @Date: 2022-04-13 23:48
* @version: V1.0.0
*/
@Component
public class Teacher {
}
3.User
package com.demo.test.entity;
import com.demo.spring.ann.Autowired;
import com.demo.spring.ann.Component;
/**
* Description: 测试实体
*
* @Author: zhx & moon hongxu_1234@163.com
* @Date: 2022-04-13 22:31
* @version: V1.0.0
*/
@Component("user")
public class User {
@Autowired
Teacher teacher;
public void test(){
System.out.println("张三");
System.out.println(teacher);
}
}
启动类
1.AppConfig
package com.demo.test;
import com.demo.spring.ann.ComponentScan;
/**
* Description: 启动类
*
* @Author: zhx & moon hongxu_1234@163.com
* @Date: 2022-04-13 22:35
* @version: V1.0.0
*/
@ComponentScan("com.demo.test.entity")
public class AppConfig {
}
测试类
1.Test
package com.demo.test;
import com.demo.spring.SpringApplicationContext;
import com.demo.test.entity.User;
/**
* Description: 测试类
*
* @Author: zhx & moon hongxu_1234@163.com
* @Date: 2022-04-13 20:04
* @version: V1.0.0
*/
public class Test {
public static void main(String[] args) {
SpringApplicationContext applicationContext = new SpringApplicationContext(AppConfig.class);
System.out.println("单例--------------------");
System.out.println(applicationContext.getBean("user"));
System.out.println(applicationContext.getBean("user"));
System.out.println(applicationContext.getBean("user"));
System.out.println("原型--------------------");
System.out.println(applicationContext.getBean("student"));
System.out.println(applicationContext.getBean("student"));
System.out.println("注入--------------------");
System.out.println(applicationContext.getBean("teacher"));
User user = (User) applicationContext.getBean("user");
user.test();
}
}
三、结果
Connected to the target VM, address: '127.0.0.1:52335', transport: 'socket'
Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
单例--------------------
com.demo.test.entity.User@27fa135a
com.demo.test.entity.User@27fa135a
com.demo.test.entity.User@27fa135a
原型--------------------
com.demo.test.entity.Student@46f7f36a
com.demo.test.entity.Student@421faab1
注入--------------------
com.demo.test.entity.Teacher@2b71fc7e
张三
com.demo.test.entity.Teacher@2b71fc7e
Disconnected from the target VM, address: '127.0.0.1:52335', transport: 'socket'
Process finished with exit code 0