最近我手写了一个spring框架,spring框架主要运用了反射和动态代理的方式,我们都知道spring框架主要的两个功能是IOC和AOP,我今天就说说控制反转IOC。这个项目上我运用了注解,xml文件解析,类的反射机制等技术。

我的手写的spring框架项目架构:

spring框架的crud代码自动生成 spring框架实现_xml

首先,根据spring中的注解创建4个自定义的注解

创建MyAutowired注解,该注解主要用于参数注入,代码如下:

package com.springframework.org.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(ElementType.FIELD)
public @interface MyAutowired {
	String value() default "";
}

MyController注解,用于标记Controller层类,代码如下:

package com.springframework.org.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(ElementType.TYPE)
public @interface MyController {
	String value() default "";
}

MyService注解,用于标记Service层类,代码如下:

package com.springframework.org.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(ElementType.TYPE)
public @interface MyService {
	String value() default "";
}

MyRepository注解,用于标记Dao层类,代码如下:

package com.springframework.org.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(ElementType.TYPE)
public @interface MyRepository {
	String value() default "";
}

 

然后,创建web中三层目录,controller,dao,service

创建UserDao接口:

package com.chen.dao;

public interface UserDao {
	public void saveUser();
}

创建UserDaoImpl实现类:

package com.chen.dao;

import com.springframework.org.annotation.MyRepository;

@MyRepository
public class UserDaoImpl implements UserDao {

	@Override
	public void saveUser() {
		System.out.println("保存用户");
	}

}

创建UserService接口:

package com.chen.service;

public interface UserService {
	public void saveUser();
}

创建UserService接口实现类UserServiceImpl,代码如下:

package com.chen.service;

import com.chen.dao.UserDao;
import com.springframework.org.annotation.MyAutowired;
import com.springframework.org.annotation.MyService;

@MyService
public class UserServiceImpl implements UserService {
	@MyAutowired
	private UserDao userDao;
	@Override
	public void saveUser() {
		userDao.saveUser();
	}

}

创建UserController,代码如下:

package com.chen.controller;

import com.chen.service.UserService;
import com.springframework.org.annotation.MyAutowired;
import com.springframework.org.annotation.MyController;

@MyController("controller")
public class UserController {
	@MyAutowired
	private UserService userService;
	
	public void saveUser(){
		userService.saveUser();
	}
}

相应的注解和类都实现好了,现在就是创建xml文件ApplicationContext.xml,也就是整个项目的管理文件

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
	<component-scan base-package="com.chen"></component-scan>
</beans>

现在就是需要个类来解析xml文件获取相对应包下的被注解标记的类

在com.springframework.org.handler.resolver包下创建XMLConfiguration类用于解析ApplicationContext.xml文件,代码如下:

package com.springframework.org.handler.resolver;

import java.io.IOException;
import java.io.InputStream;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

public class XMLConfiguration {
	private static final String CONFIG_PATH = "resource/ApplicationContext.xml";
	
