Java bean是数据存储类的不良设计吗?

时间:2021-07-25 16:13:42

Usually JavaPractices.com is a good site with good idea's, but this one troubles me: JavaBeans are bad.

通常JavaPractices.com是一个好网站,有好点子,但是这个让我很困扰:javabean是坏的。

The article cites several reasons, mainly that the term JavaBean means "A Java Bean is a reusable software component that can be manipulated visually in a builder tool." not Data storage, violates certain patters, and is more complex.

文章引用了几个原因,主要是JavaBean这个术语的意思是“JavaBean是可重用的软件组件,可以在构建工具中可视化地操作”,而不是数据存储,违反了某些模式,而且更加复杂。

Now I can agree with the last one, but in my eyes JavaBeans in a list makes a lot more sense than nested Maps. The article claims that database mapping frameworks should call constructors, not set* methods, and the object should be immutable. In my mind however, calling set* methods when trying to build an object is easier to read than new MappedObject("column1", "column2", "yet another column", "this is stupid");

现在我同意最后一个,但是在我看来,列表中的JavaBeans比嵌套映射更有意义。本文声称,数据库映射框架应该调用构造函数,而不是set*方法,并且对象应该是不可变的。然而,在我看来,在尝试构建对象时调用set*方法要比读取新的MappedObject(“column1”、“column2”、“另一个列”、“这很愚蠢”)更容易;

I also use the JavaBean style class for other things besides database mapping, eg for an IRC bot, having an object per user that gets updated with various things. I don't want to create a new object every time new information is given, I want to add it to an existing one.

我还将JavaBean样式类用于除数据库映射之外的其他东西,例如用于IRC机器人,每个用户都有一个对象,该对象可以用各种东西进行更新。我不希望每次提供新信息时都创建一个新对象,我希望将它添加到一个现有的对象中。

So my question: Is using JavaBeans for data storage a bad practice and should be avoided, or is it perfectly safe?

所以我的问题是:使用javabean来存储数据是一种错误的做法,应该避免,还是完全安全?

6 个解决方案

#1


19  

It seems that you are misreading the text.

看来你读错了课文。

Now I can agree with the last one, but in my eye's JavaBeans in a list makes alot more sense than nested Maps

现在我同意最后一个,但是在我看来,列表中的JavaBeans比嵌套映射更有意义

The text never mentions nested maps as an alternative ( yiack )

文本中从来没有提到嵌套映射作为替代(yiack)

...should call constructors, not set* methods, and the object should be immutable

…应该调用构造函数,而不是set*方法,并且对象应该是不可变的吗

This is a good practice, specially useful when dealing with threads.

这是一个很好的实践,在处理线程时特别有用。

But we can't say that using setters is baaad either, specially when a single thread is using the object. That's perfectly safe.

但是我们不能说使用setter也是baaad,特别是当一个线程使用对象时。这是绝对安全的。

I don't want to create a new object every time new information is given, I want to add it to an existing one.

我不希望每次提供新信息时都创建一个新对象,我希望将它添加到一个现有的对象中。

That's fine, as long as you control the object there is no problem with this, some other may find easier to just create a new object.

这很好,只要您控制对象就没有问题了,其他一些可能会发现创建一个新对象更容易。

Is using JavaBeans for data storage a bad practice and should be avoided, or is it perfectly safe?

使用JavaBeans进行数据存储是一种不好的做法,应该避免,还是完全安全?

No, is not a bad practice. Is not perfectly safe either. Depends on the situation.

不,这不是一个坏习惯。也不是完全安全的。视情况而定。

The problem with mutable objects ( not with JavaBeans per se ) is using different threads to access them.

可变对象(而不是javabean本身)的问题是使用不同的线程来访问它们。

You have to synchronize the access to avoid one thread modify the object while other is accessing it.

您必须同步访问,以避免一个线程在访问对象时修改对象。

Immutable objects doesn't have this problem, because, .. well they can't change, and thus, you don't have to synchronize anything.

不可变对象没有这个问题,因为。它们不能改变,因此,你不需要同步任何东西。

To make sure an object is immutable you have to declare your attributes as final.

为了确保对象是不可变的,您必须将属性声明为final。

class MyBean  {
    private final int i;
}

If you want to assign a reasonable value to MyBean.i you have to specify it in the constructor:

