手写spring

一、Spring

1.IOC概念(控制反转)

IOC,或控制反转,是一种设计原则,它将对象创建和依赖的管理从应用程序代码中移除,由容器来负责。这使得应用程序组件更加松耦合,便于维护和测试。

  • IOC方式:对象的创建和管理由Spring容器来负责,依赖通过配置文件或注解来注入。
  • 传统开发方式:对象在代码中直接创建和管理,依赖关系是硬编码的

2.IOC容器运行流程

2.1. 容器启动

Spring容器的启动通常从加载配置开始。配置可以是基于XML文件的配置、Java注解配置类,或者是基于Java代码的配置类。Spring会解析这些配置来构建Bean定义,并初始化容器。

  • 基于XML配置的启动
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  • 基于注解配置的启动
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

2.2. 读取配置和扫描

容器启动后,Spring会根据配置读取Bean定义。它可以通过解析XML文件中的Bean定义、读取Java配置类中的@Bean方法、扫描@Component注解的类等方式来获取Bean的定义信息。

  • XML配置文件:Spring会读取XML文件中的<bean>标签,以获取Bean的定义。
  • 注解扫描:Spring通过类路径扫描,找到使用@Component@Service@Repository等注解标识的类,并将它们注册为Bean。
  • Java配置类:Spring通过读取@Configuration类中的@Bean方法,来定义和注册Bean。

2.3. Bean的定义注册

一旦Spring读取到Bean定义,便会将这些定义存储在一个内部的数据结构中(如BeanDefinition对象),这些定义包括Bean的类名、作用域、构造参数、依赖关系等。

2.4. Bean的实例化

容器会在需要使用Bean时,根据Bean的定义实例化Bean。这一步并不涉及属性的注入,仅仅是创建Bean实例。Spring通过调用Bean的构造器(无参或有参)来创建Bean的对象。

2.5. 依赖注入(Dependency Injection)

在Bean实例化后,Spring会处理依赖注入。Spring会根据Bean的依赖配置(构造器注入或Setter注入),将其他Bean注入到当前Bean中。

  • 构造器注入:Spring通过调用Bean的有参构造器,传入所需的依赖。
  • Setter注入:Spring通过调用Bean的Setter方法,注入所需的依赖。

2.6. Bean的初始化

在完成依赖注入后,Spring会对Bean进行初始化操作。这包括调用Bean的@PostConstruct注解方法(如果有),以及执行配置的初始化方法(通过init-method属性配置)。如果Bean实现了InitializingBean接口,则会调用其afterPropertiesSet()方法。

2.7. Bean的使用

经过初始化的Bean现在已准备好被容器使用。应用程序可以通过容器获取Bean实例,并调用其方法来完成具体的业务逻辑。

  • 获取Bean:通过getBean()方法从容器中获取Bean实例。
MyClass myClass = context.getBean(MyClass.class);

2.8. Bean的销毁

当容器关闭或Bean不再需要时,Spring会销毁Bean。销毁包括调用@PreDestroy注解方法(如果有),执行配置的销毁方法(通过destroy-method属性配置),以及如果Bean实现了DisposableBean接口,调用其destroy()方法。

3.AOP(待补充)

4.简单手写一个Spring(进行bean的创建和依赖注入)

  • Bean 注解(Bean创建)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
  • DI注解(依赖注入)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
  • 创建ApplicationContext接口定义getBean方法
public interface ApplicationContext {
    Object getBean(Class clazz);
}
  • 创建AnnotationApplicationContext对象实现ApplicationContext接口进行bean的创建和依赖注入
