Spring Boot vs .NET Core: Complete Developer Migration Guide

Whether you’re moving from Spring Boot to .NET Core or vice versa — your transition guide is here

Supported Languages

Both frameworks allow development in multiple languages, but Spring Boot is tied to JVM-based languages, while .NET Core is more flexible with multiple language options.

Supported Build Tools

Spring Boot primarily uses Maven or Gradle, while .NET Core uses MSBuild and dotnet CLI. Dependency management is handled via Maven/Gradle in Spring Boot and NuGet in .NET Core.

Supported IDEs for Development

You can choose any IDE what ever suits to you. I prefer IntelliJ for Spring Boot and VS Code for .Net Core.

Supported Embedded Servers

Spring Boot applications typically run on Tomcat by default but can be configured to use Jetty or Undertow. .NET Core applications use Kestrel by default but can be hosted behind IIS, HTTP.sys, or Nginx.

Project Setup & Structure

Spring Boot Project Structure

A typical Spring Boot project structure with multiple environment properties:

.NET Core Project Structure

A typical .NET Core project structure with multiple environment settings:

Both frameworks separate concerns using MVC architecture and include configuration files for managing environments. Additionally, both have a dedicated test structure to ensure test-driven development.

Application Initialization

Spring Boot Initialization

Spring Boot applications are initialized using SpringApplication.run() in the main method:

.NET Core Initialization

.NET Core applications are initialized using CreateDefaultBuilder() in Program.cs:

Startup Sequence & Main Application Entry Points

Spring Boot handles configuration automatically using SpringApplication.run(), while .NET Core provides more explicit control via Program.cs and Startup.cs.

Dependency Injection

Both Spring Boot and .NET Core support Dependency Injection (DI) for managing application components.

Registering Dependencies in Spring Boot

Registering Dependencies in .NET Core

Both frameworks support dependency injection, but while Spring Boot uses annotations, .NET Core relies on explicit configuration in Startup.cs.

CRUD Operations with ORM

Spring Boot CRUD Example

@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// Getters and Setters
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {}
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User createUser(User user) {
return userRepository.save(user);
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
public User updateUser(Long id, User userDetails) {
User user = userRepository.findById(id).orElseThrow();
user.setName(userDetails.getName());
user.setEmail(userDetails.getEmail());
return userRepository.save(user);
}
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
@GetMapping
public List<User> getAllUsers() {
return userService.getAllUsers();
}
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User userDetails) {
return userService.updateUser(id, userDetails);
}
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
}
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;

    // Getters and Setters
}

@Repository
public interface UserRepository extends JpaRepository<User, Long> {}

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public User createUser(User user) {
        return userRepository.save(user);
    }

    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    public User updateUser(Long id, User userDetails) {
        User user = userRepository.findById(id).orElseThrow();
        user.setName(userDetails.getName());
        user.setEmail(userDetails.getEmail());
        return userRepository.save(user);
    }

    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;

    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.createUser(user);
    }

    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }

    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User userDetails) {
        return userService.updateUser(id, userDetails);
    }

    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
}
@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String email; // Getters and Setters } @Repository public interface UserRepository extends JpaRepository<User, Long> {} @Service public class UserService { @Autowired private UserRepository userRepository; public User createUser(User user) { return userRepository.save(user); } public List<User> getAllUsers() { return userRepository.findAll(); } public User updateUser(Long id, User userDetails) { User user = userRepository.findById(id).orElseThrow(); user.setName(userDetails.getName()); user.setEmail(userDetails.getEmail()); return userRepository.save(user); } public void deleteUser(Long id) { userRepository.deleteById(id); } } @RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; @PostMapping public User createUser(@RequestBody User user) { return userService.createUser(user); } @GetMapping public List<User> getAllUsers() { return userService.getAllUsers(); } @PutMapping("/{id}") public User updateUser(@PathVariable Long id, @RequestBody User userDetails) { return userService.updateUser(id, userDetails); } @DeleteMapping("/{id}") public void deleteUser(@PathVariable Long id) { userService.deleteUser(id); } }