如果你想给MyBean分配一个合理的值。我必须在构造函数中指定它

 public MyBean( int i ) {
     this.i = i;
 }

Since the variable is final, you can't use a setter. You can just provide a getter.

由于变量是final的,所以不能使用setter。您可以提供一个getter。

This is perfectly thread-safe and the best is, you don't have to synchronize the access, because if two threads try to get the value of i they both will always see the value that was assigned on instantiation, you don't have to synchronize anything.

这是线程安全的,最好的是,您不需要同步访问,因为如果两个线程试图获取i的值,它们都将始终看到在实例化上分配的值,您不需要同步任何东西。

Is not bad practice or good practice. Must of us have to work with a single thread, even in multithread environments like servlets.

不是坏习惯或好习惯。我们必须使用一个线程,即使在像servlet这样的多线程环境中也是如此。

If in the future you have to deal with multi thread applications, you may consider using an immutable JavaBean ;)

如果将来您必须处理多线程应用程序,您可以考虑使用不可变的JavaBean;)

BTW, the alternative to create immutable beans, and still provide a bunch of setters is using Builders like:

顺便说一句,创建不可变bean并仍然提供一系列setter的替代方法是使用生成器,比如:

 Employee e = new EmployeeBuilder()
                  .setName("Oscar")
                  .setLastName("Reyes")
                  .setAge(0x1F)
                  .setEmployeeId("123forme")
                  .build(); 

Which looks pretty similar to the regular setXyz used in regular beans with the benefit of using immutable data.

它看起来非常类似于常规bean中使用的常规setXyz,其优点是使用不可变数据。

If you need to change one value, you can use a class method:

如果需要更改一个值,可以使用类方法:

 Employee e = Employee.withName( e, "Mr. Oscar");

Which takes the existing object, and copy all the values, and set's a new one....

将现有的对象,并复制所有的值,设置一个新的....

 public static EmployeeWithName( Employee e , String newName ){
      return new Employee( newName, e.lastName, e.age, e.employeeId );
  }

But again, in a single thread model is perfectly safe to use getters/setters.

但是,在一个线程模型中使用getter /setter是绝对安全的。

PS I strongly encourage you to buy this book: Effective Java. You'll never regret about it, and you'll have information to judge better articles like one cited.

我强烈建议你买这本书:有效的Java。你永远不会后悔,你将会有信息来判断更好的文章,就像一个被引用的。

#2


5  

My objection to using JavaBeans as data storage classes is that they allow for objects with inconsistent state. In the typical use case for such beans, you have the following steps:

我反对使用JavaBeans作为数据存储类,是因为它们允许对象具有不一致的状态。在这种bean的典型用例中,您有以下步骤:

  • instantiate the class;
  • 实例化类;
  • set the first property;
  • 设置第一个属性;
  • set the second property;
  • 设置第二个属性;
  • ...
  • set the final property;
  • 设置最后的属性;
  • use the object instance.
  • 使用对象实例。

Now your class is ready to use. So what's the problem here? Between instantiating the class and setting the final property you have an object which is in an internally-inconsistent or unusable state, but there's nothing preventing you from accidentally using it. I prefer a system where the class is automatically in a consistent, usable state upon instantiation. For this reason I prefer to either pass in all the initial state in the constructor or, if said initial state is too complicated, pass in the initial state in the form of a hash map or set or the like. The use case scenario is now:

现在您的类可以使用了。这里的问题是什么?在实例化类和设置最终属性之间,有一个对象处于内部不一致或不可用的状态,但是没有什么可以阻止您意外地使用它。我更喜欢这样的系统:类在实例化时自动处于一致的、可用的状态。出于这个原因,我宁愿在构造函数中传递所有的初始状态,或者,如果说初始状态太复杂,那么以哈希映射或set之类的形式传递初始状态。用例场景现在是:

  • (Optional: set up a parameters object);
  • (可选:设置参数对象);
  • instantiate the class;
  • 实例化类;
  • use the object instance.
  • 使用对象实例。

At no point in this work flow is it possible for me to accidentally start using an object with inconsistent state. If I use a parameters object, it is not directly used for anything at all and its contents will be vetted upon instantiation of my main class. The main class itself, upon returning from instantiation, will give me an object instance that is of immediate use.