public class AnnotationApplicationContext implements ApplicationContext{
    //创建map集合用于存放bean对象
    Map<Class,Object> beanFactory=new HashMap<>();
    private String rootPath;
    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }
    //设置包扫描规则
    //当前包及其子包,那个类有@Bean注解,把这个类通过反射实例化出来
    public  AnnotationApplicationContext(String basePackage){
//    public static void path1(String basePackage){
        try {

            //com.java
            //把.换成\
            String pagePath = basePackage.replaceAll("\\.", "\\\\");
            //获取包的绝对路径
            Enumeration<URL> resources = ApplicationContext.class.getClassLoader().getResources(pagePath);
            while (resources.hasMoreElements()){
                URL url = resources.nextElement();
                //对url进行转码
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");
                //获取包前面路径部分,字符串截取
                rootPath= filePath.substring(0, filePath.length() - pagePath.length());
                loadBean(new File(filePath));
                //包扫描

//                System.out.println(filePath);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        loadDi();
    }

    //包扫描
    private void loadBean(File file) {
        //判断是不是文件夹
        if (file.isDirectory()) {
            //获取文件夹内容
            File[] childrenFiles = file.listFiles();
            //判断文件夹为空直接返回
            if(childrenFiles==null&&childrenFiles.length==0){
                return;
            }
            //遍历问价夹所有内容
            for (File childrenFile : childrenFiles) {
                if (childrenFile.isDirectory()){
                    loadBean(childrenFile);
                }else {
                    //通过文件路径转变成全类名,第一步把绝对路径部分去掉
                    String pathWithClass = childrenFile.getAbsolutePath().substring(rootPath.length() - 1);
                    //选中其中的class文件
                    if(pathWithClass.contains(".class")){
                        //去掉.class并且将\替换为.
                        String fullPath = pathWithClass.replaceAll( "\\\\",".").replaceAll(".class", "");
                        try {
                            Class<?> aClass = Class.forName(fullPath);
                            //获取非接口类
                            if(!aClass.isInterface()){
                                //获取含有bean注解的类
                                Bean annotation = aClass.getAnnotation(Bean.class);
                                if(annotation!=null){
                                    Object instance = aClass.newInstance();
                                    //判断instance有没有接口
                                    if(aClass.getInterfaces().length>0){
                                        //如果有的话创建实例对象
                                        System.out.println("正在加载【"+ aClass.getInterfaces()[0] +"】,实例对象是:" + instance.getClass().getName());
                                        beanFactory.put(aClass.getInterfaces()[0],instance);
                                    }else {
                                        //如果没有就将class对象当key
                                        beanFactory.put(aClass,instance);
                                    }
                                }
                            }
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }

        }

    }

    //属性注入
    private void loadDi(){
        //实例化对象在beanFactory的map集合里面
        //1.遍历beanFactory中的集合
        Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
        for (Map.Entry<Class, Object> entry : entries) {
            //2.获取map集合的每个对象
            Object value = entry.getValue();
            //3.获取属性
            Field[] declaredFields = value.getClass().getDeclaredFields();
            //4.遍历属性
            for (Field declaredField : declaredFields) {
                //判断属性上是否有@Di注解
                Di annotation = declaredField.getAnnotation(Di.class);
                if(annotation!=null){
                    //属性赋值
                    try {
                        declaredField.setAccessible(true);
                        declaredField.set(value,beanFactory.get(declaredField.getType()));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}

5. 手写一个复杂的Spring

4.1.流程

4.2.案例:

  • Autowired注解实现自动注入
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
  • Component注解标准需要被Spring管理的类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    String value() default "";//自定义bean的名称
}
  • ComponentScan 注解标注需要扫描的包名
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    String value() default "";
}
  • Scope 注解标注是否为单例bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
    String value() default "";
}
  • BeanDefinition 存放扫描到的bean
@Data
public class BeanDefinition {
    private Class type;
    private String scope;
}
  • 执行Aware方法时调用的方法
public interface BeanNameAware {
    public void setName(String name);
}
  • Aspect注解标注需要解析代理的bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
}
  • BeanPostProcessor定义初始化前后执行的方法
 public Object postProcessBeforeInitialization(String beanName,Object bean);
 public Object postProcessAfterInitialization(String beanName,Object bean);
  • BeanPostProcessorImpl实现BeanPostProcessor接口
@Component
public class BeanPostProcessorImpl implements BeanPostProcessor {
    //初始化前调用方法
    @Override
    public Object postProcessBeforeInitialization(String beanName,Object bean) {
        if(beanName.equals("userService")){
            System.out.println(beanName+" 初始化的前置处理!");
        }
        return bean;
    }
    //初始化后调用方法
    @Override
    public Object postProcessAfterInitialization(String beanName,Object bean) {
//        System.out.println(bean.getClass().getName()+"的后置通知");
//        System.out.println(bean.getClass().isAnnotationPresent(Aspect.class));
        //aop
        if(bean.getClass().isAnnotationPresent(Aspect.class)){
            Object instance = Proxy.newProxyInstance(BeanPostProcessorImpl.class.getClassLoader(),
                    bean.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("切面逻辑");
                            return method.invoke(bean, args);
                        }
                    });
            return instance;
        }

        return bean;
    }
}
  • MyApplicationContext容器主要的功能实现
public class MyApplicationContext {
    private Class configClass;
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap=new ConcurrentHashMap<>();
    private ConcurrentHashMap<String,Object> beanFactory=new ConcurrentHashMap<>();
    //存放初始化前后方法对象的集合
    private List<BeanPostProcessor> beanPostProcessors=new ArrayList<>();
    private String rootPath;

    public MyApplicationContext(Class configClass) {
        this.configClass = configClass;

        //扫描
        if(configClass.isAnnotationPresent(ComponentScan.class)){
            ComponentScan componentScan= (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScan.value();//扫描路径 com.java.service
            //com/java/service
            path=path.replaceAll("\\.","\\\\");

            //通过类加载器获取文件路径
            URL url = MyApplicationContext.class.getClassLoader().getResource(path);
            //对url进行转码
            String filePath = null;
            try {
                filePath = URLDecoder.decode(url.getFile(), "utf-8");
                //获取包前面路径部分,字符串截取
                rootPath= filePath.substring(0, filePath.length() - path.length());
                //获取beanDefinition集合
                loadBean(new File(filePath));

                //通过beanDefinition集合对其中的bean进行创建
                for (String beanName : beanDefinitionMap.keySet()) {
                    BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
                    //如果是单例模式就直接创建一个bean
                    if(beanDefinition.getScope().equals("singleton")){
                        Object bean = createBean(beanName, beanDefinition);
                        beanFactory.put(beanName,bean);
                    }
                }

            } catch (Exception e) {
                throw new RuntimeException(e);
            }


        }


    }
    //bean的注册
    private Object createBean(String beanName,BeanDefinition beanDefinition) {
        Class aClass = beanDefinition.getType();
        Object instance = null;
        try {
            instance = aClass.getConstructor().newInstance();
            //依赖注入
            for (Field declaredField : aClass.getDeclaredFields()) {
                if(declaredField.isAnnotationPresent(Autowired.class)){
                    declaredField.setAccessible(true);
                    declaredField.set(instance,getBean(declaredField.getName()));
                }
            }
            //Aware
            //通过aware回调对其bean的name进行赋值
            if(instance instanceof BeanNameAware){
                ((BeanNameAware)instance).setName(beanName);
            }

            //初始化前执行方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                instance = beanPostProcessor.postProcessBeforeInitialization(beanName, instance);
            }

            //初始化
            if(instance instanceof InitializingBean){
                ((InitializingBean)instance).afterPropertiesSet();
            }

            //初始化后执行方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                instance=beanPostProcessor.postProcessAfterInitialization(beanName,instance);
            }


        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return instance;
    }

    private void loadBean(File file) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        //判断文件是否为文件夹
        if (file.isDirectory()) {
            //获取所有的文件
            File[] files = file.listFiles();
            if(files.length==0||files==null){
                return;
            }
            //遍历所有的文件
            for (File childrenFile : files) {
                if (childrenFile.isDirectory()) {
                    loadBean(childrenFile);
                }
                //通过文件路径转变成全类名,第一步把绝对路径部分去掉
                String pathWithClass = childrenFile.getAbsolutePath().substring(rootPath.length() - 1);
//                System.out.println(pathWithClass);
                //获取.class的文件
                if(pathWithClass.contains(".class")){
                    pathWithClass=pathWithClass.replace("\\",".").replace(".class","");
//                    System.out.println(pathWithClass);
                    //获取需要扫描的字节码文件
                    Class<?> aClass = Class.forName(pathWithClass);
                    //扫描带Component的注解,并将其存入一个beanDefinition
                    if(aClass.isAnnotationPresent(Component.class)){
                        BeanDefinition beanDefinition=new BeanDefinition();

                        //判断是否实现beanPostProcessor接口
                        if(BeanPostProcessor.class.isAssignableFrom(aClass)){
                            BeanPostProcessor beanPostProcessor = (BeanPostProcessor) aClass.newInstance();
                            beanPostProcessors.add(beanPostProcessor);
                        }

//                       //通过component注解获取bean的名字
                        Component component = aClass.getAnnotation(Component.class);

                        String beanName = component.value();

                        if(beanName.equals("")){
                            beanName= Introspector.decapitalize(aClass.getSimpleName());
                        }
                        //判断scope注解中的内容(判断单例还是多例)
                        if(aClass.isAnnotationPresent(Scope.class)){
                            //获取scope注解
                            Scope scopeAnno=aClass.getAnnotation(Scope.class);
                            //获取注解中value值
                            String scope = scopeAnno.value();
                            beanDefinition.setScope(scope);
                        }else {
                            beanDefinition.setScope("singleton");
                        }
                        beanDefinition.setType(aClass);
                        beanDefinitionMap.put(beanName,beanDefinition);
                    }
                }
            }
        }
    }
    public Object getBean(String beanName){
        BeanDefinition beanDefinition=beanDefinitionMap.get(beanName);
        if(beanDefinition==null){
            throw new NullPointerException();
        }else {
            String scope=beanDefinition.getScope();
            //为单例bean时直接取bean工厂拿
            if(scope.equals("singleton")){
                Object bean = beanFactory.get(beanName);
                if(bean==null){
                    //如果bean为空创建bean
                    Object bean1 = createBean(beanName, beanDefinition);
                    beanFactory.put(beanName,bean1);
                }
                return bean;
            }else {
                //如果是多例bean则不需要加入bean工厂集合
                return createBean(beanName, beanDefinition);
            }
        }
    }

二、SpringMVC

1.核心概念

  1. DispatcherServlet(调度器)

    • DispatcherServlet 是 Spring MVC 的核心组件,相当于一个前端控制器(Front Controller)。它接收所有的 HTTP 请求,并将它们分发给合适的处理器(Controller)。
    • 它负责初始化 Spring MVC 环境,包括加载配置文件、创建上下文等。
  2. Controller(控制器)

    • 控制器是处理 HTTP 请求的核心组件。它接收用户的输入、处理业务逻辑,并返回相应的视图或数据。
    • 控制器通过注解 @Controller 标记,并通过方法级别的注解(如 @RequestMapping)来定义如何处理不同的请求路径和请求方法。
  3. Model(模型)

    • 模型包含了应用程序的业务数据和状态。控制器将数据封装在模型中,供视图展示。
    • 在 Spring MVC 中,模型通常是 ModelModelMap 类型的对象,用于传递数据到视图层。
  4. View(视图)

    • 视图负责将模型中的数据展示给用户。Spring MVC 支持多种视图技术,如 JSP、Thymeleaf、FreeMarker 等。
    • 视图的选择通常由视图解析器(View Resolver)决定。视图解析器根据逻辑视图名(由控制器返回)找到实际的视图实现。
  5. View Resolver(视图解析器)

    • 视图解析器是 Spring MVC 中用于解析视图名并将其转换为实际视图对象的组件。通过配置视图解析器,可以指定视图技术和视图文件的路径。
  6. HandlerMapping(处理器映射)

    • 处理器映射是 Spring MVC 中用于将请求 URL 映射到具体处理器(Controller)的组件。Spring 提供了多种处理器映射策略,如基于注解的映射、基于路径的映射等。

2.工作流程

  • 流程图

img

  • 具体流程

    1. Tomecat服务器接收用户请求后创建两个对象HttpServletRequest和HttpServletResqponse并将对象交给DispetcherServlet(调度器)。
    2. DispetcherServlet(调度器)获取req请求对象中的访问路径url交给HandlerMapping(处理器映射器).
    3. HandlerMapping会根据我们在Controller上的注解找到对应的Handler处理器并交给DispetcherServlet(调度器).
    4. DispetcherServlet(调度器)会将处理器和req对象交给HandlerAdapter(处理器适配器),让处理器适配器去调用处理器中相应的处理方法并返回处理好的ModelAndView(视图模型和视图名称).(因为处理器方法会有多种对参数的要求,因此调度器只需要将req和处理器交给适配器即可,适配器可以通过req随意传入参数)
    5. DispetcherServlet(调度器)将视图名传给ViewResolver(视图解析器)解析出对应的视图文件返回给调度器.
      作流程
  • 流程图

[外链图片转存中…(img-hAiOCSPY-1728438204087)]

  • 具体流程
    1. Tomecat服务器接收用户请求后创建两个对象HttpServletRequest和HttpServletResqponse并将对象交给DispetcherServlet(调度器)。
    2. DispetcherServlet(调度器)获取req请求对象中的访问路径url交给HandlerMapping(处理器映射器).
    3. HandlerMapping会根据我们在Controller上的注解找到对应的Handler处理器并交给DispetcherServlet(调度器).
    4. DispetcherServlet(调度器)会将处理器和req对象交给HandlerAdapter(处理器适配器),让处理器适配器去调用处理器中相应的处理方法并返回处理好的ModelAndView(视图模型和视图名称).(因为处理器方法会有多种对参数的要求,因此调度器只需要将req和处理器交给适配器即可,适配器可以通过req随意传入参数)
    5. DispetcherServlet(调度器)将视图名传给ViewResolver(视图解析器)解析出对应的视图文件返回给调度器.
    6. DispetcherServlet(调度器)将model(视图模型)对对应的view(视图文件)进行渲染得到一个有内容的也页面通过resp对象返回给客户端的浏览器.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值