Skip to content

Commit

Permalink
aop代理生成器
Browse files Browse the repository at this point in the history
  • Loading branch information
MarvelTiter committed Aug 27, 2024
1 parent 437c20a commit d3fa5f5
Show file tree
Hide file tree
Showing 20 changed files with 354 additions and 100 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/push_nuget.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,12 @@ jobs:
- name: Push
run: |
dotnet nuget push 'publish/genmapper/*.nupkg' -s https://api.nuget.org/v3/index.json -k ${{secrets.GEN_MAPPER}} --skip-duplicate
# AutoAopProxyGenerator
- name: Pack
run: |
dotnet pack ./src/AutoAopProxyGenerator/AutoAopProxyGenerator.csproj -c Release -o publish/genaopproxy
- name: Push
run: |
dotnet nuget push 'publish/genaopproxy/*.nupkg' -s https://api.nuget.org/v3/index.json -k ${{secrets.GEN_AOPPROXY}} --skip-duplicate
114 changes: 72 additions & 42 deletions src/AutoAopProxy.Roslyn/AutoAopProxyClassGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class AutoAopProxyClassGenerator : IIncrementalGenerator
{
public const string Aspectable = "AutoAopProxyGenerator.GenAspectProxyAttribute";
public const string AspectHandler = "AutoAopProxyGenerator.AddAspectHandlerAttribute";
public const string IgnoreAspect = "AutoAopProxyGenerator.IgnoreAspectAttribute";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var source = context.SyntaxProvider.ForAttributeWithMetadataName(
Expand Down Expand Up @@ -131,57 +132,86 @@ private static IEnumerable<MethodBuilder> CreateProxyMethod(INamedTypeSymbol ifa

foreach (var m in iface.GetMethods())
{
var method = m.IsGenericMethod ? m.ConstructedFrom : m;
var returnType = method.ReturnType.GetGenericTypes().FirstOrDefault() ?? method.ReturnType;
var isAsync = method.IsAsync || method.ReturnType.ToDisplayString().StartsWith("System.Threading.Tasks.Task");
var builder = MethodBuilder.Default
.MethodName(method.Name)
.Async(isAsync)
.Generic([.. method.GetTypeParameters()])
.AddParameter([.. method.Parameters.Select(p => $"{p.Type.ToDisplayString()} {p.Name}")])
.ReturnType(method.ReturnType.ToDisplayString())
.AddGeneratedCodeAttribute(typeof(AutoAopProxyClassGenerator));
List<Statement> statements = [];
var hasReturn = !method.ReturnsVoid && returnType.ToDisplayString() != "System.Threading.Tasks.Task";
if (hasReturn)
{
statements.Add($"{returnType.ToDisplayString()} returnValue = default;");
}
var done = LocalFunction.Default
.MethodName("Done")
.AddParameters("ProxyContext ctx")
.Async(isAsync)
.Return("System.Threading.Tasks.Task")
.AddBody([.. CreateLocalFunctionBody(method, builder.ConstructedMethodName, isAsync, hasReturn)]);

statements.Add(done);
statements.Add("var builder = AsyncPipelineBuilder<ProxyContext>.Create(Done)");
foreach (var handler in handlers)
MethodBuilder methodBuilder;
if (m.HasAttribute(IgnoreAspect))
{
statements.Add($"builder.Use({handler.MetadataName}.Invoke)");
methodBuilder = CreateDirectInvokeMethod();
}
statements.Add("var job = builder.Build()");
var ptypes = method.Parameters.Length > 0 ? $"[{string.Join(", ", method.Parameters.Select(p => $"typeof({p.Type.ToDisplayString()})"))}]" : "Type.EmptyTypes";
statements.Add($"var context = ContextHelper.GetOrCreate(typeof({iface.ToDisplayString()}), typeof({classSymbol.ToDisplayString()}), nameof({method.Name}), {ptypes})");
if (hasReturn)
else
{
statements.Add("context.HasReturnValue = true");
methodBuilder = CreateProxyMethod();
}
statements.Add($"context.Parameters = new object?[] {{{string.Join(", ", method.Parameters.Select(p => p.Name))}}};");
if (isAsync)

yield return methodBuilder;

MethodBuilder CreateDirectInvokeMethod()
{
statements.Add("await job.Invoke(context)");
var builder = MethodBuilder.Default
.MethodName(m.Name)
.Generic([.. m.GetTypeParameters()])
.AddParameter([.. m.Parameters.Select(p => $"{p.Type.ToDisplayString()} {p.Name}")])
.ReturnType(m.ReturnType.ToDisplayString())
.AddGeneratedCodeAttribute(typeof(AutoAopProxyClassGenerator));
builder = builder
.Lambda($"proxy.{builder.ConstructedMethodName}({string.Join(", ", m.Parameters.Select(p => p.Name))})");

return builder;
}
else

MethodBuilder CreateProxyMethod()
{
statements.Add("job.Invoke(context).GetAwaiter().GetResult()");
}
if (hasReturn)
statements.Add("return returnValue");
var method = m.IsGenericMethod ? m.ConstructedFrom : m;
var returnType = method.ReturnType.GetGenericTypes().FirstOrDefault() ?? method.ReturnType;
var isAsync = method.IsAsync || method.ReturnType.ToDisplayString().StartsWith("System.Threading.Tasks.Task");
var builder = MethodBuilder.Default
.MethodName(method.Name)
.Async(isAsync)
.Generic([.. method.GetTypeParameters()])
.AddParameter([.. method.Parameters.Select(p => $"{p.Type.ToDisplayString()} {p.Name}")])
.ReturnType(method.ReturnType.ToDisplayString())
.AddGeneratedCodeAttribute(typeof(AutoAopProxyClassGenerator));
List<Statement> statements = [];
var hasReturn = !method.ReturnsVoid && returnType.ToDisplayString() != "System.Threading.Tasks.Task";
if (hasReturn)
{
statements.Add($"{returnType.ToDisplayString()} returnValue = default;");
}
var done = LocalFunction.Default
.MethodName("Done")
.AddParameters("ProxyContext ctx")
.Async(isAsync)
.Return("System.Threading.Tasks.Task")
.AddBody([.. CreateLocalFunctionBody(method, builder.ConstructedMethodName, isAsync, hasReturn)]);

statements.Add(done);
statements.Add("var builder = AsyncPipelineBuilder<ProxyContext>.Create(Done)");
foreach (var handler in handlers)
{
statements.Add($"builder.Use({handler.MetadataName}.Invoke)");
}
statements.Add("var job = builder.Build()");
var ptypes = method.Parameters.Length > 0 ? $"[{string.Join(", ", method.Parameters.Select(p => $"typeof({p.Type.ToDisplayString()})"))}]" : "Type.EmptyTypes";
statements.Add($"var context = ContextHelper<{iface.ToDisplayString()}, {classSymbol.ToDisplayString()}>.GetOrCreate(nameof({method.Name}), {ptypes})");
if (hasReturn)
{
statements.Add("context.HasReturnValue = true");
}
statements.Add($"context.Parameters = new object?[] {{{string.Join(", ", method.Parameters.Select(p => p.Name))}}};");
if (isAsync)
{
statements.Add("await job.Invoke(context)");
}
else
{
statements.Add("job.Invoke(context).GetAwaiter().GetResult()");
}
if (hasReturn)
statements.Add("return returnValue");

builder.AddBody([.. statements]);
builder.AddBody([.. statements]);

yield return builder;
return builder;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,11 @@

namespace AutoAopProxyGenerator;

/// <summary>
/// 标记需要生成代理类的类型
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class GenAspectProxyAttribute : Attribute
{

}

/// <summary>
/// 在类或者接口上配置切面处理
/// </summary>
[AttributeUsage(AttributeTargets.Interface)]
[AttributeUsage(AttributeTargets.Interface, AllowMultiple = true)]
public sealed class AddAspectHandlerAttribute : Attribute
{
public Type? AspectType { get; set; }
}

/// <summary>
/// 配置不需要切面的方法
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public sealed class IgnoreAspectAttribute : Attribute
{

}
12 changes: 12 additions & 0 deletions src/AutoAopProxyGenerator/Attributes/GenAspectProxyAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace AutoAopProxyGenerator;

/// <summary>
/// 标记需要生成代理类的类型
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class GenAspectProxyAttribute : Attribute
{

}
12 changes: 12 additions & 0 deletions src/AutoAopProxyGenerator/Attributes/IgnoreAspectAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace AutoAopProxyGenerator;

/// <summary>
/// 配置不需要切面的方法
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public sealed class IgnoreAspectAttribute : Attribute
{

}
93 changes: 90 additions & 3 deletions src/AutoAopProxyGenerator/AutoAopProxyServiceProviderFactory.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace AutoAopProxyGenerator
Expand All @@ -15,12 +16,98 @@ public IServiceCollection CreateBuilder(IServiceCollection services)
public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder)
{
IServiceCollection serviceCollection = new ServiceCollection();
foreach (var service in containerBuilder)
foreach (var sd in containerBuilder)
{
//ServiceDescriptor.
//serviceCollection.AddKeyedScoped
//service.
var implType = GetImplType(sd);
if (implType?.GetCustomAttribute<GenAspectProxyAttribute>() != null)
{
var proxyType = implType.Assembly.GetType($"{implType.FullName}GeneratedProxy");
if (proxyType != null)
{
AddServiceDescriptors(serviceCollection, sd, implType, proxyType);
continue;
}
}
serviceCollection.Add(sd);
}
return serviceCollection.BuildServiceProvider();

static Type? GetImplType(ServiceDescriptor sd)
{
#if NET8_0_OR_GREATER
if (sd.IsKeyedService)
{
if (sd.KeyedImplementationType != null)
{
return sd.KeyedImplementationType;
}
else if (sd.KeyedImplementationInstance != null)
{
return sd.KeyedImplementationInstance.GetType();
}
else if (sd.KeyedImplementationFactory != null)
{
var typeArguments = sd.KeyedImplementationFactory.GetType().GenericTypeArguments;

return typeArguments[1];
}

return null;
}
#endif
if (sd.ImplementationType != null)
{
return sd.ImplementationType;
}
else if (sd.ImplementationInstance != null)
{
return sd.ImplementationInstance.GetType();
}
else if (sd.ImplementationFactory != null)
{
var typeArguments = sd.ImplementationFactory.GetType().GenericTypeArguments;

return typeArguments[1];
}

return null;
}
}


private static void AddServiceDescriptors(IServiceCollection serviceCollection, ServiceDescriptor sd, Type implType, Type proxyType)
{
// 将原实现注册为自身,在代理类中注入
var nsd = sd.IsKeyedService
? ServiceDescriptor.DescribeKeyed(
implType,
sd.ServiceKey,
implType,
sd.Lifetime
)
: ServiceDescriptor.Describe(
implType,
implType,
sd.Lifetime
);
serviceCollection.Add(nsd);

var proxySd = sd.IsKeyedService
? ServiceDescriptor.DescribeKeyed(
sd.ServiceType,
sd.ServiceKey,
proxyType,
sd.Lifetime
)
: ServiceDescriptor.Describe(
sd.ServiceType,
proxyType,
sd.Lifetime
);

serviceCollection.Add(nsd);
serviceCollection.Add(proxySd);
}
}
}
30 changes: 30 additions & 0 deletions src/AutoAopProxyGenerator/ContextHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Concurrent;
using System.Linq;

namespace AutoAopProxyGenerator;

public static class ContextHelper<TService, TImpl>
{
private static readonly ConcurrentDictionary<string, ProxyContext> caches = [];
private static readonly Type ServiceType = typeof(TService);
private static readonly Type ImplType = typeof(TImpl);
public static ProxyContext GetOrCreate(string methodName, Type[] types)
{
var key = $"{ServiceType.FullName}_{ImplType.FullName}_{methodName}_{string.Join("_", types.Select(t => t.Name))}";
return caches.GetOrAdd(key, (k) =>
{
var serviceMethod = ServiceType.GetMethod(methodName, types);
var implMethod = ImplType.GetMethod(methodName, types);
return new ProxyContext()
{
ServiceType = ServiceType,
ImplementType = ImplType,
ServiceMethod = serviceMethod,
ImplementMethod = implMethod
};
});

}
}

22 changes: 0 additions & 22 deletions src/AutoAopProxyGenerator/IAspectHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,3 @@ public interface IAspectHandler
Task Invoke(ProxyContext context, Func<Task> process);
}

public class ContextHelper
{
private static readonly ConcurrentDictionary<string, ProxyContext> caches = [];
public static ProxyContext GetOrCreate(Type @interface, Type impl, string methodName, Type[] types)
{
var key = $"{@interface.FullName}_{impl.FullName}_{methodName}_{string.Join("_", types.Select(t => t.Name))}";
return caches.GetOrAdd(key, (k) =>
{
var interfacesMethods = @interface.GetMethod(methodName, types);
var implMethod = impl.GetMethod(methodName, types);
return new ProxyContext()
{
ServiceType = @interface,
ImplementType = impl,
ServiceMethod = interfacesMethods,
ImplementMethod = implMethod
};
});

}
}

8 changes: 5 additions & 3 deletions src/AutoAopProxyGenerator/readme.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
`AutoInjectAttribute`
`AddAspectHandlerAttribute`

`AutoInjectContextAttribute`
`GenAspectProxyAttribute`

`AutoInjectConfiguration`
`IgnoreAspectAttribute`

`IAspectHandler`
Loading

0 comments on commit d3fa5f5

Please sign in to comment.