模拟SpringIOCAOP

一、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不能为空");//前面已经判断过   == nullif(bd.isSingleton()) { //如果是单例   DCLsynchronized (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的Classtype = 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 获得其对应的所有的BeanDefinition2 如果只有一个,直接获取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 {//找PrimaryBeanDefinition 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);}// 记录正在创建的BeanbuildingBeans.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());//暴力访问  privatep.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);}@Overridepublic boolean matchsClass(Class<?> targetClass) {return pe.couldMatchJoinPointsInType(targetClass);}@Overridepublic 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;}@Overridepublic 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<>());@Overridepublic 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中得到用户配置的所有切面Advisorif (!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的listList<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 {@Overridepublic void addUser(String name) {System.out.println("添加用户: " + name);}@Overridepublic 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;}@Overridepublic 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 {@Overridepublic 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;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory);}@Overridepublic Object getProxy() {return this.getProxy(target.getClass().getClassLoader());}@Overridepublic 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;}@Overridepublic Object getProxy() {return this.getProxy(target.getClass().getClassLoader());}@Overridepublic 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());}}@Overridepublic 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、获取要对当前方法进行增强的adviceList<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());}//处理primaryPrimary 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;}//找@QualifierQualifier 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());//处理ScopeScope scope = method.getAnnotation(Scope.class);if (scope != null) {bd.setScope(scope.value());}//处理primaryif (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类的BeanDefinitionnew 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实例,注册到BeanFactoryList<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.mzph.cn/web/65848.shtml

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

相关文章

预编译SQL

预编译SQL 预编译SQL是指在数据库应用程序中&#xff0c;SQL语句在执行之前已经通过某种机制&#xff08;如预编译器&#xff09;进行了解析、优化和准备&#xff0c;使得实际执行时可以直接使用优化后的执行计划&#xff0c;而不需要每次都重新解析和编译。这么说可能有一些抽…

软件测试预备知识⑥—搭建Web服务器

在软件测试的广阔领域中&#xff0c;搭建Web服务器是一项极为关键的技能。它不仅有助于模拟真实的应用环境&#xff0c;方便我们对Web应用进行全面且深入的测试&#xff0c;还能让测试人员更好地掌控测试场景&#xff0c;提升测试效率与质量。接下来&#xff0c;让我们一同深入…

计算机视觉算法实战——打电话行为检测

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​​​​​​​ ​​​​​​​​​​​​​​​ ​​​​​​ ​ 1. 引言✨✨ 随着智能手机的普及&#xff0c;打电话行为检测成为了计算机视…

Linux第二课:LinuxC高级 学习记录day01

0、大纲 0.1、Linux 软件安装&#xff0c;用户管理&#xff0c;进程管理&#xff0c;shell 命令&#xff0c;硬链接和软连接&#xff0c;解压和压缩&#xff0c;功能性语句&#xff0c;结构性语句&#xff0c;分文件&#xff0c;make工具&#xff0c;shell脚本 0.2、C高级 …

ISP流程--去马赛克详解

前言 本期我们将深入讨论ISP流程中的去马赛克处理。我们熟知&#xff0c;彩色图像由一个个像元组成&#xff0c;每个像元又由红、绿、蓝&#xff08;RGB&#xff09;三通道构成。而相机传感器只能感知光的强度&#xff0c;无法直接感知光谱信息&#xff0c;即只有亮暗而没有颜色…

阿里云-通义灵码:在 PyCharm 中的强大助力(下)

目录 六.通义灵码在 PyCharm 中的优势与不足 1.优势 (1).提高开发效率 (2).提升代码质量 (3).易于使用 (4).不断学习和改进 2.不足 (1).依赖网络 (2).准确性有待提高 (3).局限性 七.未来发展展望 1.提高准确性和可靠性 2.与其他工具的集成 3.智能化程度的提升 八…

开源项目stable-diffusion-webui部署及生成照片

参考链接 https://www.freedidi.com/13133.html 基础环境部署 python 官网链接 Python Release Python 3.10.6 | Python.org 下载 Python 3.10.6 版本安装包 下载好后双击 点击安装&#xff0c;这里需要选择一下&#xff0c;把环境变量加上。&#xff08;这里是默认安装到C盘…

【芯片封测学习专栏 -- 单 Die 与 多Die(Chiplet)介绍】

请阅读【嵌入式开发学习必备专栏 Cache | MMU | AMBA BUS | CoreSight | Trace32 | CoreLink | ARM GCC | CSH】 文章目录 Overview单个Die&#xff08;Monolithic Die&#xff09;多个Die&#xff08;Chiplet Architecture or Heterogeneous SoC&#xff09;如何判断一个SoC是…

