当前位置: 首页 > news >正文

建设求职网站查网站开通时间

建设求职网站,查网站开通时间,国际形势最新消息,编程软件scratch免费下载Spring踩坑#xff1a;抽象类作为父类#xff0c;使用子类Autowired属性进行填充#xff0c;属性值为null Spring Boot中抽象类和依赖注入的最佳实践引言在抽象类中使用Autowired注解protected vs private修饰符低版本Spring Boot的注意事项 构造器中的依赖注入陷阱为什么不… Spring踩坑抽象类作为父类使用子类Autowired属性进行填充属性值为null Spring Boot中抽象类和依赖注入的最佳实践引言在抽象类中使用Autowired注解protected vs private修饰符低版本Spring Boot的注意事项 构造器中的依赖注入陷阱为什么不能在构造器中使用注入的属性子类构造的问题 PostConstruct的使用正确使用PostConstruct的例子子类中的PostConstruct 避免在构造器中使用ApplicationContext.getBean错误示例正确做法 最佳实践示例常见问题和解决方案1. 循环依赖2. 依赖注入在单元测试中的问题3. 属性注入vs构造器注入4. 抽象类中的 Autowired 方法5. 运行时依赖注入 最佳实践总结结论 Spring Boot中抽象类和依赖注入的最佳实践 引言 在Spring Boot应用程序中抽象类经常被用作一种强大的设计模式用于封装共同的行为和属性。然而当涉及到依赖注入时特别是在抽象类中我们需要格外小心。本文将深入探讨在Spring Boot 2.0及以上版本中使用抽象类作为父类时的最佳实践特别关注依赖注入的正确使用方式。 在抽象类中使用Autowired注解 在Spring Boot 2.0及以上版本中我们可以直接在抽象类的属性上使用Autowired注解进行依赖注入。这为我们提供了一种方便的方式来在父类中定义共同的依赖供子类使用。 protected vs private修饰符 当在抽象类中使用Autowired注解时我们通常有两种选择来修饰这些属性protected或private。 使用protected修饰符: public abstract class AbstractService {Autowiredprotected SomeRepository repository; }优点 允许子类直接访问注入的依赖提供了更大的灵活性子类可以根据需要重写或扩展这些依赖的使用 缺点 可能会破坏封装性因为子类可以直接修改这些依赖 使用private修饰符: public abstract class AbstractService {Autowiredprivate SomeRepository repository;protected SomeRepository getRepository() {return repository;} }优点 保持了良好的封装性父类可以控制子类如何访问这些依赖 缺点 需要额外的getter方法来允许子类访问这些依赖 在Spring Boot 2.0中这两种方式都是可行的。选择哪种方式主要取决于你的设计需求和偏好。如果你希望严格控制依赖的访问使用private加getter方法可能是更好的选择。如果你希望提供最大的灵活性给子类使用protected可能更合适。 低版本Spring Boot的注意事项 在低于2.0的Spring Boot版本中使用protected修饰符通常是更安全的选择。这是因为在一些早期版本中private字段的自动注入可能会遇到问题。如果你正在使用较旧的Spring Boot版本建议使用protected修饰符来确保依赖能够正确注入。 构造器中的依赖注入陷阱 在抽象类中我们经常需要在构造器中执行一些初始化逻辑。然而这里有一个重要的陷阱需要注意不应该在构造器中引用通过Autowired注入的属性。 为什么不能在构造器中使用注入的属性 原因在于Spring的bean生命周期和依赖注入的时机。当Spring创建一个bean时它遵循以下步骤 实例化bean调用构造器注入依赖设置Autowired字段调用初始化方法如PostConstruct注解的方法 这意味着在构造器执行时Autowired注解的属性还没有被注入它们的值为null。如果你在构造器中尝试使用这些属性很可能会遇到NullPointerException。 让我们看一个错误的例子 public abstract class AbstractService {Autowiredprivate SomeRepository repository;public AbstractService() {// 错误此时repository还是nullrepository.doSomething();} }这段代码会在运行时抛出NullPointerException因为在构造器执行时repository还没有被注入。 子类构造的问题 这个问题在子类中更加复杂。当你创建一个抽象类的子类时子类的构造器会首先调用父类的构造器。这意味着即使是在子类的构造器中父类中Autowired注解的属性仍然是null。 public class ConcreteService extends AbstractService {public ConcreteService() {super(); // 调用AbstractService的构造器// 错误此时父类中的repository仍然是nullgetRepository().doSomething();} }这段代码同样会抛出NullPointerException因为在调用子类构造器时父类中的依赖还没有被注入。 PostConstruct的使用 为了解决构造器中无法使用注入依赖的问题Spring提供了PostConstruct注解。被PostConstruct注解的方法会在依赖注入完成后被自动调用这使得它成为执行初始化逻辑的理想位置。 正确使用PostConstruct的例子 public abstract class AbstractService {Autowiredprivate SomeRepository repository;PostConstructpublic void init() {// 正确此时repository已经被注入repository.doSomething();} }在这个例子中init()方法会在所有依赖注入完成后被调用因此可以安全地使用repository。 子类中的PostConstruct 子类也可以定义自己的PostConstruct方法这些方法会在父类的PostConstruct方法之后被调用 public class ConcreteService extends AbstractService {Autowiredprivate AnotherDependency anotherDependency;PostConstructpublic void initChild() {// 父类的init()方法已经被调用// 可以安全地使用父类和子类的所有依赖getRepository().doSomething();anotherDependency.doSomethingElse();} }这种方式确保了所有的初始化逻辑都在依赖注入完成后执行避免了NullPointerException的风险。 避免在构造器中使用ApplicationContext.getBean 另一个常见的陷阱是在构造器中使用ApplicationContext.getBean()方法来获取bean。这种做法应该被避免原因如下 在构造器执行时ApplicationContextAware接口可能还没有被调用这意味着ApplicationContext可能还不可用。即使ApplicationContext可用其他bean可能还没有被完全初始化调用getBean()可能会返回未完全初始化的bean或触发意外的初始化。使用ApplicationContext.getBean()会使你的代码与Spring框架紧密耦合降低了可测试性和可维护性。 错误示例 public abstract class AbstractService implements ApplicationContextAware {private ApplicationContext context;public AbstractService() {// 错误此时context还是nullSomeBean someBean context.getBean(SomeBean.class);}Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.context applicationContext;} }这段代码会抛出NullPointerException因为在构造器执行时setApplicationContext()方法还没有被调用。 正确做法 正确的做法是使用依赖注入让Spring容器管理对象的创建和依赖关系 public abstract class AbstractService {Autowiredprivate SomeBean someBean;PostConstructpublic void init() {// 正确此时someBean已经被注入someBean.doSomething();} }这种方式不仅避免了NullPointerException还降低了与Spring框架的耦合度使代码更易于测试和维护。 最佳实践示例 让我们通过一个完整的例子来展示这些最佳实践 Service public abstract class AbstractUserService {Autowiredprivate UserRepository userRepository;Autowiredprivate EmailService emailService;protected AbstractUserService() {// 构造器中不做任何依赖相关的操作}PostConstructprotected void init() {// 初始化逻辑System.out.println(AbstractUserService initialized with userRepository.getClass().getSimpleName());}public User findUserById(Long id) {return userRepository.findById(id).orElse(null);}protected void sendEmail(User user, String message) {emailService.sendEmail(user.getEmail(), message);}// 抽象方法由子类实现public abstract void processUser(User user); }Service public class ConcreteUserService extends AbstractUserService {Autowiredprivate SpecialProcessor specialProcessor;PostConstructprotected void initChild() {System.out.println(ConcreteUserService initialized with specialProcessor.getClass().getSimpleName());}Overridepublic void processUser(User user) {User processedUser specialProcessor.process(user);sendEmail(processedUser, Your account has been processed.);} }// 使用示例 RestController RequestMapping(/users) public class UserController {Autowiredprivate ConcreteUserService userService;GetMapping(/{id})public ResponseEntityUser getUser(PathVariable Long id) {User user userService.findUserById(id);if (user ! null) {userService.processUser(user);return ResponseEntity.ok(user);} else {return ResponseEntity.notFound().build();}} }在这个例子中 ​AbstractUserService​ 是一个抽象类它定义了一些通用的用户服务逻辑。依赖UserRepository​ 和 EmailService​通过 Autowired​ 注入到抽象类中。初始化逻辑放在 PostConstruct​ 注解的 init()​ 方法中确保在所有依赖注入完成后执行。​ConcreteUserService​ 继承自 AbstractUserService​并实现了抽象方法。​ConcreteUserService​ 有自己的依赖SpecialProcessor​和初始化逻辑。在 UserController​ 中我们注入并使用 ConcreteUserService​。 这个设计遵循了我们讨论的所有最佳实践 在抽象类中使用 Autowired​ 注入依赖避免在构造器中使用注入的依赖使用 PostConstruct​ 进行初始化不使用 ApplicationContext.getBean()​ 常见问题和解决方案 在使用抽象类和依赖注入时开发者可能会遇到一些常见问题。以下是一些问题及其解决方案 1. 循环依赖 问题当两个类相互依赖时可能会导致循环依赖问题。 解决方案 重新设计以消除循环依赖使用 Lazy​ 注解来延迟其中一个依赖的初始化使用 setter 注入而不是构造器注入 Service public class ServiceA {private ServiceB serviceB;Autowiredpublic void setServiceB(Lazy ServiceB serviceB) {this.serviceB serviceB;} }Service public class ServiceB {Autowiredprivate ServiceA serviceA; }2. 依赖注入在单元测试中的问题 问题在单元测试中可能难以模拟复杂的依赖注入场景。 解决方案 使用 Spring 的测试支持如 SpringBootTest​为测试创建一个简化的配置类使用模拟框架如 Mockito 来模拟依赖 SpringBootTest class ConcreteUserServiceTest {MockBeanprivate UserRepository userRepository;Autowiredprivate ConcreteUserService userService;Testvoid testFindUserById() {when(userRepository.findById(1L)).thenReturn(Optional.of(new User(1L, Test User)));User user userService.findUserById(1L);assertNotNull(user);assertEquals(Test User, user.getName());} }3. 属性注入vs构造器注入 问题虽然属性注入使用 Autowired​ on fields很方便但它可能使得依赖关系不那么明显。 解决方案考虑使用构造器注入特别是对于必需的依赖。这使得依赖关系更加明确并有助于创建不可变的服务。 Service public abstract class AbstractUserService {private final UserRepository userRepository;private final EmailService emailService;Autowiredprotected AbstractUserService(UserRepository userRepository, EmailService emailService) {this.userRepository userRepository;this.emailService emailService;}// ... 其他方法 }Service public class ConcreteUserService extends AbstractUserService {private final SpecialProcessor specialProcessor;Autowiredpublic ConcreteUserService(UserRepository userRepository, EmailService emailService,SpecialProcessor specialProcessor) {super(userRepository, emailService);this.specialProcessor specialProcessor;}// ... 其他方法 }这种方法的优点是 依赖关系更加明确有助于创建不可变的服务更易于单元测试 4. 抽象类中的 Autowired 方法 问题有时我们可能想在抽象类中有一个被 Autowired 注解的方法但这个方法在子类中被重写了。 解决方案使用 Autowired 注解抽象方法并在子类中实现它。 public abstract class AbstractService {Autowiredprotected abstract Dependencies getDependencies();PostConstructpublic void init() {getDependencies().doSomething();} }Service public class ConcreteService extends AbstractService {Autowiredprivate Dependencies dependencies;Overrideprotected Dependencies getDependencies() {return dependencies;} }这种方法允许子类控制依赖的具体实现同时保持父类的通用逻辑。 5. 运行时依赖注入 问题有时我们可能需要在运行时动态注入依赖而不是在启动时。 解决方案使用 ObjectProviderT​ 来延迟依赖的解析。 Service public abstract class AbstractDynamicService {Autowiredprivate ObjectProviderDynamicDependency dependencyProvider;protected DynamicDependency getDependency() {return dependencyProvider.getIfAvailable();}// ... 其他方法 }这种方法允许我们在需要时才解析依赖这在某些场景下可能很有用比如条件性的bean创建。 最佳实践总结 基于我们的讨论以下是在Spring Boot中使用抽象类和依赖注入的最佳实践总结 在抽象类中使用 Autowired 可以直接在抽象类的字段上使用 Autowired 注解。使用 protected 修饰符可以让子类直接访问这些依赖而使用 private 加 getter 方法可以提供更好的封装。避免在构造器中使用注入的依赖 构造器执行时依赖还没有被注入因此不应该在构造器中使用它们。使用 PostConstruct 进行初始化 将需要依赖的初始化逻辑放在 PostConstruct 注解的方法中确保所有依赖都已注入。不要在构造器中使用 ApplicationContext.getBean 这可能导致意外的行为因为在构造器执行时ApplicationContext 可能还未完全准备好。考虑使用构造器注入 对于必需的依赖构造器注入可以使依赖关系更加明确并有助于创建不可变的服务。处理循环依赖 使用 Lazy 注解或 setter 注入来解决循环依赖问题。合理使用抽象方法 在抽象类中定义抽象方法可以让子类控制某些依赖的具体实现。使用 ObjectProvider 进行动态依赖注入 当需要在运行时动态解析依赖时考虑使用 ObjectProvider。注意测试 在单元测试中使用 Spring 的测试支持和模拟框架来处理复杂的依赖注入场景。遵循 SOLID 原则 特别是单一责任原则和依赖倒置原则这有助于创建更易维护和测试的代码。 结论 在Spring Boot中使用抽象类和依赖注入是一种强大的技术可以帮助我们创建灵活、可维护的代码。然而它也带来了一些挑战特别是在处理依赖注入的时机和方式上。 通过遵循本文讨论的最佳实践我们可以避免常见的陷阱充分利用Spring Boot提供的依赖注入功能。记住关键是要理解Spring Bean的生命周期合理使用 PostConstruct 注解避免在不适当的时候访问依赖并选择适合你的项目的依赖注入方式。 最后虽然这些是普遍认可的最佳实践但每个项目都有其独特的需求。因此始终要根据你的具体情况来调整这些实践。持续学习和实践是掌握Spring Boot中抽象类和依赖注入的关键。
http://www.zqtcl.cn/news/959847/

