模拟SpringIOCAOP

news2025/1/13 17:28:49

一、IOC容器

Ioc负责创建,管理实例,向使用者提供实例,ioc就像一个工厂一样,称之为Bean工厂

1.1 Bean工厂的作用

先分析一下Bean工厂应具备的行为

1、需要一个获取实例的方法,根据一个参数获取对应的实例

getBean(String)

2、创建Bean实例

我们需要把Bean的定义信息,告诉Bean工厂,Bean工厂根据Bean的定义信息生成Bean。所以,我们需要一个模型来表示Bean的定义信息,还需要确定Bean工厂提供行为来接手Bean的定义信息

1.2 Bean的定义(BeanDefinition)

Bean定义的作用就是告诉Bean工厂怎么来创建某个类的实例,那么我们分析,获取实例的方式有哪些:

  1. 通过new构造方法
    1. Person p = new Person
  1. 通过静态类方法
class PersonFactory {
    public static Persion getPerson(){
        return new Person()
    }
}
  1. 通过工厂实例的成员方法
class PersonFactory {
    public Persion getPerson(){
        return new Person()
    }
}

那么需要在Bean定义中提供提供什么信息

  1. 通过构造方法,需要类的类型
  2. 通过静态方法,需要类型+方法名
  3. 通过工厂实例的成员,需要实例bean名+方法名

这样一来,Bean定义中应该具备3个基本功能

  1. getBeanClass 获取类的类型
  2. getFactroyMethodName 获取类型和方法
  3. getFactoryBeanName

除了以上方法,我们还可以给Bean一些强的功能,比如是否为单例、创建bean后执行初始方法,销毁bean时执行销毁方法

/**
 * bean定义接口
 */
public interface BeanDefinition {

    String SCOPE_SINGLETION = "singleton";

    String SCOPE_PROTOTYPE = "prototype";

    /**
     * 类
     */
    Class<?> getBeanClass();

    /**
     * Scope
     */
    String getScope();

    /**
     * 是否单例
     */
    boolean isSingleton();

    /**
     * 是否原型
     */
    boolean isPrototype();

    /**
     * 工厂bean名
     */
    String getFactoryBeanName();

    /**
     * 工厂方法名
     */
    String getFactoryMethodName();

    /**
     * 初始化方法
     */
    String getInitMethodName();

    /**
     * 销毁方法
     */
    String getDestroyMethodName();

    boolean isPrimary();

    /**
     * 校验bean定义的合法性
     */
    default boolean validate() {
        // 没定义class,工厂bean或工厂方法没指定,则不合法。
        if (this.getBeanClass() == null) {
            if (StringUtils.isBlank(getFactoryBeanName()) || StringUtils.isBlank(getFactoryMethodName())) {
                return false;
            }
        }

        // 定义了类,又定义工厂bean,不合法
        if (this.getBeanClass() != null && StringUtils.isNotBlank(getFactoryBeanName())) {
            return false;
        }

        return true;
    }

}

同时创建BeanDefinition的一个通用实现类:GenericBeanDefinition。

1.3 Bean的注册(BeanDefinition)

Bean定义清楚后,我们就要考虑BeanDefinition和BeanFactory关联了

在这里我们定义一个BeanDefinitionRegistry来实现Bean定义的注册功能,提供两个方法:

  1. 注册BeanDefinition
  2. 获取BeanDefinition

同时为了区别BeanDefinition,给每一个Bea定义一个唯一的名称,具体代码为:

public interface BeanDefinitionRegistry {
        
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegistException;

    BeanDefinition getBeanDefinition(String beanName);

    boolean containsBeanDefinition(String beanName);

}

1.4 Bean工厂实现(BeanFactory)

有了以上的基础,现在我们需要考虑Bean工厂的实现,实现一个默认的Bean工厂DefaultBeanFactory,提供以下功能:

  1. 实现Bean定义信息的注册
  2. 实现Bean工厂的getBean方法
  3. 实现初始化方法执行
  4. 实现单例要求
  5. 实现容器关闭执行单例的销毁操作

定义注册信息和单例信息的存储集合

public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable {

    protected Map<String, BeanDefinition> beanDefintionMap = new ConcurrentHashMap<>(256);

    private Map<String, Object> singletonBeanMap = new ConcurrentHashMap<>(256);

    private Map<Class<?>, Set<String>> typeMap = new ConcurrentHashMap<>(256);
}

实现Bean定义信息的注册代码:

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
        throws BeanDefinitionRegistException {
    Objects.requireNonNull(beanName, "注册bean需要给入beanName");
    Objects.requireNonNull(beanDefinition, "注册bean需要给入beanDefinition");

    // 校验给入的bean是否合法
    if (!beanDefinition.validate()) {
        throw new BeanDefinitionRegistException("名字为[" + beanName + "] 的bean定义不合法:" + beanDefinition);
    }

    /*Spring中默认是不可覆盖(抛异常),
    可通过参数 spring.main.allow-bean-definition-overriding: true 来允许覆盖*/
    if (this.containsBeanDefinition(beanName)) {
        throw new BeanDefinitionRegistException(
                "名字为[" + beanName + "] 的bean定义已存在:" + this.getBeanDefinition(beanName));
    }

    this.beanDefintionMap.put(beanName, beanDefinition);
}

实现获取Bean定义的方法

@Override
public BeanDefinition getBeanDefinition(String beanName) {
    return this.beanDefintionMap.get(beanName);
}

实现Bean工厂的getBean方法

@Override
public Object getBean(String name) throws Exception {
    return this.doGetBean(name);
}

private Object doGetBean(String beanName) throws Exception {
    Objects.requireNonNull(beanName, "beanName不能为空");

    BeanDefinition bd = this.getBeanDefinition(beanName);
    Objects.requireNonNull(bd, "beanDefinition不能为空");

    Object instance = doCreateInstance(bd);
    return instance;
}

private Object doCreateInstance(BeanDefinition bd) throws Exception {
    Class<?> type = bd.getBeanClass();
    Object instance = null;
    if (type != null) {
        if (StringUtils.isBlank(bd.getFactoryMethodName())) {
            // 构造方法来构造对象
            instance = this.createInstanceByConstructor(bd);
        } else {
            // 静态工厂方法
            instance = this.createInstanceByStaticFactoryMethod(bd);
        }
    } else {
        // 工厂bean方式来构造对象
        instance = this.createInstanceByFactoryBean(bd);
    }

    // 执行初始化方法
    this.doInit(bd, instance);

    return instance;
}

// 构造方法来构造对象
private Object createInstanceByConstructor(BeanDefinition bd)
throws InstantiationException, IllegalAccessException {
    try {
        return bd.getBeanClass().newInstance();
    } catch (SecurityException e1) {
        log.error("创建bean的实例异常,beanDefinition:" + bd, e1);
        throw e1;
    }
}

// 静态工厂方法
private Object createInstanceByStaticFactoryMethod(BeanDefinition bd) throws Exception {
    Class<?> type = bd.getBeanClass();
    Method m = type.getMethod(bd.getFactoryMethodName(), null);
    return m.invoke(type, null);
}

// 工厂bean方式来构造对象
private Object createInstanceByFactoryBean(BeanDefinition bd) throws Exception {
    Object factoryBean = this.doGetBean(bd.getFactoryBeanName());
    Method m = factoryBean.getClass().getMethod(bd.getFactoryMethodName(), null);
    return m.invoke(factoryBean, null);
}

实现初始化方法执行

/**
 * 执行初始化方法
 *
 * @param bd
 * @param instance
 * @throws Exception
 */
private void doInit(BeanDefinition bd, Object instance) throws Exception {
    // 执行初始化方法
    if (StringUtils.isNotBlank(bd.getInitMethodName())) {
        Method m = instance.getClass().getMethod(bd.getInitMethodName(), null);
        m.invoke(instance, null);
    }
}

实现单例要求

protected Object doGetBean(String beanName) throws Exception {
    Objects.requireNonNull(beanName, "beanName不能为空");

    /*单例如何实现?
       单例如何存储?
       如何保证单例?
    */
    Object instance = singletonBeanMap.get(beanName);
    if (instance != null) {
        return instance;
    }

    BeanDefinition bd = this.getBeanDefinition(beanName);
    Objects.requireNonNull(bd, "beanDefinition不能为空");

    //前面已经判断过   == null

    if(bd.isSingleton()) { //如果是单例   DCL
        synchronized (this.singletonBeanMap) { //加锁
            instance = this.singletonBeanMap.get(beanName);
            if(instance == null){//第二次检查
                instance = doCreateInstance(bd);
                this.singletonBeanMap.put(beanName,instance);
            }
        }   // volatile
    }
    else {
        instance = doCreateInstance(bd);
    }

    return instance;
}

实现容器关闭执行单例的销毁操作

@Override
public void close() throws IOException {
// 执行单例实例的销毁方法
for (Entry<String, BeanDefinition> e : this.beanDefintionMap.entrySet()) {
    String beanName = e.getKey();
    BeanDefinition bd = e.getValue();

    if (bd.isSingleton() && StringUtils.isNotBlank(bd.getDestroyMethodName())) {
        Object instance = this.singletonBeanMap.get(beanName);
        try {
            Method m = instance.getClass().getMethod(bd.getDestroyMethodName(), null);
            m.invoke(instance, null);
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
                 | InvocationTargetException e1) {
            log.error("执行bean[" + beanName + "] " + bd + " 的 销毁方法异常!", e1);
        }
    }
}

//疑问:原型Bean如果指定了销毁方法,怎么办?
//原型Bean我们没有管理,没办法对创建的所有Bean调用销毁方法
}

