spring之IOC模拟实现
Posted 梓&艺
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring之IOC模拟实现相关的知识,希望对你有一定的参考价值。
使用Spring框架已经有很长时间了,一直没有仔细的想过框架的设计思想是什么样的,底层到底是怎么实现的,这几天调试程序看了些源码,写下来做个记录。由于Spring框架博大精深,个人理解的难免有不正确的地方,希望看到的朋友可以指正,不胜感激。
一 什么是IOC
IOC是Inversion of Control的缩写,即就是所谓的控制反转,它是一种设计思想,主要包含IOC和DI两方面的内容。
IOC:不使用spring框架的时候,当我们需要一个java类对象时,我们需要手动的去创建管理这些对象。使用spring时,我们只把所有的对象同意存放在IOC容器中,要用某个类对象的时候不再需要我们去创建并管理它,完全交给spring做统一管理。
DI:依赖注入,在类加载时从外网里执行,遇到需要的对象时直接去IOC容器中获取,然后赋值给当前注入的类。
二 模拟实现
我们先创建一个项目。要把对象交给容器管理,我们就需要告诉它去管理哪些对象,所以在classpath下创建一个application.properties文件,并配置要交给容器的文件。如下表示我们要把basePackage路径(包含子目录)下的class文件都交给容器管理。
basePackage=org.wl.test.demo
创建注解类,用来标记需要被加载到IOC容器中
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 public @interface Controller { 4 5 String value() default ""; 6 7 }
1 @Target(ElementType.FIELD) 2 @Retention(RetentionPolicy.RUNTIME) 3 public @interface Autowired { 4 5 String value() default ""; 6 7 }
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Service { String value() default ""; }
1 @Target(ElementType.FIELD) 2 @Retention(RetentionPolicy.RUNTIME) 3 public @interface Qualifier { 4 5 String value() default ""; 6 7 }
创建controller和接口类及接口的实现类,并加入对应的注解
1 @Controller 2 public class UserController { 3 4 @Autowired 5 private UserService userService; 6 7 /** 8 * 接口可能会有多个实现类,所以在注入接口时必须用Autowired或者Qualifier指定具体的实现类 9 */ 10 @Autowired 11 @Qualifier("userService2") 12 private IUserService userService2; 13 14 public void save(){ 15 UserInfo user = new UserInfo(); 16 userService.saveUser(user); 17 18 userService2.saveUser(user); 19 } 20 21 }
1 public interface IUserService { 2 3 /** 4 * 保存用户信息 5 * @param user 6 */ 7 void saveUser(UserInfo user); 8 9 }
1 @Service 2 public class UserService implements IUserService { 3 @Override 4 public void saveUser(UserInfo user) { 5 System.out.println("实现类1 保存用户信息到数据库 " + user.toString()); 6 } 7 }
1 @Service 2 public class UserService2 implements IUserService { 3 @Override 4 public void saveUser(UserInfo user) { 5 System.out.println("实现类2 保存用户信息到数据库 " + user.toString()); 6 } 7 }
创建一个ApplicationContext类,在此类完成相关的方法
1 public class ApplicationContext { 2 3 public ApplicationContext() { 4 5 } 6 7 }
解析配置文件中的配置信息,获取要扫描的包路径
1 public String getBasePackage(String fileName) throws IOException { 2 String basePackage; 3 Properties prop = new Properties(); 4 InputStream in = this.getClass().getClassLoader().getResourceAsStream(fileName); 5 prop.load(in); 6 basePackage = prop.getProperty("basePackage"); 7 return basePackage; 8 }
定义一个集合,存放在指定的扫描包及其子包中扫描到的class文件
1 /** 2 * 初始化一个集合,存放扫描到的class对象 3 */ 4 private List<Class<?>> classList = Collections.synchronizedList(new ArrayList<>());
1 /** 2 * 跟据基础包名读取包及子包中的类对象 3 * @param basePackage 4 */ 5 public void scanClasses(String basePackage){ 6 if(basePackage == null || "".equals(basePackage)){return;} 7 8 doScan(basePackage); 9 System.out.println(classList); 10 }
1 private void doScan(String basePackage) { 2 String path = basePackage.replaceAll("\.","/"); 3 URL url = this.getClass().getClassLoader().getResource(path); 4 File file = new File(url.getFile()); 5 file.listFiles(new FileFilter() { 6 @Override 7 public boolean accept(File childFile) { 8 String fileName = childFile.getName(); 9 if(childFile.isDirectory()){ 10 //当前文件是目录,递归 扫描下级子目录下的class文件 11 doScan(basePackage + "." + fileName); 12 }else{ 13 if(fileName.endsWith(".class")){ 14 String className = basePackage + "." + fileName.replace(".class", ""); 15 try { 16 Class<?> clazz = this.getClass().getClassLoader().loadClass(className); 17 classList.add(clazz); 18 } catch (ClassNotFoundException e) { 19 e.printStackTrace(); 20 } 21 } 22 } 23 return false; 24 } 25 }); 26 }
将扫描到的class对象中的使用了自定义的注解的类实例,存放到容器中
1 /** 2 * 初始化map 存放别名与对象实例 3 */ 4 private Map<String, Object> aliasInstanceMap = new HashMap<>();
1 /** 2 * 完成别名与实例的映射 3 */ 4 public void buildAliasInstanceMap(String basePackage) throws Exception { 5 6 if(classList.size() == 0){return;} 7 8 for(Class<?> clazz : classList){ 9 if (clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class) 10 || clazz.isAnnotationPresent(Autowired.class)) { 11 String alias = getAlias(clazz); 12 Object obj = aliasInstanceMap.get(alias); 13 14 //如果别名实例映射关系已经存在,则给出提示 15 if(obj != null){ 16 throw new Exception("alias is exist!"); 17 }else{ 18 aliasInstanceMap.put(alias, clazz.newInstance()); 19 } 20 } 21 } 22 23 System.out.println(aliasInstanceMap); 24 }
1 /** 2 * 获取对象的别名,如果注解中配置了别名,别使用配置的别名,否则默认使用类名首字母小写 3 * @param clazz 4 * @return 5 */ 6 public String getAlias(Class<?> clazz){ 7 String alias = ""; 8 Controller controller = clazz.getAnnotation(Controller.class); 9 if(controller != null){ 10 alias = controller.value(); 11 } 12 Service service = clazz.getAnnotation(Service.class); 13 if (service != null) { 14 alias = service.value(); 15 } 16 Autowired autowired = clazz.getAnnotation(Autowired.class); 17 if(autowired != null){ 18 alias = autowired.value(); 19 } 20 21 //注解中没有配置别名 22 if("".equals(alias)){ 23 String simpleName = clazz.getSimpleName(); 24 alias = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1); 25 } 26 return alias; 27 }
扫描类,根据类属性中使用了Autowired的类别名,从IOC容器中取得对应的实例赋值给属性类,就是完成依赖注入,
1 /** 2 * 属性对象的注入 3 */ 4 public void doAutowired(){ 5 if (aliasInstanceMap.size() == 0) { 6 return; 7 } 8 9 aliasInstanceMap.forEach((k, v)->{ 10 11 Field[] fields = v.getClass().getDeclaredFields(); 12 13 for(Field field : fields){ 14 if (field.isAnnotationPresent(Autowired.class)) { 15 String alias = ""; 16 17 Autowired autowired = field.getAnnotation(Autowired.class); 18 if(autowired != null){ 19 //注入的对象是接口时,由于不知道接口有几个实现类,所以就必须在Autowired或者Qualifier上指定要注解的具体的实现类 20 if(!"".equals(autowired.value())){ 21 alias = autowired.value(); 22 }else{ 23 Qualifier qualifier = field.getAnnotation(Qualifier.class); 24 if(qualifier != null){ 25 alias = qualifier.value(); 26 } 27 } 28 } 29 30 if ("".equals(alias)) { 31 alias = getAlias(field.getType()); 32 } 33 34 Object instance = null; 35 if(!"".equals(alias)){ 36 instance = aliasInstanceMap.get(alias); 37 } 38 39 field.setAccessible(true); 40 41 try { 42 field.set(v, instance); 43 } catch (IllegalAccessException e) { 44 e.printStackTrace(); 45 } 46 } 47 48 } 49 }); 50 51 }
至此我们就实现了IOC和DI,做测试
1 public static void main(String[] args) throws Exception { 2 String fileName = "application.properties"; 3 ApplicationContext context = new ApplicationContext(); 4 String basePackage = context.getBasePackage(fileName ); 5 context.scanClasses(basePackage); 6 context.buildAliasInstanceMap(basePackage); 7 context.doAutowired(); 8 //测试 9 UserController controller = (UserController) context.getBean("userController"); 10 controller.save(); 11 }
1 /** 2 * 根据beanName 获取 3 * @param beanName 4 * @return 5 */ 6 public Object getBean(String beanName){ 7 return aliasInstanceMap.get(beanName); 8 }
执行main方法后,可以在控制台看到两个接口实现类打印的内容
以上是关于spring之IOC模拟实现的主要内容,如果未能解决你的问题,请参考以下文章