【Spring(七)】带你手写一个Spring容器

news2024/12/26 10:49:44

有关Spring的所有文章都收录于我的专栏:👉Spring👈
目录
前置准备
第一步、创建我们自定的注解
第二步、创建我们自己的容器类
测试
总结


相关文章

【Spring(一)】如何获取对象(Bean)【Spring(一)】如何获取对象(Bean)
【Spring(二)】java对象属性的配置(Bean的配置)【Spring(二)】java对象属性的配置(Bean的配置)
【Spring(三)】熟练掌握Spring的使用【Spring(三)】熟练掌握Spring的使用
【Spring(四)】Spring基于注解的配置方式【Spring(四)】Spring基于注解的配置方式
【Spring(五)】引入篇:AOP的底层原理之动态代理【Spring(五)】引入篇:AOP的底层原理之动态代理
【Spring(六)】使用篇:AOP在开发中的使用 【Spring(六)】使用篇:AOP在开发中的使用

前置准备

在此之前我们先的了解原生Spring容器中有什么,以及它们的功能都是什么,我们才能有目的实现它。我们先在Spring容器中注入一个Bean。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第一步、创建我们自定的注解

@ComponentScan

这个注解用于标注配置类。我们手写的Spring容器使用的都是基于注解的方式。这个配置类就相当于我们的XML配置文件中配置扫描的包的路径。

package com.jl.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author long
 * @date 2022/9/4
 * @Target 指定我们的Component注解可以修饰Type程序元素
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    String value() default "";
}

@Component