1.5 增强功能

1.5.1 别名增强

用一个map存放别名映射的关系

1.5.2 Type类型增强

上面实现的是根据bean的name获取实例,我们还希望通过Type来获取实例,多以定义接口为:

一般的思路为遍历所有Bean定义,判断Bean类型进行匹配:

但是比较耗费性能,所以我们可以提前把Type和Bean的对应关系存到Map,流程如下:

然后添加getType方法,用于获取Bean的Type,方便registerTypeMap()方法的使用。



public void registerTypeMap() throws Exception{
    //1 得到 type -- name 映射关系 准备工作  在所有的Bean定义信息都注册完成后执行
    for(String name : this.beanDefintionMap.keySet()){
        Class<?> type = this.getType(name);
        //映射本类
        this.registerTypeMap(name,type);
    
        //父类
        this.registerSuperClassTypeMap(name,type);
        //接口
        this.registerInterfaceTypeMap(name,type);
    }

}
@Override
public Class<?> getType(String name) throws Exception{
    BeanDefinition bd = this.getBeanDefinition(name);
    Class<?> type = bd.getBeanClass();
    if (type != null) {
        if (StringUtils.isBlank(bd.getFactoryMethodName())) {
            // 构造方法来构造对象的,Type就是beanClass,不需做什么。
        } else {
            // 静态工厂方法方式的,反射获得Method,再获取Method的返回值类型
            type = type.getDeclaredMethod(bd.getFactoryMethodName(),null).getReturnType();
        }
    } else {
        // 工厂bean方式来构造对象的
        // 获得工厂Bean的Class
        type = this.getType(bd.getFactoryBeanName());
        // 再获得工厂方法的返回值类型
        type = type.getDeclaredMethod(bd.getFactoryMethodName(),null).getReturnType();
    }

    return type;
}

private void registerTypeMap(String name, Class<?> type) {
    Set<String> names2type = this.typeMap.get(type);
    if(names2type == null){
        names2type = new HashSet<>();
        this.typeMap.put(type,names2type);
    }
    names2type.add(name);
}
private void registerSuperClassTypeMap(String name, Class<?> type) {
    Class<?> superClass = type.getSuperclass();
    if(superClass != null && !superClass.equals(Object.class)){
        this.registerTypeMap(name,superClass);
        //递归找父类
        this.registerSuperClassTypeMap(name,superClass);
        //找父类实现的接口注册
        this.registerInterfaceTypeMap(name,superClass);
    }
}


private void registerInterfaceTypeMap(String name, Class<?> type) {
    Class<?>[] interfaces = type.getInterfaces();
    if (interfaces.length > 0){
        for(Class<?> interf : interfaces) {
            this.registerTypeMap(name, interf);
            //递归找父接口
            this.registerInterfaceTypeMap(name,interf);
        }
    }
}

最后就是getBean(Class) 方法的实现了。因为Class对应的类型可能有多个,这时需要通过Primary来处理了。

@Override
public <T> T getBean(Class<T> type) throws Exception {
    /*
    逻辑:
    1 获得其对应的所有的BeanDefinition
    2 如果只有一个,直接获取bean实例返回,否则
    3 遍历找出Primary的
    4 如果primary没有,或大于1个,抛出异常
    5 返回Primary的实例
     */
    Set<String> names = this.typeMap.get(type);
    if(names != null) {
        if(names.size() == 1){
            return (T)this.getBean(names.iterator().next());
        }
        else {
            //找Primary
            BeanDefinition bd = null;
            String primaryName = null;
            StringBuilder nameStrings = new StringBuilder();
            for(String name : names){
                bd = this.getBeanDefinition(name);
                if(bd != null && bd.isPrimary()) {
                    if(primaryName != null){
                        String mess = type + " 类型的Bean存储多个Primary[" + primaryName + "," + name + "]";
                        log.error(mess);
                        throw new Exception(mess);
                    }
                    else {
                        primaryName = name;
                    }
                }
                nameStrings.append(" " + name);
            }

            if(primaryName != null){
                return (T)this.getBean(primaryName);
            }
            else {
                String mess = type + " 类型的Bean存在多个[" + nameStrings + "] 但无法确定Primary";
                log.error(mess);
                throw new Exception(mess);
            }
        }
    }
    return null;
}

@Override
public <T> Map<String, T> getBeansOfType(Class<T> type) throws Exception {
    Set<String> names = this.typeMap.get(type);
    if(names != null) {
        Map<String, T> map = new HashMap<>();
        for(String name : names){
            map.put(name,(T) this.getBean(name));
        }
        return map;
    }
    return null;
}

1.5.3 初始化bean实例

以上我们已经将bean的定义注册到了DefaultBeanFactory中,下面我们定义一个PreBuildBeanFactory,来讲bean的定义进行实例化

public class PreBuildBeanFactory extends DefaultBeanFactory {

    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PreBuildBeanFactory.class);

    public void preInstantiateSingletons() throws Exception {
        synchronized (this.beanDefintionMap) {
            for (Map.Entry<String, BeanDefinition> entry : this.beanDefintionMap.entrySet()) {
                String name = entry.getKey();
                BeanDefinition bd = entry.getValue();
                if (bd.isSingleton()) {
                    this.getBean(name);
                    if (log.isDebugEnabled()) {
                        log.debug("preInstantiate: name=" + name + " " + bd);
                    }
                }
            }
        }
    }
}

二、依赖注入DI

DI依赖注入,说白了就是给Bean对象成员进行赋值,那么哪些地方会有依赖:

  1. 构造参数依赖
  2. 属性依赖

那么依赖注入,就是给构造方法赋值,给属性复制。参数值、属性值都有什么:

构造参数值比如:

public clsss Girl{
     public Girl(String name,int age,char cup,Boy boyfriend){
         ...
     }
}

直接属性赋值比如:

  1. 基本数据类型:List、String、int
  2. 数组、集合、map

2.1 构造注入

通过ioc注入我们需要借助反射处理,首先,先将构造参数值存起来,按照构造方法参数的顺序存储到列表;然后根据构造方法参数列表的类型找到对应的构造器,通过反射创建对象的时候,将构造参数值列表传入进去,就完成了复制。

这里我们先思考下,如何表示Bean依赖呢?直接值我们可以直接放入构造方法列表里,如果是其他Bean依赖呢,所以需要一个模型来表示依赖:BeanReference;

2.2 BeanReference

BeanReference就是来表示Bean依赖,也就是这个属性依赖哪个类型的Bean

可以根据name依赖,也可以根据Type来依赖,如何区分是直接值还是Bean依赖?

可以通过:

if(args instanceof BeanDefinition)

来判断这个属性是不是Bean依赖,也就是我们对一个Bean定义时,将需要通过构造参数注入的值,生成BeanReference对象,存储到BeanDefinition中List<?> constructorArgumentValues;

2.3 BeanFactory修改,增加构造方式注入

当前我们创建bean,通过三种方式:构造方法创建、工厂静态类、工厂成员方法,需要一次对这三个方法做修改。

我们在通过构造方法创建其实是通过无参构造方法处理的,这里我们需要修改成有参构造方法。

2.3.1 构造方法创建对象的修改

获取构造方法参数值,找到对应的构造方法,反射创建对象时,将构造方法参数值传入进去。

这里我们找构造方法时,需要注意

1、根据参数类型精确匹配,如果没找到进入第二步

2、获取所有构造方法,遍历构造方法,通过参数数量进行过滤,比较形参与实参的类型,判断继承关系,如果类型是父类或者接口,则匹配成功

outer:
    for (Constructor<?> ct0 : bd.getBeanClass().getConstructors()) {
        Class<?>[] paramterTypes = ct0.getParameterTypes();
        if (paramterTypes.length == args.length) {   //通过参数数量过滤
            for (int i = 0; i < paramterTypes.length; i++) { //再依次比对形参类型与实参类型是否匹配
                if (!paramterTypes[i].isAssignableFrom(args[i].getClass())) {
                    continue outer; //参数类型不可赋值(不匹配),跳到外层循环,继续下一个
                }
            }

            ct = ct0;  //匹配上了
            break outer;
        }
    }

所以完成所有改造

改造前:

// 构造方法来构造对象
private Object createInstanceByConstructor(BeanDefinition bd)
        throws InstantiationException, IllegalAccessException {
    try {
        return bd.getBeanClass().newInstance();
    } catch (SecurityException e1) {
        log.error("创建bean的实例异常,beanDefinition:" + bd, e1);
        throw e1;
    }
}

改造后


// 构造方法来构造对象
private Object createInstanceByConstructor(BeanDefinition bd)
        throws Exception {
    /*
      构造参数依赖注入,这里需要做些什么?
    */
    /*1 得到真正的参数值,因为
    List<?> constructorArgumentValues = bd.getConstructorArgumentValues();
    constructorArgumentValues 中可能有 BeanReference
    */
    Object[] args = this.getConstructorArgumentValues(bd);

    // 2 判定该调用哪个构造方法来创建实例
    return this.determineConstructor(bd, args).newInstance(args);
}


