搜文章
推荐 原创 视频 Java开发 iOS开发 前端开发 JavaScript开发 Android开发 PHP开发 数据库 开发工具 Python开发 Kotlin开发 Ruby开发 .NET开发 服务器运维 开放平台 架构师 大数据 云计算 人工智能 开发语言 其它开发
Lambda在线 > java404 > 自己动手写 IOC

自己动手写 IOC

java404 2018-05-28

点击上方 “java404”,选择“置顶公众号”有趣有内涵的文章第一时间送达! 


概述

IOC — Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,IOC 意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。下面通过一个demo来演示什么是 IOC 。让 IOC 不在那么神秘。


1. Component注解定义

package cn.com.infcn.ioc;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;


/**

 * 指定需要容器管理的类

 * 

 * @author jijs

 */

@Target({ ElementType.TYPE })

@Retention(RetentionPolicy.RUNTIME)

public @interface Component {

}



先定义一个@Component注解。只要被@Component自定义主键注释的类都是受容器管理的Bean。


2. Inject注解定义


package cn.com.infcn.ioc;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;


/**

 * 指定需要注入的属性

 * 

 * @author jijs

 */

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

public @interface Inject {

}


定义一个@Inject注解,只要是被@Inject注解注释的属性都会自动注入,实现IOC功能。


3. 用户Bean实现


package cn.com.infcn.ioc;

/**

 * 用户Bean

 * 

 * @author jijs

 */

public class User {

 private String userName;

 private Integer age;

 public User(String userName, Integer age) {

   this.userName = userName;

   this.age = age;

 }

 public String getUserName() {

   return userName;

 }

 public void setUserName(String userName) {

   this.userName = userName;

 }

 public Integer getAge() {

   return age;

 }

 public void setAge(Integer age) {

   this.age = age;

 }

 @Override

 public String toString() {

   return "User [userName=" + userName
       + ", age=" + age + "]";

 }

}



只是一个普通的模型bean。


4. UserService实现


package cn.com.infcn.ioc;


/**

 * 用户Service实现

 * 

 * @author jijs

 */

@Component

public class UserService {

 public User getUser() {

   User user = new User("张三", 20);

   return user;

 }

}



UserService实现。使用@Component注解标注该类是受容器管理的类。


3. UserController实现


package cn.com.infcn.ioc;


/**

 * 用户Controller实现

 * 

 * @author jijs

 */

@Component

public class UserController {

 @Inject

 private UserService userService;


 public void getUser() {

   User user = userService.getUser();

   System.out.println(user);

 }

}



Usercontroller实现,该类被@Component注解注释,表示受容器管理的Bean。
userService熟悉使用了@Inject自定义注解,表示该属性是容器自动注入该实例,实现IOC功能。


6. IocContext 容器实现


package cn.com.infcn.ioc;


import java.io.File;

import java.io.FileFilter;

import java.net.URL;

import java.util.Enumeration;

import java.util.Map;

import java.util.concurrent.ConcurrentHashMap;


/**

 * Ioc 容器实现类

 * 

 * @author jijs

 */

public class IocContext {

 public static final Map<Class<?>, Object> applicationContext = new ConcurrentHashMap<Class<?>, Object>();

 static {

   String packageName = "cn.com.infcn";

   try {

     initBean(packageName);

   } catch (Exception e) {

     e.printStackTrace();

   }

 }


 private static void initBean(String packageName) throws Exception {

   Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()

       .getResources(packageName.replaceAll("\\.", "/"));

   while (urls.hasMoreElements()) {

     addClassByAnnotation(urls.nextElement().getPath(), packageName);

   }

   // IOC实现, 自定注入

   IocUtil.inject();

 }
 
 // 获取指定包路径下实现 Component主键Bean的实例

 private static void addClassByAnnotation(String filePath, String packageName) {

   try {

     File[] files = getClassFile(filePath);

     if (files != null) {

       for (File f : files) {

         String fileName = f.getName();

         if (f.isFile()) {
     // 判断该类是否实现了注解

           Class<?> clazz = Class

.forName(packageName + "." + fileName.substring(0, fileName.lastIndexOf(".")));

           if (clazz.isAnnotationPresent(Component.class)) {

             applicationContext.put(clazz, clazz.newInstance());

           }

         } else {

           addClassByAnnotation(f.getPath(), packageName + "." + fileName);

         }

       }

     }

   } catch (Exception e) {

     e.printStackTrace();

 }

}
   
 // 获取该路径下所遇的class文件和目录
 
private static File[] getClassFile(String filePath) {

   return new File(filePath).listFiles(new FileFilter() {

     @Override

     public boolean accept(File file) {

       return file.isFile() && file.getName().endsWith(".class") || file.isDirectory();

     }

   });

 }

}



  1. 扫描加载指定包路径下的所有的Class,并判断该Class是否是@Component注解的类,如果是,则创建实例,并保存到applicationContext缓存中。

  2. 调用IocUtil.inject(),进行依赖注入。


7. Ioc 依赖注入实现


package cn.com.infcn.ioc;


import java.lang.reflect.Field;

import java.util.Map;

import java.util.Map.Entry;


/**

 * Ioc 注入实现

 * 

 * @author jijs

 */

public class IocUtil {

 public static void inject() {

   Map<Class<?>, Object> map = IocContext.applicationContext;

   try {

     for (Entry<Class<?>, Object> entry : map.entrySet()) {

       Class<?> clazz = entry.getKey();

Object obj = entry.getValue();

Field[] fields = clazz.getDeclaredFields();

for (Field field : fields) {

 if (field.isAnnotationPresent(Inject.class)) {

   Class<?> fieldClazz = field.getType();

   field.setAccessible(true);

   Object fieldObj = map.get(fieldClazz);

   field.set(obj, fieldObj);

 }

}

     }

   } catch (Exception e) {

     e.printStackTrace();

   }

 }

}



循环变量 applicationContext中所有的Bean,判断每个Bean中是否有被@Inject注解修饰的属性,如果有则从applicationContext中获取要注入的实例,并使用反射实现自动注入功能。

8. 模拟调用UserController测试类


package cn.com.infcn.ioc;


/**

 * 模拟调用UserController

 * 

 * @author jijs

 */

public class Main {

 public static void main(String[] args) throws Exception {

   UserController userController = (UserController) IocContext.applicationContext.get(UserController.class);

   userController.getUser();

 }

}



从IocContext 容器中获取UserController实例,并调用getUser()方法。运行结果结果如下图。从结果中我们发现 UserController中的UserService被容器自动注入进来了。然后调用UserService.getUser() 获取用户信息。


运行结果





喜欢本文的朋友们,欢迎长按下图关注订阅号 java404,收听更多精彩的内容


版权声明:本站内容全部来自于腾讯微信公众号,属第三方自助推荐收录。《自己动手写 IOC》的版权归原作者「java404」所有,文章言论观点不代表Lambda在线的观点, Lambda在线不承担任何法律责任。如需删除可联系QQ:516101458

文章来源: 阅读原文

相关阅读