目录
1.框架具备的基本能力
2.第一步创建注解
2.提取注解标记的对象
首先指定范围,指定范围是由用户传入包的名字。
根据传入的包的名字解析资源。
原因:
获取项目的发布的实际路径,拿到一个com.imooc是拿不到实际发布路径的
类加载器:能定位到加载的资源
获取类加载器:
通过类加载器加载相应的资源:这些资源包括了哪些呢,这个方法返回值是URL
URL:
完成extractPackageClass方法:
根据不同的资源类型,采用不同的方式获取资源集合,
过滤出当前目录下的文件夹
package org.silmpleframework.utils;
import com.sun.xml.internal.ws.util.StringUtils;
import java.io.File;
import java.io.FileFilter;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import org.apache.log4j.Logger;
/**
* Created by wangxiang on 2020/11/15
*/
public class ClassUtils {
private static final Logger LOGGER = Logger.getLogger(ClassUtils.class);
/**
* 加不加?都无所谓,主要为了避免某些类型转换是的unchecked错误
*
* @param packageName 传入的包名 com.wx
* @return
*/
public static Set<Class<?>> extractPackageClass(String packageName) {
//1.获取到类的加载器
ClassLoader classLoader = getClassLoader();
//2.通过类加载器获取到类加载的资源 (接收的包名是以/来区分)
URL url = classLoader.getResource(packageName.replace(".", "/"));
if (url == null) {
LOGGER.warn("unable to retrieve anything for packageName:" + packageName);
return null;
}
//3.根据不同的资源类型采用不用的方式获取资源的集合 (首先Class放在目录下面)
Set<Class<?>> classSet = new HashSet<>();
if ("file".equals(url.getProtocol())) {
File packageDirectory = new File(url.getPath());
// 抽取文件夹下的Class类
extractClassFile(classSet, packageDirectory, packageName);
}
return classSet;
}
private static void extractClassFile(Set<Class<?>> classSet, File packageDirectory, String packageName) {
//遍历文件夹,拿到Class类
if (!packageDirectory.isDirectory()) {
return;
}
//过滤出包路劲下的文件夹和class
File[] files = packageDirectory.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
if (pathname.isDirectory()) {
return true;
} else {
//如果是文件,判断是不是class文件
String absolutePath = pathname.getAbsolutePath();
if (absolutePath.endsWith(".class")) {
//将class转换为Class类,加入到集合中
addClassToSet(absolutePath);
}
}
return false;
}
private void addClassToSet(String absolutePath) {
//1.从class文件的绝对值路径找到包含了package的类名 absolutePath: User/xx/xx/com/wx/UserDTO.class
String replace = absolutePath.replace("/", ".");
String substring = absolutePath.substring(absolutePath.indexOf(replace));
//包名加类名
String rePackageName = substring.substring(0, substring.lastIndexOf("."));
//2.利用反射生成Class类
try {
Class<?> aClass = Class.forName(rePackageName);
classSet.add(aClass);
} catch (ClassNotFoundException e) {
LOGGER.error(e);
}
}
});
//foreach之前一定要判空
if (files != null) {
for (File file : files) {
extractClassFile(classSet, file, packageName);
}
}
}
/**
* 获取当前线程的类加载器,获取到当前程序的资源信息
*
* @return
*/
public static ClassLoader getClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
}
3.实现容器
容器需要使用单例来实现:
使用饿汉模式,懒汉模式 但是单例不是足够的安全,使用反射getDeclaredConstructors()
方法,即可获得类private修饰的无参构造函数,攻破私有构造函数去创建实例,:
使用枚举实现单例模式:使用枚举创建单例模式可以防住反射的冲击
枚举根本没有无参构造函数,所以反射自然无法攻破。枚举有有参的构造函数,但是通过有参的构造函数去攻破依然不行。
我们反编译代码看看为什么不行:安装jad工具
在类加载的时候,枚举就已经被创建出来了,所以这个模式也是属于恶汉模式
除了反射,序列化也能入侵单例,怎么入侵呢?就是将创建出来的单例写入到文件里面,再逆序列化创建单例,这时很有可能出现两个不同的实例。
线程安全且能抵御反射,序列化入侵的容器:
package org.silmpleframework.core;
/**
* Created by wangxiang on 2021/8/22
*/
public class BeanContainer {
public static BeanContainer getInstance() {
return HolderContainer.HOLDER.instance;
}
private BeanContainer() {
}
//容器的实例化放到了枚举的无参构造函数里面去了,这个在类加载的时候执行
private enum HolderContainer {
HOLDER;
private BeanContainer instance;
HolderContainer() {
instance = new BeanContainer();
}
}
}
1.容器的载体:
2.容器的加载
容器的操作方式的实现
package org.silmpleframework.core;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.log4j.Logger;
import org.silmpleframework.core.annotation.WXComponent;
import org.silmpleframework.core.annotation.WXController;
import org.silmpleframework.core.annotation.WXRepository;
import org.silmpleframework.core.annotation.WXService;
import org.silmpleframework.utils.ClassUtils;
import org.silmpleframework.utils.ValidationUtils;
/**
* Created by wangxiang on 2021/8/22
*/
public class BeanContainer {
private static final Logger LOGGER = Logger.getLogger(BeanContainer.class);
/**
* 判断容器是否已经被加载过
*/
private boolean loaded = false;
public boolean isLoaded() {
return this.loaded;
}
/**
* 存放所有被配置标记的目标对象的Map
*/
private final Map<Class<?>, Object> beanMap = new ConcurrentHashMap<>();
private static final List<Class<? extends Annotation>> CLASS_MAP =
Arrays.asList(WXController.class, WXService.class, WXComponent.class, WXRepository.class);
public static BeanContainer getInstance() {
return HolderContainer.HOLDER.instance;
}
private BeanContainer() {
}
//容器的实例化放到了枚举的无参构造函数里面去了,这个在类加载的时候执行
private enum HolderContainer {
HOLDER;
private BeanContainer instance;
HolderContainer() {
instance = new BeanContainer();
}
}
/**
* 扫描加载所有的Bean进Map中
*
* @param packageName
*/
public synchronized void loadBeans(String packageName) {
if (loaded) {
LOGGER.info("已经被加载过");
return;
}
Set<Class<?>> classSet = ClassUtils.extractPackageClass(packageName);
if (ValidationUtils.classSetEmpty(classSet)) {
LOGGER.warn("extract nothing from packageName:" + packageName);
return;
}
for (Class<?> aClass : classSet) {
for (Class<? extends Annotation> annotation : CLASS_MAP) {
//如果存在标记的注解,将目标Class类和他的实例加入容器中
if (aClass.isAnnotationPresent(annotation)) {
beanMap.put(aClass, ClassUtils.newInstance(aClass, true));
}
}
}
loaded = true;
}
//对容器的增删操作
public Object addBean(Class<?> aClass, Object bean) {
return beanMap.put(aClass, bean);
}
public Object removeBean(Class<?> aClass) {
return beanMap.remove(aClass);
}
public Object getBean(Class<?> aClass) {
return beanMap.get(aClass);
}
public Set<Class<?>> getClasses() {
return beanMap.keySet();
}
public Set<Object> getBeans() {
return new HashSet<>(beanMap.values());
}
/**
* 根据注解获取对象的集合
*
* @param annotationClass
* @return
*/
public Set<Class<?>> getClassByAnnotation(Class<? extends Annotation> annotationClass) {
Set<Class<?>> classes = getClasses();
if (ValidationUtils.classSetEmpty(classes)) {
LOGGER.info("nothing in bean Map");
return Collections.EMPTY_SET;
}
Set<Class<?>> classSet = new HashSet<>();
for (Class<?> aClass : classes) {
if (aClass.isAnnotationPresent(annotationClass)) {
classSet.add(aClass);
}
}
return classSet;
}
//通过接口或者父类获取实现类或者子类的class集合,不包括其本身
public Set<Class<?>> getClassBySupper(Class<?> interfaceOrClass) {
Set<Class<?>> classes = getClasses();
if (ValidationUtils.classSetEmpty(classes)) {
LOGGER.info("nothing in bean Map");
return Collections.EMPTY_SET;
}
Set<Class<?>> classSet = new HashSet<>();
for (Class<?> aClass : classes) {
//aClass是不是interfaceOrClass的实现类或者子类
if (interfaceOrClass.isAssignableFrom(aClass) && !aClass.equals(interfaceOrClass)) {
classSet.add(aClass);
}
}
return classSet;
}
public int beanSize() {
return beanMap.size();
}
}
4.依赖注入
实现思路:只支持成员变量级别的注入
定义标签:
package org.silmpleframework.core.inject.annotation;
import java.lang.annotation.*;
/**
* Created by wangxiang on 2020/11/15
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WXAutowired {
//这个value简化Spring中Qualifier的操作
String value() default "";
}
依赖注入的步骤:
//1.获取bean容器中所有的Class对象 //2.遍历class对象的所有成员变量 //3.找出被WXAutowired标记的成员变量 //4.获取成员变量的类型 //5.z找到成员变量的实例 //6.通过反射将成员变量的实例注入到成员变量实例所在的类的实例中
package org.silmpleframework.core.inject.annotation;
import java.lang.reflect.Field;
import java.util.Set;
import org.silmpleframework.core.BeanContainer;
import org.silmpleframework.utils.ClassUtils;
import org.silmpleframework.utils.ValidationUtils;
/**
* Created by wangxiang on 2021/8/22
*/
public class DependencyInjector {
private BeanContainer beanContainer;
public DependencyInjector(BeanContainer beanContainer) {
//获取单例的容器
this.beanContainer = BeanContainer.getInstance();
}
public void doIOC() {
//1.获取bean容器中所有的Class对象
//2.遍历class对象的所有成员变量
//3.找出被WXAutowired标记的成员变量
//4.获取成员变量的类型
//5.z找到成员变量的实例
//6.通过反射将成员变量的实例注入到成员变量实例所在的类的实例中
Set<Class<?>> classes = beanContainer.getClasses();
if (ValidationUtils.classSetEmpty(classes)) {
return;
}
for (Class<?> aClass : classes) {
Field[] declaredFields = aClass.getDeclaredFields();
if (declaredFields != null || declaredFields.length > 0) {
for (Field declaredField : declaredFields) {
//找到成员变量上有WXAutowired注解的
if (declaredField.isAnnotationPresent(WXAutowired.class)) {
//拿到成员变量的类型
Class<?> fieldType = declaredField.getType();
//拿到成员上的注解,看看注解上面有没有指定注入实例的名称
WXAutowired annotation = declaredField.getAnnotation(WXAutowired.class);
String wXAutowiredValue = annotation.value();
//拿到成员变量的实例
Object fieldInstance = getFieldInstance(fieldType, wXAutowiredValue);
Object instance = beanContainer.getBean(aClass);
ClassUtils.setFiled(declaredField, instance, fieldInstance, true);
}
}
}
}
}
private Object getFieldInstance(Class<?> fieldType, String wXAutowiredValue) {
Object bean = beanContainer.getBean(fieldType);
if (bean != null) {
return bean;
} else {
//如果成员变量是接口
//接口上面没有注解,要找到他的实现类才会被标记管理起来, 所以走这里
Class<?> aClass = getImplementedClass(fieldType, wXAutowiredValue);
if (aClass != null) {
return beanContainer.getBean(aClass);
} else {
return null;
}
}
}
//获取接口的实现类
private Class<?> getImplementedClass(Class<?> fieldType, String wXAutowiredValue) {
Set<Class<?>> classBySupper = beanContainer.getClassBySupper(fieldType);
if (ValidationUtils.classSetEmpty(classBySupper)) {
return null;
}
if (classBySupper.size() == 1) {
return classBySupper.iterator().next();
} else {
if (wXAutowiredValue == null || wXAutowiredValue == "") {
throw new RuntimeException(fieldType + " are multiple implementation classes");
} else {
for (Class<?> aClass : classBySupper) {
//看看注解的值和类名字是不是相等
if (aClass.getSimpleName().equals(wXAutowiredValue)) {
return aClass;
}
}
}
}
return null;
}
}
package org.silmpleframework.utils;
import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import org.apache.log4j.Logger;
/**
* Created by wangxiang on 2020/11/15
*/
public class ClassUtils {
private static final Logger LOGGER = Logger.getLogger(ClassUtils.class);
/**
* 加不加?都无所谓,主要为了避免某些类型转换是的unchecked错误
*
* @param packageName 传入的包名 com.wx
* @return
*/
public static Set<Class<?>> extractPackageClass(String packageName) {
//1.获取到类的加载器
ClassLoader classLoader = getClassLoader();
//2.通过类加载器获取到类加载的资源 (接收的包名是以/来区分)
URL url = classLoader.getResource(packageName.replace(".", "/"));
if (url == null) {
LOGGER.warn("unable to retrieve anything for packageName:" + packageName);
return null;
}
//3.根据不同的资源类型采用不用的方式获取资源的集合 (首先Class放在目录下面)
Set<Class<?>> classSet = new HashSet<>();
if ("file".equals(url.getProtocol())) {
File packageDirectory = new File(url.getPath());
// 抽取文件夹下的Class类
extractClassFile(classSet, packageDirectory, packageName);
}
return classSet;
}
private static void extractClassFile(Set<Class<?>> classSet, File packageDirectory, String packageName) {
//遍历文件夹,拿到Class类
if (!packageDirectory.isDirectory()) {
return;
}
//过滤出包路劲下的文件夹和class
File[] files = packageDirectory.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
if (pathname.isDirectory()) {
return true;
} else {
//如果是文件,判断是不是class文件
String absolutePath = pathname.getAbsolutePath();
if (absolutePath.endsWith(".class")) {
//将class转换为Class类,加入到集合中
addClassToSet(absolutePath);
}
}
return false;
}
private void addClassToSet(String absolutePath) {
//1.从class文件的绝对值路径找到包含了package的类名 absolutePath: D:\spring-framework\silmpleframework\target\classes\org\silmpleframework\SpringRun.class
String replace = absolutePath.replace(File.separator, ".");
String substring = replace.substring(replace.indexOf(packageName));
//包名加类名
String rePackageName = substring.substring(0, substring.lastIndexOf("."));
//2.利用反射生成Class类
try {
Class<?> aClass = Class.forName(rePackageName);
classSet.add(aClass);
} catch (ClassNotFoundException e) {
LOGGER.error(e);
}
}
});
//foreach之前一定要判空
if (files != null) {
for (File file : files) {
extractClassFile(classSet, file, packageName);
}
}
}
/**
* 获取当前线程的类加载器,获取到当前程序的资源信息
*
* @return
*/
public static ClassLoader getClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
public static <T> T newInstance(Class<?> aClass, boolean b) {
try {
//这里可以直接newInstance,也可以通过无参构造有参构造来实例化
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(b);
return (T) declaredConstructor.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 给实例的某一个属性设置某个实例值
*
* @param declaredField
* @param target
* @param value
* @param accessible
*/
public static void setFiled(Field declaredField, Object instance, Object fieldInstance, boolean accessible) {
declaredField.setAccessible(accessible);
try {
declaredField.set(instance, fieldInstance);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
5.测试:
controller:
service:
DTO:
测试步骤:
//1.获得IOC容器 //2.初步加载Bean //3.执行依赖注入 //4.从容器中取出bean执行方法
依赖注入成功: