从零手写IOC
概述
IOC (Inversion of Control) 控制反轉。熟悉Spring的應該都知道。那么具體是怎么實現的呢?下面我們通過一個例子說明。
1. Component注解定義
package cn.com.infcn.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /*** 指定需要容器管理的類* * @author jijs* @date 2017-12-3*/ @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface Component { }先定義一個@Component注解。只要被@Component自定義主鍵注釋的類都是受容器管理的Bean。
2. Inject注解定義
package cn.com.infcn.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /*** 指定需要注入的屬性* @author jijs* @date 2017-12-3*/ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Inject { }定義一個@Inject注解,只要是被@Inject注解注釋的屬性都會自動注入,實現IOC功能。
3. 用戶Bean實現
package cn.com.infcn.bean; /*** 用戶Bean* * @author jijs* @date 2017-12-3*/ 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;}@Overridepublic String toString() {return "User [userName=" + userName + ", age=" + age + "]";}}只是一個普通的模型bean。
4. UserService實現
package cn.com.infcn.service; import cn.com.infcn.annotation.Component; import cn.com.infcn.bean.User; /*** 用戶Service實現* * @author jijs* @date 2017-12-3*/ @Component public class UserService {public User getUser() {User user = new User("張三", 20);return user;} }UserService實現。使用@Component注解標注該類是受容器管理的類。
3. UserController實現
package cn.com.infcn.controller; import cn.com.infcn.annotation.Component; import cn.com.infcn.annotation.Inject; import cn.com.infcn.bean.User; import cn.com.infcn.service.UserService; /*** 用戶Controller實現* * @author jijs* @date 2017-12-3*/ @Component public class UserController {@Injectprivate 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; import cn.com.infcn.annotation.Component; /*** Ioc 容器實現類* * @author jijs* @date 2017-12-3*/ 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() {@Overridepublic boolean accept(File file) {return file.isFile() && file.getName().endsWith(".class") || file.isDirectory();}});} }7. Ioc 依賴注入實現
package cn.com.infcn.ioc; import java.lang.reflect.Field; import java.util.Map; import java.util.Map.Entry; import cn.com.infcn.annotation.Inject; /*** Ioc 注入實現* * @author jijs* @date 2017-12-3*/ 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; import cn.com.infcn.controller.UserController; import cn.com.infcn.ioc.IocContext; /*** 模擬調用UserController* * @author jijs* @date 2017-12-3*/ 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() 獲取用戶信息。
運行結果
想了解更多精彩內容請關注我的公眾號
本人簡書blog地址:http://www.jianshu.com/u/1f0067e24ff8????
點擊這里快速進入簡書
GIT地址:http://git.oschina.net/brucekankan/
點擊這里快速進入GIT
總結
- 上一篇: 从零手写RPC
- 下一篇: JAVA 文件锁 FileLock