Hiển thị các bài đăng có nhãn Spring Framework. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn Spring Framework. Hiển thị tất cả bài đăng

28 tháng 9, 2017

Thực hiện tùy biến Spring AOP Annotation

1. Annotation AOP là gì?

Giới thiệu nhanh thì AOP là viết tắt của lập trình hướng khía cạnh (Aspect Oriented Programming), đây là kĩ thuật chèn thêm hành vi vào đoạn code đã tồn tại mà không cần phải sửa code trực tiếp.

Bài viết giả định bạn đọc đã có những kiến thức cơ bản về lập trình hướng khía cạnh. Nếu bạn chưa có kiến thức về AOP, hãy tìm hiểu qua về khái niệm của pointcutadvice trong lập trình hướng khía cạnh.

Loại AOP mà chúng ta sẽ thực hiện tùy biến trong bài viết này là annotation driven. Chắc hẳn mọi người đều cảm thấy rất quen thuộc nếu đã từng sử dụng qua Spring @Transaction annotaion.

@Transaction
public void saveOrder(Order order) {
    // Gọi một loạt các thao tác với database trong transaction
}

Điểm mấu chốt ở đây đó là không tác động trực tiếp vào code. Bằng cách sử dụng annotation meta-data, những logic nghiệp vụ cơ bản của ứng dụng của bạn sẽ không bị trộn lẫn với những đoạn code quản lí transaction. Việc này sẽ làm cho code của bạn dễ dàng được giải thích, tái cấu trúc và kiểm thử trong transaction isolation.

Đôi khi, những lập trình viên phát triển các ứng dụng của hệ sinh thái Spring lại coi đây là điều "vi diệu" của Framework này, mà không suy nghĩ nhiều về cách thức hoạt động chi tiết của nó. Thực tế, những gì đang xảy ra không thực sự quá phức tạp. Một khi hoàn thành các bước của bài viết này, bạn sẽ có thể tự tạo ra các annotation tùy biến của riêng mình, hiểu được cách thức hoạt động và tận dụng các lợi ích của AOP mang lại.

2. Maven Dependency

Đầu tiên, hãy thêm các Maven dependency cần thiết.

Trong ví dụ này, chúng ta sẽ sử dụng Spring Boot, vì cách tiếp cận cấu hình của module này sẽ giúp chúng ta bắt đầu ví dụ một cách nhanh nhất có thể:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.7.RELEASE</version>
</parent>
 
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
</dependencies>

Cần lưu ý là phải thêm AOP starter, nó sẽ kéo các thư viện mà ta cần để có thể triển khai AOP.

3. Tạo annotation tùy biến

Annotation sau đây mà tôi sẽ tạo ra là một annotation được sử dụng với mục đinh log khoảng thời gian thực thi của một phương thức:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {

}

Mặc dù cách thực hiện khá là đơn giản, nhưng bạn hãy chú ý đến hai meta-annotation được sử dụng.

Annotation @Target sẽ cho bạn biết nơi mà annotation tùy biến có thể áp dụng. Ở đây ta đang sử dụng ElementType.Method, điều này có nghĩa là annotation tùy biến sẽ chỉ làm việc trên các phương thức. Nếu bạn cố gắng thử sử dụng annotation đã tạo ra ở những nơi khác, thì đoạn code của chúng ta sẽ báo lỗi trong quá trình biên dịch.

Và annotation @Retention sẽ chỉ rõ liệu annotation tùy biến của chúng ta có sẵn dùng trong quá trình runtime hay không. Mặc định điều này sẽ là không, vì vậy Spring AOP sẽ không thể nhận biết được annotation mà chúng ta đã tạo ra. Đó là lí do tại sao nó được cấu hình lại.

4. Tạo Aspect

Bây giờ, chúng ta đã có một annotation, hãy tạo thêm aspect. Đây chính là điều chúng ta cần quan tâm nhất, tất cả những gì chúng ta cần là một class được đánh dấu bởi @Aspect annotation:

