C# 和java 对于相同类名,不同对象的依赖注入对比

java:

简单的方法:

package com.kaixin.mytest;

/**
 * @Auther: Zg
 * @Date: 2023/3/18 - 03 - 18 - 15:55
 * @Description: com.kaixin.mytest
 * @version: 1.0
 */
public interface MyService {
    void doSomething();
    String getName();
}

package com.kaixin.mytest;

import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

/**
 * @Auther: Zg
 * @Date: 2023/3/19 - 03 - 19 - 22:11
 * @Description: com.kaixin.mytest
 * @version: 1.0
 */
@Data
@Slf4j
@Builder
public class MyServiceImpl implements MyService {

    String name;

    public MyServiceImpl() {
        this.name = "default";
    }

    public MyServiceImpl(String name) {
        this();
        this.name = name;
    }

    @Override
    public void doSomething() {
        System.out.println("这个MyServiceImpl的名字是"+this.name);
    }
}


在configration 里面写两个bean,返回两个不同名字的对象。

package com.kaixin.mytest;

import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Auther: Zg
 * @Date: 2023/3/19 - 03 - 19 - 22:10
 * @Description: com.kaixin.mytest
 * @version: 1.0
 */
@Configuration
public class MyConfigration {

    @Bean
    MyServiceImpl myService11()
    {
        return new MyServiceImpl("MyServiceImpl11");
    }

    @Bean
    MyServiceImpl myService12()
    {
        return new MyServiceImpl("MyServiceImpl12");
    }

    @Bean(autowire = Autowire.BY_NAME,value = "thisByName1")
    MyServiceImpl myServiceByName1()
    {
        return new MyServiceImpl("myServiceByName1");
    }
}


调用

    @Autowired
    @Qualifier("myService11")
    private MyServiceImpl myS11;

    @Autowired
    @Qualifier("myService12")
    private MyServiceImpl myS12;

    @Autowired
    @Qualifier("thisByName1")
    private MyService thisByName1;

    @Test
    void test3(){
        myS11.doSomething();
        myS12.doSomething();
        thisByName1.doSomething();
    }

结果:
这个MyServiceImpl的名字是MyServiceImpl11
这个MyServiceImpl的名字是MyServiceImpl12
这个MyServiceImpl的名字是myServiceByName1
下面是麻烦的方法==========

package com.kaixin.mytest;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
 * @Auther: Zg
 * @Date: 2023/3/18 - 03 - 18 - 15:56
 * @Description: com.kaixin.mytest
 * @version: 1.0
 */
@Data
@Slf4j
@Builder
@Component("myService1")
public class MyServiceImpl1 implements MyService {

    private String name;

    @Override
    public void doSomething() {
        System.out.println("这个Component的名字是"+this.name);
    }

    public MyServiceImpl1() {
        this.name = "myService1";
    }

    public MyServiceImpl1(String name) {
        this();
        this.name = name;
    }
}

package com.kaixin.mytest;

import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
 * @Auther: Zg
 * @Date: 2023/3/18 - 03 - 18 - 15:58
 * @Description: com.kaixin.mytest
 * @version: 1.0
 */
@Data
@Slf4j
@Builder
@Component("myService2")
public class MyServiceImpl2 implements MyService {

    private String name;

    @Override
    public void doSomething() {
        System.out.println("这个Component的名字是"+this.name);
    }

    public MyServiceImpl2() {
        this("defaultName");
    }

    public MyServiceImpl2(String name) {
        this.name = name;
    }
}

package com.kaixin.mytest;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class MytestApplicationTests {
    @Autowired
    @Qualifier("myService1")
    private MyService myService1;

    @Autowired
    @Qualifier("myService2")
    private MyService myService2;


    @Test
    void test1()
    {
        myService1.getName();
        myService2.getName();
    }

}

在Spring框架中,@Component 和 @Service 都是用来定义 Bean 的注解。它们的区别在于语义上的不同,用法上类似,但是在某些场景下推荐使用不同的注解。

