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模拟实现的主要内容,如果未能解决你的问题,请参考以下文章

容器学习:动手模拟spring的IoC

JAVA模拟Spring实现IoC过程(附源码)

spring之IOC实现

面试之Spring框架IOC和AOP的实现原理

Spring基础入门之IoC注入

javaSpring 自己模拟 Spring 实现 IOC依赖注入 并且 解决 循环依赖