Enter fullscreen mode Exit fullscreen mode

.NET Core CRUD Example

public class User {
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
public class UserService
{
private readonly AppDbContext _context;
public UserService(AppDbContext context) {
_context = context;
}
public User CreateUser(User user) {
_context.Users.Add(user);
_context.SaveChanges();
return user;
}
public IEnumerable<User> GetAllUsers() {
return _context.Users.ToList();
}
public User UpdateUser(int id, User userDetails) {
var user = _context.Users.Find(id);
user.Name = userDetails.Name;
user.Email = userDetails.Email;
_context.SaveChanges();
return user;
}
public void DeleteUser(int id) {
var user = _context.Users.Find(id);
_context.Users.Remove(user);
_context.SaveChanges();
}
}
[Route("api/users")]
[ApiController]
public class UserController : ControllerBase
{
private readonly UserService _userService;
public UserController(UserService userService) {
_userService = userService;
}
[HttpPost]
public ActionResult<User> CreateUser(User user) {
return Ok(_userService.CreateUser(user));
}
[HttpGet]
public ActionResult<IEnumerable<User>> GetUsers() {
return Ok(_userService.GetAllUsers());
}
[HttpPut("{id}")]
public ActionResult<User> UpdateUser(int id, User userDetails) {
return Ok(_userService.UpdateUser(id, userDetails));
}
[HttpDelete("{id}")]
public IActionResult DeleteUser(int id) {
_userService.DeleteUser(id);
return NoContent();
}
}
public class User {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

public class UserService
{
    private readonly AppDbContext _context;
    public UserService(AppDbContext context) {
        _context = context;
    }
    public User CreateUser(User user) {
        _context.Users.Add(user);
        _context.SaveChanges();
        return user;
    }
    public IEnumerable<User> GetAllUsers() {
        return _context.Users.ToList();
    }
    public User UpdateUser(int id, User userDetails) {
        var user = _context.Users.Find(id);
        user.Name = userDetails.Name;
        user.Email = userDetails.Email;
        _context.SaveChanges();
        return user;
    }
    public void DeleteUser(int id) {
        var user = _context.Users.Find(id);
        _context.Users.Remove(user);
        _context.SaveChanges();
    }
}

[Route("api/users")]
[ApiController]
public class UserController : ControllerBase
{
    private readonly UserService _userService;
    public UserController(UserService userService) {
        _userService = userService;
    }
    [HttpPost]
    public ActionResult<User> CreateUser(User user) {
        return Ok(_userService.CreateUser(user));
    }
    [HttpGet]
    public ActionResult<IEnumerable<User>> GetUsers() {
        return Ok(_userService.GetAllUsers());
    }
    [HttpPut("{id}")]
    public ActionResult<User> UpdateUser(int id, User userDetails) {
        return Ok(_userService.UpdateUser(id, userDetails));
    }
    [HttpDelete("{id}")]
    public IActionResult DeleteUser(int id) {
        _userService.DeleteUser(id);
        return NoContent();
    }
}
public class User { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } } public class UserService { private readonly AppDbContext _context; public UserService(AppDbContext context) { _context = context; } public User CreateUser(User user) { _context.Users.Add(user); _context.SaveChanges(); return user; } public IEnumerable<User> GetAllUsers() { return _context.Users.ToList(); } public User UpdateUser(int id, User userDetails) { var user = _context.Users.Find(id); user.Name = userDetails.Name; user.Email = userDetails.Email; _context.SaveChanges(); return user; } public void DeleteUser(int id) { var user = _context.Users.Find(id); _context.Users.Remove(user); _context.SaveChanges(); } } [Route("api/users")] [ApiController] public class UserController : ControllerBase { private readonly UserService _userService; public UserController(UserService userService) { _userService = userService; } [HttpPost] public ActionResult<User> CreateUser(User user) { return Ok(_userService.CreateUser(user)); } [HttpGet] public ActionResult<IEnumerable<User>> GetUsers() { return Ok(_userService.GetAllUsers()); } [HttpPut("{id}")] public ActionResult<User> UpdateUser(int id, User userDetails) { return Ok(_userService.UpdateUser(id, userDetails)); } [HttpDelete("{id}")] public IActionResult DeleteUser(int id) { _userService.DeleteUser(id); return NoContent(); } }

Enter fullscreen mode Exit fullscreen mode

Here are the key differences between the CRUD approaches in Spring Boot and .NET Core:

![]

Authentication & Authorization

Spring Boot Security

Dependency

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.11.2</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.11.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>

Enter fullscreen mode Exit fullscreen mode

Security Configuration & Controller

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
@RestController
@RequestMapping("/auth")
public class AuthController {
@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody LoginRequest request) {
// Authenticate and generate JWT token
return ResponseEntity.ok("JWT-TOKEN");
}
}
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/auth/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
}