@Component 是一个通用的注解,表示一个类将作为组件被注入到Spring容器中。如果没有明确指明是哪一种组件,那么就可以使用@Component注解,比如一个工具类。
@Service 是一种特殊的 @Component,它用于定义业务层的 Bean。通常情况下,@Service 会和 @Autowired 一起使用来注入 DAO 层的 Bean,然后在业务层中调用 DAO 层的方法。
简单来说,@Component 是一个泛用性比较强的注解,而 @Service 则是为了更好地表示在业务逻辑层中使用的注解,让代码更加清晰易懂。

需要注意的是,虽然 @Service 是 @Component 的子集,但是在一些场景下不推荐使用 @Component 注解来定义业务层的 Bean,因为这可能会引起一些混淆。

@Bean 注解通常用于在 Spring 容器中声明一个 Bean,以便在应用程序的其他部分中进行自动装配。因此,它只能用于方法级别的注解,而不能用于类级别的注解。如果你要声明一个类作为 Spring Bean,你可以使用 @Component 或其派生注解,例如 @Service、@Repository、@Controller 等。

@Resource 和 @Autowired 都可以用来进行依赖注入,但是它们有一些不同的使用场景和特点。

主要区别如下:

@Resource 是由 J2EE 提供的注解,而 @Autowired 是由 Spring 提供的注解。因此,使用 @Autowired 需要先引入 Spring 框架的依赖。
@Resource 的默认按照属性名自动装配,如果找不到与属性名相同的 bean,则会尝试按照属性类型进行匹配。而 @Autowired 默认按照属性类型进行自动装配,如果存在多个同类型的 bean,则会尝试按照属性名进行匹配。
@Resource 可以通过 name 属性指定要注入的 bean 的名称,而 @Autowired 可以通过 @Qualifier 注解指定要注入的 bean 的名称。
@Resource 可以注入 javax.annotation.Resource 类型的对象,而 @Autowired 只能注入 org.springframework.beans.factory.annotation.Autowired 类型的对象。

===========================================================
C# :

using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;

namespace MyCsharpTest2
{

    public interface IMyService
    {
        void DoSomething();
        string Name { get; }
    }

    public class MyService : IMyService
    {
        private readonly string _name;


        public string Name
        {
            get { return _name; }

        }

        public MyService(string name) : this()
        {
            _name = name;
        }

        public MyService(int id) : this()
        {
        }

        public MyService()
        {
            this._name = "defaultName";
        }

        public void DoSomething()
        {
            Console.WriteLine($"Doing something with {_name}");
        }
    }

    public class Program
    {

        static void Main(string[] args)
        {

            // 注册 MyService 
            var services = new ServiceCollection();
            services.AddScoped<IMyService>(provider => new MyService("Instance 1"));
            services.AddScoped<IMyService>(provider => new MyService("Instance 2"));

            //创建 ServiceProvider 实例
            var serviceProvider = services.BuildServiceProvider();

            //从 ServiceProvider 中获取两个类型相同的不同对象
            var myService1 = serviceProvider.GetServices<IMyService>().FirstOrDefault(s => s.Name == "Instance 1");
            var myService2 = serviceProvider.GetServices<IMyService>().FirstOrDefault(s => s.Name == "Instance 2");

            // 调用方法,输出不同的名称
            myService1.DoSomething();
            myService2.DoSomething();

            Console.WriteLine("============");
            IMyService myService = new MyService(5);
            Console.WriteLine(myService.Name);
            Console.ReadKey();

            //Doing something with Instance 1
            //Doing something with Instance 2
            //============
            //defaultName
        }
    }


}

在构造函数中取出:

public class MyClass
{
    private readonly IMyService _service1;
    private readonly IMyService _service2;

    public MyClass(IEnumerable<IMyService> services)
    {
        _service1 = services.FirstOrDefault(s => s.GetType() == typeof(MyService) && ((MyService)s).InstanceName == "Instance 1");
        _service2 = services.FirstOrDefault(s => s.GetType() == typeof(MyService) && ((MyService)s).InstanceName == "Instance 2");
    }

