现在java开发基本就是spring的天下,某不才自己实现一个小小的ioc功能,目的并不是复制spring,而是为了更好的理解和使用spring!,本文模仿springboot只做了注解配置的。
项目结构
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qdz</groupId>
<artifactId>open_spring</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<commons.fileupload.version>1.3.3</commons.fileupload.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<!-- servlet的依赖,ioc可有可无,mvc必须要 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope>
</dependency>
<!-- 常用工具类 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons.fileupload.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<!-- 日志文件,其实用的是log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.6</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.6</version>
</dependency>
</dependencies>
</project>
常用注解,三个类代码放一起显示
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface QdzAutowired {
String value() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface QdzService {
String value() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface QdzController {
String value() default "";
}
QdzSpringApplicationContext,类似于spring的
package com.qdz.config;
import com.qdz.frameWork.mapping.AnnotationConfiguration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
public class QdzSpringApplicationContext {
//线程安全的map和list
private static Map<String,Object> concurrentHashMap = new ConcurrentHashMap<>();
private static List<Class<?>> basePackageMappingToClass = new CopyOnWriteArrayList<>();
/**
* 根据对象实例别名获取实例
* @param name
*/
public static Object getBean(String name){
return concurrentHashMap.get(name);
}
public static void run(Class<?> clazz,String[] args) {
//1.获得QdzComponentScan,得到扫描的包名集合
List<String> packageNames = AnnotationConfiguration.getPackageNames(clazz);
//2.包名集合获得Class对象集合
basePackageMappingToClass = AnnotationConfiguration.getClasses(packageNames);
//3.对象实例化
concurrentHashMap = AnnotationConfiguration.getObjects(basePackageMappingToClass);
//4.自动注入
AnnotationConfiguration.AutoDi(basePackageMappingToClass,concurrentHashMap);
}
}
AnnotationConfiguration
这个才是真正干活的类
package com.qdz.frameWork.mapping;
import com.qdz.frameWork.annotation.QdzAutowired;
import com.qdz.frameWork.annotation.QdzComponentScan;
import com.qdz.frameWork.annotation.QdzController;
import com.qdz.frameWork.annotation.QdzService;
import com.qdz.frameWork.exception.AnnotationNotExistException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
public class AnnotationConfiguration {
private static final Logger logger = LoggerFactory.getLogger(AnnotationConfiguration.class);
private static final List<Class<? extends Annotation>> lists = new ArrayList<>();
static {
lists.add(QdzController.class);
lists.add(QdzService.class);
}
/**
* 获得扫描的包名集合
* @param clazz 启动类的Class对象,clazz就是启动类上穿过来的类,类似于springboot启动类
* @return 所需扫描的包名集合
*/
public static List<String> getPackageNames(Class<?> clazz) {
List<String> propertyList = new CopyOnWriteArrayList<>();
//是否存在QdzComponentScan注解
if(!clazz.isAnnotationPresent(QdzComponentScan.class))throw new AnnotationNotExistException(clazz.getName() +"类的QdzComponentScan注解不存在");
QdzComponentScan qdzComponentScan = clazz.getAnnotation(QdzComponentScan.class);
String[] annoScans = qdzComponentScan.value();
if(annoScans==null||annoScans.length<=0)throw new AnnotationNotExistException(clazz.getName() +"类的QdzComponentScan注解不存在");
for (String s:annoScans) {propertyList.add(s);}
logger.info("扫描解析完毕");
return propertyList;
}
/**
* 根据扫描的包名集合获得Class对象集合
* @param packageNames 包名集合
* @return Class对象集合
*/
public static List<Class<?>> getClasses(List<String> packageNames){
logger.info("正在获取Class对象");
List<Class<?>> classList = new CopyOnWriteArrayList<>();
packageNames.stream().forEach(o->{
//将点换成杠,这里用了lambada只是为了熟悉,强行用
String path = o.replace(".","/");
URL url = AnnotationConfiguration.class.getClassLoader().getResource(path);
//QdzComponentScan如果是包名则会拿到url,若果是包名点类名则拿不到
if(url!=null){
File file =new File(url.getPath());
setClassList(classList,file,o);
}else{
Class<?> clazz = null;
try {
clazz = Class.forName(o);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
classList.add(clazz);
}
});
if(classList==null||classList.size()<=0)throw new AnnotationNotExistException("扫描的包不存在");
logger.info("扫描完毕");
return classList.stream().distinct().collect(Collectors.toList());
}
/**
* 将所有需要扫描的类的Class对象放进list
* @param classList
* @param file
* @param packageName
*/
private static void setClassList(List<Class<?>> classList, File file,String packageName) {
if(file.isDirectory()){
for (File f:file.listFiles()) {
setClassList(classList,f,packageName+"."+f.getName());
}
}else{
Class<?> clazz = null;
try {//全路径
if(packageName.endsWith(".class")) {
clazz = Class.forName(packageName.substring(0, packageName.lastIndexOf(".")));
classList.add(clazz);
}else {
String path = file.getName();
String pathName = packageName + "." + path.substring(0, path.lastIndexOf("."));
clazz = Class.forName(pathName);
classList.add(clazz);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
/**
* Class对象实例化
* @param classList
* @return map
*/
public static Map<String,Object> getObjects(List<Class<?>> classList){
Map<String,Object> concurrentHashMap = new ConcurrentHashMap<>();
classList.stream().forEach(o->{
setConcurrentHashMap(concurrentHashMap,o);
});
return concurrentHashMap;
}
/**
*
* @param concurrentHashMap 线程安全的map
* @param clazz Class对象
*/
private static void setConcurrentHashMap(Map<String, Object> concurrentHashMap, Class<?> clazz) {
lists.stream().forEach(o->{
Object obj = null;
String name = "";
//判断有没有注解
if(clazz.isAnnotationPresent(o)){
Object annoObj = clazz.getAnnotation(o);
try {
Method method = o.getMethod("value");
obj = clazz.getDeclaredConstructor().newInstance();
name = (String)method.invoke(annoObj);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
}
//判断注解上面有没有value,有的话作为map的key
if(StringUtils.isBlank(name)){
String key = clazz.getName().substring(clazz.getName().lastIndexOf(".")+1);
name = (new StringBuilder()).append(Character.toLowerCase(key.charAt(0))).append(key.substring(1)).toString();
logger.info("key== "+name);
}
if(obj!=null)concurrentHashMap.put(name,obj);
logger.info(clazz.getName()+"装配完毕");
}
});
}
/**
* 自动注入,解析Autowired
* @param basePackageMappingToClass
* @param concurrentHashMap
*/
public static void AutoDi(List<Class<?>> basePackageMappingToClass, Map<String, Object> concurrentHashMap) {
if(concurrentHashMap.isEmpty())return;
for (Map.Entry<String,Object> entry:concurrentHashMap.entrySet()) {
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for (Field f:fields ) {
//判断有没有QdzAutowired注解
if(!f.isAnnotationPresent(QdzAutowired.class))continue;
QdzAutowired qdzAutowired = f.getAnnotation(QdzAutowired.class);
//获得bean的名字
String beanName = qdzAutowired.value();
if(StringUtils.isBlank(beanName)){
String key = f.getType().getName().substring(f.getType().getName().lastIndexOf(".")+1);
beanName = (new StringBuilder()).append(Character.toLowerCase(key.charAt(0))).append(key.substring(1)).toString();
}
if(!f.canAccess(entry.getValue())){
f.setAccessible(true);
}
try {
Object obj1 = entry.getValue();
Object obj2 = concurrentHashMap.get(beanName);
/*if(obj2==null){//根据名称找不到,直接在根据类型找
obj2 = getBean(f.getType());
}*/
//自动注入
f.set(obj1,obj2);
} catch (IllegalAccessException e) {
e.printStackTrace();
continue;
}
}
}
}
}
Controller
package com.qdz.test.controller;
import com.qdz.frameWork.annotation.QdzAutowired;
import com.qdz.frameWork.annotation.QdzController;
import com.qdz.test.service.GoodsService;
@QdzController("asd")
public class GoodsController {
@QdzAutowired("goodsServiceImpl")
private GoodsService goodsService;
public void say(String args) {
System.out.println(goodsService.say());
}
}
Service
package com.qdz.test.service;
public interface GoodsService {
String say();
}
ServiceImpl
package com.qdz.test.service.impl;
import com.qdz.frameWork.annotation.QdzService;
import com.qdz.test.service.GoodsService;
@QdzService
public class GoodsServiceImpl implements GoodsService {
@Override
public String say() {
return "带嘎哈,我系渣渣辉";
}
}
测试方法
package com.qdz;
import com.qdz.config.QdzSpringApplicationContext;
import com.qdz.frameWork.annotation.QdzComponentScan;
import com.qdz.test.controller.GoodsController;
@QdzComponentScan("com.qdz.test")
public class RunApp {
public static void main(String[] args) {
QdzSpringApplicationContext.run(RunApp.class,args);
GoodsController goodsController = (GoodsController)QdzSpringApplicationContext.getBean("asd");
goodsController.say(null);
}
}
到这里简单的IOC依赖注入就完成了,目前存在的问题还有很多,写这个目的只是为了加深对象spring的理解和使用。
还存在的问题》
1.service是一个借口,其实现类上的注解没有加名称的时候找不到
2.一下子想不起来,代码垃圾不忍直视
github地址
原创文章,转载请注明出处!