在这个工作流中,我不可能偶然地开始使用状态不一致的对象。如果我使用一个parameters对象,它根本不直接用于任何内容,并且它的内容将在主类的实例化时进行检查。从实例化返回后,主类本身将给我一个立即使用的对象实例。

This kind of setup is best for simpler objects, of course. For more complicated objects with things like optional properties, etc. you'll want to go a step farther and use something like the Builder pattern that others have pointed you toward. Builders are nice when you have more complicated scenarios, IMO, but for simpler, more straightforward parametrization just using constructor arguments or a parameters object of some sort is more than enough.

当然,这种设置对于更简单的对象是最好的。对于具有可选属性等属性的更复杂的对象,您需要更进一步,使用其他人已经指向的构建器模式。当您有更复杂的场景时,生成器是很好的,在我看来,但是对于更简单、更直接的参数化来说,仅仅使用构造函数参数或某种类型的参数对象就足够了。

#3


4  

"The JavaBeans pattern has serious disadvantages." — Joshua Bloch, Effective Java

“JavaBeans模式有严重的缺点。”- Joshua Bloch,有效Java

I love those guys. Taking arbitrary quotes out of context is already reason for me not to trust such article.

我喜欢这些家伙。把任意的引用断章取义已经是我不相信这篇文章的原因。

BTW, referred book (Effective Java) has extensive discussion about both those models, their advantages, disadvantages and alternatives. You might want to check it out. But there's nothing inherently wrong with JavaBeans, just sometimes they are not the best choice.

顺便说一句,参考书籍(有效Java)对这两个模型、它们的优点、缺点和替代方案进行了广泛的讨论。你可能想看看。但是javabean本身并没有什么问题,只是有时候它们不是最好的选择。

Edit: See item 2 ("Consider a builder when faced with many constructor parameters") in Effective Java on Google Books: http://books.google.com/books?id=ka2VUBqHiWkC&lpg=PP1&pg=PA11#v=onepage&q&f=false

编辑:在谷歌书籍的有效Java中,请参阅第2项(“当遇到许多构造函数参数时考虑构建器”):http://books.google.com/books?

#4


2  

The main reason for avoiding setters is immutability. Code for immutability up front and you avoid any threading issues around those objects.

避免设置的主要原因是不变性。为不变性而预先编写的代码,可以避免围绕这些对象的任何线程问题。

If you end up with a constructor that reads

如果您使用的构造函数是读取的。

new Person("param 1","param 2","param 3","param 4","param 5","param 6","param 7","param 8")

新人(“param 1”、“param 2”、“param 3”、“param 4”、“param 5”、“param 6”、“param 7”、“param 8”)

then your object is too complicated. You will need Parameter objects (See the Martin Fowler Refactoring book).

那么你的对象就太复杂了。您将需要参数对象(参见Martin Fowler重构书)。