private Object[] getConstructorArgumentValues(BeanDefinition bd) throws Exception {

    List<?> defs = bd.getConstructorArgumentValues();
    if (CollectionUtils.isEmpty(defs)) {
        return null;
    }

    Object[] values = new Object[defs.size()];
    int i = 0;
    for (Object originalValue : defs) {
        values[i++] = getOneArgumentRealValue(originalValue); //获取真正参数值的逻辑应该是怎样的?
    }
    return values;
}

private Object getOneArgumentRealValue(Object originalValue) throws Exception {
    //获取真正参数值,主要是处理BeanReference,得到真正的Bean实例
    Object realValue = null;
    if (originalValue != null) {
        if (originalValue instanceof BeanReference) {
            BeanReference br = (BeanReference) originalValue;
            if (StringUtils.isNotBlank(br.getBeanName())) {
                realValue = this.getBean(br.getBeanName());
            } else if (br.getType() != null) {
                realValue = this.getBean(br.getType());
            }
        } else if (originalValue instanceof Object[]) {
            // TODO 处理集合中的bean引用
        } else if (originalValue instanceof Collection) {
            // TODO 处理集合中的bean引用
        } else if (originalValue instanceof Map) {
            // TODO 处理Map中的bean引用
        } else {
            realValue = originalValue;
        }
        //请大家完成上面未完成的逻辑
    }
        return realValue;
    }

    private Constructor<?> determineConstructor(BeanDefinition bd, Object[] args) throws Exception {
        /*判定构造方法的逻辑应是怎样的?
        1 先根据参数的类型进行精确匹配查找,如未找到,则进行第2步查找;
        2获得所有的构造方法,遍历,通过参数数量过滤,再比对形参类型与实参类型。
        * */

        Constructor<?> ct = null;

        //没有参数,则用无参构造方法
        if (args == null) {
            return bd.getBeanClass().getConstructor(null);
        }

        // 1 先根据参数的类型进行精确匹配查找
        Class<?>[] paramTypes = new Class[args.length];
        int j = 0;
        for (Object p : args) {
            paramTypes[j++] = p.getClass();
        }
        try {
            ct = bd.getBeanClass().getConstructor(paramTypes);
        } catch (Exception e) {
            // 这个异常不需要处理
        }

        if (ct == null) {
            // 2 没有精确参数类型匹配的,获得所有的构造方法,遍历,通过参数数量过滤,再比对形参类型与实参类型。
            // 判断逻辑:先判断参数数量,再依次比对形参类型与实参类型
            outer:
            for (Constructor<?> ct0 : bd.getBeanClass().getConstructors()) {
                Class<?>[] paramterTypes = ct0.getParameterTypes();
                if (paramterTypes.length == args.length) {   //通过参数数量过滤
                    for (int i = 0; i < paramterTypes.length; i++) { //再依次比对形参类型与实参类型是否匹配
                        if (!paramterTypes[i].isAssignableFrom(args[i].getClass())) {
                            continue outer; //参数类型不可赋值(不匹配),跳到外层循环,继续下一个
                        }
                    }

                    ct = ct0;  //匹配上了
                    break outer;
                }
            }
        }

        if (ct != null) {
            return ct;
        } else {
            throw new Exception("不存在对应的构造方法!" + bd);
        }
    }

2.4 循环依赖问题

你完整创建要依赖与我,我的完整创建也依赖于你。相互依赖从而没法完整创建造成失败。

我们通过构造参数依赖是完全可能出现上面的情况的,那么这种情况我们能解决吗?构造依赖的情况我们是解决不了的。那么为了预防出现,我们要发现循环依赖,并报错处理

创建bean的时候,将正在创建的bean放入一个集合,在创建完成时,将这个bean从集合删除;

比如:如上图,创建A对象的过程:

1、创建A对象时,需要创建B对象,则进入创建B对象流程

2、创建B对象时,需要创建C对象,则进入创建C对象流程

3、创建C对象时,需要A对象;造成循环依赖

解决方法:我们通过一个集合存储A对象创建时需要的对象;

1、A对象创建,先判断集合有没有A,没有则对象A放入list.add(A.class),发现需要B对象,则去获取B

2、获取B对象,先判断集合有没有B,没有则将B放入list.add(B.class); 此时集合里有A.class,B.class,进行创建B对象,发现需要C对象,则获取C对象

3、获取C对象,先判断集合有没有C,没有则放入list.add(C.class)此时集合里有A.class,B.class,C.class;进行创建C对象,发现需要A对象,则进行获取A对象

4、获取A对象的时候,在判断集合,发现集合已经有A了,则此时判断为循环依赖,此时抛出一个异常处理

protected Object doGetBean(String beanName) throws Exception {
    Objects.requireNonNull(beanName, "beanName不能为空");

    Object instance = singletonBeanMap.get(beanName);

    if (instance != null) {
        return instance;
    }

    BeanDefinition bd = this.getBeanDefinition(beanName);
    Objects.requireNonNull(bd, "beanDefinition不能为空");

    // 检测循环依赖
    Set<String> buildingBeans = this.buildingBeansRecordor.get();
    if (buildingBeans == null) {
        buildingBeans = new HashSet<>();
        this.buildingBeansRecordor.set(buildingBeans);
    }

    // 检测循环依赖
    if (buildingBeans.contains(beanName)) {
        throw new Exception(beanName + " 循环依赖!" + buildingBeans);
    }
    // 记录正在创建的Bean
    buildingBeans.add(beanName);

    if(bd.isSingleton()) { //如果是单例
        synchronized (this.singletonBeanMap) { //加锁
            instance = this.singletonBeanMap.get(beanName);
            if(instance == null){//第二次检查
                instance = doCreateInstance(bd);
                this.singletonBeanMap.put(beanName,instance);
            }
        }
    }
    else {
        instance = doCreateInstance(bd);
    }

    // 创建好实例后,移除创建中记录
    buildingBeans.remove(beanName);

    return instance;
}

2.5 属性注入

上面搞定了构造注入的方式,接下来我们看下属性注入的方式

2.5.1 PropertyValue

我们需要第一个实体类PropertyValue,来记录这个Bean需要注入的相关属性和值

我们对一个Bean定义时,将需要依赖注入的属性,生成PropertyValue对象,存储到BeanDefinition中List<PropertyValue> propertyValues;

2.5.2 BeanFactory修改

然后我们在BeanFactory的默认实现DefaultBeanFactory中实现属性值的依赖注入。创建完对象后,调用属性值注入

private Object doCreateInstance(String beanName,BeanDefinition bd) throws Exception {
    Class<?> type = bd.getBeanClass();
    Object instance = null;
    if (type != null) {
        if (StringUtils.isBlank(bd.getFactoryMethodName())) {
            // 构造方法来构造对象
            instance = this.createInstanceByConstructor(bd);
        } else {
            // 静态工厂方法
            instance = this.createInstanceByStaticFactoryMethod(bd);
        }
    } else {
        // 工厂bean方式来构造对象
        instance = this.createInstanceByFactoryBean(bd);
    }

    // 给入属性依赖
    this.setPropertyDIValues(bd, instance);

    // 执行初始化方法
    this.doInit(bd, instance);

    return instance;
}

给属性值注入

// 给入属性依赖
private void setPropertyDIValues(BeanDefinition bd, Object instance) throws Exception {
    if (CollectionUtils.isEmpty(bd.getPropertyValues())) {
        return;
    }
    for (PropertyValue pv : bd.getPropertyValues()) {
        if (StringUtils.isBlank(pv.getName())) {
            continue;
        }
        Class<?> clazz = instance.getClass();
        Field p = clazz.getDeclaredField(pv.getName());
        //暴力访问  private
        p.setAccessible(true);
        p.set(instance, this.getOneArgumentRealValue(pv.getValue()));

    }
}

2.5.3 循环依赖问题

属性注入的时候,也会有循环依赖问题,这种情况的解决办法,先创建需要的对象,最后进行复制

Boy b = new Boy();
Girl g = new Girl();
b.setGirl(g);
g.setBoy(b);

针对这种情况,我们可以通过提前暴漏来解决,也就是创建对象后,将这个对象放入一个缓存集合,当创建对象需要依赖其他对象时,从这个缓存集合里取

private Object doCreateInstance(String beanName,BeanDefinition bd) throws Exception {
    Class<?> type = bd.getBeanClass();
    Object instance = null;
    if (type != null) {
        if (StringUtils.isBlank(bd.getFactoryMethodName())) {
            // 构造方法来构造对象
            instance = this.createInstanceByConstructor(bd);
        } else {
            // 静态工厂方法
            instance = this.createInstanceByStaticFactoryMethod(bd);
        }
    } else {
        // 工厂bean方式来构造对象
        instance = this.createInstanceByFactoryBean(bd);
    }

    this.doEarlyExposeBuildingBeans(beanName,instance);

    // 给入属性依赖
    this.setPropertyDIValues(bd, instance);

    this.removeEarlyExposeBuildingBeans(beanName,instance);

    // 执行初始化方法
    this.doInit(bd, instance);

    return instance;
}

private void doEarlyExposeBuildingBeans(String beanName, Object instance) {
    Map<String,Object> earlyExposeBuildingBeansMap = earlyExposeBuildingBeans.get();
    if(earlyExposeBuildingBeansMap == null) {
        earlyExposeBuildingBeansMap = new HashMap<>();
        earlyExposeBuildingBeans.set(earlyExposeBuildingBeansMap);
    }
    earlyExposeBuildingBeansMap.put(beanName,instance);
}
private void removeEarlyExposeBuildingBeans(String beanName, Object instance) {
    earlyExposeBuildingBeans.get().remove(beanName);
}

