使用Java反射模擬實(shí)現(xiàn)Spring的IoC容器的操作
下面是自定義的三個(gè)注解: @AutoWired,@Component,@ComponentScan。
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface AutoWired {}@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface Component {}@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface ComponentScan { String[] value();}容器實(shí)現(xiàn)
其中AnnotationConfigApplicationContext和ClassPathXMLApplicationContext為核心的類(lèi),其中
AnnotationConfigApplicationContext類(lèi)實(shí)現(xiàn)掃描文件和解析注解等功能。
package learn.reflection.reflect;import learn.reflection.Bootstrap;import learn.reflection.annotation.AutoWired;import learn.reflection.annotation.Component;import learn.reflection.annotation.ComponentScan;import java.io.File;import java.io.InputStream;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Map;import java.util.Properties;import java.util.Set;public class AnnotationConfigApplicationContext<T>{ //使用HaspMap存儲(chǔ)Bean private HashMap<Class,Object> beanFactory=new HashMap<>(); //獲取Bean的方法 public T getBean(Class clazz){ return (T) beanFactory.get(clazz); } String path;//編譯后的字節(jié)碼存儲(chǔ)路徑 /** * 初始化ApplicationContext,加載注解修飾的Bean到beanFactory */ public void initContextByAnnotation(){ //編譯后的項(xiàng)目根目錄:D:/idea_workplace/javaAppliTechnology/target/classes/ path = AnnotationConfigApplicationContext.class.getClassLoader().getResource('').getFile(); //查看啟動(dòng)類(lèi)Bootstrap是否有定義掃描包 ComponentScan annotation = Bootstrap.class.getAnnotation(ComponentScan.class); if (annotation!=null){ //有定義就只掃描自定義的 String[] definedPaths = annotation.value(); if (definedPaths!=null&&definedPaths.length>0){loadClassInDefinedDir(path,definedPaths); } }else{ //默認(rèn)掃描整個(gè)項(xiàng)目的目錄 System.out.println(path); findClassFile(new File(path)); } assembleObject(); } /** * 給@AutoWired修飾的屬性賦值 */ private void assembleObject(){ Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet(); //掃描所有容器中的Bean for (Map.Entry<Class, Object> entry : entries) { Object value = entry.getValue(); //獲取所有屬性 Field[] fields = value.getClass().getDeclaredFields(); for (Field field : fields) {//如果被@AutoWired注解修飾則進(jìn)行賦值A(chǔ)utoWired annotation = field.getAnnotation(AutoWired.class);if (annotation!=null){ try { field.setAccessible(true); field.set(value,beanFactory.get(field.getType())); } catch (IllegalAccessException e) { e.printStackTrace(); }} } } } /** * 掃描用戶(hù)自定義的包 * @param path * @param definedPaths */ private void loadClassInDefinedDir(String path, String[] definedPaths){ for (String definedPath : definedPaths) { //轉(zhuǎn)換成絕對(duì)路徑 String s = definedPath.replaceAll('.', '/'); String fullName=path+s; System.out.println(s); findClassFile(new File(fullName)); } } /** * 掃描項(xiàng)目中的每一個(gè)文件夾找到所有的class文件 */ private void findClassFile(File pathParent) { //路徑是否是目錄,子目錄是否為空 if (pathParent.isDirectory()) { File[] childrenFiles = pathParent.listFiles(); if (childrenFiles == null || childrenFiles.length == 0) {return; } for (File childrenFile : childrenFiles) {if (childrenFile.isDirectory()) { //遞歸調(diào)用直到找到所有的文件 findClassFile(childrenFile);} else { //找到文件 loadClassWithAnnotation(childrenFile);} } } } /** * 裝配找到的所有帶有@Component注解的類(lèi)到容器 */ private void loadClassWithAnnotation(File file) { //1.去掉前面的項(xiàng)目絕對(duì)路徑 String pathWithClass=file.getAbsolutePath().substring(path.length()-1); //2.將路徑的“/”轉(zhuǎn)化為“.”和去掉后面的.class if (pathWithClass.contains('.class')){ String fullName = pathWithClass.replaceAll('', '.').replace('.class', ''); /** * 根據(jù)獲取到的類(lèi)的全限定名使用反射將實(shí)例添加到beanFactory中 */ try {Class<?> clazz = Class.forName(fullName);//3.判斷是不是接口,不是接口才創(chuàng)建實(shí)例if (!clazz.isInterface()){ //4.是否具有@Bean注解 Component annotation = clazz.getAnnotation(Component.class); if (annotation!=null){ //5.創(chuàng)建實(shí)例對(duì)象 Object instance = clazz.newInstance(); //6.判斷是否有實(shí)現(xiàn)的接口 Class<?>[] interfaces = clazz.getInterfaces(); if (interfaces!=null&&interfaces.length>0){ //如果是有接口就將其接口的class作為key,實(shí)例對(duì)象作為value System.out.println('正在加載【'+interfaces[0].getName()+'】 實(shí)例對(duì)象:'+instance.getClass().getName()); beanFactory.put(interfaces[0],instance); }else{ System.out.println('正在加載【'+clazz.getName()+'】 實(shí)例對(duì)象:'+instance.getClass().getName()); beanFactory.put(clazz,instance); } //如果沒(méi)有接口就將自己的class作為key,實(shí)例對(duì)象作為value }} } catch (Exception e) {e.printStackTrace(); } } }}
ClassPathXMLApplicationContext類(lèi)實(shí)現(xiàn)解析xml配置文件,并裝載組件到容器中。
package learn.reflection.reflect;import java.net.URL;import java.util.HashMap;import java.util.Map;import org.jdom2.Document;import org.jdom2.JDOMException;import org.jdom2.Element;import org.jdom2.xpath.XPath;import org.jdom2.input.SAXBuilder;import java.io.File;import java.io.IOException;import java.net.URL;import java.net.URISyntaxException;import java.util.*;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.InvocationTargetException;/** * @author Hai * @date 2020/5/17 - 18:47 */public class ClassPathXMLApplicationContext{ private File file; private Map<String,Object> map = new HashMap(); public ClassPathXMLApplicationContext(String config_file) { URL url = this.getClass().getClassLoader().getResource(config_file); try { file = new File(url.toURI()); XMLParsing(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void XMLParsing() throws Exception { SAXBuilder builder = new SAXBuilder(); Document document = builder.build(file); Element root = document.getRootElement(); List elementList = root.getChildren('bean'); Iterator i = elementList.iterator(); //讀取bean節(jié)點(diǎn)的所有信息 while (i.hasNext()) { Element bean = (Element) i.next(); String id = bean.getAttributeValue('id'); //根據(jù)class創(chuàng)建實(shí)例 String cls = bean.getAttributeValue('class'); Object obj = Class.forName(cls).newInstance(); Method[] method = obj.getClass().getDeclaredMethods(); List<Element> list = bean.getChildren('property'); for (Element el : list) {for (int n = 0; n < method.length; n++) { String name = method[n].getName(); String temp = null; //找到屬性對(duì)應(yīng)的setter方法進(jìn)行賦值 if (name.startsWith('set')) { temp = name.substring(3, name.length()).toLowerCase(); if (el.getAttribute('name') != null) { if (temp.equals(el.getAttribute('name').getValue())) {method[n].invoke(obj, el.getAttribute('value').getValue()); } } }} } map.put(id, obj); } } public Object getBean(String name) { return map.get(name); }}測(cè)試實(shí)體類(lèi)User的定義:
@Componentpublic class User { private String username; private String password; public User(String username, String password) { this.username = username; this.password = password; } public User() { } //省略getter,setter方法 }
在UserServiceImpl類(lèi)中添加@Component注解,并使用@AutoWired注解注入容器中的IUerDao接口的實(shí)現(xiàn)類(lèi)UserDaoImpl。
@Componentpublic class UserServiceImpl implements IUserService { @AutoWired private IUserDao userDao; @Override public void login(User user) { System.out.println('調(diào)用UserDaoImpl的login方法'); userDao.loginByUsername(user); }}
UserDaoImpl類(lèi)同樣添加@Component注解
@Componentpublic class UserDaoImpl implements IUserDao { @Override public void loginByUsername(User user) { System.out.println('驗(yàn)證用戶(hù)【'+user.getUsername()+'】登錄'); }}
在beans.xml中配置注冊(cè)User類(lèi),文件beans.xml的內(nèi)容如下:
<?xml version='1.0' encoding='UTF-8'?><beans> <bean class='learn.reflection.entity.User'><property name='username' value='張三' /><property name='password' value='123' /> </bean></beans>
下面同時(shí)使用 AnnotationConfigApplicationContext類(lèi)和 ClassPathXMLApplicationContext類(lèi)。
Bootstrap類(lèi)作為啟動(dòng)類(lèi)添加注解@ComponentScan,指定掃描learn.reflection.dao和learn.reflection.service這兩個(gè)包。
@ComponentScan(value = {'learn.reflection.dao','learn.reflection.service'})public class Bootstrap { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.initContextByAnnotation(); UserServiceImpl userService = (UserServiceImpl) applicationContext.getBean(IUserService.class); ClassPathXMLApplicationContext xmlApplicationContext = new ClassPathXMLApplicationContext('beans.xml'); User user = (User) xmlApplicationContext.getBean('user'); System.out.println(user); userService.login(user); }}
運(yùn)行Bootstrap類(lèi),程序運(yùn)行結(jié)果如下:
learn/reflection/dao正在加載【learn.reflection.dao.IUserDao】 實(shí)例對(duì)象:learn.reflection.dao.impl.UserDaoImpllearn/reflection/service正在加載【learn.reflection.service.IUserService】 實(shí)例對(duì)象:learn.reflection.service.impl.UserServiceImplUser{username=’張三’, password=’123’}調(diào)用UserDaoImpl的login方法驗(yàn)證用戶(hù)【張三】登錄
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持好吧啦網(wǎng)。
相關(guān)文章:
1. moment轉(zhuǎn)化時(shí)間戳出現(xiàn)Invalid Date的問(wèn)題及解決2. python爬蟲(chóng)實(shí)戰(zhàn)之制作屬于自己的一個(gè)IP代理模塊3. ajax請(qǐng)求后臺(tái)得到j(luò)son數(shù)據(jù)后動(dòng)態(tài)生成樹(shù)形下拉框的方法4. Ajax返回值類(lèi)型與用法實(shí)例分析5. 解決ajax請(qǐng)求后臺(tái),有時(shí)收不到返回值的問(wèn)題6. HTML 絕對(duì)路徑與相對(duì)路徑概念詳細(xì)7. asp批量添加修改刪除操作示例代碼8. 如何在jsp界面中插入圖片9. .NET6打包部署到Windows Service的全過(guò)程10. 詳解盒子端CSS動(dòng)畫(huà)性能提升