Code defensively at the start and the people who come along and maintain your code will either thank you (good) or curse you (because they can't be lazy and just mutate objects).

一开始就进行防御性的代码编写,那些来维护您代码的人要么感谢您(很好),要么诅咒您(因为他们不能偷懒,而只是修改对象)。

When you need to change the object, add a copy constructor (i.e. clone method). Modern JVMs deal with this easily and quickly enough and there is little speed penalty. You also make things easy for Hotspot and the GC.

当您需要更改对象时,添加一个复制构造函数(即克隆方法)。现代jvm很容易和足够快地处理这个问题,并且没有什么速度损失。您还可以简化Hotspot和GC的工作。

#5


0  

Its perfectly safe, and far more preferable than endlessly nested java.util.Map. You gain type safety, easier to understand code, and avoid your code ending up on The Daily WTF. Note that the implementation should be segregated - don't mix too much logic (beyond simple validation) into your data storage classes. You should read about POJOs

它非常安全,而且比无休止地嵌套java.util.Map要好得多。您获得了类型安全,更容易理解代码,并避免代码最终出现在每日WTF上。注意,实现应该是隔离的——不要将太多的逻辑(除了简单的验证)混合到数据存储类中。你应该读读关于pojo的书

#6


0  

The article's intentions are well-meant, but in practice avoiding beans and setters is hard to uphold. For example, having frameworks use constructors is not possible in many cases since names are usually the identifying feature, and method/constructor parameter names are not maintained in the .class file. (Although there is at least one library to work around this.)

这篇文章的意图是善意的,但是在实践中避免使用bean和setter是很难坚持的。例如,在很多情况下,让框架使用构造函数是不可能的,因为名称通常是标识特性,并且.class文件中不维护方法/构造函数参数名称。(尽管至少有一个库可以处理这个问题。)

Setters are the next best thing - not as "pure" OO, but a huge convenience over constructors and work well in practice.

setter是下一个最好的东西——不是“纯”OO,而是对构造函数的极大方便,并且在实践中工作良好。

Whem tests start failing because of "bad" java beans, then I'll reconsider it cause for concern. But I've not seen that so far. The only time I'd say they are probably inappropriate is having shared beans in multithreaded code, since synchronizing the shared mutable state is tricky. Steer clear of that and beans are just fine.

Whem测试因为“坏”的java bean而开始失败,然后我将重新考虑它引起的关注。但我还没见过。我唯一可能认为它们不合适的地方是在多线程代码中使用共享bean,因为同步共享可变状态很棘手。远离这些,豆类也没什么问题。

#1


19  

It seems that you are misreading the text.

看来你读错了课文。

Now I can agree with the last one, but in my eye's JavaBeans in a list makes alot more sense than nested Maps

现在我同意最后一个,但是在我看来,列表中的JavaBeans比嵌套映射更有意义

The text never mentions nested maps as an alternative ( yiack )

文本中从来没有提到嵌套映射作为替代(yiack)

...should call constructors, not set* methods, and the object should be immutable

…应该调用构造函数,而不是set*方法,并且对象应该是不可变的吗

This is a good practice, specially useful when dealing with threads.

这是一个很好的实践,在处理线程时特别有用。

But we can't say that using setters is baaad either, specially when a single thread is using the object. That's perfectly safe.

但是我们不能说使用setter也是baaad,特别是当一个线程使用对象时。这是绝对安全的。

I don't want to create a new object every time new information is given, I want to add it to an existing one.

我不希望每次提供新信息时都创建一个新对象,我希望将它添加到一个现有的对象中。

That's fine, as long as you control the object there is no problem with this, some other may find easier to just create a new object.

这很好,只要您控制对象就没有问题了,其他一些可能会发现创建一个新对象更容易。

Is using JavaBeans for data storage a bad practice and should be avoided, or is it perfectly safe?

使用JavaBeans进行数据存储是一种不好的做法,应该避免,还是完全安全?

No, is not a bad practice. Is not perfectly safe either. Depends on the situation.

不,这不是一个坏习惯。也不是完全安全的。视情况而定。

The problem with mutable objects ( not with JavaBeans per se ) is using different threads to access them.

可变对象(而不是javabean本身)的问题是使用不同的线程来访问它们。

You have to synchronize the access to avoid one thread modify the object while other is accessing it.

您必须同步访问,以避免一个线程在访问对象时修改对象。

Immutable objects doesn't have this problem, because, .. well they can't change, and thus, you don't have to synchronize anything.

不可变对象没有这个问题,因为。它们不能改变,因此,你不需要同步任何东西。

To make sure an object is immutable you have to declare your attributes as final.

为了确保对象是不可变的,您必须将属性声明为final。

class MyBean  {
    private final int i;
}

If you want to assign a reasonable value to MyBean.i you have to specify it in the constructor:

如果你想给MyBean分配一个合理的值。我必须在构造函数中指定它

 public MyBean( int i ) {
     this.i = i;
 }

Since the variable is final, you can't use a setter. You can just provide a getter.

由于变量是final的,所以不能使用setter。您可以提供一个getter。

This is perfectly thread-safe and the best is, you don't have to synchronize the access, because if two threads try to get the value of i they both will always see the value that was assigned on instantiation, you don't have to synchronize anything.

这是线程安全的,最好的是,您不需要同步访问,因为如果两个线程试图获取i的值,它们都将始终看到在实例化上分配的值,您不需要同步任何东西。

Is not bad practice or good practice. Must of us have to work with a single thread, even in multithread environments like servlets.

不是坏习惯或好习惯。我们必须使用一个线程,即使在像servlet这样的多线程环境中也是如此。

If in the future you have to deal with multi thread applications, you may consider using an immutable JavaBean ;)

如果将来您必须处理多线程应用程序,您可以考虑使用不可变的JavaBean;)

