我有一个网络抓取应用程序,可以简单地从两家大书店抓取书籍。这个想法是用户在 URL 中放置类别类型,例如 /romances
、 /biographies
。
Controller :
package bookstore.scraper.controller;
imports...
import java.util.List;
import java.util.Map;
@RestController
public class BookController {
private final MostPreciseBookService mostPreciseBookService;
private final CategorizedBookService categorizedBookService;
private final BestSellersService bestSellersService;
@Autowired
public BookController(MostPreciseBookService bookOperationsService, CategorizedBookService categorizedBookService, BestSellersService bestSellersService) {
this.mostPreciseBookService = bookOperationsService;
this.categorizedBookService = categorizedBookService;
this.bestSellersService = bestSellersService;
}
.....
@GetMapping("/romances")
public Map<Bookstore, List<Book>> get15RomanticBooks() {
return categorizedBookService.get15BooksFromRomanceCategory();
}
@GetMapping("/biographies")
public Map<Bookstore, List<Book>> get15BiographiesBooks() {
return categorizedBookService.get15BooksFromBiographiesCategory();
}
@GetMapping("/guides")
public Map<Bookstore, List<Book>> get15GuidesBooks() {
return categorizedBookService.get15BooksFromGuidesCategory();
}
@GetMapping("/fantasy")
public Map<Bookstore, List<Book>> get15FantasyBooks() {
return categorizedBookService.get15BooksFromFantasyCategory();
}
@GetMapping("/crime")
public Map<Bookstore, List<Book>> get15CrimeBooks() {
return categorizedBookService.get15BooksFromCrimeCategory();
}
}
以及服务:
package bookstore.scraper.book.scrapingtypes;
import bookstore.scraper.Bookstore;
import bookstore.scraper.book.Book;
import bookstore.scraper.empik.EmpikFetchingBookService;
import bookstore.scraper.merlin.MerlinFetchingBookService;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.nodes.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static bookstore.scraper.JSoupConnector.connect;
@Service
@Slf4j
public class CategorizedBookService {
private final EmpikFetchingBookService empikBookService;
private final MerlinFetchingBookService merlinFetchingBookService;
@Value("${external.library.url.empik.romances}")
private String romancesCategoryEmpikURL;
@Value("${external.library.url.empik.biographies}")
private String biographiesCategoryEmpikURL;
@Value("${external.library.url.empik.crime}")
private String crimeCategoryEmpikURL;
@Value("${external.library.url.empik.guides}")
private String guidesCategoryEmpikURL;
@Value("${external.library.url.empik.fantasy}")
private String fantasyCategoryEmpikURL;
@Value("${external.library.url.merlin.romances}")
private String romancesCategoryMerlinURL;
@Value("${external.library.url.merlin.biographies}")
private String biographiesCategoryMerlinURL;
@Value("${external.library.url.merlin.crime}")
private String crimeCategoryMerlinURL;
@Value("${external.library.url.merlin.guides}")
private String guidesCategoryMerlinURL;
@Value("${external.library.url.merlin.fantasy}")
private String fantasyCategoryMerlinURL;
@Autowired
public CategorizedBookService(EmpikFetchingBookService empikBookService, MerlinFetchingBookService merlinFetchingBookService) {
this.empikBookService = empikBookService;
this.merlinFetchingBookService = merlinFetchingBookService;
}
public Map<Bookstore, List<Book>> get15BooksFromRomanceCategory() {
Map<Bookstore, List<Book>> bookstoreWith15CategorizedBooks = new HashMap<>();
bookstoreWith15CategorizedBooks.put(Bookstore.EMPIK, empikBookService
.get15BooksFromCategory(connect(romancesCategoryEmpikURL)));
bookstoreWith15CategorizedBooks.put(Bookstore.MERLIN, merlinFetchingBookService
.get15BooksFromCategory(connect(romancesCategoryMerlinURL)));
return bookstoreWith15CategorizedBooks;
}
public Map<Bookstore, List<Book>> get15BooksFromFantasyCategory() {
Map<Bookstore, List<Book>> bookstoreWith15CategorizedBooks = new HashMap<>();
bookstoreWith15CategorizedBooks.put(Bookstore.EMPIK, empikBookService
.get15BooksFromCategory(connect(fantasyCategoryEmpikURL)));
bookstoreWith15CategorizedBooks.put(Bookstore.MERLIN, merlinFetchingBookService
.get15BooksFromCategory(connect(fantasyCategoryMerlinURL)));
return bookstoreWith15CategorizedBooks;
}
public Map<Bookstore, List<Book>> get15BooksFromCrimeCategory() {
Map<Bookstore, List<Book>> bookstoreWith15CategorizedBooks = new HashMap<>();
bookstoreWith15CategorizedBooks.put(Bookstore.EMPIK, empikBookService
.get15BooksFromCategory(connect(crimeCategoryEmpikURL)));
bookstoreWith15CategorizedBooks.put(Bookstore.MERLIN, merlinFetchingBookService
.get15BooksFromCategory(connect(crimeCategoryMerlinURL)));
return bookstoreWith15CategorizedBooks;
}
public Map<Bookstore, List<Book>> get15BooksFromGuidesCategory() {
Map<Bookstore, List<Book>> bookstoreWith15CategorizedBooks = new HashMap<>();
bookstoreWith15CategorizedBooks.put(Bookstore.EMPIK, empikBookService
.get15BooksFromCategory(connect(guidesCategoryEmpikURL)));
bookstoreWith15CategorizedBooks.put(Bookstore.MERLIN, merlinFetchingBookService
.get15BooksFromCategory(connect(guidesCategoryMerlinURL)));
return bookstoreWith15CategorizedBooks;
}
public Map<Bookstore, List<Book>> get15BooksFromBiographiesCategory() {
Map<Bookstore, List<Book>> bookstoreWith15CategorizedBooks = new HashMap<>();
bookstoreWith15CategorizedBooks.put(Bookstore.EMPIK, empikBookService
.get15BooksFromCategory(connect(biographiesCategoryEmpikURL)));
bookstoreWith15CategorizedBooks.put(Bookstore.MERLIN, merlinFetchingBookService
.get15BooksFromCategory(connect(biographiesCategoryMerlinURL)));
return bookstoreWith15CategorizedBooks;
}
}
正如你所看到的,有很多相同的代码。我做了这样的东西,但不知道做得好不好:
public Map<Bookstore, List<Book>> get15BooksFromCategory(String merlinCategoryURL, String empikCategoryURL) {
Map<Bookstore, List<Book>> bookstoreWith15CategorizedBooks = new HashMap<>();
bookstoreWith15CategorizedBooks.put(Bookstore.EMPIK, empikBookService
.get15BooksFromCategory(connect(empikCategoryURL)));
bookstoreWith15CategorizedBooks.put(Bookstore.MERLIN, merlinFetchingBookService
.get15BooksFromCategory(connect(merlinCategoryURL)));
return bookstoreWith15CategorizedBooks;
}
我还在考虑在 Controller 中创建一种方法,例如:
@GetMapping("/{category}")
public Map<Bookstore, List<Book>> get15CategorizedBooks(@PathVariable String category) {
...
}
但我很难在 Service 中创建通用方法。
问题在最后。在 Service 中使用这么多 @Value
注释是个好方法吗?/(使用 yml 文件来存储 URL)。
最佳答案
The question in the end. Is it good way to have that many @Value annotations in Service?/ (using yml files to store URL's).
这并不是因为您注入(inject)了太细粒度的属性。这些应该被封装到一个特定的对象中:更具可读性、可维护性和可测试性。
Spring 提供了 @ConfigurationProperties
来做到这一点。
它必须注释包含属性的类。
你可以这样做:
@Component
@ConfigurationProperties("external.library.url")
public class BookStoreUrlProperties {
private Empik empik = new Empik();
private Merlin merlin = new Merlin();
// getters/setters
public BookStoreUrlProperties() {
}
public static class Empik {
private String romances;
private String biographies;
private String crime;
private String guides;
private String fantasy;
// getters/setters
}
public static class Merlin {
private String romances;
private String biographies;
private String crime;
private String guides;
private String fantasy;
// getters/setters
}
}
然后像任何其他bean一样注入(inject)这个bean:
@Autowired
BookStoreUrlProperties bookStoreUrlProperties;
并使用 getter 来检索 url,例如:
String RomanceUrl = bookStoreUrlProperties.getMerlin().getRomances();
我建议的另一种选择是将属性(merlin 和 empik)拆分为两个属性类。
关于服务类中的重复,您可以通过提取参数重构轻松地排除它们,因为唯一的区别是 URL 值。
例如:
public Map<Bookstore, List<Book>> get15BooksFromGuidesCategory() {
return get15BooksFrom(guidesCategoryEmpikURL, guidesCategoryMerlinURL)
}
public Map<Bookstore, List<Book>> get15BooksFromBiographiesCategory() {
return get15BooksFrom(biographiesCategoryEmpikURL, biographiesCategoryMerlinURL)
}
public Map<Bookstore, List<Book>> get15BooksFrom(String bookStoreEmpikURL, String bookStoreMerlinURL) {
bookstoreWith15CategorizedBooks.put(Bookstore.EMPIK, empikBookService
.get15BooksFromCategory(connect(bookStoreEmpikURL)));
bookstoreWith15CategorizedBooks.put(Bookstore.MERLIN, merlinFetchingBookService
.get15BooksFromCategory(connect(bookStoreMerlinURL)));
return bookstoreWith15CategorizedBooks;
}
关于java - 适应DRY原则的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57175950/