三、AOP

AOP能做什么:

1、对功能进行增强,增强的内容叫: Advice通知

2、可以被选择来进行增强的方法点:Join points

3、被增强方法的位置叫:Pointcuts 切入点

以上3条,可以诚挚为Aspect切面

4、不改变原来类的代码,Weaving植入

所以经过上面分析,设计AOP功能,需要响应的4个组件

3.1 Advice 通知

Advice增强功能,是用户提供的,我们来使用,就是需要用户编写Advice内容,框架来实现将Advice织入对应的方法诶之,

那么我们怎么拿到用户的写的Advice代码呢?面向接口编程,用户实现我们提供的接口,在这里我们定义一个空接口

为什么是空接口后面再讲

3.1.1 Advice插入的位置

如图,我们可以将Advice插入5个位置,分别是:

  1. 前置通知,被增强的方法执行前执行
  2. 后置通知,被增强的方法执行后执行
  3. 环绕通知,被增强的方法执行前和执行后都执行
  4. 异常处理通知,被增强的方法出现异常时执行
  5. 最终通知,被增强的方法执行完毕后执行

下面我们一个一个分析各种通知

3.1.2 前置通知

在方法执行前进行增强。对方法进行增强,我们需要知道增强的方法,和增强方法的位置,所以需要一些参数

运行时方法有哪些信息呢:

  1. 方法本身Method
  2. 方法所属的对象
  3. 方法的参数

前置增强返回值是什么,不需要返回值

综上我们定义出前置通知接口

public interface MethodBeforeAdvice extends Advice {

    /**
     * 实现该方法进行前置增强
     * 
     * @param method
     *            被增强的方法
     * @param args
     *            方法的参数
     * @param target
     *            被增强的目标对象
     * @throws Throwable
     */
    void before(Method method, Object[] args, Object target) throws Throwable;
}

3.1.3 后置通知

被增强法方法执行之后增强

它需要的参数:

  1. 方法本身
  2. 方法所属的对象
  3. 方法的参数
  4. 方法的返回值

返回值是什么,这就要看你的设计,如果设计成、需要更改返回结果,如果需要则返回结果,如果不需要更改则不需要返回结果

定义出后置通知接口

public interface AfterReturningAdvice extends Advice {
    /**
     * 实现该方法,提供AfterRetun增强
     * 
     * @param returnValue
     *            返回值
     * @param method
     *            被增强的方法
     * @param args
     *            方法的参数
     * @param target
     *            方法的所属对象
     * @throws Throwable
     */
    void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
}

3.1.4 环绕通知

包裹方法进行增强,被增强方法前后都执行

它需要的参数

  1. 方法本身
  2. 方法所属的对象
  3. 方法参数

它需要返回值吗,因为方法被它报过,则方法是由它执行,它需要返回方法的返回值

定义环绕通知接口

public interface MethodInterceptor extends Advice {
    /**
     * 对方法进行环绕(前置、后置)增强、异常处理增强,方法实现中需调用目标方法。
     * 
     * @param method
     *            被增强的方法
     * @param args
     *            方法的参数
     * @param target
     *            方法所属对象
     * @return Object 返回值
     * @throws Throwable
     */
    Object invoke(Method method, Object[] args, Object target) throws Throwable;
}

3.1.5 异常通知

对方法执行时的异常,进行增强处理

它需要的参数:

  1. 一定要Exception
  2. 可能需要方法本身
  3. 可能需要方法所属的对象
  4. 可能需要方法的参数

它的返回值:同样看你的设计,需要修改返回结果则需要返回值不需要则该则不用返回值

3.1.6 最终通知

方法执行后在执行

它需要的参数

  1. 方法本身
  2. 方法所在的对象
  3. 方法的参数
  4. 方法的返回值
public interface AfterAdvice extends Advice {
    /**
     * 实现该方法,提供后置增强
     * 
     * @param returnValue
     *            返回值
     * @param method
     *            被增强的方法
     * @param args
     *            方法的参数
     * @param target
     *            方法的所属对象
     * @throws Throwable
     */
    void after(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
}

所以综上,Advice的设计如下类图

3.2 Pointcut 切入点

被增强方法的切入点,它需要用户进行指定,而且用户可以指定多个方法作为切入点。

分析:切入点是由用户指定可以在哪些方法上进行增强,那么这些方法的切入点如何表示

  1. 执行那些方法,是不是一个描述信息
  2. 如果执行一个方法
  3. 如果有重载怎么办

所以以上三点就是一个方法的签名表示

比如:

com.boge.spring.aop.Girl.dbj(Boy,Time)


进一步分析,为了更多的灵活性,还需要描述一些指定一类的某些方法:

某个包喜爱某个类的某个方法

某个包下某个类中的所有方法

某个包下某个类以do开头的方法

某个报下以serviceer结尾的类中的以do开头的方法

所以我们需要一个表达式能灵活描述上面的信息,表达式的内容有:

而且,每一个部分要能模糊匹配,方法参数类型可以有多个;所以基于我们设计的表达式,要能够匹配到类、匹配到方法;实现上,有两种,1、正则表达式,2、AspectJ表达式

我们可以选择AspectJ表达式

AspectJ官网:Eclipse AspectJ™ | projects.eclipse.org


execution表达式中就是方法的签名,中括号的部分可以省略,各部分间隔用空格分开,还可以使用以下符号

例子:

execution(public (. .))
指定切入点为:任意公共方法。
execution( set (. .))
指定切入点为:任何一个以“set”开始的方法。
execution( com.xyz.service..(. .))
指定切入点为:定义在service包里的任意类的任意方法。
execution( com.xyz.service. ..(. .))
指定切入点为:定义在service包或者子包里的任意类的任意方法。“..”出现在类名中时,
后面必须跟“”,表示包、子包下的所有类。
execution( .service..(. .))
指定只有一级包下的serivce子包下所有类(接口)中的所有方法为切入点
execution( . .service..*(. .))
指定所有包下的serivce子包下所有类(接口)中的所有方法为切入点

3.2.1 Piubtcut类设计

首先我们分析下:

  1. 切入点应该具有的属性-》切入点表达式
  2. 切入点对外提供什么行为,匹配类和方法是不是我们的切入点
  3. 切入点用来做什么
    1. 对类和方法进行匹配,切入点提供匹配类和方法的行为
  1. 如果需要灵活扩展切入点,如何设计
    1. 面相接口编程,定义好基本行为,不管那些方案都能实现匹配类和匹配方法

针对以上,我们定义出Pointcut接口,具有两个行为:1、匹配类对象;2、匹配方法;并且对它有两个实现,一个是正则表达式实现,另一个是AspectJ实现

public interface Pointcut {

    boolean matchsClass(Class<?> targetClass);

    boolean matchsMethod(Method method, Class<?> targetClass);
}

然后来看看AspectJ的实现

public class AspectJExpressionPointcut implements Pointcut {

    private static PointcutParser pp = PointcutParser
            .getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();

    private String expression;

    private PointcutExpression pe;

    public AspectJExpressionPointcut(String expression) {
        super();
        this.expression = expression;
        pe = pp.parsePointcutExpression(expression);
    }

    @Override
    public boolean matchsClass(Class<?> targetClass) {
        return pe.couldMatchJoinPointsInType(targetClass);
    }

    @Override
    public boolean matchsMethod(Method method, Class<?> targetClass) {
        ShadowMatch sm = pe.matchesMethodExecution(method);
        return sm.alwaysMatches();
    }

    public String getExpression() {
        return expression;
    }

}

3.3 Aspect 切面

通过上面,我们得到了Advice(增强内容和增强的通知点)、Pointcut(切点,匹配需要增强的类和方法)二者结合就可以实现AOP了,但这二者如何组合呢,为此我们创建一个接口来管理Advisor

3.3.1 Advisor 接口

为用户提供更简单的外观,Advisor(通知者)组合Advice和Pointcut。

扩展的形式比较多:

或者


3.3 织入

织入要做的就是把用户提供的增强功能,加到指定的方法

分析一下

  1. 在什么时候织入
    1. 在创建bean的时候,在bean初始化,再对其增强
  1. 如何确定bean要增强
    1. 对beanlei和方法挨个匹配用户配置的切面,如果有匹配成功就要增强
  1. 如何实现织入
    1. 代理方式

3.3.1 织入的设计

织入的设计

织入的逻辑卸载哪里?

如果写入BeanFactory,后续有其他需求是不是也要修改BeanFactory,这样BeanFactory会耦合度越来越高,不利于扩展,那么如何实现?

在bean的生产过程中,我们可以使用观察者模式,通过各个节点加入扩展点

3.3.1.1 回顾一下观察者模式

观察者模式是一种一对多的通知机制。当一个对象(称为“被观察者”)的状态发生变化时,会自动通知依赖它的其他对象(称为“观察者”)。这样,观察者们就可以根据被观察者的变化来做出相应的反应。

生活中的例子

想象一下:

你订阅了一个天气预报服务(被观察者)。

每当天气变化时,天气预报服务会给你发送一条通知(通知观察者)。

你可以根据通知决定要不要带伞、穿暖一些等(观察者的响应)。

观察者模式的核心在于:被观察者负责维护观察者的列表,当状态改变时,通知这些观察者。

在 Nacos 中的应用

在 Nacos 中,观察者模式主要体现在配置管理和服务注册与发现中。例如:

