最近我手写了一个spring框架,spring框架主要运用了反射和动态代理的方式,我们都知道spring框架主要的两个功能是IOC和AOP,我今天就说说控制反转IOC。这个项目上我运用了注解,xml文件解析,类的反射机制等技术。
我的手写的spring框架项目架构:
首先,根据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();
}
}
我感觉这个类的代码中,分为三部分,第一部分是扫描包,获取类对象,我感觉对包的扫描,解析有点难,但挺值得学习的;第二部分,给类对象实例化,主要运用的知识是注解和反射,只要有点反射基础就应该不难;第三部分,是对象之间的依赖注入,自己看吧。我是一个菜鸟,有写错的地方,希望大神多多指点。