Tag: Java

  • Kiến trúc phân tầng (Layered Architecture) (Phần 1)

    Kiến trúc phân tầng (Layered Architecture) (Phần 1)

    Kiến trúc phân tầng(hay còn được gọi là kiến trúc n-tier) là kiến trúc phổ biến nhất. Kiến trúc này được xem là chuẩn không chính thức cho các ứng dụng Java EE và được biết đến rộng rãi bởi hầu hết kiến trúc sư, nhà thiết kế và nhà phát triển. Kiến trúc này quen thuộc với các cơ cấu tổ chức và truyền thông CNTT truyền thống, được tìm thấy ở hầu hết các công ty khiến nó trở thành một lựa chọn tự nhiên cho các công ty phát triển ứng dụng doanh nghiệp.

    Giới thiệu kiến trúc phân tầng

    Các thành phần bên trong kiến trúc phân tầng được tổ chức thành các tầng nằm ngang, mỗi tầng thực hiện một vai trò cụ thể trong ứng dụng, ví dụ như tầng trình diễn(presentation) hay tầng nghiệp vụ (business). Mặc dù kiến trúc phân tầng không qui định số lượng hay các loại tầng phải tồn tại, hầu hết các kiến trúc phân tầng bao gồm 4 tầng: tầng trình diễn, tầng nghiệp vụ, tầng lưu trữ(persistence), tầng cơ sở dữ liệu(database).

    4-tier

    Trong một vài trường hợp tầng nghiệp vụ và tầng lưu trữ có thể được kết hợp thành một tầng nghiệp vụ, đặc biệt khi logic lưu trữ (SQL hay HSQL) được nhúng bên trong các thành phần của tầng nghiệp vụ. Vì vậy với các ứng dụng nhỏ có thể chỉ có ba tầng, ngược lại với các ứng dụng lớn hoặc các ứng dụng có nghiệp vụ phức tạp có thể chứa năm tầng hoặc nhiều hơn.

    Mỗi tầng trong kiến trúc phân tầng có một vai trò và trách nhiệm cụ thể trong ứng dụng. Ví dụ tầng trình diễn sẽ có trách nhiệm xử lí tất cả giao diện người dùng và logic giao tiếp trình duyệt, trái lại tầng nghiệp vụ sẽ chịu trách nhiệm thực thi các quy tắc nghiệp vụ cụ thể. Mỗi tầng trong kiến trúc phân tầng trừu tượng hoá các công việc cần phải hoàn thành để đáp ứng một yêu cầu nghiệp vụ cụ thể. Ví dụ tầng trình diễn không cần phải biết hay lo lắng về việc lấy dự liệu khách hàng như thế nào; nó chỉ cần hiển thị các thông tin đó lên màn hình theo định dạng cụ thể. Tương tự như vậy, tầng nghiệp vụ không cần bận tâm làm thế nào định dạng dữ liệu khách hàng để hiển khị lên màn hình hoặc ngay cả việc dự liệu khách hàng được lưu trữ ở đâu; nó chỉ cần lấy dữ liệu từ tầng lưu trữ, thực hiện logic nghiệp vụ dựa trên dữ liệu đó(tính toán các giá trị hay tổng hợp dữ liệu), và truyền các thông tin đó tới tầng trình diễn.

    Một trong những tính năng mạnh mẹ của kiến trúc phân tầng sự tách biệt các mỗi bận tâm giữa các thành phần. Các thành phần bên trong một tầng cụ thể chỉ giải quyết các logic liên quan đến tầng đó. Ví dụ các thành phần ở tầng trình diễn chỉ giải quyết các logic hiển thị, ngược lại các thành phần cư trú ở tầng nghiệp vụ chỉ giải quyết các logic nghiệp vụ. Việc phân loại các loại thành phần như thế này giúp cho việc xây dựng mô hình trách nhiệm và vai trò có hiệu quả trở nên dễ dàng hơn, đồng thời giúp bạn dễ dàng phát triển, kiểm thử, quản lí, và bảo trì các ứng dụng nhờ vào các giao diện thành phần được mô tả rõ ràng và phạm vi thành phần được giới hạn.

    Tầng đóng(closed layer)

    Chúng ta cùng xem hình dưới đây:

    closed_layer.png

    Mỗi tầng trong kiến trúc này được đánh dấu CLOSED. Đây là một khái niệm rất quan trọng trong kiến trúc phân tầng. Một tầng đóng có nghĩa là một yêu cầu(request) đi từ tầng này sang tầng khác phải đi qua tầng ngay bên dưới nó để đi tới tầng tiếp theo bên dưới tầng đó. Ví dụ một request bắt nguồn từ tầng trình diễn phải đi qua tầng trình diễn sau đó tới tầng lưu trữ trước cuối cùng chốt lại ở tầng cơ sở dữ liệu.

    Tại sao không cho phép tầng trình diễn truy cập trực tiếp đến tầng lưu trữ hay tầng cơ sở dữ liệu?

    Truy cập trực tiếp cơ sở dữ liệu từ tầng trình diễn thì nhanh hơn là thông qua một loạt các tầng không cần thiết chỉ để lấy ra hay lưu lại thông tin cơ sở dữ liệu. Câu trả lời cho câu hỏi này nằm ở một khái niệm tầng cô lập(layers of isolation).

    Tầng cô lập

    Khái niệm tầng cô lập có nghĩa là những thay đổi được tạo ra trong một tầng của kiến trúc nhìn chung không có tác động hay ảnh hướng đến các thành phần của tầng khác: thay đổi được cô lập trong các thành phần bên trong lớp đó và một lớp nào đó có có thể có liên quan(ví dụ như tầng lưu trữ có chứa SQL). Nếu chúng ta cho phép tầng trình diễn truy cập trực tiếp tới tầng lưu trữ thì những thay đổi được tạo ra với SQL trong tầng lưu trữ sẽ có tác động tới cả tầng nghiệp vụ và tầng trình diễn, do đó tạo ra một ứng dụng liên kết rất chặt chẽ với rất nhiều phụ thuộc chéo giữa các thành phần. Những ứng dụng như thế này quá cứng nhắc và tốn kém khi có thay đổi.

    Khái niệm tầng cô lập cũng có nghĩa là mỗi một tầng độc lập với các tầng khác, do đó chúng biết rất ít hoặc không biết về hoạt động bên trong của các tầng khác trong kiến trúc. Để hiểu được sức mạnh và tầm quan trọng của khái niệm này, chúng ta cùng xem xét nỗ lực tái cấu trúc để chuyển đổi presentation framework từ JSP (Java Server Pages) to JSF (Java Server Faces). Giả sử rằng hợp đồng(model) được sử dụng giữa tầng trình diễn và tầng nghiệp vụ không thay đổi, tầng nghiệp vụ không bị ảnh hưởng bởi việc tái cấu trúc và giữ độc lập hoàn toàn với các loại giao diện người dùng được sử dụng bởi tầng trình diễn.

    Tầng mở

    Tầng đóng tạo điều kiện thuận lợi cho tầng cô lập và do đó giúp cô lập thay đổi trong kiến trúc, đôi khi tầng mở có ý nghĩa đối với các tầng nhất định. Giả sử bạn muốn thêm vào tầng dịch vụ chia sẻ(shared services) chứa các thành phần dịch vụ dùng chung được truy cập bởi các thành phần trong tầng nghiệp vụ(các lớp tiện ích dữ liệu hay chuổi, các lớp kiểm tra và ghi nhật ký). Trong trường hợp này việc tạo ra tầng dịch vụ thường là ý tưởng tốt bởi lẽ về mặt kiến trúc thì nó hạn chế các truy cập vào các dịch vụ chia sẻ đổi với tầng nghiệp vụ(chứ không phải tầng trình diễn). Không có sự tách biệt về tầng, về mặt kiến trúc không có gì hạn chế tầng trình diễn truy cập vào các dịch vụ dùng chung này, gây khó khăn cho việc quản lý hạn chế truy cập.

    Trong ví dụ này, tầng dịch vụ mới sẽ có thể sẽ nằm bên dưới tầng nghiệp vụ để chỉ rằng các thành phần trong tầng dịch vụ này không thể truy cập từ tầng trình diễn. Tuy nhiên, điều này có nghĩa rằng tầng nghiệp vụ lúc này bắt buộc phải đi qua tầng dịch vụ để tới tầng lưu trữ, điều này không có ý nghĩa gì cả. Đây là vấn đề lâu đời của kiến trúc phân tầng. Để giải quyết vấn đề này chúng ta tạo ra tầng mở trong kiến trúc.

    open_layer.png

    Các tầng dịch vụ trong trường hợp này được đánh dấu là OPEN, có nghĩa là các yêu cầu được cho phép đi qua tầng mở và đi trực tiếp tới tầng bên dưới. Trong ví dụ trên vì tầng dịch vụ là tầng mở nên tầng nghiệp vụ bây giờ được phép bỏ qua nó và đi trực tiếp tới tầng lưu trữ, điều này hoàn toàn hợp lí.

    Chúng ta có thể tận dụng khái niệm về tầng mở và đóng để xác định mối quan hệ giữa các tầng trong kiến trúc và luồng yêu cầu cũng như cung cấp cho nhà thiết kế, nhà phát triển các thông tin cần thiết để hiểu các hạn chế truy cập tới các tầng khác nhau trong kiến trúc. Việc không thể xác định chính xác các tầng nào trong kiến trúc là mở hay đóng và tại sao thường dẫn tới các cấu trúc liên kết chặt chẽ và dễ vỡ, chúng rất khó để kiểm thử, bảo trì và triển khai.

    Tài liệu tham khảo

    • Software Architecture Patterns
  • [MyBatis] Sử dụng MyBatis với Spring Boot

    [MyBatis] Sử dụng MyBatis với Spring Boot

    MyBatis là gì?

    MyBatis là một framework nổi tiếng trong cộng đồng Java. Nó là triển khai của tầng lưu trữ trong kiến trúc phân tầng trên nền tảng Java tương tự như Hibernate hoặc ngay cả là JDBC thuần. Nó giúp việc triển khai tầng lưu trữ trở nên đơn giản hơn với nhà phát triển. Thông tin về MyBatis bạn có thể tham khảo tại đây. Nội dung bài viết này tập trung vào cách sử dụng MyBatis cùng với Spring Boot.

    MyBatis-Spring-Boot-Starter

    Để sử dụng MyBatis với Spring Boot chúng ta sử dụng thư viện MyBatis-Spring-Boot-Starter. Đây là thư viện hỗ trợ cấu hình nhanh chóng một ứng dụng Spring Boot có sử dụng MyBatis. Thư viện này được hỗ trợ chính thức từ nhóm phát triển MyBatis. Các bước cấu hình tại đây.

    Repository hay Mapper

    Mapper là một Java interface mà các phương thức được ánh xạ tới các truy vấn SQL tương ứng. Mặc định MyBatis sẽ ánh xạ các method được định nghĩa trong các interface được đánh dấu với annotation @Mapper tới các truy vấn SQL tương ứng.

    ProductRepository

    Dưới đây là một ví dụ về Mapper. ProductRepository được định nghĩa trong package app.demo.mybatis.repository

    
    @Mapper
    public interface ProductRepository {
    
      void create(Product product);
    }
    

    Để định nghĩa truy vấn SQL tương ứng chúng ta tạo ProductRepository.xml trong thư mục app.demo.mybatis.repository bên trong resources.

    
    
    
    
      
        
      
    
    

    Trong ví dụ trên chúng ta đang ánh xạ phương thức create với câu truy vấn INSERT. Khi phương thức create được gọi thì câu truy vấn sẽ được thực thi. Kết quả câu truy vấn sẽ được trả về phương thức create.

    DataSource

    Trong bài viết này chúng ta sẽ sử dụng MySQL để thực hành với MyBatis. Chúng ta có thể sử dụng docker để tạo container chạy MySQL với câu lệnh sau:

    docker run --name demo -e MYSQL_ROOT_PASSWORD=demo@123 -p 3306:3306 -d mysql --lower_case_table_names=1
    

    Với câu lệnh trên chúng ta đã tạo xong MySQL với schema demo cũng như mật khẩu cho tài khoản rootdemo@123.

    Để cấu hình data source với Spring Boot chúng ta thêm các cấu hình sau trong application.yml:

    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/demo?createDatabaseIfNotExist=true
        username: root
        password: demo@123
        driverClassName: com.mysql.cj.jdbc.Driver
    

    BindingException

    org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)
    

    Lỗi này xuất hiện khi chưa cấu hình thuộc tính mybatis.mapper-locations. Cấu hình thuộc tính này trong application.yml hoặc application.properties như sau:

    mybatis:
      mapper-locations: "classpath:app.demo.mybatis.repository/*.xml"
    
    mybatis.mapper-locations=classpath:app.demo.mybatis.repository/*.xml
    

    Tổng kết

    Trong bài viết này tôi đã hướng dẫn các bạn các bước cơ bản để tạo một ứng dụng Spring Boot có sử dụng MyBatis. Hi vọng bài viết sẽ giúp ích cho các bạn mới bắt đầu tiếp cận với MyBatis cũng như là cung cấp một hướng dẫn khi bắt đầu xây dựng một dự án.

  • Spring Security: Tìm hiểu về internal flow

    Spring Security: Tìm hiểu về internal flow

    Spring Security là gì?

    Spring Security là một framework được cung cấp bởi Spring cung cấp khả năng xác thực, bảo vệ, kiểm soát truy cập và có khả năng tuỳ biến cao. Tập trung chủ yếu vào Authentication và Authorization cho một ứng dụng Java.

    Giống như hầu hết các Spring projects khác, sức mạnh thực sự của Spring Security đến từ việc nó có thể dễ dàng mở rộng khi cần thiết với những yêu cầu cụ thể trong một dự án.

    Benefits/features chính:

    • Hỗ trợ authentication và authorization một cách toàn diện.
    • Ngăn chặn các nguy cơ bảo mật đến từ Cross-site Forgery, CSRF Attacks, ClickJacking,…
    • Hỗ trợ tích hợp với Spring Web MVC
    • Hỗ trợ tích hợp với Servlet API

    Internal Workflow: Cách Spring Security hoạt động?

    Dưới đây là workflow cách mà Spring Security mặc định (User Credentials) hoạt động:

    User Credentials Authentication workflow

    Ta hãy cùng đến với những objects chính có trong flow và tìm hiểu định nghĩa của chúng nhé.

    Spring Security Filters:

    Spring Security Authentication Filters là những filter sẽ nằm giữa client request với server. Khi nhận được request, các filter sẽ tách lọc những thông tin từ request thành các authentication details (username, password, roles,…). Default Spring Security sẽ sử dụng class UsernamePasswordAuthenticationFilter.

    UsernamePasswordAuthenticationFilter extends từ Abstract class AbstractAuthenticationProcessingFilter.

    Authentication: là một base object làm nhiệm vụ validate user credentials nhận được từ phía client. Ở behavior mặc định, Authentication object sẽ là class UsernamePasswordAuthenticationToken.

    UsernamePasswordAuthenticationToken sẽ được sử dụng để chứa user credentials.

    AuthenticationManager:

    AuthenticationManager là một interface với method authenticate() làm nhiệm vụ xác định những Authentication providers phù hợp nhất để xử lý Authentication object nhận được từ filters. AuthenticationManager sẽ nhận kết quả authenticate từ Provider (Success hoặc Not success). Nếu không success, nó sẽ thử một provider phù hợp khác.

    Ở behavior mặc định của Spring security, class ProviderManager sẽ được chọn để xử lý các request.

    ProviderManager implements interface AuthenticationManager.

    AuthenticationProvider:

    AuthenticationProvider là những classes implement interface AuthenticationProvider với method authenticate() làm nhiệm vụ xử lý các logic liên quan đến authentication. DaoAuthenticationProvider sẽ là authentication provider mặc định cho behavior mặc định của Spring Security.

    DaoAuthenticationProvider.

    UserDetailsService: là interface chứa thông tin, schema của user details. Ở behavior mặc định, Spring Security sẽ sử dụng class InMemoryUserDetailsManager, với method loadUserByUsername() để lấy ra thông tin của user từ memory của hệ thống.

    PasswordEncoder: là interface có nhiệm vụ encode, encrypt và decrypt password của user, validate và trả về kết quả valid/invalid cho Authentication Provider xử lý.

    Security Context:

    Sau khi Spring Security đã validate đủ, user details sẽ được lưu vào Security context. Ở lần truy cập tới, thông tin user sẽ được filter retrieve ở đây thay vì thực hiện đầy đủ các bước flow như ở trên.

    Kết luận

    Trên đây là tổng hợp về Spring Security cũng như một flow mặc định của Spring Security sẽ diễn ra như thế nào. Tất nhiên, còn rất nhiều những vấn đề to lớn khác từ Spring Security mà phạm vi bài viết không thể mô tả đủ. Hi vọng, qua bài viết trên, bạn đã có thể có cái nhìn tổng quan nhất về Spring Security, rất cảm ơn bạn đã dành thời gian ra để đọc qua bài viết trên của mình.

    Tài liệu tham khảo

    https://spring.io/projects/spring-security

    https://blog.knoldus.com/spring-security-internal/

    https://www.linkedin.com/pulse/how-does-spring-security-works-internally-ayush-jain/

  • Dependency Injection

    Dependency Injection

    Dependency Injection là một mẫu thiết kế được sử dụng để triển khai Inversion of Control. Nó cho phép tạo các đối tượng phụ thuộc bên ngoài một lớp và cung cấp các đối tượng đó cho một lớp thông qua các cách khác nhau. Sử dụng DI, chúng ta di chuyển việc tạo và ràng buộc các đối tượng phụ thuộc ra bên ngoài lớp phụ thuộc vào chúng. Điều này mang lại mức độ linh hoạt cao hơn, phân tách và kiểm tra dễ dàng hơn.

    Khi mà class A sử dụng một số chức năng của class class B, thì có thể nói là class A có quan hệ phụ thuộc với class B.

    Trong java, trước khi ta có thể sử dụng method của class khác, ta phải khởi tạo một đối tượng của class đấy.

    Tại sao cần sử dụng Dependency Injection?

    Ví dụ chúng ta có một class Car, trong đó có chứa đối tượng khác như Wheel.

    Ở đây, class Car chịu trách nhiệm khởi tạo tất cả các dependency object. Nhưng chuyện gì sẽ xảy ra nếu chúng ta muốn bỏ Wheel và thay thế bằng SteelWheel hoặc PlasticWheel.

    Để giải quyết vấn đề trên thì chúng ta phải tạo một class Car mới với SteelWheel hoặc PlasticWheel. Tuy nhiên khi sử dụng dependency injection, chúng ta có thể đổi Wheel trong thời gian chương trình chạy (Runtime) vì dependency có thể được đẩy vào ở Runtime thay vì Compiletime.

    Có 3 loại Dependency Injection

    1. Constructor injection: Các dependency được cung cấp thông qua constructor của class.
    2. Setter Injection: Khách hàng tạo ra một setter method để các class khác có thể sử dụng chúng để cấp dependency.
    3. Interface injection: Dependency sẽ cung cấp một hàm injector để inject nó vào bất kỳ khách hàng nào được truyền vào. Các khách hàng phải implement một interface mà có một setter method dành cho việc nhận dependency.

    Trách nhiệm của dependency injection là:

    1. Tạo ra các object.
    2. Biết class nào cần object đấy.
    3. Cung cấp cho những class đó object chúng cần.

    Bằng cách này, nếu trong tương lai object đó có sự thay đổi thì dependency injection có nhiệm vụ cấp lại những object cần thiết cho class.

    Điểm mạnh

    1. Giúp unit test dễ hơn.
    2. Giảm thiểu được code mẫu (boilerplate code) vì việc khởi tạo dependency được làm bởi một component khác.
    3. Mở dụng dự án dễ dàng hơn.
    4. Giúp ích trong việc liên kết lỏng giữa các thành phần trong dự án

    Điểm yếu

    1. Khá phức tạp để học.
    2. Rất nhiều các lỗi ở compile time có thể đẩy sang runtime.
    3. Có thể làm ảnh hường tới chức năng auto-complete hay Find references của một số IDE.

    Nguồn tham khảo:

    1. https://viblo.asia/p/dependency-injection-la-gi-va-khi-nao-thi-nen-su-dung-no-LzD5d0d05jY
  • [Spring] ApplicationContext trong Spring Framework

    [Spring] ApplicationContext trong Spring Framework

    Trong bài viết này chúng ta sẽ tìm hiểu chi tiết về ApplicationContext interface.

    ApplicationContext?

    Ta hãy cùng nhớ lại 2 khái niệm DI(Dependency Injection) và IoC(Inversion of Control) gây thương nhớ cho những developer của Spring framework. IoC Container chính là lõi của Spring Framework. IoC Container có chức năng tạo ra các đối tượng, liên kết chúng lại với nhau, cấu hình chúng, và quản lí vòng đời của chúng từ khi tạo ra đến khi bị hủy. IoC container sẽ sử dụng DI để quản lí các thành phần tạo nên một ứng dụng.

    Trong Spring framwork IoC được mô tả qua BeanFactory và ApplicationContext interface. BeanFactory là root interface truy cập vào Spring IoC, cung cấp chức năng cơ bản để quản lí Bean. Còn ApplicationContext là sub-interface cúa BeanFactory interface. Do đó, nó cung cấp tất cả các chức năng cơ bản của BeanFactory cùng nhưng chức năng tiên tiến hơn cho các ứng dụng Spring và phù hợp hơn cho những ứng dụng J2EE.

    Spring Bean

    Trước khi đi chi tiết hơn về ApplicationContext interface ta cần xem qua khái niệm về Spring Bean.

    In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container.
    

    Hay hiểu đơn giản với các ứng dụng Spring, Bean là object được tạo ra và quản lí bới Spring IoC container.

    Cấu hình Bean trong Container

    Để ApplicationContext có thể quản lí được các Bean, ứng dụng phải cũng cấp cấu hình bean cho ApplicationContext container. Ta sẽ có những cách khác nhau cấu hình để cấu hình bean:

    1. XML-Based Configuration
    2. Java-Based Configuration
    3. Annotation-Based Configuration

    XML-Based Configuration

    Cuối cùng với cách cấu hình dựa trên XML, ta sẽ khai báo tất cả các cấu hình của Bean trong một file XML.

    Ta sẽ khai báo Bean cho class UserConfiguration trong một file user-config.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    
      <bean id="userService" class="com.cuongnm.applicationcontext.UserService">
        <constructor-arg name="userRepository" ref="userRepository" />
      </bean>
      
      <bean id="userRepository" class="com.cuongnm.applicationcontext.UserRepository" />
    </beans>
    

    Java-Based Configuration

    Cấu hình dựa trên Java bằng cách sử dụng @Bean annotation bên trong 1 lớp @Configuration. Với mỗi @Bean annotation được khai báo đánh dấu cho một method để tạo ra 1 Spring Bean. Và những phương thức này đc chứa trong 1 class được đánh dấu là @Configuration – một class chứa các cấu hình Spring bean.

    Ví dụ:

    @Configuration
    public class UserConfig {
    
      @Bean
      public UserService userService() {
        return new AccountService(userRepository());
      }
    
      @Bean
      public UserRepository userRepository() {
        return new UserRepository();
      }
    }
    

    Bằng cách đưa ra @Configuration ta sẽ xử lí class UserConfig như thẻ bean trong XML

    Annotation-Based Configuration

    Để sử dụng được phương pháp này, đầu tiên ta sẽ cấu hình trong XML để cho phép sử dụng Annotation-Based Configuration trong ứng dụng.

    <context:annotation-config/>
    <context:component-scan base-package="com.cuongnm.applicationcontext"/>
    

    Thẻ context:annotation-config để khai báo cho việc sử dụng annotation-based mappings và thẻ context:component-scan với tham số base-package cho Spring tìm được package chưa các annotated classes.

    Sau đó, ta sẽ sử dụng các annotation được cung cấp bởi Spring để đánh dấu cho các class, method, constructor, field trong Java để cấu hình cho Bean như @Component, @Controller, @Service, @Repository, @Autowired.

    Ví dụ với class UserService được khai báo là một Bean sử dụng @Component annotation:

    @Component
    public class UserService {
      
    }
    

    Ta có thể lấy ra Bean này bằng cách:

    ApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext/user-bean-config.xml");
    UserService userService = context.getBean(UserService.class);
    assertNotNull(userService);
    

    Cách sử dụng ApplicationContext

    Cũng như việc cấu hình cho Bean thì cũng có rất nhiều cách sử dụng ApplicationContext interface.

    1) ClassPathXmlApplicationContext

    Phương pháp này sẽ tải các Bean config từ các file XML nằm trong đường dẫn classpath.

    ApplicationContext context = new ClassPathXmlApplicationContext ("user-bean-config.xml");
    

    2) FileSystemXmlApplicationContext

    Sử dụng các Bean config từ các file XML trong FileSystem hay từ URL.

    ApplicationContext context = new FileSystemXmlApplicationContext (“c: /myconfig.xml”);
    

    3) AnnotationConfigApplicationContext

    AnnotationConfigApplicationContext được sử dụng với Java-Based Configuration cho các cấu hình Bean.

    Ví dụ:

    public static void main(String[]args){
    /* Creating Spring IoC Container Without XML configuration file*/
    ApplicationContext context= new AnnotationConfigApplicationContext(UserConfig.class);
    MyBean beanObj = context.getBean(UserService.class);
    }
    

    Với ví dụ này ApplicationContext được lấy từ UserConfig class. Chúng ta lấy các cấu hình Bean từ một class được chú thích @Configuratation và nó sẽ được khai báo:

    @Configuration
    public class UserConfig {
    
      @Bean
      public UserService userService() {
        return new AccountService(userRepository());
      }
    
      @Bean
      public UserRepository userRepository() {
        return new UserRepository();
      }
    }
    

    3) XmlWebApplicationContext và AnnotationConfigWebApplicationContext

    XmlWebApplicationContext được sử dụng để đại diện cho Spring container trong ứng dụng Web. Và nó tải các cấu hình bean từ những file XML với mặc định từ đường dẫn “/WEB-INF/applicationContext.xml”. Chúng ta cũng có thể chỉ định được dẫn của nó qua tham số contextConfigLocation của ContextLoaderListener hoặc DispatcherServlet trong web.xml.

    Giống như XmlWebApplicationContext là class tương ứng với ClassPathXmlApplicationContext và FileSystemXmlApplicationContext và được sử dụng để tạo ra ApplicationContext cho các ứng dụng web, tương tự, AnnotationConfigWebApplicationContext là class tương ứng với AnnotationConfigApplicationContext.

    Lời kết

    Tóm tắt lại những gì mình muốn nói ở bài viết này, mục đích giúp chúng ta hiểu về ApplicationContext trong Spring, hiểu cách sử dụng, triển khai ApplicationContext. Cũng như cách gọi Spring container bằng ApplicationContext mang đến nhiều chức năng hơn so với BeanFactory.

    Bài viết được tham khảo từ “https://www.baeldung.com/spring-application-context“.

    Cảm ơn các bạn đã đọc bài viết!

  • [Spring] Sử dụng Spring ResponseStatusException

    [Spring] Sử dụng Spring ResponseStatusException

    Sử dụng Spring ResponseStatusException

    Giới thiệu

    Một ứng dụng RESTful, bằng cách trả về các HTTP status code trong HTTP response nó có thể thông báo về sự thành công hay thất bại của một HTTP request. Ví dụ như nếu người dùng request lên một id không hề tồn tại, các HTTP status code có thể giúp xác định được các vấn đề có thể xảy ra khi xử lí request.

    Trong Spring chúng ta cũng có rất nhiều cách để đặt HTTP status code cho một HTTP response. Tuy nhiên trong bài viết này mình sẽ giới thiệu về một class mới được giới thiệu trong Spring 5 đó chính là ResponseStatusException sẽ hỗ trợ cho ta việc áp dụng HTTP status code.

    @ResponseStatus

    Trước khi tìm hiểu về ResponseStatusException , ta sẽ tìm hiểu qua về @ResponseStatus annotation. Annotation này được giới thiệu trong Spring 3 để giải quyết vấn đề áp dụng HTTP status code cho HTTP response.

    Với annotaion này chúng ta sẽ sử dụng để định nghĩa status code và reason cho HTTP response:

    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.ResponseStatus;
    
    @ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Image not found")
    public class ImageNotFoundException extends Exception{
        //...
    }
    

    Ở trong ví dụ này, nếu Exception được đưa ra trong một xử lí HTTP request, thì trong response sẽ bao gồm HTTP status code được chỉ định sẽ là 404.

    Tuy nhiên với phương pháp sử dụng @ResponseStatus ta có một nhược điểm là nó sẽ tạo ra mối quan hệ phụ thuộc chặt chẽ vói Exception. Và như trường hợp ta xét ở trên thì tất cả các Exception có kiểu ImageNotFoundException khi được đưa ra thì đều cho ta một response với thông báo lỗi và status code là như nhau trong mọi trường hợp.

    ResponseStatusException

    ResponseStatusException được tạo ra nhằm thay thế cho @ResponseStatus và là một base class cho các exception được sử dụng đề apply các HTTP status cho response. Và đây cũng là một RuntimeException.

    Với ResponseStatusException class sẽ cung cấp cho ta 3 contructor:

    Với các đối số truyền vào cho contructor method:

    • status – HTTP status được set cho HTTP response
    • reason – message được hiển thị để giải thích cho exception
    • cause – là một java.lang.Throwable nguyên nhân của ResponseStatusException

    Những điểm mạnh cảu việc dùng ResponseStatusException class:

    • Thứ nhất, việc khai báo và sử dụng dễ dàng
    • Thứ hai, các exception cùng loại ta có thể xử lí riêng biệt và có thể linh hoạt các stutas code khác nhau có thể được set trong response, giảm sự phụ thuộc vào nhau.
    • Thứ ba, ta có thể tránh được việc phải tạo các class exception không cần thiết.
    • Và cuối cùng, class cấp cho ta nhiều quyền hơn trong việc xử lí exception, vì các exception được tạo theo chương trình nên được kiểm soát tốt hơn.

    Ví dụ

    Bây giờ, hãy xem một ví dụ về cách sử dụng ResponseStatusException trong thực tế:

    Ta có 1 class ToDoController khái báo “/todo” mapping truyền vào tham số id để lấy ra Todo object tương ứng

    @RestController
    public class ToDoController {
    
        @Autowired
        private ToDoService toDoService;
    
        @GetMapping(value = "/todo")
        public ToDo getTodo(@RequestParam(value = "id", required = false) Long id) {
            try {
                return toDoService.getTodo(id);
            } catch (TodoNotFoundException e) {
                throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Todo not found", e);
            }
        }
    
    }
    

    Với một exception được khai báo

    public class TodoNotFoundException extends Exception{
    
        public TodoNotFoundException(String errorMessage){
            super(errorMessage);
        }
    }
    

    Spring sẽ cung cấp cho ta một “/error” mapping sẽ trả về response dưới định dạng JSON với HTTP status. Trong ví dụ này khi truyền vào id không hề tồn tại chương trình sẽ trả về ResponseStatusException với response chứa status code tương ứng.

    Đây sẽ là response trong trường hợp có exception (ta sẽ dùng Postman để gửi request):

    Để xem được message về lỗi trong response ta sẽ thêm thuộc tính server.error.include-message=always. Khi đó response trả về sẽ có nội dung:

    {
        "timestamp": "2021-08-03T01:35:20.878+00:00",
        "status": 404,
        "error": "Not Found",
        "message": "Todo not found",
        "path": "/todo"
    }
    

    Ngoài ra với lợi ích là tính linh hoạt khi có thể gán các status code khác nhau cho cùng một exception ta có thể thứ với một ví dụ khác:

    @GetMapping(value = "/todo")
        public ToDo getTodo(@RequestParam(value = "id", required = false) Long id) {
            try {
                return toDoService.getTodo(id);
            } catch (TodoNotFoundException e) {
                throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Provide correct Todo Id", e);
            }
        }
    

    Và response trả về sẽ là:

    {
        "timestamp": "2021-08-03T01:57:41.502+00:00",
        "status": 400,
        "error": "Bad Request",
        "message": "Provide correct Todo Id",
        "path": "/todo"
    }
    

    Lời kết

    Như vậy trong bài viết này, chúng ta đã cùng tìm hiểu về ResponseStatusException – một cách tốt hơn để tạo một HTTP status code trong HTTP response so với annotation @ResponseStatus. Tuy nhiên với việc nên thận trọng với việc sử dụng vì nếu không có phương pháp xử lí exception thống nhất thì việc thực thi một số quy ước trên toàn ứng dụng sẽ khó khăn và có thể xảy ra code bị trùng lặp.

    Cảm ơn các bạn đã đọc bài viết!

    Tài liệu tham khảo: https://www.baeldung.com/spring-response-status-exception

  • Hướng dẫn sử dụng Project Lombok

    Hướng dẫn sử dụng Project Lombok

    Hướng dẫn sử dụng Project Lombok

    Chinh chiến với Java nhiều năm, bạn có cảm thấy nhàm chán khi làm việc với những đoạn code theo khuôn mẫu của nó hay lười biếng phải khai báo các phương thức Getter, Setter cho các class Java? Nếu câu trả lời là có thì hãy sử dụng Project Lombok. Vậy Project Lombok là gì?

    • Lombok?
    • Cài đặt
    • Sử dụng Lombok
    • Lời kết

    Lombok?

    Lombok là một thư viện, một plugin, giúp chúng ta giảm thiểu các đoạn code thừa (boilerplate) bằng cách tự động sinh ra các hàm GetterSetterConstructor, v.v..

    Lombok giúp chúng ta generate code một cách tự động nhưng không giống như cách các IDE làm cho chúng ta. Các IDE generate các phương thức Getter, Setter và một số phương thức khác trong các tập tin .java. Lombok cũng generate các phương thức đó nhưng là trong các tập tin .class file. Không những làm cho code sáng sửa mà còn trông rất hợp lý, dễ quản lý hơn giúp developer tập trung vào tầng nghiệp vụ và logic thay vì mất thời gian làm những việc thừa thãi.

    Cài đặt

    Để sử dụng Lombok trong project ta cần:

    Bước 1

    Thêm lib vào project bằng Maven hoặc Gradle

    Maven

    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
        <scope>provided</scope>
    </dependency>
    

    Gradle

    
    // https://mvnrepository.com/artifact/org.projectlombok/lombok
    compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.20'
    

    Bước 2

    Đến đây có thể bạn sẽ thắc mắc tại sao đã thêm Lombok vào project rồi mà còn phải cài thêm Lombok Plugin vào IDE nữa???

    Ví dụ bạn muốn sử dụng Lombok để generate Get/Set thì nó sẽ tự động thêm code vào class đó trước khi thành file .jar. Nhưng các IDE thì chỉ nhìn thấy các dòng code hiện tại của bạn và tham chiếu tới nó, điều này sẽ dẫn đến những thông báo lỗi khi bạn sử dụng hàm Get/Set này.

    Nên để IDE hiểu rằng các class đã có các hàm Get/Set rồi, thì bạn phải cài thêm Lombok Plugin.

    Bây giờ ta sẽ cài đặt cho IntelliJ IDEA

    Với IntelliJ IDEA version 2020.3 trở lên thì IDE đã hỗ trợ Lombok mà ko cần cài plugin(phần hướng dẫn này cho version trước 2020.3 )

    Vào File -> Setting -> Plugin …

    Search “Lombok”, chọn Lombok Plugin và Install.

    Sử dụng Lombok

    Lombok dùng các Annotation để khai báo

    @Data

    Ví dụ này 2 đoạn code sẽ tương đương

    public class User {
    
        private String name;
    
        private int age;
    
        public User() {
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "User: " + name
                    + " - " + age;
        }
    }
    
    import lombok.Data;
    
    @Data
    public class User {
    
        private String name;
    
        private int age;
    }
    

    Khi bạn đánh dấu 1 class là @Data, thì nó sẽ generate ra Constructor rỗng hoặc có tham số theo yêu cầu, toàn bộ Get/Set, hàm equals, hashCode, toString().

    @NoArgsConstructor@RequiredArgsConstructor@AllArgsConstructor

    Các annotation được dùng trong trường hợp bạn muốn định nghĩa các Contructor theo yêu cầu khác nhau:

    • @NoArgsConstructor: Hàm khởi tạo rỗng, đã đề cập ở trên
    • @RequiredArgsConstructor: Hàm khởi tạo chứa tất cả thuộc tính, ví dụ Champion(String name, String type)
    • @AllArgsConstructor: Hàm khởi tạo theo yêu cầu. Bạn chỉ muốn hàm khởi tạo có vài thuộc tính do bạn chọn thôi, thì bạn thêm final trước thuộc tính trong class, nó sẽ tự sinh ra Constructor như thế.

    @Getter@Setter

    Được sử dụng trong trường hợp chỉ muốn generate Get/Set và không muốn dùng @Data vì có chức năng không cần thiết.

    import lombok.Getter;
    import lombok.Setter;
    
    @Getter
    @Setter
    public class User {
    
        private String name;
    
        private int age;
    }
    

    @Builder

    Thông thường, khi chúng ta cần khởi tạo một đối tượng với rất nhiều thông tin, chúng ta có thể sử dụng Builder Pattern để làm điều này.

    Ví dụ với class User như sau:

    public class Users {
    
        private String name;
    
        private int age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "User: " + name
                    + " - " + age;
        }
    }
    

    Để tạo đối tượng User với tất cả các thông tin sử dụng Builder Pattern, chúng ta cần tạo một đối tượng UserBuilder như sau:

    public class UserBuilder {
    
        private Users user;
    
        public UserBuilder() {
            user = new User();
        }
    
        public UserBuilder name(String name){
            user.setName(name);
            return this;
        }
    
        public UserBuilder age(int age){
            user.setAge(age);
            return this;
        }
    
        public Users build(){
            return user;
        }
    }
    

    Và dùng UserBuilder này:

    Users users = new UserBuilder()
                    .name("CuongNM")
                    .age(20)
                    .build();
    

    Chắc hẳn ai cũng ngại khi viết 1 class Builder cổ điển như trên, tự dưng phải tạo thêm 1 class nữa, gấp đôi số lượng thuộc tính khai báo, gấp đôi số hàm cần viết.

    Với Lombok vấn đề này đc giải quyết rất nhanh gọn

    @Data
    @Builder
    public class Users {
    
        private String name;
    
        private int age;
    }
    
    Users users = Users.builder()
                    .name("CuongNM")
                    .age(20)
                    .build();
    

    Logging với Project Lombok(@Slf4j)

    Thông thường, khi chúng ta sử dụng các Logging frameworks như Log4J, Logback hay Simple Logging Facade for Java (SLF4J), ta sẽ khai báo như sau:

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
     
    public class Example {
     
        public static final Logger LOGGER = LoggerFactory.getLogger(Example.class);
     
        public void print(String message) {
            LOGGER.info(message);
        }
    }
    

    Nhưng đối với Lombok, ta chỉ cần dùng annotation dành cho Logging framework mà chúng ta muốn sử dụng mà thôi, sau đó thì có thể sử dụng như bình thường. Như ví dụ ở đây, mình đang sử dụng Simple Logging Facade for Java(SLF4J) để logging thì mình sẽ khai báo annotation @Slf4j của Lombok dành cho thư viện này như sau:

    import lombok.extern.slf4j.Slf4j;
     
    @Slf4j
    public class Example {
     
        public void print(String message) {
            log.info(message);
        }
    }
    

    Lời kết

    Lombok sinh ra để giúp cho code của chúng ta trở nên ngắn gọn dễ hiểu hơn và nó còn rất nhiều tính năng hay ho khác. Trong khuôn khổ bài viết này mình chỉ giới thiệu về các tính năng cơ bản của Lombok và được mình sử dụng nhiều nhất trong suốt quá trình làm việc.

    Cảm ơn các bạn đã đọc bài viết! Các bạn có thể tìm hiểu thêm tại trang chủ của Lombok.

    Tác giả

    [email protected]

  • [AWS] Remote Debugging ứng dụng Lambda viết bằng Java với Visual Studio Code

    [AWS] Remote Debugging ứng dụng Lambda viết bằng Java với Visual Studio Code

    Debug cũng quan trọng giống như lúc bạn code vậy. Với những bạn làm quen với Lambda thì không phải ai cũng biết làm sao để có thể debug được. Đa phần các bạn sẽ chọn cách in dữ liệu ra màn hình để debug. Hôm nay tôi sẽ hướng dẫn các bạn cách debug ứng dụng viết bằng Lamba nhé.

    Remote Debugging

    Như các bạn đều biết thì để có thể debug được ứng dụng Java thì bạn cần phải Remote tới cổng Debug của trình thực thi Java. Quá trình này được gọi là Remote Debugging. Với ứng dụng Java bình thường các bạn có thể dễ dàng sử dụng các IDE có hỗ trợ Remote Debugging một cách dễ dàng. Với các ứng dụng Lambda bằng Java thì sao?

    Khởi động ứng dụng Lambda với chế độ Remote Debugging

    Trong bài viết Phát triển ứng dụng Lambda bằng Java, tôi đã hướng dẫn các bạn cách sử dụng SAM để chạy các ứng dụng Lambda viết bằng ngôn ngữ Java. Các bạn theo dõi bài viết trên sẽ thấy ứng dụng được chạy trên một máy ảo Java trông một docker container. Để khời động chế để Remote Debugging thì các bạn gõ lệnh sau(các bạn nhớ khởi động Docker trước khi khởi động SAM nhé):

    hieunv@HieuNV sam-app % sam local start-api -d 5858
    Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
    You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
    2020-03-30 20:10:33  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
    

    Các bạn sẽ thấy SAM được khởi động và lắng nghe ở cổng 3000. Còn cổng 5858 thì sao? Tại thời điểm này nó chưa được khởi động. Khi bạn access vào http://127.0.0.1:3000/hello thì cổng Remote Debugging 5858 mới được khởi động.

    Invoking helloworld.App::handleRequest (java11)
    
    Fetching lambci/lambda:java11 Docker container image......
    Mounting /Users/hieunv/Projects/hieunv/sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
    Picked up _JAVA_OPTIONS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=*:5858 -XX:MaxHeapSize=2834432k -XX:MaxMetaspaceSize=163840k -XX:ReservedCodeCacheSize=81920k -XX:+UseSerialGC -XX:-TieredCompilation -Djava.net.preferIPv4Stack=true
    

    Cấu hình Remote Debug trong Visual Studio Code

    Các bạn quay lại Visual Studio Code, vào Tab Debug sau đó chọn create a launch.json file. Tại mục chọn kiểu Debug bạn chon Add Configuration và chọn

    add configuration

    Sau đó các bạn chon Attach To Remote Program

    Attach To Remote Program

    Tiếp đó các bạn sửa lại cấu hình hostName thành localhostport thành 5858(đấy là cổng Remote Debug của trình thực thi Java)

    {
      // Use IntelliSense to learn about possible attributes.
      // Hover to view descriptions of existing attributes.
      // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
      "version": "0.2.0",
      "configurations": [
        {
          "type": "java",
          "name": "Debug (Attach) - Remote",
          "request": "attach",
          "hostName": "localhost",
          "port": 5858
        }
      ]
    }
    

    Đặt break point

    Các bạn quay lại Visual Studio Code và mở mã nguồn muốn debug sau đó đặt break point

    break point

    Khởi động Visual Studio Code Debug bằng các click vào nut Start

    Xem output log bạn sẽ thấy thông báo sau:

    START RequestId: 4f69214b-9a3a-19ef-0137-5081d7caccea Version: $LATEST
    END RequestId: 4f69214b-9a3a-19ef-0137-5081d7caccea
    REPORT RequestId: 4f69214b-9a3a-19ef-0137-5081d7caccea	Init Duration: 58932.30 ms	Duration: 10421.29 ms	Billed Duration: 10500 ms	Memory Size: 512 MB	Max Memory Used: 73 MB
    2020-03-30 20:39:18 127.0.0.1 - - [30/Mar/2020 20:39:18] "GET /hello HTTP/1.1" 500 -
    ``
    

    Sau đó bạn access http://127.0.0.1:3000/hello bằng Postman và quay lại Visual Studio Code

    debug mode

    Như vậy là chúng ta đã debug thành công vào hàm Lambda rồi.

    Cám ơn các bạn đã theo dõi bài viết. Hy vọng bài viết sẽ giúp ích với dự án của các bạn. Chúc các bạn thành công.

  • [AWS] Phát triển ứng dụng Lambda bằng Java

    [AWS] Phát triển ứng dụng Lambda bằng Java

    Như các bạn đã biết hiện nay môi trường thực thi sử dụng trong Lambda phần lớn đang sử dụng Node hay Python. Tuy nhiên trên thực tế đôi khi bạn cần sử dụng một môi trường thực thi khác như Java chẳng hạn. Trên thực tế thì AWS cũng đang hỗ trợ khá nhiều môi trường thực thi khác nhau. Có nhiều lý do dẫn tới việc chúng ta phải sử dụng một môi trường thực thi nào đó tuỳ vào tình hình dự án. Trong bài viết này tôi sẽ hướng dẫn các bạn xây dựng ứng dụng Lamba sử dụng môi trường thực thi là Java.

    Các công cụ cần thiết

    Docker

    Chúng ta cần Docker bởi vì công cụ thực thi SAM CLI sẽ sử dụng docker container để thực thi ứng dụng. Bạn thao khảo đường dẫn sau để cài đặt Docker

    SAM

    Chúng ta sẽ sử dụng SAM vì chúng ta cần một môi trường thực thi có thể chạy trên môi trường cục bộ và có thể debug được. Để cài SAM bạn làm theo hướng dẫn sau:

    brew tap aws/tap
    brew install aws-sam-cli
    

    Chúng ta sử dụng brew để cài SAM nên bạn cần cài brew trước. Nếu chưa cài brew thì bạn có thể thao khảo cách cài brew như sau:

    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
    hieunv@HieuNV ~ % brew --version
    Homebrew 2.2.10
    Homebrew/homebrew-core (git revision f0179; last commit 2020-03-22)
    Homebrew/homebrew-cask (git revision 0a88ae; last commit 2020-03-22)
    

    Để kiểm tra xem bạn đã cài đặt thành công chưa, bạn sử dụng lệnh sau:

    hieunv@HieuNV ~ % sam --version
    SAM CLI, version 0.45.0
    

    Trên Windows thì bạn thao khảo đường dẫn này

    Oracle JDK

    Chúng ta sẽ sử dụng môi trường thực thi Java nên việc cài đặt Oracle JDK là đương nhiên đúng không. Các bạn tham khảo cách cài đặt Oracle JDK tại đây nhé.

    Maven

    SAM sẽ sử dụng maven để build nên chúng ta cần cài đặt thêm maven. Để cài đặt Maven các bạn sử dụng lệnh sau:

    brew install --ignore-dependencies maven
    

    Các bạn chú ý, chúng ta cần sử dụng --ignore-dependencies để bỏ qua việc cài đặt Open JDK nhé. Mặc định maven sẽ sử dụng Open JDK. Tuy nhiên chúng ta đã cài đặt Oracle JDK rồi nên không cần cài Open JDK nữa.

    Tài liệu tham khảo:

    Tạo project bằng SAM

    • Tạo một project mới
    hieunv@HieuNV hieunv % sam init -r java11
    Which template source would you like to use?
    	1 - AWS Quick Start Templates
    	2 - Custom Template Location
    Choice: 1
    
    Which dependency manager would you like to use?
    	1 - maven
    	2 - gradle
    Dependency manager: 1
    
    Project name [sam-app]:
    
    Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git
    
    AWS quick start application templates:
    	1 - Hello World Example: Maven
    	2 - EventBridge Hello World: Maven
    	3 - EventBridge App from scratch (100+ Event Schemas): Maven
    Template selection: 1
    
    -----------------------
    Generating application:
    -----------------------
    Name: sam-app
    Runtime: java11
    Dependency Manager: maven
    Application Template: hello-world
    Output Directory: .
    
    Next steps can be found in the README file at ./sam-app/README.md
    
    • Trước khi thực thi bạn cần build project trước
    hieunv@HieuNV hieunv % cd sam-app
    hieunv@HieuNV sam-app % sam build
    Building resource 'HelloWorldFunction'
    /usr/local/bin/mvn is using a JVM with major version 13 which is newer than 11 that is supported by AWS Lambda. The compiled function code may not run in AWS Lambda unless the project has been configured to be compatible with Java 11 using 'maven.compiler.target' in Maven.
    Running JavaMavenWorkflow:CopySource
    Running JavaMavenWorkflow:MavenBuild
    Running JavaMavenWorkflow:MavenCopyDependency
    Running JavaMavenWorkflow:MavenCopyArtifacts
    
    Build Succeeded
    
    Built Artifacts  : .aws-sam/build
    Built Template   : .aws-sam/build/template.yaml
    
    Commands you can use next
    =========================
    [*] Invoke Function: sam local invoke
    [*] Deploy: sam deploy --guided
    
    • Khởi động ứng dụng (trước khi khởi động bạn cần đảm bảo rằng Docker đang hoạt động)
    hieunv@HieuNV sam-app % sam local start-api
    Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
    You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
    2020-03-22 22:07:45  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
    

    Chúng ta thử truy cập vào http://127.0.0.1:3000/hello bằng Postman. Nếu các bạn chưa chạy lần nào thì sẽ phải chờ hơi lâu một chút để Docker tải image cần thiết.

    start-api

    Trong bài viết này tôi đã hướng dẫn các bạn cách viết một API bằng Lambda sử dụng môi trường thực thi Java. Hy vọng bài viết sẽ giúp ích cho dự án của các bạn.

  • [MacOS] Hướng dẫn cài đặt Oracle JDK

    [MacOS] Hướng dẫn cài đặt Oracle JDK

    Mặc định thì Oracle JDK sẽ được chọn cài đặt trên MacOS. Do đó nếu muốn sử dụng Oracle JDK thì bạn cần phải cài đặt lại. Trong bài viết này tôi sẽ hướng dẫn các bạn cài đặt Oracle JDK.

    Homebrew

    • Nếu bạn chưa cài đặt brew thì có thể sử dụng lệnh sau để tiến hành cài đặt
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
    
    • Nếu đã cài đặt rồi thì tiến hành update lastest brew như sau:
    hieunv@HieuNV ~ % brew update && brew upgrade
    Updated 1 tap (homebrew/core).
    ==> New Formulae
    swift-sh
    ==> Updated Formulae
    apache-spark               jetty                      xcodegen
    docker-slim                vim                        zsh-syntax-highlighting
    Updating Homebrew...
    

    Kiểm tra caskjava

    brew cask info java
    

    Nếu homebrew/cask chưa được cài đặt thì nó sẽ tự động cài đặt luôn

    hieunv@HieuNV ~ % brew cask info java
    ==> Tapping homebrew/cask
    Cloning into '/usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask'...
    remote: Enumerating objects: 3655, done.
    remote: Counting objects: 100% (3655/3655), done.
    remote: Compressing objects: 100% (3648/3648), done.
    remote: Total 3655 (delta 26), reused 510 (delta 5), pack-reused 0
    Receiving objects: 100% (3655/3655), 1.23 MiB | 215.00 KiB/s, done.
    Resolving deltas: 100% (26/26), done.
    Tapped 1 command and 3543 casks (3,660 files, 4.0MB).
    java: 13.0.2,8:d4173c853231432d94f001e99d882ca7
    https://openjdk.java.net/
    Not installed
    From: https://github.com/Homebrew/homebrew-cask/blob/master/Casks/java.rb
    ==> Name
    OpenJDK Java Development Kit
    ==> Artifacts
    jdk-13.0.2.jdk -> /Library/Java/JavaVirtualMachines/openjdk-13.0.2.jdk (Generic Artifact)
    
    • Nếu đã cài đặt rồi bạn sẽ nhận được thông tin về phiên bản java đã được cài đặt
    hieunv@HieuNV ~ % brew cask info java
    java: 13.0.2,8:d4173c853231432d94f001e99d882ca7
    https://openjdk.java.net/
    Not installed
    From: https://github.com/Homebrew/homebrew-cask/blob/master/Casks/java.rb
    ==> Name
    OpenJDK Java Development Kit
    ==> Artifacts
    jdk-13.0.2.jdk -> /Library/Java/JavaVirtualMachines/openjdk-13.0.2.jdk (Generic Artifact)
    

    Tiến hành cài đặt Oracle JDK sử dụng brew cask

    hieunv@HieuNV ~ % brew cask install oracle-jdk
    ==> Caveats
    Installing oracle-jdk means you have AGREED to the license at:
      https://www.oracle.com/technetwork/java/javase/terms/license/javase-license.html
    
    ==> Downloading https://download.oracle.com/otn-pub/java/jdk/13.0.2+8/d4173c8532
    ==> Downloading from https://download.oracle.com/otn-pub/java/jdk/13.0.2+8/d4173
    ######################################################################## 100.0%
    ==> Verifying SHA-256 checksum for Cask 'oracle-jdk'.
    ==> Installing Cask oracle-jdk
    ==> Running installer for oracle-jdk; your password may be necessary.
    ==> Package installers may write to any location; options such as --appdir are i
    Password:
    installer: Package name is JDK 13.0.2
    installer: Installing at base path /
    installer: The install was successful.
    ?  oracle-jdk was successfully installed!
    

    Kiểm tra phiên bản java sau khi cài đặt

    hieunv@HieuNV ~ % java --version
    java 13.0.2 2020-01-14
    Java(TM) SE Runtime Environment (build 13.0.2+8)
    Java HotSpot(TM) 64-Bit Server VM (build 13.0.2+8, mixed mode, sharing)
    
    hieunv@HieuNV ~ % javac --version
    javac 13.0.2
    

    setting JAVA_HOME

    Thêm export JAVA_HOME=$(/usr/libexec/java_home) vào ~/.zshrc

    echo 'export JAVA_HOME=$(/usr/libexec/java_home)' >> ~/.zshrc
    

    Kiểm tra biến JAVA_HOME

    Đóng Termial sau đó bật lại và kiểm tra biến JAVA_HOME

    hieunv@HieuNV libexec % echo $JAVA_HOME
    /Library/Java/JavaVirtualMachines/jdk-13.0.2.jdk/Contents/Home
    

    Như vậy là bạn đã tiến hành cài đặt thành công Oracle Java rồi.
    Tài liệu tham khảo
    https://emcorrales.com/blog/install-oracle-jdk-macos-homebrew