此注解用于标注Bean对象。我们这里只写一个@Component`,其他例如:@Service这些都是相同的原理。

package com.jl.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    // 可以通过此注解指定bean的id值
    String value() default "";
}

@Scope

此注解用于声明Bean为单例还是多例。

package com.jl.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 可以指定bean的作用范围
 * singleton,prototype
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
    String value() default "";
}

@Autowired

用于自动装配的注解。

package com.jl.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
    // 可以通过此注解指定bean的id值
//    String value() default "";
}

@Aspect

用于标注切面类的注解。

package com.jl.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
    String value() default "";
}

@Before

前置通知的注解。

package com.jl.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Before {
    String value();
    String argNames() default "";
}

@AfterReturning

返回通知的注解。

package com.jl.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AfterReturning {
    String value() default "";
    String pointcut() default "";
    String returning() default "";
    String argNames() default "";
}

@After

后置通知的注解。

package com.jl.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface After {
    String value();
    String argNames() default "";
}

第二步、创建我们自己的容器类

在【一】中我们可以看到原生Spring容器中有几个很重要的属性:

  1. beanDefinitionMap :用于存放Bean的配置信息。
  2. singletonObjects:存放创建好的Bean实例。
  3. beanPostProcessorList:我们这里再添加到一个存放后置处理器的数组。
private Class configClass;
// 定义属性BeanDefinitionMap->存放BeanDefinition对象
private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
// 定义属性SingletonObjects->存放单例对象
private ConcurrentHashMap<String,Object> singletonObjects= new ConcurrentHashMap<>();
// 定义属性,存放我们的后置处理器
private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

扫描类

// 该方法完成对指定包的扫描,并将Bean信息封装到BeanDefinition对象,再放入Map中
public void beanDefinitionByScan(Class configClass){
   this.configClass = configClass;
   //1. 获取要扫描的包
   ComponentScan componentScan =
           (ComponentScan)this.configClass.getDeclaredAnnotation(ComponentScan.class);
   //2. 通过ComponentScan的value =》即要扫描的包
   String path = componentScan.value();

   //得到要扫描包下的所有资源(.class)
   //1) 得到类的加载器
   ClassLoader classLoader = JlSpringApplicationContext.class.getClassLoader();
   //2) 通过类的加载器获取到要扫描的包的资源url
   path = path.replace(".","/");
   URL resource = classLoader.getResource(path);
   //3) 将要加载的资源(.class)路径下的文件进行遍历=》io
   File file = new File(resource.getFile());
   if (file.isDirectory()){
       File[] files = file.listFiles();
       for (File f : files) {
           //class文件的绝对路径
           String fileAbsolutePath = f.getAbsolutePath();
           // 这里我们进行过滤,只处理.class文件
           if (fileAbsolutePath.endsWith(".class")) {
               //1. 获取到类名
               String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
               //2. 获取类的全路径
               String classFullName = path.replace("/", ".") + "." + className;
               //3. 判断该.class文件是不是需要注入到容器中;看该类是不是有注解@Component @Service
               try {
                   //这是我们就得到了该类的Class对象
                   // 这里也可以通过class.forName得到一个Class对象
                   // 区别是:class.forName()会调用类的静态方法,classLoader.loadClass()不会
                   Class<?> clazz = classLoader.loadClass(classFullName);
                   //aClass.isAnnotationPresent(Component.class) 判断该类是否有@Component注解
                   if (clazz.isAnnotationPresent(Component.class)){
                       System.out.println("这是一个SpringBean=" + clazz + " 类名=" + className);

                       // 为了方便,我们将后置处理器放入集合中(真实的还是在走createBean和getBean这些逻辑,这里是简化的)
                       // 如果是一个后置处理器就放入集合中
                       // 这里我们不能通过instanceof来判断是否实现了BeanPostProcessor
                       // 原因:这个的clazz是一个类对象,不是一个实例对象
                       if (BeanPostProcessor.class.isAssignableFrom(clazz)){
                           BeanPostProcessor beanPostProcessor = (BeanPostProcessor)clazz.newInstance();
                           beanPostProcessorList.add(beanPostProcessor);
                           continue;
                       }
                       //先得到beanName
                       // 1. 得到component注解
                       Component declaredAnnotation = clazz.getDeclaredAnnotation(Component.class);
                       // 2. 得到value值(1.配置了value 2.没有配置value)
                       String beanName = declaredAnnotation.value();
                       if ("".equals(beanName)){ // 如果没有写value
                           // 将该类的类名首字母小写作为beanName
                           beanName = StringUtils.uncapitalize(className);
                       }
                       // 3. 将bean的信息封装到BeanDefinition对象
                       BeanDefinition beanDefinition = new BeanDefinition();
                       beanDefinition.setClazz(clazz);
                       // 4. 获取scope的值
                       if(clazz.isAnnotationPresent(Scope.class)){// 如果有scope值
                           Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                           beanDefinition.setScope(scopeAnnotation.value());
                       }else { //如果没有scope值
                           beanDefinition.setScope("singleton");
                       }
                       // 5. 将BeanDefinition放入到Map中
                       beanDefinitionMap.put(beanName,beanDefinition);
                   }else {
                       System.out.println("这不是一个SpringBean=" + clazz + " 类名" + className);
                   }
               } catch (Exception e) {
                   e.printStackTrace();
               }

           }
       }
   }
}
  1. 因为我们需要知道需要扫描的包的路径,所以需要借助配置类。我们这里将配置类的Class对象作为参数传递给我们的容器类的构造器(这一步在容器的构造器中体现)。
  2. 有了配置类的Class对象之后,我们就可以通过@ComponentScan拿到要扫描类的路径。
  3. 拿到包的路径之后,就得去扫描包中的类,而且得判断哪些类是加了@Component。只有加了此注解的将来才可以注入到容器中。
  4. 如果有@Component,那么还得判断它是否是后置处理器,如何是后置处理器,实例化之后就将其直接加入到后置处理器集合中。
  5. 如果不是后置处理器,接下来拿到@Component中的value值。如果没有value值为空,就默认按照id=类名首字母小写的方式命名。如果value值不为空,就将获取到的value值作为id。
  6. 接下来判断,类上是否有@Scope。如果有就拿到它的value值,反之就默认为singleton(单例)。最后将此信息存入到BeanDefinition中。BeanDefinition结构:
package com.jl.spring.ioc;
/**
 * 用于记录bean的信息(bean为单例还是多例Scope;bean对应的class对象,反射可以生成对应的对象)
 *
 */
public class BeanDefinition {
    private String scope;
    private Class clazz;

    public BeanDefinition(String scope, Class clazz) {
        this.scope = scope;
        this.clazz = clazz;
    }

    public BeanDefinition() {
    }

    @Override
    public String toString() {
        return "BeanDefinition{" +
                "scope='" + scope + '\'' +
                ", clazz=" + clazz +
                '}';
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }
}
  1. 之后将我们得到的beanName(也就是id值)beanDefinition(记录单例还是多例),存到我们的beanDefinitionMap

完成Bean实例的创建

在上一步中我们对所有的类进行了扫描,并且将所有Bean的信息存入到了beanDefinitionMap中。接下来我们就可以创建Bean实例了。

// 完成  createBean(BeanDefinition beanDefinition)方法
private Object createBean(String beanName,BeanDefinition beanDefinition){
    // 得到bean的class对象
    Class clazz = beanDefinition.getClazz();
    // 使用反射得到实例
    try {
        Object instance = clazz.getDeclaredConstructor().newInstance();
        // 注入对象
        // 1. 遍历当前要创建的对象的所有字段
        for (Field declaredField : clazz.getDeclaredFields()){
            //2.  判断该字段是否有Autowired修饰
            if (declaredField.isAnnotationPresent(Autowired.class)){
                // 3.得到字段的名称
                String name = declaredField.getName();
                // 4.通过getBean方法来获取要组装对象
                Object bean = getBean(name);
                // 5. 进行组装
                // 因为属性是私有的,所以要暴破
                declaredField.setAccessible(true);
                declaredField.set(instance,bean);
            }

        }
        System.out.println("创建好了一个实例" + instance);


        // 我们在Bean的初始化方法前,调用我们后置处理器的before方法
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            // 在后置处理器的before方法,可以对容器生成的bean进行处理
            // 然后返回处理后的bean实例
            Object current = beanPostProcessor.postProcessBeforeInitialization(instance,beanName);
            if (current != null){
                instance = current;
            }
        }

        //判断是否需要执行Bean的初始化方法
        // 1. 判断当前创建的Bean对象是否实现了InitializingBean
        if (instance instanceof InitializingBean){ //如果实现了InitializingBean
            // 将 instance转成接口类型
            try {
                ((InitializingBean)instance).afterPropertiesSet();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 我们在Bean的初始化方法前,调用我们后置处理器的after方法
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            // 在后置处理器的after方法,可以对容器生成的bean进行处理
            // 然后返回处理后的bean实例
            Object current = beanPostProcessor.postProcessAfterInitialization(instance,beanName);
            if (current != null){
                instance = current; 
            }
        }
        System.out.println("-----------------------------------------");
        return instance;
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }
    // 如果反射对象失败,则返回空
    return null;
}
  1. 我们从BeanDefinition中拿到需要创建的Class对象。
  2. 使用Class对象通过反射来创建Bean对象实例。
  3. 因为我们创建对象中还有属性,所以还得考虑自动装配的情况。
  4. 拿到所有Class对象所有的Field对象数组,遍历判断是否有@Autowired。如果有就拿到此属性的名称,以此名称作为id去获取容器中的创建好的Bean实例。最后通过反射完成对属性的赋值。这里还得对属性进行暴破,因为属性是私有的.
  5. 接下来还不能将Bean注入到容器中,因为我们还有后置处理器初始化类
  6. BeanPostProcessor结构:
package com.jl.spring.processor;

import org.apache.commons.lang.StringUtils;

/**
 * 参考原生Spring容器定义的一个接口
 * 该接口有两个方法
 */
public interface BeanPostProcessor {
    /**
     * 这连个方法会对所有的bean生效
     * 该方法在bean的初始化方法前调用
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessBeforeInitialization(Object bean, String beanName){
        return bean;
    };

    /**
     *  该方法在bena的初始化方法之后调用
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessAfterInitialization(Object bean, String beanName){
        return bean;
    };
}
  1. 遍历我们的beanPostProcessorList(后置处理器集合),调用它们的postProcessBeforeInitialization(),实现对Bean实例的前置处理。
  2. 判断当前的Bean对象是否实现了InitializingBean。如果实现了,执行它的afterPropertiesSet()InitializingBean结构如下:
package com.jl.spring.processor;

/**
 * 根据原生Spring定义一个接口
 * 这个接口有一个方法
 * afterPropertiesSet在Bean的Setter方法后执行,即就是我们的初始化方法
 * 当一个Bean实现这个接口后,就实现afterPropertiesSet
 */
public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}
  1. 遍历我们的beanPostProcessorList(后置处理器集合),调用它们的postProcessAfterInitialization(),实现对Bean实例的后置处理。
  2. 最后返回我们的创建好的Bean实例。

获取Bean实例

// 编写方法返回容器的对象
public Object getBean(String name){
    // 判断传入的beanName是否在beanDefinition中存在
    if (beanDefinitionMap.containsKey(name)){
        BeanDefinition beanDefinition = beanDefinitionMap.get(name);
        // 得到beanDefinition的scope,分别进行处理
        if("singleton".equalsIgnoreCase(beanDefinition.getScope())){
            // 单例的,直接从单例池中获取
            return  singletonObjects.get(name);
        }else { // 如果不是单例,就通过createBean,反射一个对象
            return createBean(name,beanDefinition);
        }
    }else {
        // 如果不存在
        throw new NullPointerException("没有该bean");
    }
}
  1. 获取Bean实例,首先就得判断容器中是否有该Bean的信息。
  2. 判断要获取的Bean是否是单例,如果是单例就直接从单例池singletonObjects中获取。如果不是就调用createBean(),造一个Bean实例返回。

容器的构造方法

public JlSpringApplicationContext(Class configClass){
    beanDefinitionByScan(configClass);
    // 通过beanDefinition对象,初始化singletonObject单例池
    Enumeration<String> keys = beanDefinitionMap.keys();
    // 遍历所有的beanDefinition对象
    while (keys.hasMoreElements()){
        String beanName = keys.nextElement();
        // 通过beanName得到对应的beanDefinition对象
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        // 判断该bean是singleton还是一个prototype
        if ("singleton".equalsIgnoreCase(beanDefinition.getScope())){
            // 将该bean实例放入到singletonObjects这个集合中
            Object bean = createBean(beanName,beanDefinition);
            singletonObjects.put(beanName,bean);
        }
    }
//        System.out.println("单例池" + singletonObjects);
}

测试

Bean的测试(默认)

配置类

package com.jl.spring.ioc;

import com.jl.spring.annotation.ComponentScan;

/**
 * 这是一个配置类,作用类似我们原生spring的beans.xml 容器配置文件
 */
@ComponentScan(value = "com.jl.spring.component")
public class JlSpringConfig {
}

测试类:

package com.jl.spring;

import com.jl.spring.component.MonsterService;
import com.jl.spring.component.SmartAnimalable;
import com.jl.spring.ioc.JlSpringApplicationContext;
import com.jl.spring.ioc.JlSpringConfig;

public class AppMain {
    public static void main(String[] args) {
        JlSpringApplicationContext jlSpringApplicationContext =
                new JlSpringApplicationContext(JlSpringConfig.class);
        Object car = jlSpringApplicationContext.getBean("car");
        System.out.println(car);
    }
}

结果截图:
在这里插入图片描述

Bean的测试(通过指定id)

package com.jl.spring.component;

import com.jl.spring.annotation.Component;

@Component(value = "myCar")
public class Car {
}

测试类:

package com.jl.spring;

import com.jl.spring.component.MonsterService;
import com.jl.spring.component.SmartAnimalable;
import com.jl.spring.ioc.JlSpringApplicationContext;
import com.jl.spring.ioc.JlSpringConfig;

public class AppMain {
    public static void main(String[] args) {
        JlSpringApplicationContext jlSpringApplicationContext =
                new JlSpringApplicationContext(JlSpringConfig.class);
        Object car = jlSpringApplicationContext.getBean("myCar");
        System.out.println(car);
    }
}

结果截图:
在这里插入图片描述

自动装配测试

package com.jl.spring.component;

import com.jl.spring.annotation.Component;
import com.jl.spring.processor.InitializingBean;

@Component(value = "monsterDao")
public class MonsterDao implements InitializingBean {
    public void hi(){
        System.out.println("MonsterDao-hi()被调用。。。");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("MonsterDao初始化方法被调用");
    }
}
package com.jl.spring.component;

import com.jl.spring.annotation.Autowired;
import com.jl.spring.annotation.Component;
import com.jl.spring.annotation.Scope;
import com.jl.spring.processor.InitializingBean;

/**
 * MonsterService是一个service
 */
@Component(value = "monsterService") // 把MonsterService注入到我们自己的Spring容器中
public class MonsterService implements InitializingBean {
    @Autowired
    private MonsterDao monsterDao;
    public void m1(){
        monsterDao.hi();
    }

    /**
     * 该方法就是Bean的方法执行过后,被容器调用
     * 即就是初始化方法
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("MonsterService初始化方法被调用");
    }
}

测试类:

package com.jl.spring;

import com.jl.spring.component.MonsterService;
import com.jl.spring.component.SmartAnimalable;
import com.jl.spring.ioc.JlSpringApplicationContext;
import com.jl.spring.ioc.JlSpringConfig;

public class AppMain {
    public static void main(String[] args) {
        JlSpringApplicationContext jlSpringApplicationContext =
                new JlSpringApplicationContext(JlSpringConfig.class);
        Object bean = jlSpringApplicationContext.getBean("monsterService");
        System.out.println(bean);
    }
}

结果截图:
在这里插入图片描述

单例多例测试

package com.jl.spring.component;

import com.jl.spring.annotation.Autowired;
import com.jl.spring.annotation.Component;
import com.jl.spring.annotation.Scope;
import com.jl.spring.processor.InitializingBean;

/**
 * MonsterService是一个service
 */
@Component(value = "monsterService") // 把MonsterService注入到我们自己的Spring容器中
@Scope(value = "prototype")
public class MonsterService implements InitializingBean {
    @Autowired
    private MonsterDao monsterDao;
    public void m1(){
        monsterDao.hi();
    }

    /**
     * 该方法就是Bean的方法执行过后,被容器调用
     * 即就是初始化方法
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("MonsterService初始化方法被调用");
    }
}

测试类

package com.jl.spring;

import com.jl.spring.component.MonsterService;
import com.jl.spring.component.SmartAnimalable;
import com.jl.spring.ioc.JlSpringApplicationContext;
import com.jl.spring.ioc.JlSpringConfig;

public class AppMain {
    public static void main(String[] args) {
        JlSpringApplicationContext jlSpringApplicationContext =
                new JlSpringApplicationContext(JlSpringConfig.class);
        Object bean = jlSpringApplicationContext.getBean("monsterService");
        Object bean1 = jlSpringApplicationContext.getBean("monsterService");
        System.out.println(bean);
        System.out.println(bean1);
    }
}

结果截图:
在这里插入图片描述

后置处理器和初始化方法的测试

package com.jl.spring.component;

import com.jl.spring.annotation.Component;
import com.jl.spring.processor.InitializingBean;

@Component(value = "myCar")
public class Car implements InitializingBean {
    public Car() {
        System.out.println("Car无参构造被调用...");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("初始化方法");
    }
}
package com.jl.spring.component;

import com.jl.spring.annotation.Component;
import com.jl.spring.processor.BeanPostProcessor;

@Component
public class BeanPostProcessorImpl implements BeanPostProcessor{
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("前置通知");
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("后置通知");
        return null;
    }
}

测试类:

package com.jl.spring;

import com.jl.spring.component.MonsterService;
import com.jl.spring.component.SmartAnimalable;
import com.jl.spring.ioc.JlSpringApplicationContext;
import com.jl.spring.ioc.JlSpringConfig;

public class AppMain {
    public static void main(String[] args) {
        JlSpringApplicationContext jlSpringApplicationContext =
                new JlSpringApplicationContext(JlSpringConfig.class);
        Object car = jlSpringApplicationContext.getBean("myCar");
        System.out.println(car);
    }
}

结果截图:
在这里插入图片描述

AOP的编写

我们AOP和后置处理器有所联系。AOP就是利用前后置处理器进行切面处理。单例加强。

我们先看一下,用后置处理器实现的切面。

package com.jl.spring.component;

public interface SmartAnimalable {
    float getSum(float i ,float j);
    float getSub(float i ,float j);
}
package com.jl.spring.component;

import com.jl.spring.annotation.Component;

@Component(value="smartDog")
public class SmartDog implements SmartAnimalable{
    @Override
    public float getSum(float i, float j) {
        float res = i+j;
        System.out.println("SmartDog-getSum-res=" + res);
        return res;
    }


    @Override
    public float getSub(float i, float j) {
        float res = i -j;
        System.out.println("SmartDog-getSub-res=" + res);
        return res;
    }
}

后置处理器实现类:

package com.jl.spring.component;

import com.jl.spring.annotation.Component;
import com.jl.spring.processor.BeanPostProcessor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 这是我们自己的后置处理器
 * 它是实现了BeanPostProcessor
 * 我们可以重写里边的方法
 * 在Spring容器中仍然将我们的后置处理器当作一个Bean来处理,要注入到容器中
 * 还得考虑多个后置处理器的情况
 */
@Component
public class JlBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("后置处理器的JlBeanPostProcessor的before()方法被调用" + bean.getClass() + " beanName=" + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("后置处理器的JlBeanPostProcessor的after()方法被调用" + bean.getClass() + " beanName=" + beanName);
        // 实现AOP,返回代理对象,即对Bean进行包装
        if("smartDog".equals(beanName)){
            // 使用JDK的动态代理,返回Bean的代理对象
            Object proxyInstance = Proxy.newProxyInstance(JlBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("method"+ method.getName());
                            Object result = null;
                            // 加入要进行前置通知处理的方法是getSum
                            if ("getSum".equals(method.getName())){
                                SmartAnimalAspect.showBeginLog();
                                result = method.invoke(bean,args);
                                // 进行返回通知的处理
                                SmartAnimalAspect.showSuccessLog();
                            }else {
                                result = method.invoke(bean,args);
                            }
                            return result;
                        }
                    });
            // 如果bean是需要返回代理对象的,这里就直接返回代理对象
            return proxyInstance;
        }
        // 如果不需要AOP处理,我们就返回原生对象
        return bean;
    }
}

看到这里不知道小伙伴们有种似曾相识的感juo。没错!这里就是我们【AOP引入篇】中提到的动态代理。我们因为是简单演示,所以这里只是在postProcessAfterInitialization()中做了处理。

测试类:

package com.jl.spring;

import com.jl.spring.component.MonsterService;
import com.jl.spring.component.SmartAnimalable;
import com.jl.spring.ioc.JlSpringApplicationContext;
import com.jl.spring.ioc.JlSpringConfig;

public class AppMain {
    public static void main(String[] args) {
        JlSpringApplicationContext jlSpringApplicationContext =
                new JlSpringApplicationContext(JlSpringConfig.class);
 // 测试AOP是否生效
        SmartAnimalable smartDog = (SmartAnimalable)jlSpringApplicationContext.getBean("smartDog");
        smartDog.getSum(10,2);
        smartDog.getSub(10,2);
        System.out.println(smartDog.getClass());
    }
}

结果截图:
在这里插入图片描述
我们只对getSum做了增强,所以结果显示也只是对getSum的增强。
其实真正的AOP也和这个很类似,但他对很多内容进行了封装。我们下边模拟一个AOP:

package com.jl.spring.component;

public interface SmartAnimalable {
    float getSum(float i ,float j);
    float getSub(float i ,float j);
}

切面类

package com.jl.spring.component;

import com.jl.spring.annotation.*;

/**
 * 先将SmartAnimalAspect当作一个切面类来使用
 * 后面再改的更加灵活
 */
@Aspect
@Component
public class SmartAnimalAspect {
    @Before(value = "execution float com.jl.spring.component.SmartDog.getSum")
    public static void showBeginLog(){
        System.out.println("前置通知");
    }
    @AfterReturning(value = "execution float com.jl.spring.component.SmartDog.getSum")
    public static void showSuccessLog(){
        System.out.println("返回通知");
    }
}
package com.jl.spring.component;

import com.jl.spring.annotation.Component;

@Component(value="smartDog")
public class SmartDog implements SmartAnimalable{
    @Override
    public float getSum(float i, float j) {
        float res = i+j;
        System.out.println("SmartDog-getSum-res=" + res);
        return res;
    }


    @Override
    public float getSub(float i, float j) {
        float res = i -j;
        System.out.println("SmartDog-getSub-res=" + res);
        return res;
    }
}

测试类:
虽然是测试类,但这里边还对注解进行了处理,有兴趣的小伙伴可以将其抽取出来,做一些完善。

package com.jl.spring;

import com.jl.spring.annotation.AfterReturning;
import com.jl.spring.annotation.Before;
import com.jl.spring.component.SmartAnimalAspect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class JlTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Class<SmartAnimalAspect> smartAnimalAspectClass = SmartAnimalAspect.class;
        for (Method declaredMethod : smartAnimalAspectClass.getDeclaredMethods()) {
            if(declaredMethod.isAnnotationPresent(Before.class)){

                String name = declaredMethod.getName();
                Before annotation = declaredMethod.getAnnotation(Before.class);
                String value = annotation.value();

                Method declaredMethod1 = smartAnimalAspectClass.getDeclaredMethod(declaredMethod.getName());
                declaredMethod1.invoke(smartAnimalAspectClass.newInstance(),null);
            }else if(declaredMethod.isAnnotationPresent(AfterReturning.class)){
                String name = declaredMethod.getName();
                Before annotation = declaredMethod.getAnnotation(Before.class);
                String value = annotation.value();

                Method declaredMethod1 = smartAnimalAspectClass.getDeclaredMethod(declaredMethod.getName());
                declaredMethod1.invoke(smartAnimalAspectClass.newInstance(),null);
            }
        }
    }
}

结果截图:
在这里插入图片描述

总结

以上就是我们手写的一个简易的Spring,主要是帮助大家理解Spring的基本执行流程。

如果文章中有描述不准确或者错误的地方,还望指正。您可以留言📫或者私信我。🙏
最后希望大家多多 关注+点赞+收藏^_^,你们的鼓励是我不断前进的动力!!!
感谢感谢~~~🙏🙏🙏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/51114.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSS伪类使用详解

基本描述 CSS伪类是很常用的功能&#xff0c;主要应用于选择器的关键字&#xff0c;用来改变被选择元素的特殊状态下的样式。 伪类类似于普通CSS类的用法&#xff0c;是对CSS选择器的一种扩展&#xff0c;增强选择器的功能。 目前可用的伪类有大概40多个&#xff0c;少部分有兼…

Spring Bean的生命周期理解

一、Spring Bean的生命周期大的概括起来有四个阶段&#xff1a; 1、实例化 2、属性填充注入 3、初始化使用 4、Bean的销毁 二、如流程图所示 三、步骤说明 1、实例化 实例化一个Bean&#xff0c;即new 2、IOC依赖注入 按照Spring上下文对实例化的Bean进行属性填充注入 3、setB…

昆船智能上市:预计年营收19亿到22.5亿 市值48亿

雷递网 雷建平 11月30日昆船智能技术股份有限公司&#xff08;简称&#xff1a;“昆船智能”&#xff0c;证券代码&#xff1a;301311&#xff09;今日在深交所创业板上市。昆船智能本次发行股票6000万股&#xff0c;发行价为13.88元&#xff0c;募资8.33亿元。昆船智能开盘价为…

2022CTF培训(七)逆向专项练习

附件下载链接 babyre 首先是一个迷宫&#xff0c;由于答案不唯一&#xff0c;因此到 dfs 求出所有路径。 #include <bits/stdc.h>constexpr char s[] "**************.****.**s..*..******.****.***********..***..**..#*..***..***.********************.**..*…

springMVC01,springMVC的执行流程【第一个springMVC例子(XML配置版本):HelloWorld】

springMVC01,springMVC的执行流程【第一个springMVC项目&#xff1a;HelloWorld】springMVC的简介springMVC的执行流程第一个springMVC项目&#xff08;XML配置版本&#xff09;1.创建项目1.1 新建maven项目&#xff1a;1.2 添加web支持1.3 在pom.xml中导入依赖1.4 配置tomcat2…

【云享·人物】华为云AI高级专家白小龙:AI如何释放应用生产力,向AI工程化前行?

摘要&#xff1a;AI技术发展&#xff0c;正由应用落地阶段向效率化生产阶段演进&#xff0c;AI工程化能力将会不断深入业务&#xff0c;释放企业生产力。本文分享自华为云社区《【云享人物】华为云AI高级专家白小龙&#xff1a;AI如何释放应用生产力&#xff0c;向AI工程化前行…

[附源码]Python计算机毕业设计Django飞越青少儿兴趣培训机构管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

旅游景区地图导览系统,传统导览智慧新升级

地图在景区导览中一直扮演着重要角色。 从传统导览的纸质地图&#xff0c;再到智慧导览的电子地图&#xff0c;游客都可以从景区地图上了解到景点名称、游玩路线、服务设施等内容&#xff0c;帮助游客更好地游览景区。 相比传统的纸质地图导览&#xff0c;电子地图导览系统有哪…

计算机组成原理习题课第四章-4(唐朔飞)

计算机组成原理习题课第四章-4&#xff08;唐朔飞&#xff09; ✨欢迎关注&#x1f5b1;点赞&#x1f380;收藏⭐留言✒ &#x1f52e;本文由京与旧铺原创&#xff0c;csdn首发&#xff01; &#x1f618;系列专栏&#xff1a;java学习 &#x1f4bb;首发时间&#xff1a;&…

TIA博途中通用函数库指令FIFO先入先出的具体使用方法

TIA博途中通用函数库指令FIFO先入先出的具体使用方法 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 如下图所示,在TIA博途中添加通用函数库指令,然后在库指令中找到FIFO,鼠标直接拖拽到程序段中,系统会自动生成一…

【毕业设计】10-基于单片机的车站安检门_磁性霍尔传感器系统设计(原理图+源码+仿真工程+答辩论文)

【毕业设计】10-基于单片机的车站安检门/磁性霍尔传感器系统设计&#xff08;原理图源码仿真工程答辩论文&#xff09; 文章目录【毕业设计】10-基于单片机的车站安检门/磁性霍尔传感器系统设计&#xff08;原理图源码仿真工程答辩论文&#xff09;任务书设计说明书摘要设计框架…

https加密解密过程二、名词解析及文件生成

https加密解密过程二、名词解析及文件生成 密钥仓库keystore文件 Keytool是一个Java数据证书的管理工具 &#xff0c;Keytool将密钥(key)和证书(certificates)存在一个称为keystore的文件中 keystore文件的内容其实就是把私钥、公钥以及公钥对应的地址等信息输出为json格式的…

git的基础操作

git的基础操作 一、Git理论 &#xff08;一&#xff09;工作区域 基本概念&#xff1a; 工作区&#xff1a;平时存放项目代码的地方。 暂存区(Stage/Index)&#xff1a;暂存区&#xff0c;用于临时存放你的改动&#xff0c;事实上它只是一个文件&#xff0c;保存即将提交到…

(四)DepthAI-python相关接口:OAK Messages

消息快播&#xff1a;OpenCV众筹了一款ROS2机器人rae&#xff0c;开源、功能强、上手简单。来瞅瞅~ 编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查…

mapstruct常见错误及解决方案

1 问题集合 mapstruct-jdk8 编译报错 我以前项目使用的是mapstruct-jdk8<1.3.1.Final &#xff0c;现在做改造升级&#xff0c;比如springboot升级等&#xff0c;但是报错了 我们去mvn仓库去看下&#xff1a; Deprecated MapStruct artifact containing annotations to …

Postman之CSV或JOSN文件实现数据驱动

目录 一、适用场景 二、接口信息 三、数据驱动实现 3.1.data.文件设置 3.1.1.data.csv文件设置 3.1.2.data.json文件设置 3.3.接口传参设置 3.4.断言设置 四、执行结果 4.1.data.csv执行脚本上传设置 4.2.data.json执行脚本上传设置​ 4.3.执行结果展示 一、适用场…

12 【操作mongodb数据库】

12 【操作mongodb数据库】 1.简介 1.Mongoose是一个让我们可以通过Node来操作MongoDB的模块。2.Mongoose是一个对象文档模型(ODM)库,它对Node原生的MongoDB模块进行了进一步的优化封装&#xff0c;并提供了更多的功能。在大多数情况下&#xff0c;它被用来把结构化的模式应用…

Stable Diffusion 关键词tag语法教程

Stable Diffusion 关键词tag语法教程 AI绘图在线体验 二次元绘图 在线体验地址:Stable Diffusion 模型包括&#xff1a; NovelAI&#xff0c;NovelAI的模型训练使用了数千个网站的数十亿张图片&#xff0c;包括 Pixiv、Twitter、DeviantArt、Tumblr等网站的作品。 Waifu&am…

Flink系列之大数据分布式计算引擎设计实现剖析

声明&#xff1a; 文章中代码及相关语句为自己根据相应理解编写&#xff0c;文章中出现的相关图片为自己实践中的截图和相关技术对应的图片&#xff0c;若有相关异议&#xff0c;请联系删除。感谢。转载请注明出处&#xff0c;感谢。 By luoyepiaoxue2014 B站&#xff…

MySQL基本语句操作

目录 一. MySQKL基本操作命令&#xff08;增&#xff0c;删&#xff0c;该&#xff0c;查&#xff09; 1.1 基本概述 1.2 查看当前服务器种的数据库 1.3 查看数据库结构​编辑 二.SQL语句 三. 创建及删除数据库和表 3.1 创建新的数据库 3.2 创建新的表 ​3.3 删除指…