模拟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.rhkb.cn/news/815.html

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

相关文章

科研绘图系列:R语言绘制Y轴截断分组柱状图(y-axis break bar plot)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍特点意义加载R包数据下载导入数据数据预处理画图输出总结系统信息介绍 Y轴截断分组柱状图是一种特殊的柱状图,其特点是Y轴的刻度被截断,即在某个范围内省略了部分刻度。这种图表…

django基于 Python 的考研学习系统的设计与实现

以下是对Django基于Python的考研学习系统的设计与实现&#xff1a; 一、系统概述 Django基于Python的考研学习系统是一个为考研学子提供一站式学习辅助的平台。它整合了丰富的学习资源、学习计划制定、学习进度跟踪以及交流互动等功能&#xff0c;旨在满足考生在备考过程中的…

C#,图论与图算法,有向图(Direct Graph)广度优先遍历(BFS,Breadth First Search)算法与源程序

1 图的广度优先遍历 图的广度优先遍历&#xff08;或搜索&#xff09;类似于树的广度优先遍历&#xff08;参见本文的方法2&#xff09;。这里唯一需要注意的是&#xff0c;与树不同&#xff0c;图可能包含循环&#xff0c;因此我们可能再次来到同一个节点。为了避免多次处理节…

Kafka 主题管理

主题作为消息的归类&#xff0c;分区则是对消息的二次归类。分区可以有一至多个副本&#xff0c;每个副本对应一个日志文件。 分区的划分不仅为Kafka提供了可伸缩性、水平扩展的功能&#xff0c;还通过多副本机制来为Kafka提供数据冗余以提高可靠性。 图 主题、分区、副本和日…

案例研究:UML用例图中的结账系统

在软件工程和系统分析中&#xff0c;统一建模语言&#xff08;UML&#xff09;用例图是一种强有力的工具&#xff0c;用于描述系统与其用户之间的交互。本文将通过一个具体的案例研究&#xff0c;详细解释UML用例图的关键概念&#xff0c;并说明其在设计结账系统中的应用。 用…

51c自动驾驶~合集46

我自己的原文哦~ https://blog.51cto.com/whaosoft/13050104 #世界模型会是L3自动驾驶的唯一解吗 三维空间占有率&#xff08;3D Occupancy&#xff09;预测的目的是预测三维空间中的每个体素是否被占有&#xff0c;如果被占有&#xff0c;则对应的体素将被标记。3D Semant…

忘记了PDF文件的密码,怎么办?

PDF文件可以加密&#xff0c;大家都不陌生&#xff0c;并且大家应该也都知道PDF文件有两种密码&#xff0c;一个打开密码、一个限制编辑密码&#xff0c;因为PDF文件设置了密码&#xff0c;那么打开、编辑PDF文件就会受到限制。忘记了PDF密码该如何解密&#xff1f; PDF和offi…

fastapi 使用

参考&#xff1a; https://fastapi.tiangolo.com/zh/tutorial/first-steps/https://fastapi.tiangolo.com/zh/tutorial/first-steps/ FastAPI 用于基于标准 Python 类型提示使用 Python 构建 API&#xff0c;使用 ASGI 的标准来构建 Python Web 框架和服务器。所有简单理解&a…

单片机(MCU)-简单认识

简介&#xff1a; 内部集成了CPU&#xff0c;RAM&#xff0c;ROM&#xff0c;定时器&#xff0c;中断系统&#xff0c;通讯接口等一系列电脑的常用硬件功能。 单片机的任务是信息采集&#xff08;依靠传感器&#xff09;&#xff0c;处理&#xff08;依靠CPU&#xff09;&…

解决el-table表格数据量过大导致页面卡顿问题 又名《umy-ui---虚拟表格仅渲染可视区域dom的神》

后台管理系统的某个页面需要展示多个列表 数据量过多 页面渲染dom卡顿 经调研发现两个组件 pl-table和umy-ui &#xff08;也就是u-table&#xff09; 最终决定使用umy-ui 它是专门基于 Vue 2.0 的桌面端组件库 流畅渲染表格万级数据 而且他是对element-ui的表格做了二次优化…