  1. 配置管理:
    1. 客户端(观察者)订阅了某个配置。
    2. 当配置变更时,Nacos(被观察者)会通知所有订阅该配置的客户端。
  1. 服务发现:
    1. 客户端订阅了某个服务。
    2. 当服务上下线时,Nacos 会通知所有订阅该服务的客户端,让它们感知变化。

举个代码例子(配置管理)

// 被观察者
class NacosConfig {
    private String configValue; // 配置内容
    private List<Observer> observers = new ArrayList<>(); // 观察者列表

    // 添加观察者
    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    // 修改配置并通知观察者
    public void updateConfig(String newValue) {
        this.configValue = newValue;
        notifyObservers();
    }

    // 通知所有观察者
    private void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(configValue);
        }
    }
}

// 观察者接口
interface Observer {
    void update(String newValue);
}

// 具体的观察者
class Client implements Observer {
    private String name;

    public Client(String name) {
        this.name = name;
    }

    @Override
    public void update(String newValue) {
        System.out.println(name + " 收到了配置更新: " + newValue);
    }
}
public class ObserverPatternExample {
    public static void main(String[] args) {
        // 创建被观察者(Nacos 配置中心)
        NacosConfig nacosConfig = new NacosConfig();

        // 创建多个观察者(客户端)
        Client client1 = new Client("客户端1");
        Client client2 = new Client("客户端2");

        // 将观察者订阅到被观察者
        nacosConfig.addObserver(client1);
        nacosConfig.addObserver(client2);

        // 更新配置
        nacosConfig.updateConfig("新配置值:服务超时时间=5000ms");
        // 输出:
        // 客户端1 收到了配置更新: 新配置值:服务超时时间=5000ms
        // 客户端2 收到了配置更新: 新配置值:服务超时时间=5000ms
    }
}

所以,我们就用观察着模式,加入一个Beand的后置处理器BeanPostProcessor;

public interface BeanPostProcessor {

    default Object postProcessBeforeInitialization(Object bean, String beanName) throws Throwable {
        return bean;
    }

    default Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable {
        return bean;
    }
}

在Bean对象初始化前后分别调用前后置处理器

private Object doCreateInstance(String beanName, BeanDefinition bd) throws Throwable {
    Class<?> type = bd.getBeanClass();
    Object instance = null;
    if (type != null) {
        if (StringUtils.isBlank(bd.getFactoryMethodName())) {
            // 构造方法来构造对象
            instance = this.createInstanceByConstructor(bd);
        } else {
            // 静态工厂方法
            instance = this.createInstanceByStaticFactoryMethod(bd);
        }
    } else {
        // 工厂bean方式来构造对象
        instance = this.createInstanceByFactoryBean(bd);
    }

    this.doEarlyExposeBuildingBeans(beanName,instance);

    // 给入属性依赖
    this.setPropertyDIValues(bd, instance);

    this.removeEarlyExposeBuildingBeans(beanName,instance);

    // 应用bean初始化前的处理
    instance = this.applyPostProcessBeforeInitialization(instance, beanName);

    // 执行初始化方法
    this.doInit(bd, instance);

    // 应用bean初始化后的处理
    instance = this.applyPostProcessAfterInitialization(instance, beanName);


    return instance;
}

// 应用bean初始化前的处理
private Object applyPostProcessBeforeInitialization(Object bean, String beanName) throws Throwable {
    for (BeanPostProcessor bpp : this.beanPostProcessors) {
        bean = bpp.postProcessBeforeInitialization(bean, beanName);
    }
    return bean;
}

// 应用bean初始化后的处理
private Object applyPostProcessAfterInitialization(Object bean, String beanName) throws Throwable {
    for (BeanPostProcessor bpp : this.beanPostProcessors) {
        bean = bpp.postProcessAfterInitialization(bean, beanName);
    }
    return bean;
}

还要定义一个Bean前后置处理器的注册


public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable {
    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultBeanFactory.class);

    protected Map<String, BeanDefinition> beanDefintionMap = new ConcurrentHashMap<>(256);

    private Map<String, Object> singletonBeanMap = new ConcurrentHashMap<>(256);

    private Map<Class<?>, Set<String>> typeMap = new ConcurrentHashMap<>(256);

    private ThreadLocal<Set<String>> buildingBeansRecordor = new ThreadLocal<>();

    private List<BeanPostProcessor> beanPostProcessors = Collections.synchronizedList(new ArrayList<>());

    @Override
    public void registerBeanPostProcessor(BeanPostProcessor bpp) {
        this.beanPostProcessors.add(bpp);
        if (bpp instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bpp).setBeanFactory(this);
        }
    }

3.3.1 织入的实现

前面我们已经定义好Bean的前置处理和后置处理,那么实现具体的织入就需要来看看在对应前置和后置方法中我们要实现的功能

3.3.1.1 判断是否需要增强

如何判断Bean是否需要增强,那就是看这个Bean是否满足切点表达式,我们需要拿到Bean所属的类和所有方法,然后遍历Advisor,去除advisor中的Pointcut来匹配类和方法

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable {

    //不能对Advisor 和 Advice 类型的bean作处理
    if(bean instanceof  Advisor || bean instanceof Advice){
        return bean;
    }
    /*逻辑
    1 判断Bean是否需要增强
    2 创建代理来实现增强
    */

    //1 判断Bean是否需要增强
    List<Advisor> matchAdvisors = getMatchedAdvisors(bean, beanName);

    // 2如有切面切中,创建代理来实现增强
    if (CollectionUtils.isNotEmpty(matchAdvisors)) {
        bean = this.createProxy(bean, beanName, matchAdvisors);
    }

    return bean;
}

获取bean的class和所有方法,遍历所有Advisor,用每个Advisor队class和方法做匹配,匹配到的放在一个集合中

private List<Advisor> getMatchedAdvisors(Object bean, String beanName) throws Throwable {
    //第一次执行该方法,先从BeanFactory中得到用户配置的所有切面Advisor
    if (!gettedAllAdvisors) {
        synchronized (this) {
            if (!gettedAllAdvisors) {
                advisors = this.beanFactory.getBeansOfTypeList(Advisor.class);
                gettedAllAdvisors = true;
            }
        }
    }

    //如果没有配置切面
    if (CollectionUtils.isEmpty(this.advisors)) {
        return null;
    }

    //有配置切面
    // 得到Bean的类、所有的方法
    Class<?> beanClass = bean.getClass();
    List<Method> allMethods = this.getAllMethodForClass(beanClass);

    // 存放匹配的Advisor的list
    List<Advisor> matchAdvisors = new ArrayList<>();
    // 遍历Advisor来找匹配的
    for (Advisor ad : this.advisors) {
        if (ad instanceof PointcutAdvisor) {
            if (isPointcutMatchBean((PointcutAdvisor) ad, beanClass, allMethods)) {
                matchAdvisors.add(ad);
            }
        }
    }
    return matchAdvisors;
}

private List<Method> getAllMethodForClass(Class<?> beanClass) {
    //注意需要获得本类以及所实现的接口的方法
    List<Method> allMethods = new LinkedList<>();
    Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(beanClass));
    classes.add(beanClass);
    for (Class<?> clazz : classes) {
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method m : methods) {
            allMethods.add(m);
        }
    }

    return allMethods;
}

private boolean isPointcutMatchBean(PointcutAdvisor pa, Class<?> beanClass, List<Method> methods) {
    Pointcut p = pa.getPointcut();

    // 首先判断类是否匹配
    if (!p.matchsClass(beanClass)) {
        return false;
    }

    // 再判断是否有方法匹配
    for (Method method : methods) {
        if (p.matchsMethod(method, beanClass)) {
            return true;
        }
    }
    return false;
}

3.3.1.2 代理对象

我们先复习一下动态代理模式

JDK动态代理

JDK 动态代理是 Java 提供的一种机制,可以在运行时为目标对象创建一个代理对象,并动态地定义代理对象的行为。通过代理对象,可以间接地访问目标对象,同时可以在方法调用前后添加一些额外的逻辑(比如日志记录、权限验证等)。

JDK 动态代理的核心是:
  1. java.lang.reflect.Proxy:用来生成代理类和代理实例。
  2. java.lang.reflect.InvocationHandler:定义了代理对象的方法调用逻辑。
JDK 动态代理的局限

JDK 动态代理只能代理实现了接口的类,如果目标类没有实现接口,可以使用 CGLIB 等其他动态代理技术。

JDK 动态代理的实现步骤
  1. 定义接口和目标类
// 定义一个接口
public interface UserService {
    void addUser(String name);
    void deleteUser(String name);
}

// 实现类(目标对象)
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("添加用户: " + name);
    }

    @Override
    public void deleteUser(String name) {
        System.out.println("删除用户: " + name);
    }
}
  1. 实现 InvocationHandler 接口