相关文章:

  • python网站开发简单吗小程序开发定制北京公司
  • 做网站什么都不懂 怎么做wordpress10款音乐插件
  • 何使网站的页面结构更为合理建用vs2013做网站案例
  • 帮人做空间网站怎么赚钱静态网站怎么维护
  • 3d网站带后台下载深圳建站公司设计深业集团
  • 上海人才中心网站电脑培训班
  • 桂林网站建设服务电话网页开发基础
  • 企业型网站建设策划网站案例模板
  • 怎么做产品网站wordpress ajax form
  • 智能建站设计开发电子商务网站的主流语言
  • 大型建站公司是干嘛的北京最富裕的三个区
  • 深圳网站建设设计公司苏州营销网站建设公司排名
  • 网站h1标签的应用漯河网站关键词优化
  • 企业做推广哪些网站比较好环球资源网官方网站
  • 没有网站如何做落地页城市门户网站建设
  • 网易梦幻西游手游官方网站下载制作网站谁家做的好
  • 北京网站制作外包如何在易语言上做网站
  • 中国的网站做欧美风广告设计是干什么的
  • 做酱菜网站做网站什么是解析什么是跳转
  • 西安企业网站备案一般得多少天网站建设公司2018
  • 网站建设安全方案许昌正规网站优化公司
  • 厦门 外贸网站一般什么企业需要建站
  • 代理注册公司需要什么条件网络推广优化服务
  • 做淘宝客网站需要备案吗物流企业
  • 珠海正规网站制作系统东莞建站多少钱
  • 做网站框架图哪个在线网站好用中铁三局招聘官网
  • wordpress百度站内搜索wordpress 修改用户名
  • 网络企业网站建设方案做网站大概需要多少费用
  • 网站301是什么意思自动友链网
  • 淘宝客怎么建设自己网站wordpress links插件