public final class PropsUtil {
/**
* 获取文件流,转成properti map内存
*/
public static Properties loadProps(String fileName){
InputStream inputStream=Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
Properties properties=new Properties();
properties.load(inputStream);
}
/**
* 通过key 获取对应value
*/
public static String getPropertie(Properties properties,String key){
String value="";
if (properties.contains(key)){
value=properties.getProperty(key);
}
return value;
}
}
/**
* @author zhongailing
* @version V1.0
* @Description: 通过url加载对应类 如同scan配置 传入最基础的包名,通过包名装配初始化所有bean,
* @date Created in 2018-11-5 15:51
* 1.根据包名获取(文件路径),get类.class文件
* 2.传入loadClass方法,返回类
*/
public class ClassLoadHandler {
/**
* 获取类加载期
* @return
*/
public static ClassLoader getClassLoader(){
return Thread.currentThread().getContextClassLoader();
}
/**
* 通过className加载类
* @param className
* @param isInitialized
* @return
*/
public static Class> loadClass(String className,Boolean isInitialized){
Class> cls = null;
try {
//isInitialized标识是否初始化(执行静态代码块,new对象)
cls=Class.forName(className,isInitialized,getClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return cls;
}
/**
* 获取该包名下的所有类
* @param packageName
* @return
*/
public static Set> getClassSet(String packageName){
/**
* 1. 获取指定包名下的的所有类
* 2. 根据包名将其转换为文件路径
* 3. 读取class文件或jar包
* 4. 获取指定的类名去加载类
*/
Set> classSet = new HashSet<>();
try {
Enumeration urls = getClassLoader().getResources(packageName.replace(".", "/"));
while (urls.hasMoreElements()){
URL url = urls.nextElement();
String protocol = url.getProtocol(); //获取此 URL 的协议名称。
if(protocol.equals("file")){
// %20 表示file协议?
String packagePath = url.getPath().replaceAll("%20", "");
addClass(classSet,packagePath,packageName);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classSet;
}
/**
* 如果是文件,就根据包名 和 文件名 组成类的全限定名称,然后 加载类
* @param classSet
* @param packagePath 文件(夹)的绝对路径
* @param packageName 和当前文件(夹) 对应的包名
*/
public static void addClass(Set> classSet,String packagePath,String packageName){
File[] files = new File(packagePath).listFiles(file -> {
// 只需要 文件并且是.class的文件,或则是目录 都返回true
return file.isFile() && file.getName().endsWith(".class") || file.isDirectory();
});
for (File file : files) {
String fileName = file.getName();
if(file.isFile()){ // 是指定的文件 就获取到全限定类名 然后装载它
String className = fileName.substring(0, fileName.lastIndexOf(".")); // 把.class后最截取掉
if(StringUtils.isNotBlank(packageName)){
className = packageName + "." + className; // 根据包名 + 文件名 得到这个类的全限定名称,
}
Class> cls = loadClass(className, false);
classSet.add(cls);
}else { // 是文件 就递归自己. 获取 文件夹的绝对路径,和 当前文件夹对应的 限定包名.方便 文件里面直接使用
String subPackagePath= fileName;
if(StringUtils.isNotBlank(subPackagePath)){
subPackagePath = packagePath + "/" + subPackagePath; // 第一次:由基础包名 得到绝对路径,再加上当前文件夹名称 = 当前文件夹的绝对路径
}
subPackagePath = file.getAbsolutePath(); // 该方法获得文件的绝对路径.和上面的代码效果是一致的
String subPackageName = fileName;
if(StringUtils.isNotBlank(subPackageName)){
subPackageName = packageName + "." + subPackageName; // 第一次: 基础包名 加文件夹名称 组合成 当前包名 +
}
addClass(classSet,subPackagePath, subPackageName);
}
}
}
public static void main(String[] args) {
getClassSet("java/ioc");
}
增加按照注解区分@service@controller 不同bean的kv(class-obj)结构
/**
* @author zhongailing
* @version V1.0
* @Description: 在装配的class中区分出@Service@Controller对应的bean
* @date Created in 2018-11-5 16:15
*/
public final class AnnotationMapHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(PropsUtil.class);
private static final Set> CLASS_SET;
static{
LOGGER.debug("是否被加载了两次");
CLASS_SET = ClassLoadHandler.getClassSet(PropsUtil.getPropertie(null,"scan-package-name"));
}
public static Set> getClassSet() {
return CLASS_SET;
}
/**
* 获取所有Services类型的注解类
* @return
*/
public static Set> getServiceClassSet(){
Set> serviceClassSet = new HashSet<>();
for (Class> c : CLASS_SET) {
if(c.isAnnotationPresent(Services.class)){ // 是否存在 services注解
serviceClassSet.add(c);
};
}
return serviceClassSet;
}
/**
* 获取所有Controller类型的注解类
* @return
*/
public static Set> getControllerClassSet(){
Set> controllerClassSet = new HashSet<>();
for (Class> c : CLASS_SET) {
if(c.isAnnotationPresent(Controller.class)){
controllerClassSet.add(c);
};
}
return controllerClassSet;
}
/**
* 获取所有的 bean : controller 和 Services 注解的类
* 类似 component =所有controller、service、repository bean
* @return
*/
public static Set> getBeanClassSet(){
Set> beanClassSet = new HashSet<>();
beanClassSet.addAll(getControllerClassSet());
beanClassSet.addAll(getServiceClassSet());
return beanClassSet;
}
/**
* 维护静态map 通过类名获取bean对象
* bean初始化 即加载包下所有class,add到map中
*/
public class BeanContainer {
private static final Map, Object> BEAN_MAP = new HashMap<>();
static {
Set> beanClassSet = AnnotationMapHandler.getClassSet();
for (Class> c : beanClassSet) {
BEAN_MAP.put(c, ReflectionHandler.newInstance(c)); //创建所有class的实例(自己定义的注解类)
}
}
/**
* bean 容器
**/
public static Map, Object> getBeanMap() {
return BEAN_MAP;
}
/**
* 获取bean
**/
public static T getBean(Class cls) {
if (!BEAN_MAP.containsKey(cls)) {
throw new RuntimeException("can not get bean by class : " + cls);
}
return (T) BEAN_MAP.get(cls);
}
/**
* 添加bean 实例
**/
public static void setBean(Class> cls, Object obj) {
BEAN_MAP.put(cls, obj);
}
}
IOC 反射将bean 通过setField 设置到使用bean的成员变量中
/**
* @author zhongailing
* @version V1.0
* @Description: 通过遍历BeanContainer里的所有cls,初始化对应bean实例,然后通过反射setField把注入的bean set到类中成为其成员变量。
* @date Created in 2018-11-5 16:37
*/
public class IocHandler {
static {
Map, Object> beanMap = BeanContainer.getBeanMap(); //框架需要管理的bean映射
if (MapUtils.isNotEmpty(beanMap)) {
for (Map.Entry, Object> ent : beanMap.entrySet()) {
Class> beanCls = ent.getKey();
Object beanInstance = ent.getValue();
// 获取该class所有的成员属性,包括公共、保护、默认(包)访问和私有字段,但不包括继承的字段
Field[] beanFields = beanCls.getDeclaredFields();
for (Field beanField : beanFields) {
//判断 该字段是否 包含 inject注解
if (beanField.isAnnotationPresent(Inject.class)) {
Class> beanFieldType = beanField.getType(); //声明类型 成员变量的Class
Object beanFieldInstance = beanMap.get(beanFieldType); // 获取该类型的实例
if (beanFieldInstance != null) {
ReflectionHandler.setField(beanInstance, beanField, beanFieldInstance); // 把对应的成员变量属性 赋值
}
}
}
}
}
}
}
反射处理类
/**
* @author zhongailing
* @version V1.0
* @Description: 通过ClassLoaderHandler 加载的class类来初始化对象
* java反射 clazz.newInstance
* @date Created in 2018-11-5 16:23
*/
public class ReflectionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionUtil.class);
/** 创建实例 **/
public static Object newInstance(Class> cls){
Object ins = null;
try {
ins = cls.newInstance();
} catch (Exception e) {
LOGGER.error("new instance failure");
throw new RuntimeException(e);
}
return ins;
}
public static Object newInstance(String className){
Object ins = null;
try {
Class> cls = Class.forName(className);
ins = cls.newInstance();
} catch (Exception e) {
LOGGER.error("new instance failure");
throw new RuntimeException(e);
}
return ins;
}
/**
* 调用方法
* @param obj 实例
* @param method 方法
* @param args 参数
* @return
*/
public static Object invokeMethod(Object obj, Method method, Object...args){
method.setAccessible(true); //取消访问权限检查
Object result = null;
try {
result = method.invoke(obj, args);
} catch (Exception e) {
LOGGER.error("invoe method fail",e);
throw new RuntimeException(e);
}
return result;
}
/**
* 调用不带参数的方法
* @param obj
* @param method
* @return
*/
public static Object invokeMethod(Object obj,Method method){
method.setAccessible(true); //取消访问权限检查
Object result = null;
try {
result = method.invoke(obj);
} catch (Exception e) {
LOGGER.error("invoe method fail",e);
throw new RuntimeException(e);
}
return result;
}
/**
* 设置成员变量
* @param obj
* @param field
* @param value
*/
public static void setField(Object obj, Field field, Object value){
try {
field.setAccessible(true); //取消访问权限检查
field.set(obj,value);
} catch (Exception e) {
LOGGER.error("set Field fail",e);
throw new RuntimeException(e);
}
}
}
最后流程总结,别人家的图一个意思