InvocationHandler 是代理逻辑的核心,所有对代理对象方法的调用都会转发到 InvocationHandlerinvoke 方法中。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class LoggingHandler implements InvocationHandler {
    private final Object target; // 被代理的目标对象

    public LoggingHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在方法执行前打印日志
        System.out.println("调用方法: " + method.getName() + ", 参数: " + (args != null ? args[0] : "无"));

        // 调用目标对象的方法
        Object result = method.invoke(target, args);

        // 在方法执行后打印日志
        System.out.println("方法 " + method.getName() + " 执行完毕");

        return result;
    }
}
  1. 通过 Proxy.newProxyInstance 方法为目标对象创建代理对象。
import java.lang.reflect.Proxy;

public class DynamicProxyExample {
    public static void main(String[] args) {
        // 创建目标对象
        UserService target = new UserServiceImpl();

        // 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),      // 类加载器
            target.getClass().getInterfaces(),       // 目标对象实现的接口
            new LoggingHandler(target)               // 代理逻辑
        );

        // 调用代理对象的方法
        proxy.addUser("小明");
        proxy.deleteUser("小明");
    }
}
Cglib 动态代理

Cglib(Code Generation Library)是一种基于字节码生成的动态代理技术,它与 JDK 动态代理的不同之处在于:

  • JDK 动态代理只能代理实现了接口的类
  • Cglib 动态代理可以代理没有实现接口的类,直接为目标类创建子类,并在子类中拦截方法调用。

Cglib 动态代理通过继承目标类并重写其方法来实现代理,因此不能代理 final 类或 final 方法(因为它们无法被继承或重写)。

核心类:

  • Enhancer:Cglib 的核心类,用于创建代理对象。
  • MethodInterceptor:Cglib 提供的接口,用于定义拦截逻辑。
Cglib 动态代理的实现步骤
  1. 定义目标类
public class UserService {
    public void addUser(String name) {
        System.out.println("添加用户: " + name);
    }

    public void deleteUser(String name) {
        System.out.println("删除用户: " + name);
    }
}
  1. 创建代理逻辑
    1. 实现 MethodInterceptor 接口,定义代理逻辑。
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class LoggingInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 在方法执行前打印日志
        System.out.println("调用方法: " + method.getName() + ", 参数: " + (args != null ? args[0] : "无"));

        // 调用目标类的方法
        Object result = proxy.invokeSuper(obj, args);

        // 在方法执行后打印日志
        System.out.println("方法 " + method.getName() + " 执行完毕");

        return result;
    }
}
  1. 创建代理对象

使用 Enhancer 来生成目标类的代理对象。

import net.sf.cglib.proxy.Enhancer;

public class CglibProxyExample {
    public static void main(String[] args) {
        // 创建 Enhancer 对象
        Enhancer enhancer = new Enhancer();

        // 设置目标类
        enhancer.setSuperclass(UserService.class);

        // 设置回调方法(代理逻辑)
        enhancer.setCallback(new LoggingInterceptor());

        // 创建代理对象
        UserService proxy = (UserService) enhancer.create();

        // 调用代理对象的方法
        proxy.addUser("小明");
        proxy.deleteUser("小明");
    }
}
增强逻辑实现

接下来我们aop代理对象的创建

jdk创建代理类

package com.study.spring.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

import com.study.spring.aop.advisor.Advisor;
import com.study.spring.beans.BeanFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
    private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);

    private String beanName;
    private Object target;
    private List<Advisor> matchAdvisors;

    private BeanFactory beanFactory;

    public JdkDynamicAopProxy(String beanName, Object target, List<Advisor> matchAdvisors, BeanFactory beanFactory) {
        super();
        this.beanName = beanName;
        this.target = target;
        this.matchAdvisors = matchAdvisors;
        this.beanFactory = beanFactory;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory);
    }

    @Override
    public Object getProxy() {
        return this.getProxy(target.getClass().getClassLoader());
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("为" + target + "创建代理。");
        }
        return Proxy.newProxyInstance(classLoader, target.getClass().getInterfaces(), this);
    }

}

Cglib代理实现

package com.study.spring.aop;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;

import com.study.spring.beans.BeanDefinition;
import com.study.spring.beans.BeanFactory;
import com.study.spring.beans.DefaultBeanFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.study.spring.aop.advisor.Advisor;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CglibDynamicAopProxy implements AopProxy, MethodInterceptor {
    private static final Log logger = LogFactory.getLog(CglibDynamicAopProxy.class);
    private static Enhancer enhancer = new Enhancer();

    private String beanName;
    private Object target;

    private List<Advisor> matchAdvisors;

    private BeanFactory beanFactory;

    public CglibDynamicAopProxy(String beanName, Object target, List<Advisor> matchAdvisors, BeanFactory beanFactory) {
        super();
        this.beanName = beanName;
        this.target = target;
        this.matchAdvisors = matchAdvisors;
        this.beanFactory = beanFactory;
    }

    @Override
    public Object getProxy() {
        return this.getProxy(target.getClass().getClassLoader());
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("为" + target + "创建cglib代理。");
        }
        Class<?> superClass = this.target.getClass();
        enhancer.setSuperclass(superClass);
        enhancer.setInterfaces(this.getClass().getInterfaces());
        enhancer.setCallback(this);
        Constructor<?> constructor = null;
        try {
            constructor = superClass.getConstructor(new Class<?>[] {});
        } catch (NoSuchMethodException | SecurityException e) {

        }
        if (constructor != null) {
            return enhancer.create();
        } else {
            BeanDefinition bd = ((DefaultBeanFactory) beanFactory).getBeanDefinition(beanName);
            return enhancer.create(bd.getConstructor().getParameterTypes(), bd.getConstructorArgumentRealValues());
        }
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory);
    }

}

增强实现的流程为:

以上我们对bean创建了代理,下面对这个代理进行增强,

	/**
	 * 对方法应用advices增强
	 * 
	 * @param target
	 * @param method
	 * @param args
	 * @param matchAdvisors
	 * @param proxy
	 * @param beanFactory
	 * @return
	 * @throws Throwable
	 */
	public static Object applyAdvices(Object target, Method method, Object[] args, List<Advisor> matchAdvisors,
			Object proxy, BeanFactory beanFactory) throws Throwable {
		// 这里要做什么?
		// 1、获取要对当前方法进行增强的advice
		List<Object> advices = AopProxyUtils.getShouldApplyAdvices(target.getClass(), method, matchAdvisors,
				beanFactory);
		// 2、如有增强的advice,责任链式增强执行
		if (CollectionUtils.isEmpty(advices)) {
			return method.invoke(target, args);
		} else {
			// 责任链式执行增强
			AopAdviceChainInvocation chain = new AopAdviceChainInvocation(proxy, target, method, args, advices);
			return chain.invoke();
		}
	}

获取当前方法进行进行增强的Advice

/**
 * 获取与方法匹配的切面的advices
 * 
 * @param beanClass
 * @param method
 * @param matchAdvisors
 * @param beanFactory
 * @return
 * @throws Exception
 */
public static List<Object> getShouldApplyAdvices(Class<?> beanClass, Method method, List<Advisor> matchAdvisors,
                                                 BeanFactory beanFactory) throws Throwable {
    if (CollectionUtils.isEmpty(matchAdvisors)) {
        return null;
    }
    List<Object> advices = new ArrayList<>();
    for (Advisor ad : matchAdvisors) {
        if (ad instanceof PointcutAdvisor) {
            if (((PointcutAdvisor) ad).getPointcut().matchsMethod(method, beanClass)) {
                advices.add(beanFactory.getBean(ad.getAdviceBeanName()));
            }
        }
    }

    return advices;
}

增强逻辑插入


	public Object invoke() throws Throwable {
		if (i < this.advices.size()) {
			Object advice = this.advices.get(i++);
			if (advice instanceof MethodBeforeAdvice) {
				// 执行前置增强
				((MethodBeforeAdvice) advice).before(method, args, target);
			} else if (advice instanceof MethodInterceptor) {
				// 执行环绕增强和异常处理增强。注意这里给入的method 和 对象 是invoke方法和链对象
				return ((MethodInterceptor) advice).invoke(invokeMethod, null, this);
			} else if (advice instanceof AfterReturningAdvice) {
				// 当是AfterReturning增强时,先得得到结果,再执行后置增强逻辑
				Object returnValue = this.invoke();
				((AfterReturningAdvice) advice).afterReturning(returnValue, method, args, target);
				return returnValue;
			}
			else if (advice instanceof AfterAdvice) {
				// 当是后置增强时
				Object returnValue = null;
				try {
					returnValue = this.invoke();
				}finally {
					((AfterAdvice) advice).after(returnValue, method, args, target);
				}
				return returnValue;
			}
			else if (advice instanceof ThrowsAdvice) {
				try {
					return this.invoke();
				}catch (Exception e){
					((ThrowsAdvice) advice).afterThrowing(method,args,target,e);
				}
			}else{
                return this.invoke();
            }


		} else {
			return method.invoke(target, args);
		}
}

四、Bean定义配置

通过以上,我们完成了ioc和aop的功能,但要使用,还是非常繁琐的

我们有两种方式简化上面的代码

1.通过xml配置方式

2.通过注解方式

4.1 xml实现方式

我们可以自己写一个解析器,来解析xml文件

4.2 注解实现方式

首先分析下,需要那些注解

  1. 该类是否被定义Bean
    1. @Component,参数:BeanName Scope和Primary @Scope @Primary
  1. 通过配置类配置Bean
    1. @Bean
  1. 初始化方法、销毁方法
    1. @PostConstruct @PreDestory
  1. 构造参数依赖
    1. @Autowired @Value
  1. 属性依赖 @Qualifier

