一、包扫描和创建单例bean案例
创建bean的定义
public class BeanDefinition { private Class clazz; private String scope; public Class getClazz() { return clazz; } public void setClazz(Class clazz) { this.clazz = clazz; } public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; }}
创建applicationContext对象 处理包扫描以及创建单例Bean的主要功能
package com.example.spring.demo.spring;import com.example.spring.demo.test.service.UserService;import java.io.File;import java.lang.reflect.InvocationTargetException;import java.net.URL;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;public class cjwApplicationContext { private Class configClass; // 单例池 private ConcurrentHashMap singletonObjects = new ConcurrentHashMap<>(); private ConcurrentHashMap beanDefiniationMap = new ConcurrentHashMap<>();//bean的定义 public cjwApplicationContext(Class configClass) throws ClassNotFoundException { this.configClass = configClass; //解析配置类 //ComponentScan注解------》扫描路径-----》扫描具体事情---->Beandefinition--->BeanDefinitionMap //对spring而言对当前传递的类是否有spring的相应注解 scan(configClass); for (Map.Entry entry : beanDefiniationMap.entrySet()) { String beanName = entry.getKey(); BeanDefinition beanDefinition = beanDefiniationMap.get(beanName); if (beanDefinition.getScope().equals("singleton")) { Object bean = createBean(beanDefinition);//单列Bean singletonObjects.put(beanName, bean); } } } public Object createBean (BeanDefinition beanDefinition) { Class clazz = beanDefinition.getClazz(); try { Object instance = clazz.getDeclaredConstructor().newInstance(); return instance; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } return null; } //Refactor--->Extact--->Field private void scan(Class configClass) throws ClassNotFoundException { ComponentScan cs = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class); String path = cs.value();//扫描路径 System.out.println("path = " + path); path = path.replace(".", "/"); ClassLoader classLoader = cjwApplicationContext.class.getClassLoader();//app //URL resource = classLoader.getResource("com/example/spring/demo/test/service"); URL resource = classLoader.getResource(path); File file = new File(resource.getFile()); if (file.isDirectory()) { File[] files = file.listFiles(); for (int i = 0; i < files.length; i++) { // System.out.println("files[i] = " + files[i]); // F:ideaWorkVspringdemotargetclassescomexamplespringdemotestserviceUserService.class // classLoader.loadClass("com.example.spring.demo.test.service.UserService"); String fileName = files[i].getAbsolutePath(); if (fileName.endsWith(".class")) { String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class")); className = className.replace("\", "."); System.out.println("className = " + className); Class clazz = classLoader.loadClass(className); if (clazz.isAnnotationPresent(Component.class)) { // 表示当前这个类是一个Bean // Class--> bean? 解析类,判断当前bean是单例bean,还是prototype的bean // BeanDefinitBean 解析类生成一个beanDefinitBean Component componentAnnoation = (Component) clazz.getDeclaredAnnotation(Component.class); String beanName = componentAnnoation.value(); BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setClazz(clazz); if (clazz.isAnnotationPresent(Scope.class)) { Scope scopeAnnotaion = (Scope)clazz.getDeclaredAnnotation(Scope.class); beanDefinition.setScope(scopeAnnotaion.value()); } else { beanDefinition.setScope("singleton"); } beanDefiniationMap.put(beanName, beanDefinition);//将bean的定义存储到map中 } } } } } public Object getBean(String beanName) { //根据类 判断是否是单例bean还是prototype的bean if (beanDefiniationMap.containsKey(beanName)) { BeanDefinition beanDefinition = (BeanDefinition) beanDefiniationMap.get(beanName); if (beanDefinition.getScope().equals("singleton")) { Object o = singletonObjects.get(beanName); return o; } else { // 创建Bean对象 Object o = createBean(beanDefinition); return o; } } else { //不存在对应的bean throw new NullPointerException(); } }}
自定义component注解
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface Component { //service 层 不写 spring解析类名 String value() default "";}
自定义包扫描注解
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface ComponentScan { //定义扫描路径 一般不用给默认值,使用该注解时候直接设置 String value() default "";}
自定义域注解
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface Scope { String value();}
创建userService,并指定bean的域为原型,(域分为单例和原型两种,单例地址不变化,原型bean地址不断变化)
@Component("userService")@Scope("prototype")public class UserService {}
public class xxxUtil {}
@ComponentScan("com.example.spring.demo.test.service")public class AppConfig {}
public class Test { public static void main(String[] args) throws ClassNotFoundException { //传入参数为spring的配置文件参数 cjwApplicationContext context = new cjwApplicationContext(AppConfig.class); Object userService = context.getBean("userService");//map 单例池 Object userService1 = context.getBean("userService");//map 单例池 Object userService2 = context.getBean("userService");//map 单例池 System.out.println("userService = " + userService); System.out.println("userService1 = " + userService1); System.out.println("userService2 = " + userService2); }}
二、相应的知识点
类加载
根据包名得到类 类加载器 以及对应的扫描路径位置
1、bootstrap --->jre/lib
2、ext --->jre/ext/lib
3、app --->classpath
相应注解理解
@Retention
source:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;被编译器忽略
class:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期
runtime:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码。
@Target:
@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、
类型成员(方法、构造方法、成员变量、枚举值)、
方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
参考链接:深入理解Java类加载 - czwbig - 博客园