@RestController
@RequestMapping("/auth")
public class AuthController {
    @PostMapping("/login")
    public ResponseEntity<String> login(@RequestBody LoginRequest request) {
        // Authenticate and generate JWT token
        return ResponseEntity.ok("JWT-TOKEN");
    }
}
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/auth/**").permitAll() .anyRequest().authenticated() .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } } @RestController @RequestMapping("/auth") public class AuthController { @PostMapping("/login") public ResponseEntity<String> login(@RequestBody LoginRequest request) { // Authenticate and generate JWT token return ResponseEntity.ok("JWT-TOKEN"); } }

Enter fullscreen mode Exit fullscreen mode

.NET Core Identity Authentication

Install Required Packages

dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

Enter fullscreen mode Exit fullscreen mode

Identity Configuration

public class ApplicationUser : IdentityUser {}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser> {
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options) { }
}
public class ApplicationUser : IdentityUser {}

public class ApplicationDbContext : IdentityDbContext<ApplicationUser> {
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options) { }
}
public class ApplicationUser : IdentityUser {} public class ApplicationDbContext : IdentityDbContext<ApplicationUser> { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } }

Enter fullscreen mode Exit fullscreen mode

Authentication Middleware in Startup.cs

public void ConfigureServices(IServiceCollection services) {
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("YOUR_SECRET_KEY")),
ValidateIssuer = false,
ValidateAudience = false
};
});
}
public void Configure(IApplicationBuilder app) {
app.UseAuthentication();
app.UseAuthorization();
}
public void ConfigureServices(IServiceCollection services) {
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options => {
            options.RequireHttpsMetadata = false;
            options.SaveToken = true;
            options.TokenValidationParameters = new TokenValidationParameters {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("YOUR_SECRET_KEY")),
                ValidateIssuer = false,
                ValidateAudience = false
            };
        });
}

public void Configure(IApplicationBuilder app) {
    app.UseAuthentication();
    app.UseAuthorization();
}
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.RequireHttpsMetadata = false; options.SaveToken = true; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("YOUR_SECRET_KEY")), ValidateIssuer = false, ValidateAudience = false }; }); } public void Configure(IApplicationBuilder app) { app.UseAuthentication(); app.UseAuthorization(); }

Enter fullscreen mode Exit fullscreen mode

Controller for Authentication

[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase {
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly UserManager<ApplicationUser> _userManager;
public AuthController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager) {
_userManager = userManager;
_signInManager = signInManager;
}
[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] LoginRequest request) {
var result = await _signInManager.PasswordSignInAsync(request.Username, request.Password, false, false);
if (result.Succeeded) {
return Ok("JWT-TOKEN");
}
return Unauthorized();
}
}
[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase {
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly UserManager<ApplicationUser> _userManager;

    public AuthController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager) {
        _userManager = userManager;
        _signInManager = signInManager;
    }

    [HttpPost("login")]
    public async Task<IActionResult> Login([FromBody] LoginRequest request) {
        var result = await _signInManager.PasswordSignInAsync(request.Username, request.Password, false, false);
        if (result.Succeeded) {
            return Ok("JWT-TOKEN");
        }
        return Unauthorized();
    }
}
[ApiController] [Route("api/auth")] public class AuthController : ControllerBase { private readonly SignInManager<ApplicationUser> _signInManager; private readonly UserManager<ApplicationUser> _userManager; public AuthController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager) { _userManager = userManager; _signInManager = signInManager; } [HttpPost("login")] public async Task<IActionResult> Login([FromBody] LoginRequest request) { var result = await _signInManager.PasswordSignInAsync(request.Username, request.Password, false, false); if (result.Succeeded) { return Ok("JWT-TOKEN"); } return Unauthorized(); } }

Enter fullscreen mode Exit fullscreen mode

Key differences between Spring security & .NET core Identity framework

Writing Unit Tests (TDD Approach)

Unit Testing Frameworks

Sample Unit Test — Spring Boot

@SpringBootTest
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
public void testGetUserById() {
User user = new User(1L, "John Doe");
Mockito.when(userRepository.findById(1L)).thenReturn(Optional.of(user));
User result = userService.getUserById(1L);
assertEquals("John Doe", result.getName());
}
}
@SpringBootTest
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    public void testGetUserById() {
        User user = new User(1L, "John Doe");
        Mockito.when(userRepository.findById(1L)).thenReturn(Optional.of(user));

        User result = userService.getUserById(1L);
        assertEquals("John Doe", result.getName());
    }
}
@SpringBootTest @RunWith(MockitoJUnitRunner.class) public class UserServiceTest { @Mock private UserRepository userRepository; @InjectMocks private UserService userService; @Test public void testGetUserById() { User user = new User(1L, "John Doe"); Mockito.when(userRepository.findById(1L)).thenReturn(Optional.of(user)); User result = userService.getUserById(1L); assertEquals("John Doe", result.getName()); } }

Enter fullscreen mode Exit fullscreen mode

Sample Unit Test — .NET Core

public class UserServiceTest {
private readonly Mock<IUserRepository> _userRepositoryMock;
private readonly UserService _userService;
public UserServiceTest() {
_userRepositoryMock = new Mock<IUserRepository>();
_userService = new UserService(_userRepositoryMock.Object);
}
[Fact]
public void TestGetUserById() {
var user = new User { Id = 1, Name = "John Doe" };
_userRepositoryMock.Setup(repo => repo.GetById(1)).Returns(user);
var result = _userService.GetUserById(1);
Assert.Equal("John Doe", result.Name);
}
}
public class UserServiceTest {
    private readonly Mock<IUserRepository> _userRepositoryMock;
    private readonly UserService _userService;

    public UserServiceTest() {
        _userRepositoryMock = new Mock<IUserRepository>();
        _userService = new UserService(_userRepositoryMock.Object);
    }

    [Fact]
    public void TestGetUserById() {
        var user = new User { Id = 1, Name = "John Doe" };
        _userRepositoryMock.Setup(repo => repo.GetById(1)).Returns(user);

        var result = _userService.GetUserById(1);
        Assert.Equal("John Doe", result.Name);
    }
}
public class UserServiceTest { private readonly Mock<IUserRepository> _userRepositoryMock; private readonly UserService _userService; public UserServiceTest() { _userRepositoryMock = new Mock<IUserRepository>(); _userService = new UserService(_userRepositoryMock.Object); } [Fact] public void TestGetUserById() { var user = new User { Id = 1, Name = "John Doe" }; _userRepositoryMock.Setup(repo => repo.GetById(1)).Returns(user); var result = _userService.GetUserById(1); Assert.Equal("John Doe", result.Name); } }

Enter fullscreen mode Exit fullscreen mode

Running & Testing the Application

Spring Boot

.NET Core

References

Spring Boot

.NET Core

原文链接:Spring Boot vs .NET Core: Complete Developer Migration Guide

© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享
It is during our darkest moments that we must focus to see the light.
越是在艰难困苦的时候,我们越是要看到希望
评论 抢沙发

请登录后发表评论

    暂无评论内容