@Aspect
@Component
public class ExampleAspect {

}

Bạn cũng sẽ cần phải sử dụng đến annotation @Component vì class này cũng cần phải là một spring bean trong Spring container. Về cơ bản, thì đây là class mà ta sẽ thực hiện những logic mong muốn.

5. Tạo Pointcut và Advice

Bây giờ, hãy tạo ra các pointcutadvice. Đây sẽ là một phương thức chú thích được đặt bên trong class Aspect của chúng ta:

@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    return joinPoint.proceed();
}

Về mặt kĩ thuật, điều này sẽ không thay đổi hành vi của bất cứ điều gì, nhưng vẫn còn rất nhiều điều đang cần phân tích.

Đầu tiên, ta đánh dấu phương thức bằng annotation @Around. Đây là advice của chúng ta, và around advice nghĩa là chúng ta đang thêm code bổ sung trước và sau khi thực thi phương thức. Cũng có một số loại advice khác, ví dụ như before, after nhưng trong phạm vi bài viết này, ta sẽ không sử dụng đến chúng.

Tiếp theo, hãy chú ý đến annotation @Around của chúng ta có một đối số point cut. Pointcut của chúng ta nói rằng: 'Áp dụng advice này lên bất kì phương thức nào được đánh dấu với annotation @LogExecutionTime.'. Cũng có rất nhiều loại pointcut khác nữa, nhưng một lần nữa chúng ta sẽ không đề cập đến chúng trong phạm vi bài viết này.

Chính phương thức logExecutionTime() sẽ là một advice, phương thức này có một đối số là ProceedingJoinPoint. Trong trường hợp của chúng ta, thì đây sẽ là một phương thức thực thi mà đã được đánh dấu bằng annotation @LogExecutionTime.

Cuối cùng, bất cứ khi nào một phương thức đã được đánh dấu được gọi đến, thì advice của chúng ta sẽ được gọi đầu tiên. Sau đó, tùy thuộc vào advice của chúng ta quyết định làm gì tiếp theo. Trong trường hợp này, advice của chúng ta không làm gì khác ngoài việc gọi đến phương thức proceed(), điều này sẽ chỉ gọi đến phướng thức gốc đã được đánh dấu.

6. Log thời gian thực thi

Bây giờ chúng ta đã có một bộ khung, tất cả những gì chúng ta cần làm là thêm một vài logic vào trong advice. Chúng ta sẽ ghi lại thời gian thực thi của phương thức ban đầu.

@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    long start = System.currentTimeMillis();
 
    Object proceed = joinPoint.proceed();
 
    long executionTime = System.currentTimeMillis() - start;
 
    System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
    return proceed;
}

Một lần nữa, chúng ta không làm bất cứ điều gì quá phức tạp ở đây cả. Chúng ta chỉ lưu lại thời gian hiện lại, thực thi phương thức, sau đó in ra console khoảng thời gian đã tính toán được. Chúng ra cũng log cả tên của phương thức, api này được cung cấp bởi joinpoint instance. Ta cũng có thể truy cập được một tá các thông tin khác nếu muốn, ví dụ như các đối số của phương thức.

Bây giờ, hãy thử đánh dấu một phương thức bằng @LogExecutionTime, và sau đó thực thi nó để xem điều gì sẽ xảy ra. Chú ý rằng, phương thức mà chúng ta muốn đánh dấu phải thuộc về một Spring bean:

@LogExecutionTime
public void serve() throws InterruptedException {
    Thread.sleep(2000);
}

Sau khi thực thi, ta sẽ thấy kết quả trên console như sau:

void com.example.Service.serve() executed in 2030ms

26 tháng 8, 2017

Tùy biến Thymeleaf

Gần đây, tôi có dịp được tham gia một dự án Java có sử dụng đến Thymeleaf, một trong số các công việc mà đội phát triển chúng tôi thực hiện đó là phải tùy biến các thẻ hoặc thuộc tính của template khi triển khai dự án.

1. Tại sao phải tùy biến thư viện Thymeleaf?


