Skip to content

2.Component和Autowired和Import

俞正东 edited this page Dec 5, 2022 · 10 revisions

Componet标签把类型注册到DI容器

原理解释

框架会扫描打了Componet标签的class。如果Componet标签里面指定了要注册的类型,则会只注册为这个类型到DI容器。如果没有指定则会把当前class(参考下面的4),以及父类,以及接口都会注册到DI容器(参考下面的1,2和3)。

如何修改这个默认配置呢,比如关闭自动注册父类和接口。 可以参考 https://github.com/yuzd/Autofac.Annotation/issues/11

Componet有哪些属性

属性名称 类型 含义
Service Type 注册指定单个的类型
Key String 注册指定单个的key(为了同个类型注册多次避免歧义)
Services Type[] 注册指定多个的类型
Keys String[] 注册指定多个的key(如果指定多个类型又想避免歧义可以搭配上面一起使用)
AutofacScope Enum InstancePerDependency(默认每次都是一个新实例,也可以全局更改) SingleInstance(单例) InstancePerLifetimeScope(每个scope获取新的实例) InstancePerRequest(根据每个请求一个实例) 可以在插件初始化的时候指定一个默认的 参考下方的截图说明
AutoActivate bool 默认false 当DI容器初始化完成后会自动完成实例化
InitMethod string 当实例化后自动执行的方法名称
DestroyMethod string 当实例会DI容器回收会自动执行的方法名称
Ownership Enum LifetimeScope(DI容器管理自动回收策略,默认)External(自己手动管理实例回收)具体2者可参考autofac官方文档
Interceptor Type 指定拦截器的class的Type类型
InterceptorType Enum Interface(使用接口模式) Class(使用class的虚方法模式)
InterceptorKey string 如果同一个类型的拦截器有多个 避免歧义可以指定Key
InjectPropertyType Enum 属性自动装配的类型 ,Autowired(默认是这个,代表打了Autowired标签的才会装配),All(代表全部自动装配,采用autofac的属性注入方式)

Componet的常用构造方法

  1. 默认的构造方法
//默认的构造方法会把当前class,以及父类,以及接口都会注册到DI容器
//这里只会注册A8到DI容器
[Component]
public class A8
{
   
}

//通过A8类型可以装配成功
[Autowired]
public A8 A8 { get; set; }

·························································

public class B
{

}

//这里会把A8 和 B 都注册到DI容器
[Component]
public class A8:B
{
   
}


//通过A8类型可以装配成功
[Autowired]
public A8 A8 { get; set; }

//通过B类型也可以装配成功 拿到的是A8类型
[Autowired]
public B B { get; set; }
··························································

public interface IA
{
    
}

public interface IB:IA
{
    
}

public class B:IB
{

}

//这里会把A8  B IB IA 全都注册到DI容器 
//如果你只想要注册A8不要注册父类 可以参考https://github.com/yuzd/Autofac.Annotation/issues/11 去配置
[Component]
public class A8:B
{
   
}


//通过A8类型可以装配成功 
[Autowired]
public A8 A8 { get; set; }

//通过B类型也可以装配成功 拿到的是A8类型
[Autowired]
public B B { get; set; }

//通过IB类型也可以装配成功 拿到的是A8类型
[Autowired]
public IB IB { get; set; }

//通过IA类型也可以装配成功 拿到的是A8类型
[Autowired]
public IA IA { get; set; }
  1. 注册为指定类型
public class B
{

}

//这个构造方法就是将A6注册为B类型 
[Component(typeof(B))]
public class A6:B
{

}

//可以通过下面的方式自动装配 因为上面注册的是B类型 通过B类型可以装配成功 拿到的是A6类型
[Autowired]
public B B { get; set; }

//通过下面的方式自动装配会失败 会失败 会失败
[Autowired]
public A6 A6 { get; set; }

//也可以打在方法上面
[Autowired]
public void SetA6(A6 a6){
  A6  = a6;
}

[Autowired]
public void SetA6(A6 a6,, [Value("${a9}")] string school){
  A6  = a6;
  Console.WriteLine(school)
}
  1. 同一个注册类型有多个 采用Key方式解决歧义
public interface ITestAutowiredModal
{
}

// ITestAutowiredModal这个类型被注册多个了 避免歧义用"abc"来解决 
[Component("abc")]
public class TestAutowiredModal1:ITestAutowiredModal
{
}

// ITestAutowiredModal这个类型被注册多个了 避免歧义用"def"来解决
[Component("def")]
public class TestAutowiredModal2:ITestAutowiredModal
{
}

//可以用下面的方式来自动装配 拿到的是TestAutowiredModal1类型对象
[Autowired("abc")]
 public ITestAutowiredModal TestAutowiredModal1 { get; set; }

//如果不指定的话会先尝试根据byType模式匹配,因为是指定了Key 所以根据byType拿不到,拿不到就会再次根据属性名称 “abc” 去匹配,就和上面的方式一样了 
[Autowired]
 public ITestAutowiredModal abc { get; set; }


//可以用下面的方式来自动装配 拿到的是TestAutowiredModal2类型对象
[Autowired("def")]
 public ITestAutowiredModal TestAutowiredModal2 { get; set; }
 