BTW, the alternative to create immutable beans, and still provide a bunch of setters is using Builders like:

顺便说一句,创建不可变bean并仍然提供一系列setter的替代方法是使用生成器,比如:

 Employee e = new EmployeeBuilder()
                  .setName("Oscar")
                  .setLastName("Reyes")
                  .setAge(0x1F)
                  .setEmployeeId("123forme")
                  .build(); 

Which looks pretty similar to the regular setXyz used in regular beans with the benefit of using immutable data.

它看起来非常类似于常规bean中使用的常规setXyz,其优点是使用不可变数据。

If you need to change one value, you can use a class method:

如果需要更改一个值,可以使用类方法:

 Employee e = Employee.withName( e, "Mr. Oscar");

Which takes the existing object, and copy all the values, and set's a new one....

将现有的对象,并复制所有的值,设置一个新的....

 public static EmployeeWithName( Employee e , String newName ){
      return new Employee( newName, e.lastName, e.age, e.employeeId );
  }

But again, in a single thread model is perfectly safe to use getters/setters.

但是,在一个线程模型中使用getter /setter是绝对安全的。

PS I strongly encourage you to buy this book: Effective Java. You'll never regret about it, and you'll have information to judge better articles like one cited.

我强烈建议你买这本书:有效的Java。你永远不会后悔,你将会有信息来判断更好的文章,就像一个被引用的。

#2


5  

My objection to using JavaBeans as data storage classes is that they allow for objects with inconsistent state. In the typical use case for such beans, you have the following steps:

我反对使用JavaBeans作为数据存储类,是因为它们允许对象具有不一致的状态。在这种bean的典型用例中,您有以下步骤:

  • instantiate the class;
  • 实例化类;
  • set the first property;
  • 设置第一个属性;
  • set the second property;
  • 设置第二个属性;
  • ...
  • set the final property;
  • 设置最后的属性;
  • use the object instance.
  • 使用对象实例。

Now your class is ready to use. So what's the problem here? Between instantiating the class and setting the final property you have an object which is in an internally-inconsistent or unusable state, but there's nothing preventing you from accidentally using it. I prefer a system where the class is automatically in a consistent, usable state upon instantiation. For this reason I prefer to either pass in all the initial state in the constructor or, if said initial state is too complicated, pass in the initial state in the form of a hash map or set or the like. The use case scenario is now:

现在您的类可以使用了。这里的问题是什么?在实例化类和设置最终属性之间,有一个对象处于内部不一致或不可用的状态,但是没有什么可以阻止您意外地使用它。我更喜欢这样的系统:类在实例化时自动处于一致的、可用的状态。出于这个原因,我宁愿在构造函数中传递所有的初始状态,或者,如果说初始状态太复杂,那么以哈希映射或set之类的形式传递初始状态。用例场景现在是:

  • (Optional: set up a parameters object);
  • (可选:设置参数对象);
  • instantiate the class;
  • 实例化类;
  • use the object instance.
  • 使用对象实例。

At no point in this work flow is it possible for me to accidentally start using an object with inconsistent state. If I use a parameters object, it is not directly used for anything at all and its contents will be vetted upon instantiation of my main class. The main class itself, upon returning from instantiation, will give me an object instance that is of immediate use.

在这个工作流中,我不可能偶然地开始使用状态不一致的对象。如果我使用一个parameters对象,它根本不直接用于任何内容,并且它的内容将在主类的实例化时进行检查。从实例化返回后,主类本身将给我一个立即使用的对象实例。

This kind of setup is best for simpler objects, of course. For more complicated objects with things like optional properties, etc. you'll want to go a step farther and use something like the Builder pattern that others have pointed you toward. Builders are nice when you have more complicated scenarios, IMO, but for simpler, more straightforward parametrization just using constructor arguments or a parameters object of some sort is more than enough.

当然,这种设置对于更简单的对象是最好的。对于具有可选属性等属性的更复杂的对象,您需要更进一步,使用其他人已经指向的构建器模式。当您有更复杂的场景时,生成器是很好的,在我看来,但是对于更简单、更直接的参数化来说,仅仅使用构造函数参数或某种类型的参数对象就足够了。

#3


4  

"The JavaBeans pattern has serious disadvantages." — Joshua Bloch, Effective Java

“JavaBeans模式有严重的缺点。”- Joshua Bloch,有效Java