提升租赁效率的租赁小程序全解析

内容概要 在如今快节奏的生活中&#xff0c;租赁小程序俨然成为了提升租赁效率的一把利器。无论是个人还是企业&#xff0c;都会因其便捷的功能而受益。简单来说&#xff0c;租赁小程序能让繁琐的租赁流程变得轻松、高效。在这里&#xff0c;我们将带您畅游租赁小程序的海洋&a…

【2024年华为OD机试】(C卷,100分)- 输出指定字母在字符串的中的索引(Java JS PythonC/C++)

一、问题描述 题目描述 给定一个字符串&#xff0c;把字符串按照大写在前小写在后排序&#xff0c;输出排好后的第 K 个字母在原来字符串的索引。相同字母输出第一个出现的位置。 输入描述 无 输出描述 无 用例 用例 1 输入: hAkDAjByBq 4输出: 6说明: 排好序后 AAB…

Vue sm3国密 IE模式报错处理

1、sm-crypto 转义错误 查看报错信息包名 在vue.config.js的transpileDependencies中把依赖包添加进去&#xff0c;让babel能够转译sm-crypto包 babel.config.js module.exports {presets: [[vue/app, {useBuiltIns: entry}]] }2、exports.destroy (() &#xff1e; { … }&a…

超燃预告!Origin百图绘制系列即将登场

Hello&#xff0c;大家好 这里是练习时长两年半的菜狗~ 持续更新各种竞赛&#xff0c;科研&#xff0c;保研&#xff0c;学习干货ing 回想刚开始打比赛那会&#xff0c;啥都不懂&#xff0c;就从用 Excel 画图起步&#xff0c;绘制的图形实在太难看。后来运用 Matlab&#xf…

burpsiute的基础使用(2)

爆破模块&#xff08;intruder&#xff09;&#xff1a; csrf请求伪造访问&#xff08;模拟攻击&#xff09;: 方法一&#xff1a; 通过burp将修改&#xff0c;删除等行为的数据包压缩成一个可访问链接&#xff0c;通过本地浏览器访问&#xff08;该浏览器用户处于登陆状态&a…

【日常小记】Ubuntu启动后无图形界面且网络配置消失

【日常小记】Ubuntu启动后无图形界面且网络配置消失 解决方法&#xff1a; 1. 输入后恢复网络: #sudo dhclient 2. 重新安装ubuntu-desktop #sudo apt-get install ubuntu-desktop&#xff01;&#xff01;&#xff01;请关注是否能ping通某网站&#xff08;例如百度&…

30天开发操作系统 第 12 天 -- 定时器 v1.0

前言 定时器(Timer)对于操作系统非常重要。它在原理上却很简单&#xff0c;只是每隔一段时间(比如0.01秒)就发送一个中断信号给CPU。幸亏有了定时器&#xff0c;CPU才不用辛苦地去计量时间。……如果没有定时器会怎么样呢?让我们想象一下吧。 假如CPU看不到定时器而仍想计量时…

010:传统计算机视觉之大津算法初探

本文为合集收录&#xff0c;欢迎查看合集/专栏链接进行全部合集的系统学习。 合集完整版请参考这里。 上一节学习了利用 Canny 算法来完成一个图片的边缘检测&#xff0c;从而可以区分出图像的边缘。 本节再了解一个计算机视觉中更常见的应用&#xff0c;那就是把图片的前景和…

Python中定位包含特定文本信息的元素

目录 一、为什么需要定位包含文本信息的元素 二、使用Selenium定位包含文本的元素 1. 使用find_element_by_link_text 2. 使用find_element_by_partial_link_text 3. 使用XPath定位包含文本的元素 4. 使用CSS选择器定位包含文本的元素 三、使用BeautifulSoup定位包含文本…

使用uniapp 微信小程序一些好用的插件分享

总结一下自己在开发中遇见的一问题&#xff0c;通过引入组件可以快速的解决 1.zxz-uni-data-select 下拉框选择器(添加下拉框检索&#xff0c;多选功能&#xff0c;多选搜索功能&#xff0c;自定义 下拉框插件&#xff0c;使用这个的原因是因为 uniui uview 组件库下拉框太…