为什么要实现这个功能
闲来无事,就想着玩玩Spring的类注入实现,本文只为了更好的学习和理解Spring的IOC+DI实现原理,不用于项目实践,有不对的欢迎指正,不喜请绕行。
实现目的
在不引入Spring框架的情况下,实现在业务类中自动注入功能接口
不说废话直接来干的!
实现
思路
- 扫描此包下所有类文件,以获取类全名列表
- 通过反射将所有标记MyService注解的类放入容器中
- 再通过反射将对类中标记MyAutowired注解的属性注入实现类,对于一个接口两个实现类情况,此处只是通过名称来获取实现类
创建Annotation
- MyService 用于注解类
package com.icodesoft.service;
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 {
}
- MyAutowired 用于注解属性
package com.icodesoft.service;
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 name();
}
创建功能接口以及实现类
- 功能接口
package com.icodesoft.service;
public interface IService {
String getName();
}
- 实现类user
package com.icodesoft.service;
@MyService
public class UserService implements IService {
@Override
public String getName() {
return "我User Service类已成功注入啦!";
}
}
- 实现类proudct
package com.icodesoft.service;
@MyService
public class ProductService implements IService {
@Override
public String getName() {
return "我Product Service类已成功注入啦!";
}
}
创建业务类
- 业务类TestService
package com.icodesoft.service;
@MyService
public class TestService {
@MyAutowired(name = "productService")
private IService service;
public String getServiceName() {
return this.service.getName();
}
}
到目前为此我们的所有业务类只依赖于功能接口而非实现类,下面我们来实现怎样把功能实现类注入到 TestService 类 的private IService service;属性中
创建类MyFactoryBean,此类我将类容器以及注入功能全放这个类中,看客们可以奖其他分开实现功能单一
package com.icodesoft;
import com.icodesoft.service.MyAutowired;
import com.icodesoft.service.MyService;
import org.apache.commons.lang3.ArrayUtils;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.*;
/**
* 扫描此包下所有类文件,以获取类全名列表
* 通过反射将所有标记MyService注解的类放入容器中
* 再通过反射将对类中标记MyAutowired注解的属性注入实现类
*/
public class MyFactoryBean {
public static Map<String, Object[]> allBean = new HashMap<>();
private static String packagePath; // 包全名, 如 com.icodesoft.service
public static List<String> scan(String packageName) throws Exception {
packagePath = packageName.replaceAll("\.", "/");
List<String> list = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> urls = classLoader.getResources(packagePath);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
if (url.getProtocol().equals("file")) {
// 扫描此包下所有类文件,以获取类全名列表
list = scanFiles(url.getPath(), packageName);
}
}
System.out.println("*******: " + list);
// 通过反射将所有标记MyService注解的类放入容器中
initBeans(list);
// 再通过反射将对类中标记MyAutowired注解的属性注入实现类
di();
return list;
}
private static void di() throws Exception {
for (String className : allBean.keySet()) {
Class<?> clzz = Class.forName(className);
if (clzz.isInterface()) continue;
Object o = allBean.get(className)[0];
Field[] declaredFields = clzz.getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
if (field.isAnnotationPresent(MyAutowired.class)) {
MyAutowired annotation = field.getAnnotation(MyAutowired.class);
String name = annotation.name();
Class<?> fieldType = field.getType();
Object[] beans = allBean.get(fieldType.getName());
if (fieldType.isInterface() && beans.length > 1) {
if (name == null || name.trim().isEmpty())
throw new ClassNotFoundException("当接口有多个实现类时,必须指定name值");
for (Object bean : beans) {
if (name.equalsIgnoreCase(bean.getClass().getSimpleName())) {
field.set(o, bean);
break;
}
}
} else {
field.set(o, beans[0]);
}
}
}
}
}
private static void initBeans(List<String> classNames) {
for (String className : classNames) {
try {
Class<?> clzz = Class.forName(className);
if (clzz.isInterface() || clzz.isAnnotation() || Modifier.isPrivate(clzz.getDeclaredConstructor().getModifiers()))
continue;
if (clzz.isAnnotationPresent(MyService.class)) {
Object object = clzz.getDeclaredConstructor().newInstance();
Class<?>[] interfaces = clzz.getInterfaces();
if (interfaces.length > 0) {
for (Class<?> classInterface : interfaces) {
if (allBean.containsKey(classInterface.getName())) {
Object[] objects = allBean.get(classInterface.getName());
allBean.put(classInterface.getName(), ArrayUtils.add(objects, object));
} else {
allBean.put(classInterface.getName(), new Object[]{object});
}
}
}
allBean.put(className, new Object[]{object});
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
// 扫描此包下所有类文件,以获取类全名列表
private static List<String> scanFiles(String path, String basePkg) {
File direc = new File(path);
List<String> classNames = new ArrayList<>();
File[] files = direc.listFiles();
if (null == files) {
return classNames;
}
for (int i = 0; i < files.length; ++i) {
File file = files[i];
if (file.isDirectory()) {
List<String> list = scanFiles(file.getAbsolutePath(), basePkg + "." + file.getName());
classNames.addAll(list);
} else if (file.getName().endsWith(".class")) {
String className = file.getName().substring(0, file.getName().lastIndexOf("."));
if (-1 != className.lastIndexOf("$")) {
continue;
}
String result = basePkg + "." + className;
classNames.add(result);
}
}
return classNames;
}
}
我们运行起来玩一玩看能否成功
public class DemoApplication {
static {
try {
MyFactoryBean.scan("com.icodesoft.service");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
System.out.println(": " + MyFactoryBean.allBean);
TestService testService = (TestService) MyFactoryBean.allBean.get(TestService.class.getName())[0];
String serviceName = testService.getServiceName();
System.out.println("==================>serviceName: " + serviceName);
}
}
结果
可以看到程序运行正常并已成功注入功能类