Spring、IoC容器和静态代码--设计原则介绍

171 阅读6分钟

Spring、IoC容器和静态代码--设计原则

控制模式的反转,Spring等IoC框架内的静态代码,以及IoC框架内静态代码中实用方法的布线问题。

在这篇文章中,我想讨论控制模式的反转、spring等IoC框架内的静态代码(可以是遗留的或新创建的),以及IoC框架内静态代码中实用方法的布线问题。

静态方法

例如,我们有一个HttpUtils类,里面有一堆静态方法。我们是否应该去掉static关键字,给这个类加上一个Spring的刻板印象,这样就可以立即把它变成一个Spring的单子

在java中,static关键字表示一个成员或方法属于该类型本身,而不是该类型的一个实例。这意味着这个静态成员只有一个实例被创建,并且在类的所有实例之间共享;如果它是IoC框架内的一个静态方法,这意味着这个方法的运行方式对于这个类和这个方法的所有使用是唯一的。

在这种情况下,有3种可能性:

  1. 该方法不修改上下文数据(由应用程序处理的数据)。所以我们可以认为,尽管它是静态的,但它是线程安全的。
  2. 该方法修改了上下文数据,所以它是线程不安全的
  3. 方法修改了上下文数据,但它是线程安全的,因为它是 "同步 "的(一次执行一个),这可能会严重影响性能。

Java中的静态方法在构建时是按原样提供的。这就是为什么不可能对它们进行重载。因此,抽象方法不能是静态的,而且它们在没有作为参数传递的情况下永远不能访问类或上下文值(不能访问this或super)。

那么静态代码是否适用于 "Utils "类?是的,因为我们处于情况1,不会有来自上下文的修改数据,输入被转化为输出,作为一般规则,这种类型的类可以被导出,而不管上下文如何。但是,如果它是可以导出的,那么这段代码难道不应该被放在一个所有项目都可以使用的lib中吗?是的,跨职能的团队会很高兴收到来自团队的定期贡献,这些团队已经想到了将实用代码提供给不需要上下文的所有人。

静态代码是否适用于其他东西?如果有更好的事情可以做就不适用。

在Spring上可用的范围

范围描述
单元这是将Bean定义的范围扩大到每个Spring IoC容器中的一个实例(默认)。
原型这种情况下,单个Bean定义的范围可以有任意数量的对象实例。
请求将Bean定义的范围扩大到一个HTTP请求。仅在Web感知的Spring ApplicationContext的上下文中有效。
会话此处将Bean定义的范围扩大到HTTP会话。仅在 Web 识别的 Spring ApplicationContext 的上下文中有效。
全局会话此处将Bean定义的作用范围扩大到全局HTTP会话。仅在 Web 识别的 Spring ApplicationContext 的上下文中有效。

单一子

如果一个作用域被设置为Singleton,spring IoC容器就会为该Bean定义所定义的对象创建一个确切的实例。这个单一的实例被存储在这些 单子Bean的缓存中,并且所有对这个Bean的后续请求和引用都会返回缓存中的对象。

引擎会像管理任何缓存一样,管理过期,以及在Bean被取出来时的再生。如果Bean没有了或没有状态,缓存退出是可能的。

例如,当调用httpsUtils.callUrl()时,容器必须决定是重用一个已经实例化的Bean还是创建一个新的。容器是如何决定的呢?当在容器中(在框架上)注册Bean httpUtils时,可以向这个容器表明我们希望它如何行事。另外,第一个实例可以在应用程序开始时或对Bean的第一次调用时创建。也就是说,Bean的调用者不需要知道Bean的生命周期;他们调用httpUtils.callUrl()。

这是在一个包含一定数量的类和每个类的方法的应用程序中提供类方法的最佳方式。

因此,我们有了第一个(全局)性能论据,将所有的静态方法转化为单子,以便从spring IoC缓存中获益。访问静态方法在理论上更快,因为JVM在执行之前不需要查找函数,但在实践中,大多数JDK都会优化实例方法调用。也就是说,当涉及到性能时,最常见的原因是设计不当。如果静态代码更快,那么我们就可以用静态代码来编写整个应用程序;既然IoC模式和静态代码可以很好地完成同样的工作,但它们的构造却不同,那么为什么还要按照IoC模式来声明类和方法呢?

如果我们再以HttpUtils类为例,使用静态代码的原则是我可以直接访问类中的callUrl()方法;我们把callUrl()的定义和它的实现结合在一起。IoC致力于分离这种细节,所以相反,我们在接口中定义了它之外的一个方法。这允许,例如,用几个实现来下降这个接口。HttpUtils的一个接口实现将成为HttpUtils的一个配置。请注意,使用HttpUtils的类将不知道callUrl()的执行范围,因为框架中使用的作用域也取决于一个配置。

总而言之,IoC和静态通常是不一致的。IoC促进变化,而静态,根据定义,则是防止变化。根据框架注入你的依赖关系,你会发现你可以完全不使用静态功能。

安全性

还有一个论点:根据sonar,spring bean的所有成员都应该被注入(参见rules.sonarsource.com/java/type/V…

Vulnerability: Critical

spring

owasp

Spring @Component, @Controller, @Service, and @Repository classes are singletons by default, meaning only one instance of the class is ever instantiated in the application. Typically such a class might have a few static members, such as a logger, but all non-static members should be managed by Spring. That is, they should have one of these annotations: @Resource, @Inject, @Autowired or @Value.

Having non-injected members in one of these classes could indicate an attempt to manage state. Because they are singletons, such an attempt is almost guaranteed to eventually expose data from User1's session to User2.

当一个没有用@ConfigurationProperties注解的单子@Component、@Controller、@Service或@Repository的非静态成员没有用以下之一注解时,这个规则就会引起问题:

  • org.springframework.beans.factory.annotation.Autowired
  • org.springframework.beans.factory.annotation.Value
  • javax.annotation.Inject
  • javax.annotation.Resource

不符合规定的代码

@Controller
public class HelloWorld {
  private String name = null;
  @RequestMapping("/greet", method = GET)
  public String greet(String greetee) {
    if (greete != null) {
      this.name = greetee;
    }
    return "Hello " + this.name; // if greetee is null, you see the previous user's data
  }

}

字符串是一个类,任何类都可以关注,包括那些包含静态方法的类,这些静态方法会在spring bean中被调用。