通过简单的Spring方面摆脱null参数

什么是世界上最令人讨厌的,同时也是最受欢迎的例外?

我敢打赌这是NullPointerException。

NullPointerException可以表示任何东西,从简单的“ ups,我认为不能为空”到数小时和数天的第三方库调试(我敢于尝试使用Dozer进行复杂的转换)。

有趣的是,摆脱代码中的所有NullPointerExceptions很简单。 这种琐碎性是一种称为“ 按合同设计 ”的技术的副作用。

我不会详细介绍该理论,您可以在Wikipedia上找到所需的所有内容,但在简而言之,按合同设计意味着:

  • 每个方法都有一个先决条件(调用前期望的条件)
  • 每个方法都有一个后置条件(它保证什么,返回什么)
  • 每个类对其状态都有约束(类不变)

因此,在每种方法的开头,您都要检查是否满足先决条件,最后检查是否满足后置条件和不变式,如果出了问题,则抛出异常,指出错误之处。

使用Spring的内部静态方法引发适当的异常(IllegalArgumentException),它看起来可能像这样:

import static org.springframework.util.Assert.notNull;
import static org.springframework.util.StringUtils.hasText;

public class BranchCreator {
    public Story createNewBranch(Story story, User user, String title) {
        verifyParameters(story, user, title);
        Story branch = //... the body of the class returnig an object
        verifyRetunedValue(branch);
        return branch;
    }

    private void verifyParameters(Story story, User user, String title) {
        notNull(story);
        notNull(user);
        hasText(title);
    }

    private void verifyRetunedValue(Story branch) {
        notNull(branch);
    }
}

您还可以使用来自Apache Commons的Validate类,而不是spring的notNull / hasText。

通常,我只检查先决条件,并为后置条件和约束编写测试。 但这仍然是所有样板代码。 要将其移出类,可以使用许多“按合同设计”库,例如
SpringContractsContract4J 。 无论哪种方式,您最终都会检查每种公共方法的前提条件。

你猜怎么着? 除了数据传输对象和某些设置器外,我编写的每个公共方法都希望其参数不为空。

因此,为了节省一些编写此样板代码的文字,如何添加一个简单的方面(将使其在整个应用程序中不可能),将null传递给DTO和setter之外的其他事物呢? 没有任何其他库(我假设您已经在使用Spring Framework),注释以及其他功能。

为什么我不想在参数中允许使用空值? 因为我们在现代语言中有方法重载。 认真地说,您希望多久看到一次这样的事情:

Address address = AddressFactory.create(null, null, null, null);

而且这也不是更好

Microsoft.Office.Interop.Excel.Workbook theWorkbook = ExcelObj.Workbooks.Open(openFileDialog.FileName, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);


解决方案

因此,这是一个简单的解决方案:您将一个类添加到您的项目,并添加几行spring IoC配置。

类(方面)如下所示:

import org.aspectj.lang.JoinPoint;
import static org.springframework.util.Assert.notNull;

public class NotNullParametersAspect {
    public void throwExceptionIfParametersAreNull(JoinPoint joinPoint) {
        for(Object argument : joinPoint.getArgs()) {
            notNull(argument);
        }
    }
}

Spring配置在这里(请记住,要更改项目的名称空间)。

<aop:config proxy-target-class='true'> 
    <aop:aspect ref='notNullParametersAspect'>
        <aop:pointcut expression='execution(public * eu.solidcraft.*..*.*(..))
                          && !execution(public * eu.solidcraft.*..*Dto.*(..))
                          && !execution(public * eu.solidcraft.*..*.set*(..))' id='allPublicApplicationOperationsExceptDtoAndSetters'> 
            <aop:before method='throwExceptionIfParametersAreNull' pointcut-ref='allPublicApplicationOperationsExceptDtoAndSetters'></aop:before>     
        </aop:pointcut> 

        <task:annotation-driven>
            <bean class='eu.solidcraft.aspects.NotNullParametersAspect' id='notNullParametersAspect'></bean>
        </task:annotation-driven>
    </aop:aspect>
</aop:config>

“ &&”没有错误,只是在XML中转义了&&条件。 如果您不了解Aspectj切入点定义语法,这是一些备忘单

这是一个测试,告诉我们配置已成功。

public class NotNullParametersAspectIntegrationTest extends AbstractIntegrationTest {
    @Resource(name = 'userFeedbackFacade')
    private UserFeedbackFacade userFeedbackFacade;

    @Test(expected = IllegalArgumentException.class)
    public void shouldThrowExceptionIfParametersAreNull() {
        //when
        userFeedbackFacade.sendFeedback(null);

        //then exception is thrown
    }

    @Test
    public void shouldNotThrowExceptionForNullParametersOnDto() {
        //when
        UserBookmarkDto userBookmarkDto = new UserBookmarkDto();
        userBookmarkDto.withChapter(null);
        StoryAncestorDto ancestorDto = new StoryAncestorDto(null, null, null, null);

        //then no exception is thrown
    }
}

AbstractIntegrationTest是一个简单的类,用于启动弹簧测试上下文。 您可以将AbstractTransactionalJUnit4SpringContextTests与@ContextConfiguration(..)结合使用。

抓住

是的,有一个陷阱。 由于spring AOP使用基于接口的J2SE动态代理或aspectj CGLIB代理,因此每个类都将需要接口(用于基于简单代理的方面编织)或不带任何参数的构造函数(用于cglib编织)。 好消息是构造函数可以是私有的。

参考: Solid Craft博客上的JCG合作伙伴 Jakub Nabrdalik 通过简单的spring方面消除了空参数

翻译自: https://www.javacodegeeks.com/2012/11/getting-rid-of-null-parameters-with-a-simple-spring-aspect.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值