IOC 實(shí)現(xiàn)原理
開發(fā)工作多年叠国,spring源碼沒有特意去看過泡一。理解實(shí)現(xiàn)原理查吊,不如自己實(shí)現(xiàn)簡易版的進(jìn)一步理解IOC到底是怎樣實(shí)現(xiàn)近她。下面實(shí)現(xiàn)一個(gè)最簡單的ioc容器
模擬IOC容器獲取bean
- 注解
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的名稱
String value() default "";
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 注冊bean到IOC容器
*/
@Target(value = {ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyBean {
//存入到IOC容器,bean的名稱
String value() default "";
}
- BeanFactory
package com.lg.ioc.core;
import com.lg.ioc.core.annotation.AutoInject;
import com.lg.ioc.core.annotation.MyBean;
import com.lg.ioc.core.utils.ClassUtils;
import com.sun.deploy.util.StringUtils;
import java.lang.annotation.Annotation;
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工廠
* 1.掃描包到IOC容器(注意IOC容器是存儲(chǔ)源對象代理對象)
* 2.給bean注入依賴對象(依賴對象也是代理對象)
* 3.獲取bean(獲取的也是代理對象)
*/
public class BeanFactory {
//基礎(chǔ)掃描包路徑
private String basePackage;
//上下文對象
private Context context = new Context();
//工廠構(gòu)造函數(shù)
public BeanFactory(String basePackage) {
this.basePackage = basePackage;
init();
}
//工廠初始化
private void init() {
//1.掃描包到IOC容器(注意IOC容器是存儲(chǔ)源對象代理對象)
List<BeanInfo> beanInfoList = scanPackageAndLoadBeans();
//2.給bean注入依賴對象(依賴對象也是代理對象)
injectBeans(beanInfoList);
}
private void injectBeans(List<BeanInfo> beanInfoList) {
//遍歷每一個(gè)bean
for (BeanInfo beanInfo : beanInfoList) {
try {
//獲取IOC的bean類型
Class beanClass = beanInfo.getClz();
//獲取IOC的bean實(shí)例對象
Object bean = beanInfo.getBean();
//查詢當(dāng)前bean所有字段
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
if(declaredField.isAnnotationPresent(AutoInject.class)) {
//獲取@AutoInject注解信息
AutoInject autoInjectAnnotation = declaredField.getAnnotation(AutoInject.class);
//獲取注入bean名稱
String injectBeanName = autoInjectAnnotation.value();
//獲取注入bean類型
Class injectBeanType = declaredField.getType();
//從IOC容器查找bean對象
Object proxyBean;
if(!"".equals(injectBeanName)) {
//根據(jù)名稱竟宋,獲取bean
proxyBean = context.getBean(injectBeanName);
}else {
//根據(jù)類型提完,獲取bean
proxyBean = context.getBean(injectBeanType);
}
//設(shè)置當(dāng)前字段可訪問
declaredField.setAccessible(true);
//將從IOC獲取的bean,注入到當(dāng)前字段
declaredField.set(bean, proxyBean);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
private List<BeanInfo> scanPackageAndLoadBeans() {
List<BeanInfo> myBeanList = new ArrayList<>();
//獲取包路徑下的所有類
Set<String> classNames = ClassUtils.getClassName(basePackage, true);
for (String className : classNames) {
try {
//獲取反射
//Class<? extends String> aClass = className.getClass();
Class aClass = Class.forName(className);
//判斷是否存在MyBean注解
if (aClass.isAnnotationPresent(MyBean.class)) {
//獲取注解信息
MyBean myBeanAnnotation = (MyBean)aClass.getAnnotation(MyBean.class);
//獲取注解value
String beanName = myBeanAnnotation.value();
//獲取當(dāng)前類實(shí)現(xiàn)的接口
Class[] interfaces = aClass.getInterfaces();
//記錄是否可以使用jdk動(dòng)態(tài)代理(有接口方可進(jìn)入jdk動(dòng)態(tài)代理,創(chuàng)建代理對象)
boolean canJdkProxyBean = interfaces != null && interfaces.length > 0;
//獲取bean類型丘侠,存入IOC容器要用
Class beanType = getBeanType(aClass, canJdkProxyBean);
//實(shí)例對象
Object bean = aClass.newInstance();//原始對象實(shí)例對象
Object iocBean;//存入IOC容器 實(shí)例對象
if(canJdkProxyBean) {
//如果可jdk動(dòng)態(tài)代理徒欣,就創(chuàng)建動(dòng)態(tài)代理對象
iocBean = this.createProxyBean(bean);
}else {
iocBean = bean;
}
//把解析出實(shí)例對象bean,存入到IOC容器
if(!"".equals(className)) {
//按照名稱 存入到IOC容器
context.putBean(beanName, iocBean);
}
//存入容器時(shí),根據(jù)類型一定要存入的蜗字,根據(jù)名稱存入是依賴傳參
context.putBean(beanType, iocBean);
//組裝beanInfo,暫存bean信息
BeanInfo beanInfo = new BeanInfo();
beanInfo.setClz(beanType);
beanInfo.setBeanName(beanName);
beanInfo.setBeanType(beanType);
beanInfo.setBean(bean);
beanInfo.setProxyBean(iocBean);
myBeanList.add(beanInfo);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
return myBeanList;
}
private Object createProxyBean(Object bean) {
InvocationHandler invocationHandler = new BeanProxy(bean);
Object proxyBean = Proxy.newProxyInstance(bean.getClass().getClassLoader(),
bean.getClass().getInterfaces(), invocationHandler);
return proxyBean;
}
private Class getBeanType(Class aClass, boolean canJdkProxyBean) {
Class beanType;
if(canJdkProxyBean) {
//如果是實(shí)現(xiàn)接口的類帚称,可以使用jdk動(dòng)態(tài)代理類
beanType = aClass.getInterfaces()[0];
} else {
beanType = aClass;
}
return beanType;
}
//根據(jù)類型,獲取bean
public <T> T getBean(Class clz) {
return (T) context.getBean(clz);
}
//根據(jù)名稱秽澳,獲取bean
public <T> T getBean(String beanName) {
return (T) context.getBean(beanName);
}
}
- bean信息
/**
* bean類型信息
*/
public class BeanInfo {
//bean類型
private Class clz;
//存入容器IOC的bean名稱
private String beanName;
//存入容器IOC的bean類型
private Class beanType;
//存入容器IOC的bean實(shí)例對象
private Object bean;
//存入容器IOC的bean代理對象
private Object proxyBean;
public Class getClz() {
return clz;
}
public void setClz(Class clz) {
this.clz = clz;
}
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;
}
}
- context 上下文對象闯睹,用于保存應(yīng)用運(yùn)行時(shí)的信息 類似applicationContext
import java.util.HashMap;
import java.util.Map;
/**
*上下文對象,用于保存應(yīng)用運(yùn)行時(shí)的信息 類似applicationContext
* 1.map結(jié)構(gòu)IOC容器担神,存入的bean
* 2.存入bean
* 3.取出bean
*/
public class Context {
//相當(dāng)于IOC容器根據(jù)name存儲(chǔ)
private Map<String, Object> containerBeanName = new HashMap<>();
//相當(dāng)于IOC容器根據(jù)name存儲(chǔ)
private Map<Class, Object> containerBeanClass = new HashMap<>();
public Map<String, Object> getContainerBeanName() {
return containerBeanName;
}
public Object getBean(String beanName) {
return containerBeanName.get(beanName);
}
public Object getBean(Class clz) {
return containerBeanClass.get(clz);
}
public void putBean(String beanName, Object proxyBean) {
containerBeanName.put(beanName, proxyBean);
}
public void putBean(Class clz, Object proxyBean) {
containerBeanClass.put(clz, proxyBean);
}
}
- 工具類:將包路徑下的類解析出來
package com.lg.ioc.core.utils;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* 類工具
*/
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);
if (url != null) {
String protocol = url.getProtocol();
if (protocol.equals("file")) {
String filePath = null;
try {
filePath = URLDecoder.decode(url.getPath(), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (filePath != null) {
classNames = getClassNameFromDir(filePath, packageName, isRecursion);
}
}else if (protocol.equals("jar")) {
JarFile jarFile = null;
try {
jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
} catch (Exception e) {
e.printStackTrace();
}
if (jarFile != null) {
classNames = getClassNameFromJar(jarFile.entries(), packageName, isRecursion);
}
}
} else {
/*從所有的jar包中查找包名*/
classNames = getClassNameFromJars(((URLClassLoader) loader).getURLs(), packageName, isRecursion);
}
return classNames;
}
/**
* 從項(xiàng)目文件獲取某包下有類
*
* @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;
}
/**
* @param jarEntries
* @param packageName
* @param isRecursion
* @return
*/
private static Set<String> getClassNameFromJar(Enumeration<JarEntry> jarEntries, String packageName,
boolean isRecursion) {
Set<String> classNames = new HashSet();
while (jarEntries.hasMoreElements()) {
JarEntry jarEntry = jarEntries.nextElement();
if (!jarEntry.isDirectory()) {
/*
* 這里是為了方便楼吃,先把"/" 轉(zhuǎn)成 "." 再判".class" 的做法可能會(huì)有bug
* (FIXME: 先把"/" 轉(zhuǎn)成 "." 再判".class" 的做法可能會(huì)有bug)
*/
String entryName = jarEntry.getName().replace("/", ".");
if (entryName.endsWith(".class") && !entryName.contains("$") && entryName.startsWith(packageName)) {
entryName = entryName.replace(".class", "");
if (isRecursion) {
classNames.add(entryName);
} else if (!entryName.replace(packageName + ".", "").contains(".")) {
classNames.add(entryName);
}
}
}
}
return classNames;
}
/**
* 從所有jar中搜索該包,并獲取該包下所有類
*
* @param urls URL集合
* @param packageName 包名
* @param isRecursion 是否遞歸遍歷子包
* @return 類的完整名稱
*/
private static Set<String> getClassNameFromJars(URL[] urls, String packageName, boolean isRecursion) {
Set<String> classNames = new HashSet<>();
for (int i = 0; i < urls.length; i++) {
String classPath = urls[i].getPath();
//不必搜索classes文件夾妄讯?
if (classPath.endsWith("classes/")) {
continue;
}
JarFile jarFile = null;
try {
jarFile = new JarFile(classPath.substring(classPath.indexOf("/")));
} catch (IOException e) {
e.printStackTrace();
}
if (jarFile != null) {
classNames.addAll(getClassNameFromJar(jarFile.entries(), packageName, isRecursion));
}
}
return classNames;
}
}
- BeanProxy: 只有實(shí)現(xiàn)接口的類孩锡,可以jdk動(dòng)態(tài)代理,代理目的為了增強(qiáng)功能
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
代理對象,jdk動(dòng)態(tài)代理
*/
public class BeanProxy implements InvocationHandler {
//被代理的對象
private Object bean;
//構(gòu)造函數(shù)亥贸,初始化被代理的對象
public BeanProxy(Object bean) {
this.bean = bean;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理對象執(zhí)行方法前..........:" + method.getName());
Object result = method.invoke(bean, args);
System.out.println("代理對象執(zhí)行方法后............:" + method.getName());
return result;
}
}
- 驗(yàn)證測試
- controller
/**
* 模擬controller
*/
@MyBean("userController")
public class UserController {
@AutoInject("userService")
private IUserService userService;
public String getUser(Long id) {
return userService.getUser(id);
}
}
- service
public interface IUserService {
String getUser(Long id);
}
@MyBean("userService")
public class UserServiceImpl implements IUserService{
@Override
public String getUser(Long id) {
return "當(dāng)前用戶id:" + id;
}
}
- main
public class Main {
public static void main(String[] args) {
//定義掃描包路徑
String basePackage = "com.lg.ioc.example";
//初始化bean工廠
BeanFactory beanFactory = new BeanFactory(basePackage);
//獲取bean
UserController userController = beanFactory.getBean(UserController.class);
//調(diào)用bean中方法
String user = userController.getUser(1l);
System.out.println(user);
}
}
- 結(jié)果打印
代理對象執(zhí)行方法前..........:getUser
代理對象執(zhí)行方法后............:getUser
當(dāng)前用戶id:1
總結(jié)
- 注解@Target和Retention,使用作用
注解@Target和@Retention可以用來修飾注解躬窜,是注解的注解,稱為元注解炕置。
@Target : Target翻譯中文為目標(biāo)荣挨,即該注解可以聲明在哪些目標(biāo)元素之前,也可理解為注釋類型的程序元素的種類朴摊。
ElementType.PACKAGE:該注解只能聲明在一個(gè)包名前默垄。
ElementType.ANNOTATION_TYPE:該注解只能聲明在一個(gè)注解類型前。
ElementType.TYPE:該注解只能聲明在一個(gè)類前甚纲。
ElementType.CONSTRUCTOR:該注解只能聲明在一個(gè)類的構(gòu)造方法前口锭。
ElementType.LOCAL_VARIABLE:該注解只能聲明在一個(gè)局部變量前。
ElementType.METHOD:該注解只能聲明在一個(gè)類的方法前介杆。
ElementType.PARAMETER:該注解只能聲明在一個(gè)方法參數(shù)前鹃操。
ElementType.FIELD:該注解只能聲明在一個(gè)類的字段前。
@Retention :Retention 翻譯成中文為保留春哨,可以理解為如何保留荆隘,即告訴編譯程序如何處理,也可理解為注解類的生命周期悲靴。
RetentionPolicy.SOURCE : 注解只保留在源文件臭胜,當(dāng)Java文件編譯成class文件的時(shí)候莫其,注解被遺棄;
RetentionPolicy.CLASS : 注解被保留到class文件耸三,但jvm加載class文件時(shí)候被遺棄乱陡,這是默認(rèn)的生命周期;
RetentionPolicy.RUNTIME : 注解不僅被保存到class文件中仪壮,jvm加載class文件之后憨颠,仍然存在;
這3個(gè)生命周期分別對應(yīng)于:Java源文件(.java文件) ---> .class文件 ---> 內(nèi)存中的字節(jié)碼积锅。
那怎么來選擇合適的注解生命周期呢爽彤?
首先要明確生命周期長度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用缚陷。
工廠模式适篙,代理模式
jdk動(dòng)態(tài)代理原理,什么情況才可以使用
它是在運(yùn)行時(shí)生成的一種類箫爷,在生成它時(shí)嚷节,必須提供一組 interfaces 給它,然后該類就會(huì)實(shí)現(xiàn)這些 interface虎锚。動(dòng)態(tài)代理類就是 Proxy硫痰,它不會(huì)替你干任何事,在生成它的時(shí)候窜护,也必須提供一個(gè) handler效斑,由它接管實(shí)際的工作。
jdk動(dòng)態(tài)代理原理名稱注入和類型注入
存入IOC容器時(shí)柱徙,存儲(chǔ)兩份缓屠,一份名稱一份類型,獲取bean時(shí)坐搔,兩者選其一