	public String getScanBasePackage(){
		String basePackageString = null;
		
		InputStream is = this.getClass().getClassLoader().getResourceAsStream(CONFIG_PATH);
		SAXReader saxReader = new SAXReader();
		
		try {
			Document document = saxReader.read(is);
			
			if(null != document){
				Element rootElement = document.getRootElement();
				Element element = rootElement.element("component-scan");
				//判断是否配置扫描
				if(null != element){
					basePackageString = element.attributeValue("base-package");
				}
			}else{
				System.out.println("获取配置文件失败!");
			}
			
		} catch (DocumentException e) {
			e.printStackTrace();
		}finally {
			try {
				is.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		return basePackageString;
	}
}

接下来就是最主要的内容了,本框架的管理器,创建MySpringApplicationContext类,在代码中有注解说明,在此就不多说了,代码如下:

package com.springframework.org.handler;

import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.chen.controller.UserController;
import com.springframework.org.annotation.MyAutowired;
import com.springframework.org.annotation.MyController;
import com.springframework.org.annotation.MyRepository;
import com.springframework.org.annotation.MyService;
import com.springframework.org.handler.resolver.XMLConfiguration;


public class MySpringApplicationContext {
	//定义一个线程安全的集合,存放类对象
	public static List<Class<?>> basePackageMappingToClass = Collections.synchronizedList(new ArrayList<>());
	//定义一个线程安全的集合,存储别名和类的实例映射
	public static Map<String, Object> aliasMappingInstance = Collections.synchronizedMap(new HashMap<>());
	private XMLConfiguration xmlConfiguration = new XMLConfiguration();
	public MySpringApplicationContext(){
		//扫描对应包下的被标记的类,并将被标记的类放在basePackageMappingToClass集合中
		scanBasePackage(xmlConfiguration.getScanBasePackage());
		//类的实例化,分有别名和没别名
		initAliasMappingToInstance();
		//完成对象之间的依赖注入操作
		initInstanceInjectionObject();
	}
	
	/**
	 * 完成对象之间的依赖对象装配
	 */
	private void initInstanceInjectionObject() {
		if(basePackageMappingToClass.size() == 0){
			return;
		}
		for(int i = 0;i < basePackageMappingToClass.size();i++){
			Class<?> clazz =basePackageMappingToClass.get(i);
			String instanceAlias = getBeanAlias(clazz);
			//获取需要依赖注入的对象
			Object needInjectionObj = aliasMappingInstance.get(instanceAlias);
			
			//获取当前类对应的字段
			Field[] fields = clazz.getDeclaredFields();
			
			if(null != fields && fields.length > 0){
				for(int j = 0;j < fields.length;j++){
					//定义存储需要装配的依赖对象
					Object injectObj = null;
					Field field = fields[j];
					//判断是否有@MyAutowired注解
					if(field.isAnnotationPresent(MyAutowired.class)){
						MyAutowired myAutowired = field.getAnnotation(MyAutowired.class);
						//判断value是否为空字符串
						if(!"".equals(myAutowired.value())){
							//装配别名名称
							String alias = myAutowired.value();
							//获取到装配的依赖对象
							injectObj = aliasMappingInstance.get(alias);
						}else{
							//按照默认方式装配依赖对象
							Class<?> fieldType = field.getType();
							//获取容器中所有实例的类型
							Collection<Object> values = aliasMappingInstance.values();
							Iterator<Object> iterator = values.iterator();
							while(iterator.hasNext()){
								Object object = iterator.next();
								//判断是否为同一个类型
								if(fieldType.isAssignableFrom(object.getClass())){
									//找到需要装配依赖对象的实例
									injectObj = object;
									break;
								}
							}
						}
					}
					//设置字段访问权限
					field.setAccessible(true);
					try {
						//把依赖对象装配到响应的实例中
						field.set(needInjectionObj, injectObj);
					} catch (IllegalArgumentException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
	}
	//完成别名与对象实例映射对应关系
	private void initAliasMappingToInstance() {
		if(basePackageMappingToClass.size() == 0){
			return;
		}
		for(int i = 0;i < basePackageMappingToClass.size();i++){
			Class<?> clazz = basePackageMappingToClass.get(i);
			String alias = getBeanAlias(clazz);
			
			try {
				aliasMappingInstance.put(alias,clazz.newInstance());
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 获取一个首字母小写的类名称
	 * @param className
	 * @return
	 */
	private String getLowerClassName(String className){
		String start = className.substring(0, 1).toLowerCase();
		String end  = className.substring(1);
		return start + end;
	}
	
	/**
	 * 根据类对象获取类的别名
	 */
	public String getBeanAlias(Class<?> clazz){
		//定义类的别名,默认类的别名为类的名称首字母小写
		String alias = clazz.getSimpleName();
		alias = getLowerClassName(alias);
		//获取当前类上注解
		MyController myController = clazz.getAnnotation(MyController.class);
		MyRepository myRepository = clazz.getAnnotation(MyRepository.class);
		MyService myService = clazz.getAnnotation(MyService.class);
		
		if(myController != null && !"".equals(myController.value())){
			alias = myController.value();
		}else if(myRepository != null && !"".equals(myRepository.value())){
			alias = myRepository.value();
		}else if(myService != null && !"".equals(myService.value())){
			alias = myService.value();
		}
		
		return alias;
	}
	private void scanBasePackage(String scanBasePackage) {
		// TODO Auto-generated method stub
		URL url = this.getClass().getClassLoader().getResource(scanBasePackage.replaceAll("\\.", "/"));
		//获取当前目录下所有文件
		
		try {
			File file = new File(url.toURI());
			file.listFiles(new FileFilter() {
				
				@Override
				public boolean accept(File pathname) {
					//判断是否为目录
					if(pathname.isDirectory()){
						scanBasePackage(scanBasePackage + "."+ pathname.getName());
					}else{
						//获取当前类的类路径
						String classPath = scanBasePackage + "." + pathname.getName().replaceAll("\\.class", "");
						//通过类的路径获取类的对象
						try {
							Class<?> clazz = this.getClass().getClassLoader().loadClass(classPath);
							//判断类上面是否有@MyController,@MyRepository,@MyService注解
							if(clazz.isAnnotationPresent(MyController.class)
									|| clazz.isAnnotationPresent(MyRepository.class)
									|| clazz.isAnnotationPresent(MyService.class)){
								//把MySpring管理的类放在集合中
								basePackageMappingToClass.add(clazz);
							}
						} catch (ClassNotFoundException e) {
							e.printStackTrace();
						}
						
					}
					return false;
				}
			});
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 根据对象实例别名获取实例
	 * @param args 
	 */
	public Object getBean(String name){
		return aliasMappingInstance.get(name);
	}
	public static void main(String[] args) {
		MySpringApplicationContext applicationContext = new MySpringApplicationContext();
		
		UserController controller = (UserController)applicationContext.getBean("controller");
		controller.saveUser();
	}
}

我感觉这个类的代码中,分为三部分,第一部分是扫描包,获取类对象,我感觉对包的扫描,解析有点难,但挺值得学习的;第二部分,给类对象实例化,主要运用的知识是注解和反射,只要有点反射基础就应该不难;第三部分,是对象之间的依赖注入,自己看吧。我是一个菜鸟,有写错的地方,希望大神多多指点。