從事開發工作多年,spring原始碼沒有特意去看過。但是相關技術原理倒是背了不少,畢竟面試的那關還是得過啊! 正所謂面試造火箭,工作擰螺絲。下面實現一個最簡單的ioc容器,供大家參考。
1.最終結果
2.涉及相關技術
(1) jdk動態代理
(2) java反射
3.原始碼
(1)包掃描工具類
package com.hdwang.ioc.core.utils; import java.io.File; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLDecoder; import java.util.HashSet; import java.util.Set; /** * 類工具 */ public class ClassUtils { /** * 獲取某包下所有類 * * @param packageName 包名 * @param isRecursion 是否遍歷子包 * @return 類的完整名稱 */ public static Set<String> getClassName(String packageName, boolean isRecursion) { Set<String> classNames = new HashSet<>(); ClassLoader loader = Thread.currentThread().getContextClassLoader(); String packagePath = packageName.replace(".", "/"); URL url = loader.getResource(packagePath); String filePath = null; try { filePath = URLDecoder.decode(url.getPath(), "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } if (filePath != null) { classNames = getClassNameFromDir(filePath, packageName, isRecursion); } return classNames; } /** * 從專案檔案獲取某包下有類 * * @param filePath 檔案路徑 * @param isRecursion 是否遍歷子包 * @return 類的完整名稱 */ private static Set<String> getClassNameFromDir(String filePath, String packageName, boolean isRecursion) { Set<String> className = new HashSet<>(); File file = new File(filePath); File[] files = file.listFiles(); for (File childFile : files) { if (childFile.isDirectory()) { if (isRecursion) { className.addAll(getClassNameFromDir(childFile.getPath(), packageName + "." + childFile.getName(), isRecursion)); } } else { String fileName = childFile.getName(); if (fileName.endsWith(".class") && !fileName.contains("$")) { className.add(packageName + "." + fileName.replace(".class", "")); } } } return className; } }
(2)字串工具類
package com.hdwang.ioc.core.utils; /** * 字串工具類 */ public class StringUtils { /** * 判斷字串是否空白 * * @param str 字串 * @return 字串是否空白 */ public static boolean isBlank(String str) { return str == null || str.trim().isEmpty(); } /** * 判斷字串是否非空白 * * @param str 字串 * @return 字串是否非空白 */ public static boolean isNotBlank(String str) { return !isBlank(str); } }
(3) Bean物件註解
package com.hdwang.ioc.core.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Bean物件註解 * 待存入ioc容器的相關物件,宣告在具體的實現類上 */ @Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.TYPE}) public @interface MyBean { /** * 待存入ioc容器的Bean名稱 * * @return Bean名稱 */ String value() default ""; }
(4) 自動注入註解
package com.hdwang.ioc.core.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自動注入註解 */ @Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.FIELD}) public @interface AutoInject { /** * 注入的bean名稱,為空時根據型別注入 * * @return Bean名稱 */ String value() default ""; }
(5) Bean資訊物件
package com.hdwang.ioc.core; /** * Bean類資訊 */ public class BeanInfo { /** * Bean類的型別 */ private Class clasz; /** * 儲存在ioc容器中的Bean名稱 */ private String beanName; /** * 儲存在ioc容器中的Bean型別 */ private Class beanType; /** * 儲存在ioc容器中的bean物件例項 */ private Object bean; /** * 儲存在ioc容器中的bean的代理物件例項 */ private Object proxyBean; public Class getClasz() { return clasz; } public void setClasz(Class clasz) { this.clasz = clasz; } public String getBeanName() { return beanName; } public void setBeanName(String beanName) { this.beanName = beanName; } public Class getBeanType() { return beanType; } public void setBeanType(Class beanType) { this.beanType = beanType; } public Object getBean() { return bean; } public void setBean(Object bean) { this.bean = bean; } public Object getProxyBean() { return proxyBean; } public void setProxyBean(Object proxyBean) { this.proxyBean = proxyBean; } }
(6) 上下文物件
package com.hdwang.ioc.core; import java.util.HashMap; import java.util.Map; /** * 上下文物件 * 用於儲存應用執行中的資訊 */ public class Context { /** * 根據Bean名稱儲存Bean的Map物件 */ private Map<String, Object> nameBeanMap = new HashMap<>(); /** * 根據Bean型別儲存Bean的Map物件 */ private Map<Class, Object> typeBeanMap = new HashMap<>(); public Object getBean(String beanName) { return nameBeanMap.get(beanName); } public Object getBean(Class clasz) { return typeBeanMap.get(clasz); } public void putBean(String beanName, Object bean) { nameBeanMap.put(beanName, bean); } public void putBean(Class beanType, Object bean) { typeBeanMap.put(beanType, bean); } }
(7) Bean的代理物件
package com.hdwang.ioc.core; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * Bean的代理物件 * 使用jdk動態代理原理實現對java物件的代理,必須依賴介面 */ public class BeanProxy implements InvocationHandler { /** * 被代理的bean物件 */ private Object bean; public BeanProxy(Object bean) { this.bean = bean; } /** * 呼叫目標bean的相關方法 * * @param proxy 代理物件 * @param method 方法 * @param args 引數 * @return 方法返回值 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before call method: " + method.getName()); Object result = method.invoke(bean, args); System.out.println("after call method: " + method.getName()); return result; } }
(8) Bean工廠類(ioc容器類)
package com.hdwang.ioc.core; import com.hdwang.ioc.core.annotation.AutoInject; import com.hdwang.ioc.core.annotation.MyBean; import com.hdwang.ioc.core.utils.ClassUtils; import com.hdwang.ioc.core.utils.StringUtils; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * Bean工廠 */ public class BeanFactory { /** * 基礎包路徑 */ private String basePackage; /** * 上下文物件 */ private Context context = new Context(); /** * 工廠構造器 * * @param basePackage 基礎包路徑 */ public BeanFactory(String basePackage) { this.basePackage = basePackage; init(); } /** * 工廠初始化 */ private void init() { //掃描包和載入bean到ioc容器 List<BeanInfo> myBeanList = scanPackageAndLoadBeans(); //給bean注入依賴物件 injectBeans(myBeanList); } /** * 掃描包和載入bean到ioc容器 * * @return 載入進ioc容器中的相關Bean資訊 */ private List<BeanInfo> scanPackageAndLoadBeans() { List<BeanInfo> myBeanList = new ArrayList<>(); //找到包下所有類 Set<String> classNames = ClassUtils.getClassName(basePackage, true); for (String className : classNames) { try { //查詢類 Class clasz = Class.forName(className); //判斷類上是否存在MyBean註解 if (clasz.isAnnotationPresent(MyBean.class)) { //獲取類上的MyBean註解 MyBean myBeanAnnotation = (MyBean) clasz.getAnnotation(MyBean.class); //獲取註解值,即Bean名稱 String beanName = myBeanAnnotation.value(); //獲取類繼承的相關介面 Class[] interfaces = clasz.getInterfaces(); //判斷類是否可以採用jdk動態代理(有介面方可進jdk動態代理,建立代理物件) boolean canJdkProxyBean = interfaces != null && interfaces.length > 0; //獲取待注入ioc容器的Bean的型別 Class beanType = getBeanType(clasz, canJdkProxyBean); //例項化當前類,生成bean例項 Object bean = clasz.newInstance(); Object iocBean = bean; if (canJdkProxyBean) { //可以使用jdk動態代理,則建立代理物件,代理此Bean Object proxyBean = this.createBeanProxy(bean); iocBean = proxyBean; } //儲存生成的bean到ioc容器 if (StringUtils.isNotBlank(beanName)) { context.putBean(beanName, iocBean); } context.putBean(beanType, iocBean); //暫存Bean資訊 BeanInfo beanInfo = new BeanInfo(); beanInfo.setClasz(clasz); beanInfo.setBeanName(beanName); beanInfo.setBeanType(beanType); beanInfo.setBean(bean); beanInfo.setProxyBean(canJdkProxyBean ? iocBean : null); myBeanList.add(beanInfo); } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { System.out.println("載入bean異常"); e.printStackTrace(); } } return myBeanList; } /** * 給相關Bean注入依賴的Bean * * @param myBeanList 注入到ioc容器中的所有的Bean */ private void injectBeans(List<BeanInfo> myBeanList) { for (BeanInfo myBeanInfo : myBeanList) { Class beanClass = myBeanInfo.getClasz(); Object bean = myBeanInfo.getBean(); //查詢Bean的宣告的所有欄位 Field[] fields = beanClass.getDeclaredFields(); for (Field field : fields) { //判斷欄位上是否有AutoInject註解 if (field.isAnnotationPresent(AutoInject.class)) { //查詢待注入的bean AutoInject autoInjectAnnotation = field.getAnnotation(AutoInject.class); //獲取註解的值,即待注入的Bean名稱 String injectBeanName = autoInjectAnnotation.value(); //獲取欄位的型別,即待注入的Bean型別 Class injectBeanType = field.getType(); Object proxyBean = null; //從查詢ioc容器中查詢待注入的Bean物件 if (StringUtils.isNotBlank(injectBeanName)) { //Bean名稱不為空,則根據名稱查詢Bean proxyBean = context.getBean(injectBeanName); } else { //Bean名稱為空,則根據Bean型別查詢Bean proxyBean = context.getBean(injectBeanType); } //設定當前欄位可訪問 field.setAccessible(true); try { //將找到的Bean注入到當前欄位上 field.set(bean, proxyBean); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } } /** * 獲取待注入到ioc容器中的Bean型別 * * @param clasz Bean型別 * @param canJdkProxyBean 是否可以使用jdk動態代理 * @return 注入到ioc容器中的Bean型別 */ private Class getBeanType(Class clasz, boolean canJdkProxyBean) { Class beanType = null; if (canJdkProxyBean) { //可以使用jdk動態代理,則bean型別取bean的介面型別 beanType = clasz.getInterfaces()[0]; } else { //不可以使用jdk動態代理,bean型別就取當前類型別 beanType = clasz; } return beanType; } /** * 根據Bean名稱獲取Bean物件 * * @param beanName Bean名稱 * @param <T> Bean型別 * @return ioc容器中的Bean, 找不到返回null */ public <T> T getBean(String beanName) { return (T) context.getBean(beanName); } /** * 根據Bean型別獲取Bean物件 * * @param clasz 注入到ioc容器中的Bean型別 * @param <T> Bean型別 * @return ioc容器中的Bean, 找不到返回null */ public <T> T getBean(Class clasz) { return (T) context.getBean(clasz); } /** * 建立代理bean * * @param bean 當前Bean物件 * @return Bean的代理物件 */ private Object createBeanProxy(Object bean) { InvocationHandler invocationHandler = new BeanProxy(bean); Object proxyBean = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), invocationHandler); return proxyBean; } }
4.示例程式碼
(1) User模型
package com.hdwang.ioc.example.model; public class User { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
(2) UserService
package com.hdwang.ioc.example.service; import com.hdwang.ioc.example.model.User; public interface UserService { User getUserById(Long id); }
(3) UserServiceImpl
package com.hdwang.ioc.example.service; import com.hdwang.ioc.core.annotation.MyBean; import com.hdwang.ioc.example.model.User; @MyBean("userService") public class UserServiceImpl implements UserService { @Override public User getUserById(Long id) { User user = new User(); if (id == 1) { user.setId(id); user.setName("張三"); } else if (id == 2) { user.setId(id); user.setName("李四"); } return user; } }
(4) UserController
package com.hdwang.ioc.example.controller; import com.hdwang.ioc.core.annotation.AutoInject; import com.hdwang.ioc.core.annotation.MyBean; import com.hdwang.ioc.example.model.User; import com.hdwang.ioc.example.service.UserService; @MyBean("userController") public class UserController { @AutoInject UserService userService; public User getUserById(Long id) { return userService.getUserById(id); } }
(5) 主函式
package com.hdwang.ioc.example; import com.hdwang.ioc.core.BeanFactory; import com.hdwang.ioc.example.controller.UserController; import com.hdwang.ioc.example.model.User; /** * 程式啟動類 */ public class Main { /** * 主函式入口 * * @param args 入參 */ public static void main(String[] args) { //定義要掃描的包名 String basePackage = "com.hdwang.ioc.example"; //初始化Bean工廠 BeanFactory beanFactory = new BeanFactory(basePackage); //獲取指定的Bean UserController userController = beanFactory.getBean(UserController.class); //呼叫Bean中的方法 User user = userController.getUserById(1L); System.out.println(user); } }
5.執行結果
before call method: getUserById
after call method: getUserById
User{id=1, name='張三'}
6.總結說明
ioc的實現,主要是用到了java的反射技術,和動態代理無關,代理物件可以實現一些增強的功能,所以人們常常稱spring的bean的代理類為增強類!哈哈。。。
7.附錄
專案原始碼:https://github.com/hdwang123/iocdemo