手写完了

刚参加工作那会接触java还是用的struct的时代,后面在SSH火爆时代的时候我转战.net,多年之后公司转java技术栈已经是Spring的天下,源码嚼了很多遍于是很想尝试把这套东西用在.net平台上。社区有个Spring.net项目已经多年不维护了,而且还是xml配置模式非基于注解的,无法与现有的SpringBoot项目同日而语。在SpringBoot项目中的常用的注解和扩展机制我都在这个项目中实现了,可以看下面介绍的已实现的功能一览!

  • Annotation是注解的意思,在java项目里面 注解的概念和 csharp里面的 Attribute 的概念是一样的。

  • 本项目是基于Autofac(巨人的肩膀)的基础之上构建,选择用Autofac是它扩展性非常好,在实现Spring的细节上提供了便捷

  • 本项目的所有实现都参考Spring的设计思想,但是并不是纯粹的把java的代码换成csharp,功能上效果是和Spring看齐的,但代码实现上是自己实现的

本项目的目的

基于参考 Java的 Spring注解方式开发思想,

所有容器的注册 和 装配 都是依赖标签来完成。

这样一来 一方面很容易分清楚 哪些是DI 哪些非DI, 哪些是拦截器,哪些需要拦截器,轻松实现切面编程,
代码也好看,吸收java的spring框架的优越的地方,配合.net语法的优越性,编程效率能够大大提升。

本篇文章主要介绍高阶玩法,基础玩法可以看项目wiki

  • 开源地址:https://github.com/yuzd/Autofac.Annotation

支持的标签一览

标签名称 使用位置 使用说明
AutoConfiguration 打在class上面 自动装配class里面带有Bean标签的方法
Bean 打在方法上面 配合AutoConfiguration标签使用
Component 打在class上面 自动注册
Autowired 打在构造方法的Parameter,类的Property,类的Field 自动装配
PropertySource 打在class上面 配合Value标签使用,设置Value的数据源,支持json,xml,支持资源内嵌
Value 打在构造方法的Parameter,类的Property,类的Field 静态/动态(例如nacos)数据装配,支持强大的EL表达式
Pointcut 打在class上面 切面配置,一个切面拦截N多个对象,配合Before After AfterReturn AfterThrows Around 实现拦截器链
Import 打在继承了ImportSelector的class上面 扩展注册Component
Order 打在了class上面,和Compoment一起使用 值越小的越先加载
Conditional 打在class或者方法上面 条件加载,自定义实现的
ConditionOnBean 打在标有Bean注解的方法上面 条件加载
ConditionOnMissingBean 打在标有Bean注解的方法上面 条件加载
ConditionOnClass 打在class或者方法上面 条件加载
ConditionOnMissingClass 打在class或者方法上面 条件加载
ConditionOnProperty 打在class或者方法上面 条件加载
ConditionOnProperties 打在class或者方法上面 条件加载
PostConstruct 打在方法上面 当类初始化完成后调用
PreDestory 打在方法上面 当容器Dispose前调用

基本使用略过

基本使用可以参考详细的wiki文档:
Wiki

下面讲讲高阶玩法

1. 拦截器原理简单介绍