Windows 安装 Docker 和 Docker Compose

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-uniapp-template &#x1f33a; 仓库主页&#xff1a; GitCode︱ Gitee ︱ Github &#x1f496; 欢迎点赞 &#x1f44d; 收藏 ⭐评论 …

java_将数据存入elasticsearch进行高效搜索

使用技术简介&#xff1a; (1) 使用Nginx实现反向代理&#xff0c;使前端可以调用多个微服务 (2) 使用nacos将多个服务管理关联起来 (3) 将数据存入elasticsearch进行高效搜索 (4) 使用消息队列rabbitmq进行消息的传递 (5) 使用 openfeign 进行多个服务之间的api调用 参…

Github Copilot学习笔记

&#xff08;一&#xff09;Prompt Engineering 利用AI工具生成prompt设计好的prompt结构使用MarkDown语法&#xff0c;按Role, Skills, Constrains, Background, Requirements和Demo这几个维度描述需求。然后收输入提示词&#xff1a;作为 [Role], 拥有 [Skills], 严格遵守 […

android分区和root

线刷包内容&#xff1a; 线刷包是一个完整的android镜像&#xff0c;不但包括android、linux和用户数据&#xff0c;还包括recovery等。当然此图中没有recovery,但是我们可以自己刷入一个。 主要分区 system.img 系统分区&#xff0c;包括linux下主要的二进制程序。 boot.img…

RabbitMQ基础(简单易懂)

RabbitMQ高级篇请看&#xff1a; RabbitMQ高级篇-CSDN博客 目录 什么是RabbitMQ&#xff1f; MQ 的核心概念 1. RabbitMQ 的核心组件 2. Exchange 的类型 3. 数据流向说明 如何安装RabbitQueue&#xff1f; WorkQueue&#xff08;工作队列&#xff09;&#xff1a; Fa…

大数据环境搭建进度

1.使用虚拟机的系统&#xff1a;centos7.xLinux 2.资源不足&#xff0c;使用云服务器&#xff1a; 1. 3.使用远程登录进行操作 用xshell 4.任务 1.虚拟机装好 2.设置IP地址 3.可以联网 4.设置远程登录访问 5.创建module和software目录&#xff0c;修改两…

Mysql--运维篇--主从复制和集群(主从复制I/O线程,SQL线程,二进制日志,中继日志,集群NDB)

一、主从复制 MySQL的主从复制&#xff08;Master-Slave Replication&#xff09;是一种数据冗余和高可用性的解决方案&#xff0c;它通过将一个或多个从服务器&#xff08;Slave&#xff09;与主服务器&#xff08;Master&#xff09;同步来实现。主从复制的基本原理是&#…

【EI会议征稿通知】第十一届机械工程、材料和自动化技术国际会议(MMEAT 2025)

本次大会旨在汇聚全球机械工程、材料科学及自动化技术的创新学者和行业专家&#xff0c;为他们提供一个卓越的交流与合作平台。随着全球对可持续技术和智能制造需求的不断增加&#xff0c;MMEAT 2025将重点关注这些领域的最新发展趋势和未来前景。此次大会的主要目标是推动机械…

OpenCV基础:视频的采集、读取与录制

从摄像头采集视频 相关接口 - VideoCapture VideoCapture 用于从视频文件、摄像头或其他视频流设备中读取视频帧。它可以捕捉来自多种源的视频。 主要参数&#xff1a; cv2.VideoCapture(source): source: 这是一个整数或字符串&#xff0c;表示视频的来源。 如果是整数&a…

解读Linux Bridge中的东西流向与南北流向

解读Linux Bridge中的东西流向与南北流向 在现代云计算和虚拟化环境中&#xff0c;网络流量的管理和优化变得越来越重要。Linux Bridge作为Linux内核提供的一个强大的二层交换机工具&#xff0c;在虚拟化和容器化应用中扮演着至关重要的角色。本文将深入探讨Linux Bridge中的两…

车联网安全--TLS握手过程详解

目录 1. TLS协议概述 2. 为什么要握手 2.1 Hello 2.2 协商 2.3 同意 3.总共握了几次手&#xff1f; 1. TLS协议概述 车内各ECU间基于CAN的安全通讯--SecOC&#xff0c;想必现目前多数通信工程师们都已经搞的差不多了&#xff08;不要再问FvM了&#xff09;&#xff1b;…

RuoYi Cloud项目解读【四、项目配置与启动】

四、项目配置与启动 当上面环境全部准备好之后&#xff0c;接下来就是项目配置。需要将项目相关配置修改成当前相关环境。 1 后端配置 1.1 数据库 创建数据库ry-cloud并导入数据脚本ry_2024xxxx.sql&#xff08;必须&#xff09;&#xff0c;quartz.sql&#xff08;可选&…