Thymeleaf là một thư viện rất mở, hầu hết các tính năng hướng tới người dùng của nó đều không được trực tiếp phát triển bên trong các thành phần cốt lõi, mà chỉ là đóng gói và thành phần hóa các tính năng này thành bộ tính năng gọi là dialect.

Thư viện sẽ cung cấp cho người dùng hai dialect có thể trực tiếp sử dụng đó là: StandardSpringStandard, nhưng bạn cũng có thể dễ dàng mở rộng, tạo ra thêm các dialect của riêng mình. Hãy cùng xem qua một số lí do có thể khiến bạn phải thực hiện việc này:

Kịch bản 1: thêm các tính năng không tồn tại trong các dialect mặc định

Giả sử, ứng dụng của bạn sử dụng dialectSpringStandard, và cần hiển thị cho người dùng cuối một thông báo có màu nền là màu xanh lam hoặc màu đỏ dựa vào role của người dùng đã đăng nhập vào hệ thống (ví dụ, admin hoặc non-admin), trong khoảng thời gian từ thứ hai đến thứ bảy hàng tuần, nếu vào chủ nhật sẽ luôn là màu xanh lá. Bạn có thể làm được điều này bằng cách thực hiện tính toán với các biểu thức điều kiện trong html template, nhưng nếu có quá nhiều điều kiện sẽ khiến code của bạn trở lên rất khó đọc, và khó bảo trì về sau.

Giải pháp: hãy tạo ra một attribute gọi là alertClass, sử dụng Java code để tính toán giá trị của attribute này và trả về CSS class mong muốn, đóng gói code này bên trong dialect có tên là MyOwnDialect, thêm dialect này vào trong template engine với prefix là th (giống như SpringStandard) và bây giờ bạn sẽ có thể sử dụng th:alertClass="${user.role}".

Kịch bản 2: view-layer components

Giả sử, công ty của bạn sử dụng Thymeleaf cho rất nhiều dự án khác nhau, và bạn mong muốn tạo ra một repository cho tất cả các chức năng phổ biến, được sử dụng lại rất nhiều lần trong một số dự án (ví dụ, các tag và/hoặc các attribute) để không phải copy-paste những đoạn code tương tự nhau từ dự án này qua các dự án kế tiếp. Bạn mong muốn tạo ra các view-layers component tương tự như các taglib trong công nghệ JSP.

Giải pháp: tạo một Thymeleaf dialect cho mỗi bộ các chức năng có liên quan với nhau, và thêm các dialect này vào ứng dụng của bạn nếu điều đó cần thiết.

Kịch bản 3: Tự tạo một template riêng

Hãy tưởng tượng rằng, bạn có một trang web cộng đồng cho phép người dùng có thể tạo ra các mẫu thiết kế của riêng họ để hiển thị nội dung. Nhưng bạn không hề muốn người dùng có thể thực hiện được toàn bộ công việc trong template của họ, thậm chí là hạn chế một vài tính năng của Standard dialect (ví dụ, các biểu thức OGNL). Vì vậy, bạn cần phải cung cấp cho người dùng khả năng thêm vào template của họ một số tính năng trong tầm kiểm soát của bạn (ví dụ như, hiển thị ảnh cá nhân, nhập nội dung văn bản, ...).

Giải pháp: Bạn cần tạo ra một Thymeleaf dialect có các thẻ hoặc thuộc tính mà bạn cho phép người dùng có thể sử dụng, giống như <mysite:profilePhoto></mysite:profilePhoto> hoặc có thể là <mysite:blogentries fromDate="23/4/2011" />. Sau đó, hãy cho phép người dùng tạo ra các template riêng có thể sử dụng các tính năng này và chỉ cho Thymeleaf cách thực hiện chúng, đảm bảo rằng không một ai có thể thực hiện được những việc mà bạn không cung cấp.

2. Dialect và Processor


2.1 Dialect