    // other code
}

几点使用IOC的注意事项
1,
在使用GetServices方法时,返回的是一个集合,即使你知道该集合中只有一个元素,你也无法直接将其转换为单个实例对象。你可以使用GetService方法来获取一个对象
2,
在使用services.AddScoped<IUser,User>()时,容器实例化User对象,然后将其注册为IUser服务。在调用sp.GetRequiredService()时,容器会返回一个已经实例化的User对象。如果你调用sp.GetRequiredService()则会返回null,因为User不是注册的服务类型。
3,
要使用sp.GetRequiredService(),你需要使用services.AddScoped(),这会将User作为其自己的服务类型进行注册。然后,当你调用sp.GetRequiredService()时,容器会返回一个已经实例化的User对象。
4,
使用 services.AddScoped<IUser,User>() 调用 sp.GetRequiredService() 报错System.InvalidOperationException:“Unable to resolve service for type ‘User’ while attempting to activate ‘User’.”

这个错误可能是因为依赖注入系统无法找到 User 类型的构造函数来实例化 User 类型。在使用 AddScoped 方法时,依赖注入系统需要在服务容器中注册类的构造函数,以便在需要时可以实例化该类。

要解决这个问题,你需要确保 User 类型的构造函数已经被注册到服务容器中。如果 User 类型没有显式定义构造函数,则编译器会为其生成一个默认的无参构造函数,你可以在构造函数上加上 [Inject] 属性来显式地注册这个构造函数,如下所示:

public class User : IUser
{
    [Inject]
    public User()
    {
        // 构造函数的逻辑
    }
    // 其他成员
}

5,sp.GetRequiredService();和sp.GetRequiredService(typeof(User)); 有什么不同:

sp.GetRequiredService() 和 sp.GetRequiredService(typeof(User)) 都是用来从 ServiceProvider 中获取一个已注册的服务,只不过前者是使用了泛型方式获取服务,后者是使用了 typeof 方式获取服务,即传入服务类型。

两者的本质是一样的,不同之处主要在于语法上的差异。使用泛型方式获取服务的优点是代码更简洁,可以直接使用服务的类型;而使用 typeof 方式获取服务的优点是可以动态传入服务的类型,更加灵活。无论使用哪种方式,都要求服务已经被注册到 ServiceCollection 中,否则会抛出异常。

C++ :
在 C++ 中,可以使用构造函数初始化列表来实现在有参构造函数中调用无参构造函数,从而实现代码中的功能。

具体实现方式是,在有参构造函数的参数列表后使用冒号和初始化列表,然后在初始化列表中调用无参构造函数即可。示例如下:

在这个示例中,我们通过调用无参构造函数来初始化 name 字段,然后在有参构造函数中调用了无参构造函数。这样,无论我们使用有参构造函数还是无参构造函数来实例化对象,name 字段都会被正确初始化

在 C++ 中,如果在无参构造函数中初始化字段,而实例化对象时调用了有参构造函数,那么就会出现字段没有被初始化的问题。

为了解决这个问题,可以在有参构造函数中调用无参构造函数,然后再对其他字段进行初始化。在 C++ 中,可以使用成员初始化列表来完成这个操作。例如,假设有以下类定义:

#include <iostream>

class MyService {
public:
    MyService() {
        this->name = "default";
    }
    MyService(std::string name) : MyService() {
        this->name = name;
    }

    void sayHello() {
        std::cout << "Hello from " << this->name << std::endl;
    }

private:
    std::string name;
};

int main() {
    MyService myService1("Service 1");
    myService1.sayHello();

    MyService myService2("Service 2");
    myService2.sayHello();

    return 0;
}

在 C++ 中,如果在无参构造函数中初始化字段,而实例化对象时调用了有参构造函数,那么就会出现字段没有被初始化的问题。

为了解决这个问题,可以在有参构造函数中调用无参构造函数,然后再对其他字段进行初始化。在 C++ 中,可以使用成员初始化列表来完成这个操作。例如,假设有以下类定义:

