IOC由DL(依赖查找-被抛弃啦,有侵入) + DI(依赖注入,spring只有这个)组成。
- setter
- interface
- constructor
- annotation
依赖倒置原则 -》 控制反转 --》 控制反转容器
- 避免在各处new来创建类,做到统一维护
- 创建实例的时候不需要了解其中的细节
- 反射+工厂模式的合体,满足开闭原则
IOC容器设计
// 作用在类上的
@Target(ElementType.TYPE)
// 运行时获得
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}
提取标记对象
- 指定范围,获取范围内的所有类
- 遍历所有类,获取被注解标记的类并加载进容器里
public class ClassUtil {
public static final String FILE = "file";
/**
* 获取包下类集合
*
*
*
* @param packageName 包名
* @return 类集合
*/
public static Set<Class<?>> extractPackageClass(String packageName){
// 1.获取类加载器
ClassLoader classLoader = getClassLoader();
// 2.通过类加载器获取到加载的资源信息 -- 不能让用户传绝对路径 通过url获取
URL url = classLoader.getResource(packageName.replace(".","/"));
if ( url == null ){
log.warn("获取不到任何路径鸭");
return null;
}
// 3.依据不同的资源类型,采用不同的方式获取资源的集合
Set<Class<?>> classSet = null;
// 过滤出文件类型的资源
if (url.getProtocol().equalsIgnoreCase(FILE)){
classSet = new HashSet <>();
File packageDirectory = new File(url.getPath());
// 提取所有的class文件
extractClassFile(classSet, packageDirectory, packageName);
}
// todo 增加对其他资源的处理 (如jar)
return classSet;
}
/**
* 递归-获取目标package里面里面所有的class文件
* (包括子package里面的)
* @param emptyClassSet 装载目标类的集合
* @param fileSource 文件 or 目录
* @param packageName 包名
*/
private static void extractClassFile(Set<Class<?>> emptyClassSet,File fileSource,String packageName) {
// 找到文件停住
if (!fileSource.isDirectory()){
return;
}
//如果是一个文件夹,则调用获取所有的文件/文件夹
File[] files = fileSource.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
if (pathname.isDirectory()){
return true;
} else {
// class文件 - 获取绝对值路径
String absoluteFilePath = fileSource.getAbsolutePath();
if (absoluteFilePath.endsWith(".class")){
// 加载class
addToClassSet(absoluteFilePath);
}
}
return false;
}
private void addToClassSet(String absoluteFilePath) {
//1.从class文件的绝对值路径里提出包含package的类名
absoluteFilePath = absoluteFilePath.replace(File.separator,".");
String className = absoluteFilePath.substring(absoluteFilePath.indexOf(packageName));
className = className.substring(0,className.lastIndexOf("."));
//2.通过反射机制获取对应的class对象并加入到classSet里
Class targetClass = loadClass(className);
emptyClassSet.add(targetClass);
}
});
// foreach遇到files为空会异常
if (files != null){
for ( File f : files ){
// 递归调用
extractClassFile(emptyClassSet,f,packageName);
}
}
}
/**
* 获取Class对象
* @param className
* @return
*/
public static Class<?> loadClass(String className){
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
log.error("load error");
throw new RuntimeException(e);
}
}
/**
* 获取ClassLoader
* @return
*/
public static ClassLoader getClassLoader(){
return Thread.currentThread().getContextClassLoader();
}
}
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
/**
* 存放所有被配置标记的目标对象的Map
* 将目标类本身作为键,目标类的实例作为值
*/
private final Map<Class<?>, Object> beanMap = new ConcurrentHashMap();
/**
* 加载bean的注解列表
*/
private static final List<Class<? extends Annotation>> BEAN_ANNOTATION
= Arrays.asList(Component.class, Controller.class, Service.class, Repository.class);
/**
* 获取Bean容器实例
*
* @return BeanContainer
*/
public static BeanContainer getInstance() {
return ContainerHolder.HOLDER.instance;
}
private enum ContainerHolder {
HOLDER;
private BeanContainer instance;
ContainerHolder() {
instance = new BeanContainer();
}
}
/**
* 容器是否已经加载过bean
*/
private boolean loaded = false;
/**
* 是否已加载过Bean
*
* @return 是否已加载
*/
public boolean isLoaded() {
return loaded;
}
/**
* Bean实例数量
*
* @return 数量
*/
public int size() {
return beanMap.size();
}
/**
* 扫描加载所有Bean
*
* @param packageName 包名
*/
public synchronized void loadBeans(String packageName) {
//判断bean容器是否被加载过
if (isLoaded()) {
log.warn("BeanContainer has been loaded.");
return;
}
Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
if (ValidationUtil.isEmpty(classSet)) {
log.warn("extract nothing from packageName" + packageName);
return;
}
for (Class<?> clazz : classSet) {
for (Class<? extends Annotation> annotation : BEAN_ANNOTATION) {
//如果类上面标记了定义的注解
if (clazz.isAnnotationPresent(annotation)) {
//将目标类本身作为键,目标类的实例作为值,放入到beanMap中
beanMap.put(clazz, ClassUtil.newInstance(clazz, true));
}
}
}
loaded = true;
}
/**
* 添加一个class对象及其Bean实例
*
* @param clazz Class对象
* @param bean Bean实例
* @return 原有的Bean实例, 没有则返回null
*/
public Object addBean(Class<?> clazz, Object bean) {
return beanMap.put(clazz, bean);
}
/**
* 移除一个IOC容器管理的对象
*
* @param clazz Class对象
* @return 删除的Bean实例, 没有则返回null
*/
public Object removeBean(Class<?> clazz) {
return beanMap.remove(clazz);
}
/**
* 根据Class对象获取Bean实例
*
* @param clazz Class对象
* @return Bean实例
*/
public Object getBean(Class<?> clazz) {
return beanMap.get(clazz);
}
/**
* 获取容器管理的所有Class对象集合
*
* @return Class集合
*/
public Set<Class<?>> getClasses(){
return beanMap.keySet();
}
/**
* 获取所有Bean集合
*
* @return Bean集合
*/
public Set<Object> getBeans(){
return new HashSet<>( beanMap.values());
}
/**
* 根据注解筛选出Bean的Class集合
*
* @param annotation 注解
* @return Class集合
*/
public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation){
//1.获取beanMap的所有class对象
Set<Class<?>> keySet = getClasses();
if(ValidationUtil.isEmpty(keySet)){
log.warn("nothing in beanMap");
return null;
}
//2.通过注解筛选被注解标记的class对象,并添加到classSet里
Set<Class<?>> classSet = new HashSet<>();
for(Class<?> clazz : keySet){
//类是否有相关的注解标记
if(clazz.isAnnotationPresent(annotation)){
classSet.add(clazz);
}
}
return classSet.size() > 0? classSet: null;
}
/**
* 通过接口或者父类获取实现类或者子类的Class集合,不包括其本身
*
* @param interfaceOrClass 接口Class或者父类Class
* @return Class集合
*/
public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass){
//1.获取beanMap的所有class对象
Set<Class<?>> keySet = getClasses();
if(ValidationUtil.isEmpty(keySet)){
log.warn("nothing in beanMap");
return null;
}
//2.判断keySet里的元素是否是传入的接口或者类的子类,如果是,就将其添加到classSet里
Set<Class<?>> classSet = new HashSet<>();
for(Class<?> clazz : keySet){
//判断keySet里的元素是否是传入的接口或者类的子类 isAssignableFrom 本身or超类
if(interfaceOrClass.isAssignableFrom(clazz) && !clazz.equals(interfaceOrClass)){
classSet.add(clazz);
}
}
return classSet.size() > 0? classSet: null;
}
}
Spring框架有多种作用域
- singleton (单例:整个ioc容器只有一个)
- prototype(每次请求实例都是新的)
- request (每次http请求产生不同的bean)
- session
- globalsession
IOC容器实现
- 创建注解 (Controller、Service、Repository、Component)
- 提取标记对象(URL提取、获取类加载器、获取Class对象)
- 依赖注入(Autowired、成员变量实例的注入(解决多个实现类))
- 实现容器(单例模式(饿汉懒汉)、创建容器载体、实现容器加载)