Ngoài các thuộc tính th:x hoặc các thẻ <th:y> đã được cung cấp sẵn bởi Thymeleaf với các tính năng mặc định, thì bạn hoàn toàn có thể tự tạo ra các bộ thuộc tính hoặc thẻ của riêng mình bằng tên bạn muốn và sử dụng chúng để xử lý các template của bạn.

Các Dialect là những đối tượng được implement từ interface org.thymeleaf.dialect.IDialect:

public interface IDialect {

    public String getName();

}

Yêu cầu cốt lõi duy nhất của một dialect là nó phải có tên để có thể xác định được. Tuy nhiên, cách thực hiện chỉ implement duy nhất IDialect rất ít khi được lựa chọn, thay vào đó, chúng ta thường sẽ implement từ một hoặc một số các interface con của IDialect, việc này phụ thuộc vào những gì mà Thymeleaf engine cung cấp:

  • IProcessorDialect: dialect cung cấp các processor.
  • IPreProcessorDialect: dialect cung cấp các pre-processor.
  • IPostProcessorDialect: dialect cung cấp các post-processor.
  • IExpressionObjectDialect: dialect cung cấp các object biểu thức.
  • IExecutionAttributeDialect: dialect cung cấp các thuộc tính xử lý.

Processor dialects: IProcessorDialect

IProcessorDialect interface:

public interface IProcessorDialect extends IDialect {

    public String getPrefix();
    public int getDialectProcessorPrecedence();
    public Set<IProcessor> getProcessors(final String dialectPrefix);

}

Các Processor là những đối tượng phụ trách hầu hết các xử lý logic trong các Thymeleaf template, đây có thể được coi là thành phần quan trọng nhất trong Thymeleaf:

Trong diaclect này chỉ có ba mục được định nghĩa:

  • prefix: đây là phần tiền tố hoặc namespace sẽ được áp dụng mặc định cho các phần tử hoặc các thuộc tính phù hợp với các processor của dialect này. Do đó, một dialect có tiền tố là th giống với Standard Dialect sẽ có thể xác định được các processor phù hợp với các thuộc tính như th:text, th:if, hoặc th:whatever (hoặc bạn cũng có thể sử dụng cú pháp của HTML5: data-th-text, data-th-if, và data-th-whatever). Tiền tố có thể được thiết lập trong cấu hình template engine và chúng có thể nhận giá trị null nếu như bạn muốn các processor có thể được thực thi trên các tag/attribute không cố định.
  • dialect precedence: là độ ưu tiên khi sắp xếp các processor trong các dialect
  • processors: các processor được cung cấp bởi dialect. Chú ý là phương thức getProcessors(...) nhận dialectPrefix làm tham số đầu vào trong trường hợp dialect này đã được cấu hình trong Template engine với tiền tố khác mặc định. Hầu hết các trường hợp thì ```IProcessor sẽ cần thông tin này khi khởi tạo.

Pre-processor dialects: IPreProcessorDialect

Pre-processorpost-processor khác với các processor đó là thay vì chỉ thực thi trên một sự kiện hoặc mẫu sự kiện đơn lẻ (một fragment của một template), thì chúng sẽ được áp dụng lên toàn bộ quá trình xử lý template như là một bước bổ sung trong chuỗi xử lý của engine. Do vậy, chúng sẽ được thực thi theo API hoàn toàn khác so với các processor, sẽ hướng sự kiện nhiều hơn, và được xác định bởi interface tầng thấp là ITemplateHandler.

Trong trường hợp có sử dụng các pre-processor, chúng sẽ được áp dụng trước khi Thymeleaf engine bắt đầu xử lý các processor cho một template chỉ định.

IPreProcessorDialect interface:

public interface IPreProcessorDialect extends IDialect {

    public int getDialectPreProcessorPrecedence();
    public Set<IPreProcessor> getPreProcessors();

}

Interface này tương tự với IProcessorDialect, nhưng thiếu prefix vì nó không cần thiết cho các pre-processor (chúng sẽ xử lý tất cả sự kiện xảy ra, không riêng bất kì sự kiện cụ thể nào cả)

Post-processor dialects: IPostProcessorDialect

Như đã nêu ở trên, post-processor là một bước bổ sung vào dây chuyền thực thi template, nhưng lần này chúng sẽ được thực thi sau khi Thymeleaf engine đã áp dụng tất cả các processor cần thiết. Điều này nghĩa là post-processor sẽ được áp dụng ngay trước khi template có kết quả (và do đó có thể chỉnh sửa được kết quả trả về).

IPostProcessorDialect interface:

public interface IPostProcessorDialect extends IDialect {

    public int getDialectPostProcessorPrecedence();
    public Set<IPostProcessor> getPostProcessors();

}

IPostProcessorDialect có cấu trúc tương tự với IPreProcessorDialect.

Expression Object dialects: IExpressionObjectDialect

Những dialect implement interface này cung cấp thêm các đối tượng expression object hoặc các expression utility object, các đối tượng này có thể được sử dụng trong các biểu thức ở bất kì nơi nào của một template, ví dụ, Standard Dialect mặc định có cung cấp một số đối tượng sau #strings, #numbers, #dates, #list,...

Còn đây là interface IExpressionObjectDialect:

public interface IExpressionObjectDialect extends IDialect {

    public IExpressionObjectFactory getExpressionObjectFactory();

}

Như chúng ta thấy, interface này chỉ có một phương thức duy nhất và nó không hề trả về chính các đối tượng expression, mà lại là một factory. Lí do là một vài đối tượng expression sẽ phải cần dữ liệu từ ngữ cảnh xử lý để có thể build được, do đó, nó sẽ không thể tự build cho đến khi chúng ta thực sự đang trong quá trình xử lý template... Bên cạnh đó, hầu hết các biểu thức đều không cần dùng đến các đối tượng expression, vì vậy sẽ tốt hơn nếu các đối tượng này chỉ được build khi thực sự cần thiết đối với các biểu thức cụ thể (và cũng chỉ build những gì thật cần thiết).

Đây là interface IExpressionObjectFactory:

public interface IExpressionObjectFactory {

    public Map<String,ExpressionObjectDefinition> getObjectDefinitions();

    public Object buildObject(final IProcessingContext processingContext, final String expressionObjectName);

}

Dialect xử lý thuộc tính: IExecutionAttributeDialect

Các Dialect implement interface này được cung cấp khả năng xử lý các thuộc tính.

Ví dụ, Standard Dialect implement interface này để cung cấp cho mọi processor:

  • Thymeleaf Standard Expression parser: các Standard Expression trong bất kì thuộc tính đều có thể được phân tích và thực thi.
  • Variable Expression Evaluator: các biểu thức ${...} được thực thi trong OGNL hoặc SpringEL (nếu tích hợp với Spring module).
  • Conversion Service: thực hiện các tính toán chuyển đổi trong biểu thức ${{...}}.

Lưu ý rằng, những đối tượng này không có sẵn trong ngữ cảnh, vì vậy, chúng không thể được sử dụng từ các biểu thức template. Tính khả dụng của chúng bị giới hạn trong việc triển khai mở rộng, như là các processor, pre-processor,...

Interface này đơn giản chỉ là:

public interface IExecutionAttributeDialect extends IDialect {

    public Map<String,Object> getExecutionAttributes();

}

2.2. Processor


Processor là những đối tượng implement interface org.thymeleaf.processor.IProcessor, và chúng chứa các logic thật sự để áp dụng cho các phần khác nhau của một template.

Interface này có cấu trúc như sau:

public interface IProcessor {

    public TemplateMode getTemplateMode();
    public int getPrecedence();

}

Giống với các dialect, đây là một interface rất đơn giản, chỉ xác định mode của templateprocessor có thể được sử dụng và độ ưu tiên của nó.

Nhưng có một vài loại processor tương ứng với mỗi loại sự kiện có thể xảy ra:

  • Template start/end
  • Element Tags
  • Texts
  • Comments
  • CDATA Sections
  • DOCTYPE Clauses
  • XML Declarations
  • Processing Instructions

Tùy biến Thymeleaf


(by @dangquando)

26 tháng 7, 2017

Quản lý transaction trong Spring Framework - Spring @Transaction

Spring là một framework Java được sử dụng phổ biến nhất hiện nay, nó mang đến rất nhiều tính năng và nhiều phần bổ trợ cho các ứng dụng Java. Tuy nhiên, hầu hết mọi người đều có khuynh hướng sử dụng những tính năng này mà không thực sự hiểu cơ chế bên dưới của chúng.

1. Cách sử dụng và trường hợp sử dụng

@Transaction(value = "myTransactionManager", propagation = Propagation.REQUIRED)
public void myMethod() {
...
}

Thuộc tính value của annotation @Transaction không bắt buộc phải được khai báo. Nếu không khai báo thuộc tính này thì mặc định Spring sẽ tìm kiếm một bean bất kì được khai báo bên trong context có tên là "transactionManager" (đây là convention mặc định trong Spring).

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

2. Cấu hình sử dụng transaction trong Spring context

Muốn annotation @Transaction có thể hoạt động được, ta sẽ phải khai báo thẻ <tx:annotation-driven> (trong đó tx là rút gọn của namespace "http://www.springframework.org/schema/tx" hoặc cũng có thể tạm hiểu đây là một alias cho namespace này).

3. Phân tích code

3.1. Khai báo spring bean

Ở phần này, chúng ta sẽ xem xét cách mà Spring context xử lý khi khai báo sử dụng thẻ <tx:annotation-driven>

1, org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser

/**
* Parses the '<code></code>' tag. Will
* {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator}
* with the container as necessary.
*/
public BeanDefinition parse(Element element, ParserContext parserContext) {
  String mode = element.getAttribute("mode");
  if ("aspectj".equals(mode)) {
    // mode="aspectj"
    registerTransactionAspect(element, parserContext); 
  } else {
    // mode="proxy"
    // DEFAULT MODE
    AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
  }
  return null;
}

Hầu hết, chúng ta sẽ đều đi vào block else của block điều kiện if-else trong đoạn code bên trên (mode="proxy"), vì vậy chúng ta sẽ gọi AopAutoProxyConfigurer.configureAutoProxyCreator()

2 tháng 4, 2016

Tùy chỉnh ngoại lệ không tìm thấy tài nguyên khi duyệt web trong Spring MVC

@Controller
public class BookController {
private static final Logger logger = LoggerFactory.getLogger(BookController.class);
@Autowired
@Qualifier("bookService")
private BookService bookService;
@Autowired
@Qualifier("cartBean")
private Cart cartBean;
@ExceptionHandler(ResourceNotFoundException.class)
public String handleResourceNotFoundException(HttpServletRequest request) {
HttpSession session = request.getSession(true);
session.setAttribute("cartBean", cartBean);
return "404page";
}
@RequestMapping(value = "/book/detail/{bookId}")
@SuppressWarnings("unchecked")
public String details(@PathVariable("bookId") long bookId, Model model, HttpServletRequest request) {
logger.info("Show Book Detail Page");
HttpSession session = request.getSession(false);
Book book = null;
if (session != null && session.getAttribute("books") != null) {
List<Book> books = (List<Book>) session.getAttribute("books");
for (Book b : books) {
if (b.getId() == bookId) {
book = b;
model.addAttribute("book", b);
break;
}
}
if (book == null) {
throw new ResourceNotFoundException();
}
} else {
book = bookService.findBook(bookId);
if (book == null) {
throw new ResourceNotFoundException();
}
model.addAttribute("book", book);
}
return "book-detail";
}
}
@Controller
@SessionAttributes(names = "cartBean")
public class ResourceNotFoundController {
@Controller
@SessionAttributes(names = "cartBean")
public class ResourceNotFoundController {
@Autowired
@Qualifier("cartBean")
private Cart cartBean;
@RequestMapping("/404")
public String handleResourceNotFound(Model model, HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) {
model.addAttribute("cartBean", cartBean);
}
return "404page";
}
}
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
private static final long serialVersionUID = -3329819391367130855L;
}

8 tháng 1, 2016

Một số annotation trong Spring

Sau đây là một số annotation trong Spring Framework cần biết:

Spring Core annotation

@Autowired: Tự động liên kết các bean được sử dụng trong các class với các bean được Spring Container sinh ra và quản lý.

// @org.springframework.beans.factory.annotation.Autowired
@Service
public class BookServiceImpl implements BookService {
 
  @Autowired
  private BookDao bookDao;

  @Autowired
  private CustomerDao customerDao;
   
  ...
}

@Scope: mặc định trong Spring, hầu hết scope phổ biến cho các autodetected componentssingleton (singleton: Với mỗi bean, Spring IoC Container chỉ tạo duy nhất một đối tượng), để thay đổi phạm vi ta sử dụng @Scope annotation).

// @org.springframework.context.annotation.Scope
@Component
@Scope("request")
public class ContactResource {
...
}

Stereotyping Annotations

@Component: dùng để đánh dấu một class sẽ được tạo bean

// @org.springframework.stereotype.Component
@Component
public class ContactResource {
...
}

@Repository: dùng để đánh dấu các lớp Repository hoặc Dao của Spring. Mọi truy cập dữ liệu logic tới cơ sở dữ liệu sẽ đặt trong các lớp này.

// @org.springframework.stereotype.Repository
@Repository
public class BookDaoImpl implements BookDao {
...
}

@Service: dùng để đánh dấu các lớp Service. Tất cả xử lý nghiệp vụ logic sẽ đặt trong các lớp Service.

// @org.springframework.stereotype.Service
@Service
public class BookServiceImpl implements BookService {
...
}

Transaction Annotations

@Transactional: dùng để đánh dấu các class có sử dụng đến transaction, các transaction này sẽ được trao cho Spring quản lý.

// @org.springframework.transaction.annotation.Transactional
@Service
@Transactional(readOnly = true)
public class BookServiceImpl implements BookService {
 
  @Autowired
  private BookDao bookDao;
 
  @Transactional
  public Book findByName(String name) {
 
    Book book = bookDao.findByName(name);
    return book;
  }
  ...
}

Spring MVC Annotation

@Controller: dùng để đánh dấu lớp Controller

// @org.springframework.stereotype.Controller
@Controller
public class LoginController {
...
}

@RequestMapping: dùng để ánh xạ tới các URL lên toàn bộ một class hay một phương thức xử lý riêng. Thông thường dùng để ánh xạ một request path (hoặc path pattern) tới một controller.

// @org.springframework.web.bind.annotation.RequestMapping
@Controller
@RequestMapping("/login")
public class LoginController {
 
  @Autowired
  private UserService userService;
...
}

@RequestParam: dùng để gán một request parameter vào một parameter của phương thức.

// @org.springframework.web.bind.annotation.RequestParam
@Controller
@RequestMapping("/user")
public class LoginController {
 
  @RequestMapping("/login")
  public String listCompanies(@RequestParam String username, @RequestParam String password) {
    if(username.equals("admin") && password.equals("mypass")){
       return "welcome";
    } else {
       return "login?login=fail";
    }
  }
...
}

@InitBinder: dùng để đánh dấu một phương thức có tùy biến các ràng buộc dữ liệu.
@SessionAttribute: Chỉ định một modelAttribute sẽ được lưu trong session.

Spring Security Annotation

@PreAuthorize: được dùng để cho phép hoặc từ chối sử dụng một phương thức. Trong ví dụ dưới đây, chỉ những đối tượng user có quyền Admin mới có thể xóa được một đối tượng contact:

// @org.springframework.security.access.prepost.PreAuthorize
@Transactional
@PreAuthorize("hasRole('ROLE_ADMIN')")
public void removeContact(int id) {
    contactDao.removeContact(id);
}