用了Castle.Core组件 把你想要实现拦截器的目标类生成一个代理类。
然后织入拦截器,有2种方式

  1. 类拦截器:class + 方法为virtual的方式

  • 这种方式需要 从容器中是根据一个classType来获取到目标实例

  • 接口型拦截器:interface + 方法重写的方式

    • 这种方式需要 从容器中是根据一个interfaceType来获取到目标实例

    拦截器开关

    在你想要实现拦截的目标类上打开开关 【[Component(EnableAspect = true)]】,如上面的解释,打开Aspect开关标识这个class你想要走代理包装,还可以根据InterceptorType属性值设定你是哪种方式的拦截器

    InterceptorType属性 解释
    Class 使用class的虚方法模式 【默认方式】
    Interface 使用接口模式

    目的是打个标签就能够拦截目标方法

    使得我们自定义的方法能够

    • 在指定的目标方法执行之前先执行(比如参数校验)

    • 或者在指定的目标方法执行之后执行(比如说检验返回值,或其他收尾工作)

    • 或者环绕目标的方法,比如日志or事务:TransactionScope或者记录方法执行的时间或者日志

    拦截器标签 拦截器类型 使用说明
    AspectArround(抽象标签类) 环绕拦截 重写OnInvocation方法
    AspectBefore(抽象标签类) 前置拦截器 重写Before方法
    AspectAfter(抽象标签类) 后置拦截器(不管目标方法成功失败都会执行) 重写After方法
    AspectAfterReturn(抽象标签类) 后置拦截器(只有目标方法成功才会执行) 重写AfterReturn方法
    AspectAfterThrows(抽象标签类) 错误拦截器(只有目标方法失败才会执行) 重写AfterThrows方法

    每个拦截器方法都有一个

    拦截器的方法参数 AspectContext 属性说明

    名称 说明
    ComponentContext DI容器,可以从中取得你已注册的实例
    Arguments 目标方法的参数
    TargetMethod 目标方法的MethodInfo
    ReturnValue 目标方法的返回
    Method 目标方法的代理方法MethodInfo

    前置拦截器 (Before)

    1. 首先要自己写一个类继承 前置拦截器AspectBefore(抽象标签类)

    2. 实现该抽象类的Before方法

    public class TestHelloBefore:AspectBefore{public override Task Before(AspectContext aspectContext){Console.WriteLine("TestHelloBefore");return Task.CompletedTask;}}[Component(EnableAspect = true)]//注意这里需要打开开关 否则无效public class TestHello{[TestHelloBefore]public virtual void Say(){Console.WriteLine("Say");}}
    

    前置拦截器方法的执行顺序为:先执行 TestHelloBefor的Before方法再执行你的Say方法

    后置拦截器 (After) 不管目标方法成功还是抛异常都会执行

    1. 首先要自己写一个类继承后置拦截器AspectAfter(抽象标签类)

    2. 实现该抽象类的After方法

        public class TestHelloAfter:AspectAfter{//这个 returnValue 如果目标方法正常返回的话 那就是目标方法的返回值// 如果目标方法抛异常的话 那就是异常本身public override Task After(AspectContext aspectContext,object returnValue){Console.WriteLine("TestHelloAfter");return Task.CompletedTask;}}[Component(EnableAspect = true)]public class TestHello{[TestHelloAfter]public virtual void Say(){Console.WriteLine("Say");}}
    

    执行顺序为:先执行你的SayAfter方法再执行 TestHelloAfter的After方法

    这里要特别注意的是 After 拦截器 是不管你的目标方法(SayAfter是成功还是抛异常)
    都被会执行到的

    成功返回拦截器 (AfterReturn)只有目标方法成功的时候才会执行

    1. 首先要自己写一个类继承拦截器AspectReturn(抽象标签类)

    2. 实现该抽象类的After方法

    public class TestHelloAfterReturn:AspectAfterReturn{//result 是目标方法的返回 (如果目标方法是void 则为null)public override Task AfterReturn(AspectContext aspectContext, object result){Console.WriteLine("TestHelloAfterReturn");return Task.CompletedTask;}}[Component(EnableAspect = true)]public class TestHello{[TestHelloAfterReturn]public virtual void Say(){Console.WriteLine("Say");}}
    

    执行顺序为:先执行你的Say方法再执行 TestHelloAfterReturn的AfterReturn方法

    如果你的Say方法抛出异常那么就不会执行TestHelloAfterReturn的AfterReturn方法

    异常拦截器 (AfterThrows)

    1. 首先要自己写一个类继承拦截器AspectReturn(抽象标签类)

    2. 实现该抽象类的After方法

    public class TestHelloAfterThrows:AspectAfterThrows{public override Task AfterThrows(AspectContext aspectContext, Exception exception){Console.WriteLine(exception.Message);return Task.CompletedTask;}}[Component(EnableAspect = true)]public class TestHello{[TestHelloAfterThrows]public virtual void Say(){Console.WriteLine("Say");throw new ArgumentException("exception");}}
    

    执行顺序为:先执行你的Say方法再执行 TestHelloAfterThrows的AfterThrows方法

    如果你的Say方法不抛出异常那么就不会执行 TestHelloAfterThrows的AfterThrows方法

    环绕拦截器(Around)

    注意:OnInvocation方法除了AspectContext参数以外 还有一个 AspectDelegate _next 参数,
    需要在你的Around拦截器方法显示调用 _next(aspectContext) 方法,否则目标方法不会被调用

    1. 首先要自己写一个类继承拦截器AspectArround(抽象标签类)

    2. 实现该抽象类的OnInvocation方法

    public class TestHelloAround:AspectArround{public override async Task OnInvocation(AspectContext aspectContext, AspectDelegate _next){Console.WriteLine("around start");await _next(aspectContext);Console.WriteLine("around end");}}[Component(EnableAspect = true)]public class TestHello{[TestHelloAround]public virtual void Say(){Console.WriteLine("Say");}}
    

    方法的执行顺序为:

    1. 先执行TestHelloAround的OnInvocation方法

    2. 然后TestHelloAround的OnInvocation方法里面执行的 await _next(aspectContext); 就会执行被拦截方法TestHello的Say方法;

    如果Around Befor After AfterReturn AfterThrows 一起用

    正常case

        [Component(EnableAspect = true)]public class TestHello{[TestHelloAround,TestHelloBefore,TestHelloAfter,TestHelloAfterReturn,TestHelloAfterThrows]public virtual void Say(){Console.WriteLine("Say");}}
    

    代码的执行顺序为:

    1. 先执行TestHelloAround,打印 “around start” 然后执行到里面的_next(aspectContext)会触发下面

    2. 执行TestHelloBefore 打印 “TestHelloBefore”

    3. 执行目标方法 打印 “Say”

    4. 打印 “around end” TestHelloAround运行结束

    5. 执行TestHelloAfter 打印 “TestHelloAfter”

    6. 因为是目标方法成功执行 TestHelloAfterReturn 打印 “TestHelloAfterReturn”

    由于是目标方法成功返回 没有异常,所以不会走进TestHelloAfterThrows

    异常case

        [Component(EnableAspect = true)]public class TestHello{[TestHelloAround,TestHelloBefore,TestHelloAfter,TestHelloAfterReturn,TestHelloAfterThrows]public virtual void Say(){Console.WriteLine("Say");throw new ArgumentException("exception");}}
    

    代码的执行顺序为:

    1. 先执行TestHelloAround,打印 “around start” 然后执行到里面的_next(aspectContext)会触发下面

    2. 执行TestHelloBefore 打印 “TestHelloBefore”

    3. 执行目标方法 打印 “Say”

    4. 打印 “around end” TestHelloAround运行结束

    5. 执行TestHelloAfter 打印 “TestHelloAfter”

    6. 因为是目标方法异常 执行 TestHelloAfterThrows 打印异常信息

    如上述执行顺序和spring是一致的

    多组的情况

    public class TestHelloBefore1:AspectBefore{public override Task Before(AspectContext aspectContext){Console.WriteLine("TestHelloBefore1");return Task.CompletedTask;}}public class TestHelloAfter1:AspectAfter{//这个 returnValue 如果目标方法正常返回的话 那就是目标方法的返回值// 如果目标方法抛异常的话 那就是异常本身public override Task After(AspectContext aspectContext,object returnValue){Console.WriteLine("TestHelloAfter1");return Task.CompletedTask;}}public class TestHelloAfterReturn1:AspectAfterReturn{//result 是目标方法的返回 (如果目标方法是void 则为null)public override Task AfterReturn(AspectContext aspectContext, object result){Console.WriteLine("TestHelloAfterReturn1");return Task.CompletedTask;}}public class TestHelloAround1:AspectArround{public override async Task OnInvocation(AspectContext aspectContext, AspectDelegate _next){Console.WriteLine("TestHelloAround1 start");await _next(aspectContext);Console.WriteLine("TestHelloAround1 end");}}public class TestHelloAfterThrows1:AspectAfterThrows{public override Task AfterThrows(AspectContext aspectContext, Exception exception){Console.WriteLine("TestHelloAfterThrows1");return Task.CompletedTask;}}//public class TestHelloBefore2:AspectBefore{public override Task Before(AspectContext aspectContext){Console.WriteLine("TestHelloBefore2");return Task.CompletedTask;}}public class TestHelloAfter2:AspectAfter{//这个 returnValue 如果目标方法正常返回的话 那就是目标方法的返回值// 如果目标方法抛异常的话 那就是异常本身public override Task After(AspectContext aspectContext,object returnValue){Console.WriteLine("TestHelloAfter2");return Task.CompletedTask;}}public class TestHelloAfterReturn2:AspectAfterReturn{//result 是目标方法的返回 (如果目标方法是void 则为null)public override Task AfterReturn(AspectContext aspectContext, object result){Console.WriteLine("TestHelloAfterReturn2");return Task.CompletedTask;}}public class TestHelloAround2:AspectArround{public override async Task OnInvocation(AspectContext aspectContext, AspectDelegate _next){Console.WriteLine("TestHelloAround2 start");await _next(aspectContext);Console.WriteLine("TestHelloAround2 end");}}public class TestHelloAfterThrows2:AspectAfterThrows{public override Task AfterThrows(AspectContext aspectContext, Exception exception){Console.WriteLine("TestHelloAfterThrows2");return Task.CompletedTask;}}[Component(EnableAspect = true)]public class TestHello{[TestHelloAround1(GroupName = "Aspect1",OrderIndex = 10),TestHelloBefore1(GroupName = "Aspect1",OrderIndex = 10),TestHelloAfter1(GroupName = "Aspect1",OrderIndex = 10),TestHelloAfterReturn1(GroupName = "Aspect1",OrderIndex = 10),TestHelloAfterThrows1(GroupName = "Aspect1",OrderIndex = 10)][TestHelloAround2(GroupName = "Aspect2",OrderIndex = 1),TestHelloBefore2(GroupName = "Aspect2",OrderIndex = 1),TestHelloAfter2(GroupName = "Aspect2",OrderIndex = 1),TestHelloAfterReturn2(GroupName = "Aspect2",OrderIndex = 1),TestHelloAfterThrows2(GroupName = "Aspect2",OrderIndex = 1)]public virtual void SayGroup(){Console.WriteLine("SayGroup");}}
    

    如上面的代码在目标方法上打了2组 那么对应的执行顺序是:

    1. 先执行TestHelloAround2 打印 “TestHelloAround2 start” 然后执行到里面的_next(aspectContext)会触发下面

    2. 执行TestHelloBefore2 打印 “TestHelloBefore2” 然后进入到

    3. 执行TestHelloAround1 打印 “TestHelloAround1 start” 然后执行到里面的 _next(aspectContext)会触发下面

    4. 执行TestHelloBefore1 打印 “TestHelloBefore1”

    5. 执行目标方法 SayGroup 打印 “SayGroup”

    6. TestHelloAround1运行结束 打印 “TestHelloAround1 end”

    7. 执行 TestHelloAfter1 打印 “TestHelloAfter1”

    8. 执行 TestHelloAfterReturn1 打印 “TestHelloAfterReturn1”

    9. TestHelloAround2运行结束 打印 “TestHelloAround2 end”

    10. 执行 TestHelloAfter2 打印 “TestHelloAfter2”

    11. 执行 TestHelloAfterReturn2 打印 “TestHelloAfterReturn2”

      执行的顺序如下图

    2. 面向切面编程

    上面介绍了利用Aspect标签来完成拦截器功能

    Aspect是一对一的方式,我想要某个class开启拦截器功能我需要针对每个class去配置。

    比如说 我有2个 controller 每个controller都有2个action方法,

    [Component]public class ProductController{public virtual string GetProduct(string productId){return "GetProduct:" + productId;}public virtual string UpdateProduct(string productId){return "UpdateProduct:" + productId;}}[Component]public class UserController{public virtual string GetUser(string userId){return "GetUser:" + userId;}public virtual string DeleteUser(string userId){return "DeleteUser:" + userId;}}
    

    如果我需要这2个controller的action方法都在执行方法前打log 在方法执行后打log
    按照上一节Aspect的话 我需要每个controller都要配置。如果我有100个controller的话我就需要配置100次,这样我觉得太麻烦了。所以我参考了Spring的Pointcut切面编程的方式实现了,下面看如何用Pointcut的方式方便的配置一种切面去适用于N个对象。

    定义一个切面:创建一个class 上面打上Pointcut的标签 如下:

    Pointcut标签类有如下属性:

    属性名 说明
    Name 名称Pointcut切面的名称(默认为空,和拦截方法进行匹配,参考下面说明)
    RetType 匹配目标类的方法的返回类型(默认是%)
    NameSpace 匹配目标类的namespace(默认是%)
    ClassName 匹配目标类的类名称(和下面的AttributeType参数二选一必填)
    AttributeType 匹配特定的标签(和上面的ClassName参数二选一必填)
    MethodName 匹配目标类的方法名称(默认是%)

    切面如何匹配

    // *Controller 代表匹配 只要是Controller结尾的类都能匹配// Get* 代表上面匹配成功的类下 所以是Get打头的方法都能匹配[Pointcut(Class = "*Controller",Method = "Get*")]public class LoggerPointCut{}
    
    // *Controller 代表匹配 只要是Controller结尾的类都能匹配// Get* 代表上面匹配成功的类下 所以是Get打头的方法都能匹配[Pointcut(ClassName = "*Controller",MethodName = "Get*")]public class LoggerPointCut{}
    

    定义好了一个Pointcut切面后 需要定义这个切面的拦截方法(也叫切入点)

    配合Pointcut切面标签,可以在打了这个标签的class下定义拦截方法,
    在方法上得打上特定的标签,有如下几种:

    切入点 说明
    Before标签 在匹配成功的类的方法执行前执行
    After标签 在匹配成功的类的方法执行后执行(不管目标方法成功还是失败)
    AfterReturn标签 在匹配成功的类的方法执行后执行(只是目标方法成功)
    AfterThrows标签 在匹配成功的类的方法执行后执行(只是目标方法抛异常时)
    Around标签 环绕目标方法,承接了匹配成功的类的方法的执行权

    以上3种标签有一个可选的参数:Name (默认为空,可以和Pointcut的Name进行mapping)

    • 因为一个class上可以打多个Pointcut切面,一个Pointcut切面可以根据name去匹配对应拦截方法

    切入点标签所在方法的参数说明:

    • Around切入点 必须要指定 AspectContext类型 和 AspectDelegate类型的2个参数,且返回类型要是Task 否则会报错

    • 除了Around切入点以外其他的切入点的返回值只能是Task或者Void 否则会报错

    • 除了Around切入点以外其他的切入点可以指定 AspectContext类型 参数注入进来

    • After切入点 可以指定Returing参数,可以把目标方法的返回注入进来,如果目标方法抛异常则是异常本身

    • AfterReturn切入点 可以指定Returing参数,可以把目标方法的返回注入进来

    • AfterThrows切入点 可以指定 Throwing参数,可以把目标方法抛出的异常注入进来

    • 只要你参数类型是你注册到DI容器,运行时会自动从DI容器把类型注入进来

    • 可以使用Autowired,Value标签来修饰参数

        /// <summary>/// 第一组切面/// </summary>[Pointcut(NameSpace = "Autofac.Annotation.Test.test6",Class = "Pointcut*",OrderIndex = 1)]public class PointcutTest1{[Around]public async Task Around(AspectContext context,AspectDelegate next){Console.WriteLine("PointcutTest1.Around-start");await next(context);Console.WriteLine("PointcutTest1.Around-end");}[Before]public void Before(){Console.WriteLine("PointcutTest1.Before");}[After]public void After(){Console.WriteLine("PointcutTest1.After");}[AfterReturn(Returing = "value1")]public void AfterReturn(object value1){Console.WriteLine("PointcutTest1.AfterReturn");}[AfterThrows(Throwing = "ex1")]public void Throwing(Exception ex1){Console.WriteLine("PointcutTest1.Throwing");}}
    
    /// <summary>/// 第二组切面/// </summary>[Pointcut(NameSpace = "Autofac.Annotation.Test.test6",Class = "Pointcut*",OrderIndex = 0)]public class PointcutTest2{[Around]public async Task Around(AspectContext context,AspectDelegate next){Console.WriteLine("PointcutTest2.Around-start");await next(context);Console.WriteLine("PointcutTest2.Around-end");}[Before]public void Before(){Console.WriteLine("PointcutTest2.Before");}[After]public void After(){Console.WriteLine("PointcutTest2.After");}[AfterReturn(Returing = "value")]public void AfterReturn(object value){Console.WriteLine("PointcutTest2.AfterReturn");}[AfterThrows(Throwing = "ex")]public void Throwing(Exception ex){Console.WriteLine("PointcutTest2.Throwing");}}
    
        [Component]public class Pointcut1Controller{//正常casepublic virtual void TestSuccess(){Console.WriteLine("Pointcut1Controller.TestSuccess");}//异常casepublic virtual void TestThrow(){Console.WriteLine("Pointcut1Controller.TestThrow");throw new ArgumentException("ddd");}}[Component]public class Pointcut2Controller{//正常casepublic virtual void TestSuccess(){Console.WriteLine("Pointcut1Controller.TestSuccess");}//异常casepublic virtual void TestThrow(){Console.WriteLine("Pointcut1Controller.TestThrow");throw new ArgumentException("ddd");}}
    

    按照上面的配置

    • Pointcut1Controller.TestSuccess 和 TestThrow 2个方法 会被匹配

    • Pointcut2Controller.TestThrow 和 TestThrow 2个方法 会被匹配

    执行顺序

    单个切面顺序如下图

    多个切面执行的顺序如下图

    关于顺序是和上面用Aspect是一致的,只不过是1:N,1个切面来切N个目标

    切面功能与Spring相比缺少了一个灵活的切点表达式,所以功能会弱很多,这块目前我还没有很好的设计思路,欢迎来教育!

    3. BeanPostProcessor的设计

    参考Spring框架,
    在类的初始化过程中进行自定义逻辑而设计的BeanPostProcessor,有2个方法:

    • PostProcessBeforeInitialization

    • PostProcessAfterInitialization

    1. PostProcessBeforeInitialization

    该方法在bean实例化完毕(且已经注入完毕),属性设置或自定义init方法执行之前执行!

    2. PostProcessAfterInitialization

    该方法在bean实例化完毕(且已经注入完毕),在属性设置或自定义init方法执行之后

    一个使用场景例子:自定义一个注解来封装自定义逻辑

    先定义一个自定义注解

    /// <summary>
    /// 測試自己實現一個自定義註解
    /// </summary>
    [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
    public sealed class Soa : Attribute
    {/// <summary>/// 构造函数/// </summary>public Soa(Type type){Type = type;}/// <summary>/// 注册的类型/// </summary>internal Type Type { get; set; }
    }
    

    这个注解的名字叫Soa,然后有一个构造方法,传参为一个Class Type

    下面需要实现一个BeanPostProcessor

    
    [Component]
    public class SoaProcessor : BeanPostProcessor
    {//在实例化后且属性设值之前执行public object PostProcessBeforeInitialization(object bean){Type type = bean.GetType();找到bean下所有的字段var fieldInfos = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);foreach (var field in fieldInfos){//看字段上面有没有打Soa自定义注解var soaAnnotation = field.GetCustomAttribute(typeof(Soa)) as Soa;if (soaAnnotation == null){continue;}//有的话根据注解的参数Type来实例化对象并设值var instance = Activator.CreateInstance(soaAnnotation.Type) as ISoa;if (instance == null){continue;}field.SetValue(bean, instance);}return bean;}//不管返回public object PostProcessAfterInitialization(object bean){return bean;}
    }
    

    好了,实现一个BeanPostProcessor就是写一个类继承并实现它的接口即可。
    然后打上[Compoment]注册到容器中即可。

    下面测试效果

    
    [Component]
    public class Test11Models1
    {[Soa(typeof(SoaTest1))] private ISoa Soa1;[Soa(typeof(SoaTest2))] private ISoa Soa2;public string getSoa1(){return Soa1.say();}public string getSoa2(){return Soa2.say();}
    }public interface ISoa
    {string say();
    }public class SoaTest1 : ISoa
    {public string say(){return nameof(SoaTest1);}
    }public class SoaTest2 : ISoa
    {public string say(){return nameof(SoaTest2);}
    }
    

    单元测试一下

    
    [Fact]
    public void Test1()
    {var builder = new ContainerBuilder();builder.RegisterSpring(r => r.RegisterAssembly(typeof(TestBeanPostProcessor).Assembly));var container = builder.Build();var isRegisterd = container.TryResolve(out Test11Models1 model1);Assert.True(isRegisterd);Assert.Equal("SoaTest1",model1.getSoa1());Assert.Equal("SoaTest2",model1.getSoa2());
    }
    

    Test11Models1这个类打了[Compoment]注册到容器,当从容器获取它的时候会走到上面的SoaProcessor。然后识别到里面有打了自定义注解[Soa],并根据注册的参数实例化。


    Spring是一个非常庞大的框架,里面包含了非常多的细节,比如处理依赖循环,单例如何Autowired多例,FactoryBean,代理类的生成以及兼容async await,新出的valueTask的方法代理等等,这个项目是我2018年开始写的,多次重构,每次重构也是反映对spring源码的理解程度不一样;这个过程非常有趣(一次次推翻我自以为看了源码就‘懂了’spring),目前最新版4.0.4 基本上包含了常用的spring功能,还会不断更新(看我是否越来越‘懂’spring),感兴趣可以看看单元测试


    我是正东,学的越多不知道也越多。如果决定去深究一个东西, 一定要完全搞懂, 并认真总结一篇博客让以后能在短时间拾起来 ( 因为不搞懂你很难写一篇半年后还能理解的博客 )

    欢迎白嫖点赞!

我居然手写了Spring框架相关推荐

  1. 【Spring系列】- 手写模拟Spring框架

    简单模拟Spring

  2. spring源码分析01-(前期准备)spring核心原理解析和手写简易spring

    1.本文主要介绍内容 本文会把Spring中核心知识点大概解释下.可以对Spring的底层有一个整体的大致了解.主要内容包括: 手写简易spring框架,帮助更好理解spring. 代码点击链接自取 ...

  3. 从 0 开始手写一个 Spring MVC 框架,向高手进阶

    转载自   从 0 开始手写一个 Spring MVC 框架,向高手进阶 Spring框架对于Java后端程序员来说再熟悉不过了,以前只知道它用的反射实现的,但了解之后才知道有很多巧妙的设计在里面.如 ...

  4. 自己手写一个Spring MVC框架

    想要了解Spring MVC框架的原理,探究框架是如何设计的,不错的学习方式是阅读源码,然后自己手写一个框架.本文带领大家简化的手写一个Spring MVC框架. Spring框架对于Java后端程序 ...

  5. 博主熬夜手写个SpringMVC框架

    博主熬夜手写个SpringMVC框架 前言: 在spring全家桶流行的当下,只要你做的是Java技术栈基本上95%以上都使用的spring或springboot框架,剩下的5%基本上是一些老项目,政 ...

  6. 手写实现Spring(IOC、DI),SpringMVC基础功能

    手写实现Spring(IOC.DI),SpringMVC功能 spring和springMVC的用法相信大家都不陌生,我简单讲下我实现的思路 spring阶段 事项 配置 配置web.xml: ini ...

  7. 认识Vue源码 (2)-- 手写类Vue框架:Zue

    一.手写类Vue框架:zue class Zue{constructor(options){//构造函数:this永远指向实例} } 1.在zue实例下创建$el,并指向挂载点 this.$el = ...

  8. Java之手写实现ORM框架

    借鉴Mybatis框架手写一个ORM框架.mybatis整体架构中的整体思路是,首先解析一下配置文件,一个是框架的全局配置文件,一个是mapper配置文件,定义格式如下 <configurati ...

  9. 手写模拟spring扫描底层实现

    手写模拟spring扫描底层实现 前言 1.ApplicationContext和AppConfig 2.@Component和@ComponentScan注解 3.UserService和Test ...

最新文章

  1. 我在犹豫是不是该收集这几首MP3
  2. 面试官最讨厌的三种求职者
  3. 网站SEO文章关键词布局优化的技巧指南
  4. python怎么安装requests库-Python3.6安装及引入Requests库
  5. C#中怎样连接数据库并将查询结果转为实体类以及如何加入事务
  6. 通过yarn上的applicationId杀死hadoop中的任务,或通过hadoop job命令停止任务、yarn container 的日志路径
  7. 【Linux系统编程】进程常用调度算法
  8. 使用ThreadLocal来解决问题
  9. http get extension information - another way to get host url and port number of current application
  10. IOS 企业版发布后,用户通过sarafi浏览器安装无效的解决方案
  11. 课程管理系统c语言程序,课程信息管理系统C语言程序Word版
  12. Android 拷贝Asset目录下文件或者文件夹
  13. 3.写一个简单的弹出菜单
  14. 单片机课程设计八音盒
  15. 医学图像笔记(一)dicom数据格式
  16. Servlet 中的四大作用域
  17. [项目]PHP图书管理系统(附源码)
  18. 轻松掌握Mybatis(上)
  19. 图片传输(APP端将图片传至服务器端存储)
  20. java eav_entity-framework-4 – 实体框架4和ddd中的EAV

热门文章

  1. wordpress插件feed count中文版
  2. 硬盘结构及硬盘错误的解决方法(一)
  3. android显示布局边界的边距_Android设计规范 Material Design-Layout(2 度量与边框)
  4. mysql8用户管理
  5. Ubuntu宿主机与VMware中其他系统虚拟机的互通
  6. 【转】PHP的Trait 特性
  7. 方法调用(车票练习)
  8. 【转】SQL SERVER 存储过程学习笔记
  9. Mac OS使用技巧十八:Safari碉堡功能之一制作Widget
  10. 小程序禁用ios 左右滑动_如何在使用应用程序时禁用iOS控制中心