赞
踩
在很多时候我们会遇到需要将自己写的Class文件以Bean的方式注入到Spring容器中,交由Spring容器管理。
如果我们是一两个Class文件的话,我们可能直接指定类名就可以了,但是如果是一个package包需要我们去扫描呢?所以我们可以模拟一个Spring的包扫描原理,搞一个专门处理扫描Class 文件的方法。
通过 Thread.currentThread().getContextClassLoader().getResource("")
可以获取当前加载Class 文件的绝对路径。然后通过递归扫描文件,获取所有的Class文件。
首先需要一个工具类将我们需要加载的包文件中需要加载的Class文件都拿到。
import java.io.File; import java.util.*; /** * @author long */ public class ClassScanUtil { public static Map<String, Class> getBeans(String packageName) throws ClassNotFoundException { // TODO 将当前报名转为路径格式 List<String> strList = Arrays.asList(packageName.split("\\.")); StringBuilder stringBuilder = new StringBuilder(); strList.stream().forEach(item -> { stringBuilder.append(item + "/"); }); // 获取当前包的绝对路径 String file = Thread.currentThread().getContextClassLoader().getResource(stringBuilder.toString().substring(0, stringBuilder.length() - 1)).getFile(); Map<String, Class> classList = new HashMap<>(); // 递归获取Class文件 getAllClassList(packageName, file, classList); return classList.isEmpty() ? null : classList; } /** * 递归加载Class文件 * @param path */ private static void getAllClassList(String packageName, String path, Map<String, Class> classList) throws ClassNotFoundException { File files = new File(path); File[] listFiles = files.listFiles(); for(File file : listFiles) { // 是目录 if (file.isDirectory()) { String currentPackage = packageName + "." + file.getName(); getAllClassList(currentPackage, file.getAbsolutePath(), classList); } // 如果是.class结尾的文件 if (file.getName().endsWith(".class")) { String name = file.getName(); String fileName = name.substring(0, name.indexOf(".")); String currentPackage = packageName + "." + name.substring(0, name.indexOf(".")); Class forName = Class.forName(currentPackage); // 通过自定义的一个标志注解类,来标识是否需要被加载到Spring容器。 if (forName.isAnnotationPresent(SDKComponent.class)) { System.out.println("------- [ 读取到需要注入的Bean类 : " + currentPackage + " ] ...... "); classList.put(ChangeInitials(fileName), forName); } } } } // 将 类名的首字母小写 private static String ChangeInitials(String name) { char[] str = name.toCharArray(); if (str[0] >= 65 && str[0] <= 90) { str[0] += 32; } return String.valueOf(str); } }
SDKComponent
自定义注解类
import java.lang.annotation.*;
/**
* @author long
*/
@Target(value = {ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SDKComponent {
String name = "";
}
然后将这需要的注册的类注入到Spring容器。我们需要实现BeanDefinitionRegistryPostProcessor
, BeanDefinitionRegistryPostProcessor
继承自BeanFactoryPostProcessor
,是一种比较特殊的BeanFactoryPostProcessor
。BeanDefinitionRegistryPostProcessor
中定义的``postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)`方法 可以让我们实现自定义的注册bean定义的逻辑。
扫描文件的class文件工具类,返回的数据类型结构
import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import java.util.Map; /** * @author long */ public class BeanRegistrar implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { try { // 调用我们刚才的工具类,将扫描到的类注放到Map中 // 这里的Map是包含 需要注入Bean(class)的小写类名,具体类型,看上图。 Map<String, Class> classMap = ClassScanUtil.getBeans(URLConstants.PACKAGE_PATH); if (!classMap.isEmpty()) { classMap.forEach((key, value) -> { System.out.println("=======>>> 正在加载 " + key + " —— " + value + "...... "); BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(value); AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition(); registry.registerBeanDefinition(key, beanDefinition); }); } } catch (ClassNotFoundException e) { System.out.println("初始化 Beans 异常"); e.printStackTrace(); } } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } }
最后将这个BeanRegistrar
类注入到Spring容器,在项目启动的时候就可以自动启动这个类开始扫描我们自己的包中的Class文件了。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author long
*/
@Configuration
public class SSOConfig {
@Bean
public BeanRegistrar setBeanRegistrar() {
return new BeanRegistrar();
}
}
上面我们已经注入了Bean,那么如果拿到注入到容器中的Bean呢?我在介绍一个工具类。
那么最简单的就是直接实现一个接口 ApplicationContextAware
, 当一个类实现了这个接口(ApplicationContextAware
)之后,这个类就可以方便获得ApplicationContext
中的所有bean
,可以调取spring
容器中管理的各个bean
。
import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * @author long * @Description * @create 2019-04-15 0:01 */ @SDKComponent public class SpringUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (SpringUtil.applicationContext == null) { SpringUtil.applicationContext = applicationContext; } } /** * 获取applicationContext * @return */ public static ApplicationContext getApplicationContext() { return applicationContext; } /** * 通过name获取 Bean. * @param name * @return */ public static Object getBean(String name){ return getApplicationContext().getBean(name); } /** * 通过class获取Bean. * @param clazz * @param <T> * @return */ public static <T> T getBean(Class<T> clazz){ return getApplicationContext().getBean(clazz); } /** * 通过name,以及Clazz返回指定的Bean * @param name * @param clazz * @param <T> * @return */ public static <T> T getBean(String name,Class<T> clazz){ return getApplicationContext().getBean(name, clazz); } }
有些事情,不去做,陈平安心里不痛快。可有些事情,再不痛快,也只能忍着。
—— 《剑来》
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。