扫描解析注册的流程

定义注解扫描器接口


ClassPathBeanDefinitionScanner

registriy:BeanDefinitionRegistry,Bean注册的工具

scan(String ...location),设置扫描路径

扫描包目录下的class文件
public void scan(String... basePackages) throws Throwable {
    if (basePackages != null && basePackages.length > 0) {
        for (String p : basePackages) {
            /*
             1 递归扫描包目录下的.class文件
             2 组合包路径+class文件名 得到全限定类名
             3 ClassLoad.load("类名") 得到 Class 对象
             4 解析Class上的注解,获得Bean定义信息,注册Bean定义
             */

            //1 递归扫描包目录下的.class文件
            Set<File> classFiles = this.doScan(p);
            //2 得到Class对象,并解析注解、注册Bean定义
            this.readAndRegisterBeanDefintion(classFiles);
        }
    }
}
private Set<File> doScan(String basePackage) throws IOException {
    // 扫描包下的类
    // 将包名转为路径名
    String basePackagePath = "/" + StringUtils.replace(basePackage, ".", "/");
    // 得到包对应的目录
    File rootDir = new File(this.getClass().getResource(basePackagePath).getPath());

    // 存放找到的类文件的集合
    Set<File> scanedClassFiles = new HashSet<>();
    // 调用doRetrieveMatchingFiles来扫描class文件
    this.doRetrieveClassFiles(rootDir, scanedClassFiles);
    return scanedClassFiles;
}


/**
 * 递归找指定目录下的所有类,规则:.class结尾。
 *
 * @param dir
 * @param result
 * @throws IOException
 */