I love those guys. Taking arbitrary quotes out of context is already reason for me not to trust such article.

我喜欢这些家伙。把任意的引用断章取义已经是我不相信这篇文章的原因。

BTW, referred book (Effective Java) has extensive discussion about both those models, their advantages, disadvantages and alternatives. You might want to check it out. But there's nothing inherently wrong with JavaBeans, just sometimes they are not the best choice.

顺便说一句,参考书籍(有效Java)对这两个模型、它们的优点、缺点和替代方案进行了广泛的讨论。你可能想看看。但是javabean本身并没有什么问题,只是有时候它们不是最好的选择。

Edit: See item 2 ("Consider a builder when faced with many constructor parameters") in Effective Java on Google Books: http://books.google.com/books?id=ka2VUBqHiWkC&lpg=PP1&pg=PA11#v=onepage&q&f=false

编辑:在谷歌书籍的有效Java中,请参阅第2项(“当遇到许多构造函数参数时考虑构建器”):http://books.google.com/books?

#4


2  

The main reason for avoiding setters is immutability. Code for immutability up front and you avoid any threading issues around those objects.

避免设置的主要原因是不变性。为不变性而预先编写的代码,可以避免围绕这些对象的任何线程问题。

If you end up with a constructor that reads

如果您使用的构造函数是读取的。

new Person("param 1","param 2","param 3","param 4","param 5","param 6","param 7","param 8")

新人(“param 1”、“param 2”、“param 3”、“param 4”、“param 5”、“param 6”、“param 7”、“param 8”)

then your object is too complicated. You will need Parameter objects (See the Martin Fowler Refactoring book).

那么你的对象就太复杂了。您将需要参数对象(参见Martin Fowler重构书)。

Code defensively at the start and the people who come along and maintain your code will either thank you (good) or curse you (because they can't be lazy and just mutate objects).

一开始就进行防御性的代码编写,那些来维护您代码的人要么感谢您(很好),要么诅咒您(因为他们不能偷懒,而只是修改对象)。

When you need to change the object, add a copy constructor (i.e. clone method). Modern JVMs deal with this easily and quickly enough and there is little speed penalty. You also make things easy for Hotspot and the GC.

当您需要更改对象时,添加一个复制构造函数(即克隆方法)。现代jvm很容易和足够快地处理这个问题,并且没有什么速度损失。您还可以简化Hotspot和GC的工作。

#5


0  

Its perfectly safe, and far more preferable than endlessly nested java.util.Map. You gain type safety, easier to understand code, and avoid your code ending up on The Daily WTF. Note that the implementation should be segregated - don't mix too much logic (beyond simple validation) into your data storage classes. You should read about POJOs

它非常安全,而且比无休止地嵌套java.util.Map要好得多。您获得了类型安全,更容易理解代码,并避免代码最终出现在每日WTF上。注意,实现应该是隔离的——不要将太多的逻辑(除了简单的验证)混合到数据存储类中。你应该读读关于pojo的书

#6


0  

The article's intentions are well-meant, but in practice avoiding beans and setters is hard to uphold. For example, having frameworks use constructors is not possible in many cases since names are usually the identifying feature, and method/constructor parameter names are not maintained in the .class file. (Although there is at least one library to work around this.)

这篇文章的意图是善意的,但是在实践中避免使用bean和setter是很难坚持的。例如,在很多情况下,让框架使用构造函数是不可能的,因为名称通常是标识特性,并且.class文件中不维护方法/构造函数参数名称。(尽管至少有一个库可以处理这个问题。)

Setters are the next best thing - not as "pure" OO, but a huge convenience over constructors and work well in practice.

setter是下一个最好的东西——不是“纯”OO,而是对构造函数的极大方便,并且在实践中工作良好。

Whem tests start failing because of "bad" java beans, then I'll reconsider it cause for concern. But I've not seen that so far. The only time I'd say they are probably inappropriate is having shared beans in multithreaded code, since synchronizing the shared mutable state is tricky. Steer clear of that and beans are just fine.

Whem测试因为“坏”的java bean而开始失败,然后我将重新考虑它引起的关注。但我还没见过。我唯一可能认为它们不合适的地方是在多线程代码中使用共享bean,因为同步共享可变状态很棘手。远离这些,豆类也没什么问题。