class MyClass {
private:
    int x;
    string name;
public:
    MyClass() : x(0), name("") {}
    MyClass(int x, string name) : MyClass() {
        this->x = x;
        this->name = name;
    }
};

在上面的代码中,有参构造函数 MyClass(int x, string name) 调用了无参构造函数 MyClass(),并使用成员初始化列表 : MyClass() 来完成这个操作。然后,它可以使用 this->x = x 和 this->name = name 来对其他字段进行初始化。这样,在实例化对象时调用有参构造函数时,无参构造函数也会被调用,从而确保所有字段都被正确地初始化。

在 C++ 中使用初始化列表和使用 this 指针初始化字段有以下几个区别和优点:

语法简洁:使用初始化列表可以在一个初始化函数中同时初始化多个成员变量,代码更加简洁。

效率高:使用初始化列表可以避免使用 this 指针进行赋值操作,从而提高代码的执行效率。

便于维护:使用初始化列表可以将初始化操作与构造函数分离,降低代码的耦合度,使代码更加易于维护。

初始化顺序:使用初始化列表可以控制成员变量的初始化顺序,避免因为不同的初始化顺序导致的不可预期的错误。

总的来说,使用初始化列表可以提高代码的可读性、可维护性和执行效率,是 C++ 中常用的初始化成员变量的方式。

在.NET 6 Web API中,当你向容器中注册多个相同类型的服务时,后面注册的服务会覆盖前面注册的服务。因此,在你的代码中,IS7ConnService 接口的两个实现 S7ConnServiceImpl1 和 S7ConnServiceImpl2,只会注册 S7ConnServiceImpl2,并且 S7ConnServiceImpl1 不会被注册。

如果你想在控制器的构造函数中使用 S7ConnServiceImpl1 和 S7ConnServiceImpl2 的实例,你需要一种方式来让容器知道你想使用哪个实现。一种常用的方法是使用工厂模式,这样你可以在构造函数中注入一个工厂,然后通过工厂来创建所需的实例。

首先,创建一个工厂接口,用于创建 IS7ConnService 实例:

public interface IS7ConnServiceFactory
{
    IS7ConnService CreateS7ConnService();
}

然后,实现该接口,根据需要在工厂中返回不同的实现:

public class S7ConnServiceFactory : IS7ConnServiceFactory
{
    private readonly IServiceProvider _serviceProvider;

    public S7ConnServiceFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IS7ConnService CreateS7ConnService()
    {
        // 根据需要在这里判断返回哪个实现
        // 例如,可以根据配置、环境变量等条件来返回不同的实现
        // 在这个例子中,我们简单地返回 S7ConnServiceImpl1
        return _serviceProvider.GetRequiredService<S7ConnServiceImpl1>();
    }
}

在 Startup.cs 中注册工厂和服务:

public void ConfigureServices(IServiceCollection services)
{
    // 注册 S7ConnServiceImpl1 和 S7ConnServiceImpl2
    services.AddSingleton<S7ConnServiceImpl1>();
    services.AddSingleton<S7ConnServiceImpl2>();

    // 注册工厂
    services.AddSingleton<IS7ConnServiceFactory, S7ConnServiceFactory>();

    // 注册控制器
    services.AddControllers();
}

现在,你可以在你的控制器中注入 IS7ConnServiceFactory,并使用工厂来创建实例:

public class YourController : ControllerBase
{
    private readonly IS7ConnServiceFactory _s7ConnServiceFactory;

    public YourController(IS7ConnServiceFactory s7ConnServiceFactory)
    {
        _s7ConnServiceFactory = s7ConnServiceFactory;
    }

    public IActionResult YourAction()
    {
        // 使用工厂创建 IS7ConnService 实例
        var s7ConnService = _s7ConnServiceFactory.CreateS7ConnService();

        // 现在你可以使用 s7ConnService 了
        // ...
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潘诺西亚的火山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值