//也可以打在方法上面
[Autowired]
public void SetTestAutowiredModal2 ([Autowired("def")]TestAutowiredModal2 model2){
  Console.WriteLine(model2);
}

// 如果不指定key的话默认就是classname来作为默认的key
[Component] // 等同于 [Component("TestAutowiredModal1")]
public class TestAutowiredModal1:ITestAutowiredModal
{
}
//可以用下面的方式来自动装配 拿到的是TestAutowiredModal1类型对象
[Autowired("TestAutowiredModal1")]
public ITestAutowiredModal TestAutowiredModal1{ get; set; }

1. 把一个类型注册到DI容器

image

  • 上面的例子就是把Student类型注册到容器

2. 把当前类型和父类注册到DI容器

image

  • 上面的例子就是把Student2类型注册到容器
  • 并且把Person类型也注册到容器根据Person类型拿到的是Student2的实体

3. 把当前类型和接口注册到DI容器

image

  • 上面的例子就是把Student3类型注册到容器
  • 并且把ISay也注册到容器根据ISay类型拿到的是Student3的实体

4. 当继承了父类或者接口 想要指定注册类型怎么办?

image

  • 上面的例子就是只能通过ISay拿到Student3的实体
  • 不能通过Student3类型拿到!!

5. 怎么指定默认实例的Scope类型是单例,每次都是新的实例,还是每个Scope一个实例呢?

image

  • 如上图所示 可以指定AutofacScope属性 若有指定默认全局的Scope类型,那么你在Compoment标签没有指定Scope那就会用全局的!
  • 如果不做任何指定就用全局默认的Scope:是每次获取的一个新的实例

6. 当同一个类型多次注册,如何区分得到我想要的?

image

  • 如上图 ISay类型有2个实现类 Student3 和 Student4 分别指定了 Key的值
  • 通过ISay + “Student3” 可以获取到 Student3的实体
  • 通过ISay + “Student4” 可以获取到 Student4的实体

7.支持注册类型可以自动实例化对象

这个功能加上配合设置单例的模式的class,在容器完成注册后会自动由容器触发初始化。等到你用的时候其实早就准备好了(这个和spring很像)

image

  • 如上所示,设定AutoActivate = true代表是启动自动实例化
  • AutofacScope = AutofacScope.SingleInstance 代表单例模式
  • Student5类型会对象会自动实例化 并且以单例的方式存储在DI容器内

8.支持设置实例化时运行指定方法

image

  • 如上所示 设置 InitMethod 和 DestroyMethod
  • 当实例从DI容器初始化时就会调用 InitMethod
  • 当DI容器Dispose的时候会触发调用DestroyMethod

另外

image

  • InitMethod支持注入
  • DestroyMethod只能是无参数方法

Import标签

Import是为了扩展注册Component到容器中去!

1. 打在实现了ImportSelector接口的class上面
public class TestImport1
{
    public string Name { get; set; } = nameof(TestImport1);
}

    [Import]
    //注意得实现ImportSelector接口
    public class TestImportSelector:ImportSelector
    {
        //实现这个接口 返回你要注册到容器的类型 BeanDefination的定义请查看下方文档说明
        public BeanDefination[] SelectImports()
        {
            return new[]
            {
                new BeanDefination(typeof(TestImport1)) //这个TestImport1的class上面我没有打[Component]标签,是借助Import来实现注册的!
            };
        }
    }

2. 打在普通class(非实现了ImportSelector接口的class)上面,但是在指定了实现了ImportSelector的Class的类型
    [Import(typeof(TestImportSelector))] 
    //如果有多个要指定的话 可以  [Import(typeof(TestImportSelector),typeof(xxxxx),typeof(yyyy))]
    public class TestImport2
    {
        
    }

    public class TestImportSelector:ImportSelector
    {
        public BeanDefination[] SelectImports()
        {
            return new[]
            {
                new BeanDefination(typeof(TestImport1))
            };
        }
    }

public class TestImport1
{
    public string Name { get; set; } = nameof(TestImport1);
}

BeanDefination

构造方法 参数 说明
ctor Type _type 注册_type到di容器
ctor Type _type, Type asType 注册_type类型以asType到di容器
ctor string key 注册_type 如果_type会有多个注册的话用key作为避免歧义
ctor 无参构造 自定义Type和Component属性

Autowired自动装配

Autowired有哪些属性

注意事项

想要Autowire的起作用必须得是从DI容器里面获取对象,自己手工new的无法应用Autowired特性!

属性名称 类型 含义
Name String 搭配Component指定Key的时候使用,消除一个类型被多个注册带来的歧义(支持SPEL表达式动态配置
Required bool 默认装载失败会报错 设置为false装载失败不会报错
注意关于循环注入:默认Autowired方式注入到Field、Property是支持循环注入的,但是构造方法注入是无法支持循环注入的

打在构造方法上 属性

image

在单例的对象里面Autowired多实例

使用ObjectFactory来实现,和Lazy的区别是 ObjectFactory修饰的每次获取都是从容器里面获取一遍。而Lazy只有首次才会去容器获取。 image

延迟自动装配Lazy

[Component]
public class TestLazyModel1
{
    [Autowired]
    public Lazy<TestAutowiredModal4> TestAutowiredModal4 { get; set; }
    
    [Autowired]
    public TestAutowiredModal3 TestAutowiredModal3 { get; set; }
}