protected void doRetrieveClassFiles(File dir, Set<File> result) throws IOException {
    for (File file : dir.listFiles()) {
        if (file.isDirectory() && file.canRead()) {
            doRetrieveClassFiles(file, result);

        }
        if (file.getName().endsWith(".class")) {
            result.add(file);
        }
    }
}
解析类注解
private void readAndRegisterBeanDefintion(Set<File> classFiles) throws BeanDefinitionRegistException {
    for (File classFile : classFiles) {
        String className = getClassNameFromFile(classFile);
        try {
            //加载类
            //           Class<?> clazz = Class.forName(className);   不可这样去获取Class
            //           Class<?> clazz = Class.forName(className,false,this.getClass().getClassLoader());  这样可以
            Class<?> clazz = this.getClass().getClassLoader().loadClass(className);
            Component component = clazz.getAnnotation(Component.class);
            if (component != null) {// 标注了@Component注解
                String beanName = component.value();
                if (StringUtils.isBlank(beanName)) {
                    beanName = this.generateBeanName(clazz);
                }
                GenericBeanDefinition bd = new GenericBeanDefinition();
                bd.setBeanClass(clazz);

                //处理Scope,作用域,单例还是多例
                Scope scope = clazz.getAnnotation(Scope.class);
                if (scope != null) {
                    bd.setScope(scope.value());
                }
                //处理primary
                Primary primary = clazz.getAnnotation(Primary.class);
                if (primary != null) {
                    bd.setPrimary(true);
                }

                // 处理构造方法,在构造方法上找@Autowired注解,如有,将这个构造方法set到bd;
                this.handleConstructor(clazz, bd);

                //处理方法上的注解(找出初始化、销毁、工厂方法)
                this.handleMethod(clazz, bd, beanName);

                // 处理属性依赖
                this.handlePropertyDi(clazz, bd);

                // 注册bean定义
                this.registry.registerBeanDefinition(beanName, bd);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
//获取类名
private String getClassNameFromFile(File file) {
    String absPath = file.getAbsolutePath();
    String name = absPath.substring(classPathAbsLength + 1, absPath.indexOf('.'));
    return StringUtils.replace(name, File.separator, ".");
}
//获取bean名称
private String generateBeanName(Class<?> clazz) {
    //应用名称生成规则生成beanName:  类名首字母小写
    String className = clazz.getName();
    return className.substring(0, 1).toLowerCase() + className.substring(1);
}
处理构造方法
private void handleConstructor(Class<?> clazz, GenericBeanDefinition bd) {
    // 获得所有构造方法,在构造方法上找@Autowired注解,如有,将这个构造方法set到bd;
    Constructor<?>[] cs = clazz.getConstructors();
    if (cs != null && cs.length > 0) {
        for (Constructor<?> c : cs) {
            if (c.getAnnotation(Autowired.class) != null) {
                bd.setConstructor(c);
                //构造参数依赖处理
                bd.setConstructorArgumentValues(this.handleMethodParamters(c.getParameters()));
                break;
            }
        }
    }
}
//获取参数依赖的处理方法
private List<Object> handleMethodParamters(Parameter[] ps) {
    //遍历获取参数上的注解,及创建构造参数依赖
    List<Object> argValues = new ArrayList<>();
    for (Parameter parameter : ps) {
        //找@Value注解
        Value v = parameter.getAnnotation(Value.class);
        if (v != null) {
            argValues.add(v.value());
            continue;
        }
        //找@Qualifier
        Qualifier q = parameter.getAnnotation(Qualifier.class);
        if (q != null) {
            argValues.add(new BeanReference(q.value()));
        } else {
            argValues.add(new BeanReference(parameter.getType()));
        }
    }
    return argValues;
}

处理方法上的注解,前置处理

private void handleMethod(Class<?> clazz, GenericBeanDefinition bd, String beanName) throws BeanDefinitionRegistException {
    //遍历方法找初始化、销毁、工厂方法注解
    for (Method method : clazz.getMethods()) {
        if (method.isAnnotationPresent(PostConstruct.class)) {
            //初始化方法
            bd.setInitMethodName(method.getName());
        } else if (method.isAnnotationPresent(PreDestroy.class)) {
            //销毁方法
            bd.setDestroyMethodName(method.getName());
        } else if (method.isAnnotationPresent(Bean.class)) {
            this.handlerFactoryMethod(method, clazz, beanName);
        }
    }

}
//工厂方法的Bean注解
private void handlerFactoryMethod(Method method, Class<?> clazz, String beanName) throws BeanDefinitionRegistException {
    GenericBeanDefinition bd = new GenericBeanDefinition();
    //静态工厂方法
    if (Modifier.isStatic(method.getModifiers())) {
        bd.setBeanClass(clazz);
    } else { //成员工厂方法,指定工厂Bean名
        bd.setFactoryBeanName(beanName);
    }

    bd.setFactoryMethod(method);
    bd.setFactoryMethodName(method.getName());

    //处理Scope
    Scope scope = method.getAnnotation(Scope.class);
    if (scope != null) {
        bd.setScope(scope.value());
    }
    //处理primary
    if (method.isAnnotationPresent(Primary.class)) {
        bd.setPrimary(true);
    }

    //处理@Bean注解
    Bean bean = method.getAnnotation(Bean.class);
    //BeanName的得来
    String xbeanName = bean.name();
    if (StringUtils.isBlank(xbeanName)) {
        xbeanName = method.getName();
    }
    //初始化方法、销毁方法设置
    if (StringUtils.isNotBlank(bean.initMethod())) {
        bd.setInitMethodName(bean.initMethod());
    }
    if (StringUtils.isNotBlank(bean.destroyMethod())) {
        bd.setDestroyMethodName(bean.destroyMethod());
    }

    //参数依赖处理
    bd.setConstructorArgumentValues(this.handleMethodParamters(method.getParameters()));
    // 注册Bean定义
    this.registry.registerBeanDefinition(xbeanName, bd);
}
处理属性上的依赖
private void handlePropertyDi(Class<?> clazz, GenericBeanDefinition bd) {
    List<PropertyValue> propertyValues = new ArrayList<>();
    bd.setPropertyValues(propertyValues);
    for (Field field : clazz.getDeclaredFields()) {
        if (field.isAnnotationPresent(Autowired.class)) {
            BeanReference beanReference = null;
            Qualifier qualifier = field.getAnnotation(Qualifier.class);
            if (qualifier != null) {
                beanReference = new BeanReference(qualifier.value());
            } else {
                beanReference = new BeanReference(field.getType());
            }
            propertyValues.add(new PropertyValue(field.getName(), beanReference));
        }
    }
}
注册bean定义
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionRegistException {
    Objects.requireNonNull(beanName, "注册bean需要给入beanName");
    Objects.requireNonNull(beanDefinition, "注册bean需要给入beanDefinition");

    // 校验给入的bean是否合法
    if (!beanDefinition.validate()) {
        throw new BeanDefinitionRegistException("名字为[" + beanName + "] 的bean定义不合法:" + beanDefinition);
    }

    if (this.containsBeanDefinition(beanName)) {
        throw new BeanDefinitionRegistException(
            "名字为[" + beanName + "] 的bean定义已存在:" + this.getBeanDefinition(beanName));
    }

    this.beanDefintionMap.put(beanName, beanDefinition);
}

ApplicationContext

通过上面的设计,我们可以实现注解的方式来定义,但是给用户的整体体验还是不是很好,这时我们可以通过外观模式,为框架定义一个更简单的统一使用界面

public class AnnotationApplicationContext extends AbstractApplicationContext {

    public AnnotationApplicationContext(String... basePackages) throws Throwable {
        super();
        // 找到所有的被 @Componment 修饰的Java类的BeanDefinition
        new ClassPathBeanDefinitionScanner((BeanDefinitionRegistry) this.beanFactory).scan(basePackages);
        super.refresh();
    }
}

protected void refresh() throws Throwable {

    beanFactory.registerTypeMap();

    doRegisterBeanPostProcessor();

    beanFactory.preInstantiateSingletons();

}

private void doRegisterBeanPostProcessor() throws Throwable {
    // 从BeanFactory中得到所有用户配置的BeanPostProcessor类型的Bean实例,注册到BeanFactory
    List<BeanPostProcessor> beanPostProcessors = beanFactory.getBeansOfTypeList(BeanPostProcessor.class);
    if(CollectionUtils.isNotEmpty(beanPostProcessors)){
        for (BeanPostProcessor bpp : beanPostProcessors) {
            beanFactory.registerBeanPostProcessor(bpp);
        }
    }
}

public void preInstantiateSingletons() throws Throwable {
    synchronized (this.beanDefintionMap) {
        for (Map.Entry<String, BeanDefinition> entry : this.beanDefintionMap.entrySet()) {
            String name = entry.getKey();
            BeanDefinition bd = entry.getValue();
            if (bd.isSingleton()) {
                this.getBean(name);
                if (log.isDebugEnabled()) {
                    log.debug("preInstantiate: name=" + name + " " + bd);
                }
            }
        }
    }
}

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

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

相关文章

基于ILI9341液晶屏+STM32U5单片的显示试验

试验要求&#xff1a; 1、通过串口&#xff0c;下发两个命令 STR和PIC&#xff1b; 2、STR模式&#xff1a; &#xff08;1&#xff09;串口输入什么&#xff0c;屏幕上显示什么 &#xff08;2&#xff09;如果屏幕满&#xff0c;自动下滚 &#xff08;3&#xff09;输入回车&a…

Elasticsearch:向量数据库基础设施类别的兴衰

过去几年&#xff0c;我一直在观察嵌入技术如何从大型科技公司的 “秘密武器” 转变为日常开发人员工具。接下来发生的事情 —— 向量数据库淘金热、RAG 炒作周期以及最终的修正 —— 教会了我们关于新技术如何在更广泛的生态系统中找到一席之地的宝贵经验。 更多有关向量搜索…

《系统爆破:MD5易破,后台登录可爆破?》

声明&#xff1a;笔记的只是方便各位师傅学习知识&#xff0c;以下代码、网站只涉及学习内容&#xff0c;其他的都与本人无关&#xff0c;切莫逾越法律红线&#xff0c;否则后果自负。 爆破Sales系统 一、爆破MD5 场景&#xff1a;已知MD5的加密字符串&#xff0c;如何得知明…

《Spring Framework实战》14:4.1.4.5.自动装配合作者

欢迎观看《Spring Framework实战》视频教程 自动装配合作者 Spring容器可以自动连接协作bean之间的关系。您可以通过检查ApplicationContext的内容&#xff0c;让Spring自动为您的bean解析协作者&#xff08;其他bean&#xff09;。自动装配具有以下优点&#xff1a; 自动装配…

GitLab CI/CD使用runner实现自动化部署前端Vue2 后端.Net 7 Zr.Admin项目

1、查看gitlab版本 建议安装的runner版本和gitlab保持一致 2、查找runner 执行 yum list gitlab-runner --showduplicates | sort -r 找到符合gitlab版本的runner&#xff0c;我这里选择 14.9.1版本 如果执行出现找不到下载源&#xff0c;添加官方仓库 执行 curl -L &quo…

冒泡排序基础与实现

目录 1. 原理图 ​编辑 2. 什么是冒泡排序 3. 工作原理 3.1 具体步骤 3.2 时间复杂度 3.3 空间复杂度 4. 代码实现 5. 总结 1. 原理图 2. 什么是冒泡排序 冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单的排序算法&#xff0c;它通过重复地遍历要排序的列表&am…

acwing_5722_十滴水

acwing_5722_十滴水 下面这篇大佬的题解属实是把指针用明白了&#xff0c;可以好好理解一下&#xff1a; 原题解连接&#xff1a;AcWing 5722. 一个简单模拟实现 - AcWing map/unordered_map的用法:见收藏夹 #include<iostream> #include<unordered_map> #incl…

【AI进化论】 AI微信机器人 | sealos + 智能微秘书 打造AI机器人 | 智能微秘书配置教程

一、sealos 什么是sealos &#xff1f; One cloud OS for all applications 1、创建sealos账号密码 根据链接&#xff08;帮我凑点sealos使用额度感谢&#xff09;&#xff1a;https://cloud.sealos.run/?uidXfUpoQk92c 登录后如下页面&#xff1a; 2、创建应用 点击【应…

Agentless:OpenAI 采用的非代理框架

不需要代理库来解决复杂的业务问题。Agentless 是OpenAI采用的非代理框架&#xff0c;用于在 o3 的 SWE Bench 上实现最高精度。SWE-bench 是 github的真实软件工程问题基准。Agentless 遵循简单的三阶段流程&#xff1a;本地化、修复和补丁验证&#xff1a; 1 ⃣生成存储库的…

Model-based RL自动出价算法的演进之路

▐ 导读 近年来&#xff0c;强化学习自动出价算法已成为智能投放领域的标志性技术&#xff0c;然而其所存在的在离线不一致、线上数据覆盖空间受限等关键问题尚未被完全解决。在本文中&#xff0c;我们提出一种Model-based RL&#xff08;MBRL&#xff09;自动出价算法训练新范…

【Cocos TypeScript 零基础 7.1】

目录 重写 小结一下心得页面跳转背景移动精简 player敌机精灵 重写 小结一下心得 本人重写了整个项目 有了点小心得 页面跳转 director.loadScene(s2)背景移动 canvas 是画布 为什么要向上图布局? 方便计算相对坐标,脚本还是只写一个 绑定上 BG 一样跑,不影响 export cl…

鸿蒙UI(ArkUI-方舟UI框架)

参考&#xff1a;https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/arkts-layout-development-overview-V13 ArkUI简介 ArkUI&#xff08;方舟UI框架&#xff09;为应用的UI开发提供了完整的基础设施&#xff0c;包括简洁的UI语法、丰富的UI功能&#xff…

禅道使用实践(2)-产品篇

前言 本文目的皆在指导产品团队使用禅道基于实操完成产品&#xff0c;项目的创建&#xff0c;需求汇总&#xff0c;版本规划&#xff0c;以及验收发布的基本流程。希望能帮助到首次使用禅道而不知道从何下手的团队。 基本概念 此章节内容与我上一篇的开发篇相同&#xff0c;从…

js逆向说明

一 负载的内容传输用这个格式 Content-Type: multipart/form-data Content-Type 是 HTTP 请求头中的一个字段&#xff0c;它告诉服务器请求体的类型。在这个例子中&#xff0c;Content-Type 的值为 multipart/form-data&#xff0c;这表示请求体采用了 multipart/form-data 格…

django基于Python的汽车销售管理系统的设计与实现

Django 基于 Python 的汽车销售管理系统的设计与实现 一、系统概述 Django 基于 Python 的汽车销售管理系统是一款专为汽车销售企业打造的综合性信息化管理平台。该系统借助 Django 框架的高效性、安全性以及 Python 语言丰富的库和工具&#xff0c;全面覆盖汽车销售业务的各…

计算机网络相关习题整理

第一讲 传输媒介 【知识点回顾】 两种导线可以减小电磁干扰&#xff1a; 双绞线&#xff08;分为非屏蔽双绞线、屏蔽双绞线&#xff09;&#xff08;RJ-45用&#xff09;同轴电缆&#xff08;短距离使用&#xff09;网络通信的基本单位&#xff1a;位&#xff08;bit&#xff…

HTTPS SSL/TLS 工作流程

目录 一、HTTP/HTTPS 简介1、HTTP协议相关内容2、HTTPS协议3、HTTP版本差异&#xff1a; 二、HTTPS 协议工作流程解析1. 客户端请求 SSL 握手2. 服务端接收 SSL 握手连接3. TLS 握手中的密钥协商4. HTTP 数据的加密与解密5. 安全性保障 三、HTTPS 协议的相关知识拓展1. TLS 与 …

SpringBoot项目实战(39)--Beetl网页HTML文件中静态图片及CSS、JS文件的引用和展示

使用Beetl开发网页时&#xff0c;在网页中使用的CSS、JS、图片等静态资源需要进行适当的配置才可以展示。大致的过程如下&#xff1a; &#xff08;1&#xff09;首先Spring Security框架需要允许js、css、图片资源免授权访问。 &#xff08;2&#xff09;网站开发时&#xff0…

node_exporter 安装

cd /root/node_exporter wget https://github.com/prometheus/node_exporter/releases/download/v1.7.0/node_exporter-1.7.0.linux-amd64.tar.gz tar xvfz node_exporter-1.7.0.linux-amd64.tar.gz #运行 cd /root/node_exporter/node_exporter-1.7.0.linux-amd64 ./node_exp…

【STM32-学习笔记-4-】PWM、输入捕获(PWMI)

文章目录 1、PWMPWM配置 2、输入捕获配置3、编码器 1、PWM PWM配置 配置时基单元配置输出比较单元配置输出PWM波的端口 #include "stm32f10x.h" // Device headervoid PWM_Init(void) { //**配置输出PWM波的端口**********************************…