如何防止从特定类调用公共方法
我有一个现有的类,我想向其中添加一个方法。但我希望仅从特定类的特定方法调用该方法。有什么方法可以阻止其他类/方法的调用吗?
例如,我有一个现有的类 A
public final class A
{
//other stuff available for all classes/methods
//I want to add a method that does its job only if called from a specific method of a class, for example:
public void method()
{
//proceed if called from Class B.anotherMethod() else throw Exception
}
}
一种方法是获取 method()
内的 StackTrace
,然后确认父方法?
我正在寻找一种更干净、更明智的解决方案,例如模式或其他东西。
I have an existing class into which I want to add a method. But I want the method to be called only from a specific method from a specific class. Is there any way that I can prevent that call from other classes/methods?
For example, I have an existing class A
public final class A
{
//other stuff available for all classes/methods
//I want to add a method that does its job only if called from a specific method of a class, for example:
public void method()
{
//proceed if called from Class B.anotherMethod() else throw Exception
}
}
One way of doing this is getting the StackTrace
inside the method()
and then confirming the parent method?
What I am looking for is a solution that is more clean and advisable solution like a pattern or something.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
我意识到您的用例声明了“特定 方法 在特定类中”,但我认为您无法在设计时可靠地解决这个问题(而且我想不出无论如何都必须执行此操作的用例)。
以下示例创建了一个简单的设计时解决方案,用于限制对特定类的类方法的访问。但是,它可以轻松扩展到多个允许的类。
它是通过定义一个带有私有构造函数的公共内部类来实现的,该构造函数充当当前方法的关键。在下面的示例中,
Bar
类有一个只能从Foo
类的实例调用的方法。Class Foo:
Class Bar:
我认为这对于反射甚至 FooPrivateKey.class.newInstance() 之类的东西来说无论如何都不安全,但这至少会警告程序员一点比简单的评论或文档更引人注目,而您不必查看更复杂的事情,例如 Roberto 等人的建议Trunfio 和 Ronan Quillevere (这也是完全可行的答案,但在我看来,对于大多数情况来说太复杂了)。
我希望这足以满足您的用例。
I realise your use case states 'specific method in specific class', but I don't think you can reliably solve this at design time (and I can't think of a use case where this would have to be enforced anyway).
The following example creates an easy design time solution for restricting the access of a class' method to a particular class. It can, however, be easily extended to multiple allowed classes.
It is achieved by defining a public inner class with a private constructor that acts as a key to the method at hand. In the following example the class
Bar
has a method that should only be called from an instance of theFoo
class.Class Foo:
Class Bar:
I don't think this is by any means safe for things like reflection or even things like
FooPrivateKey.class.newInstance()
, but this will at least warn the programmer a little more obtrusively than a simple comment or documentation, while you don't have to look in to more complicated things like what was suggested by people like Roberto Trunfio and Ronan Quillevere (which are perfectly viable answers as well, just too complicated for most situations in my opinion).I hope this is sufficient for your use case.
说实话,你已经把自己逼到了墙角了。
如果类 A 和 B 不相关并且不是同一包的成员,那么可见性将无法解决问题。 (即使确实如此,反射也可以用来破坏可见性规则。)
如果代码可以使用反射来调用方法,静态代码分析将无法解决问题。
将
B.this
作为额外参数传递给A.method(...)
并没有帮助,因为其他一些类C
可能会这样做传递一个B
实例。这样就只剩下堆栈跟踪方法1...或者放弃并依靠程序员的良好判断力2不调用他们不应该调用的方法。
理想的解决方案是重新审视导致您陷入困境的设计和/或编码决策。
1 - 请参阅其他答案,了解使用注释、安全管理器等向应用程序程序员隐藏堆栈跟踪内容的示例。但请注意,在幕后,您可能会为每个方法调用添加数百甚至数千条指令开销。
2 - 不要低估程序员的良好判断力。大多数程序员在看到不要调用某些方法的建议时,可能会遵循该建议。
To be honest, you have painted yourself into a corner here.
If classes A and B are not related and not members of the same package, then visibility won't solve the problem. (And even if it did, reflection can be used to subvert the visibility rules.)
Static code analysis won't solve the problem if the code can use reflection to call the method.
Passing and checking
B.this
as an extra parameter toA.method(...)
doesn't help because some other classC
could pass aB
instance.This leaves only the stacktrace approach1... or giving up and relying on the good sense of the programmer2 not to call methods that they shouldn't.
The ideal solution is to revisit the design and/or coding decisions that got you into this mess.
1 - See other answers for examples that use annotations, a security manager, etc to conceal the stacktrace stuff from the application programmer. But note that under the hood you are adding probably hundreds, possibly thousands of instructions overhead per method call.
2 - Do not underestimate the programmer's good sense. Most programmers, when they see advice not to call some method, are likely to follow that advice.
执行此操作的正确方法是使用 SecurityManager。
定义所有想要调用
A.method()
的代码都必须拥有的权限,然后确保只有B
和A
拥有该权限权限(这也意味着没有类拥有AllPermission
)。在
A
中,您可以使用System.getSecurityManager().checkPermission(new BMethodPermission())
进行检查,而在B
中,您可以调用其中的方法AccessController.doPrivileged(...)
。当然,这需要安装安全管理器(并且它使用合适的策略) - 如果没有安装,所有代码都是受信任的,每个人都可以调用所有内容(如果需要,可以使用反射)。
The right way to do this would be a SecurityManager.
Define a permission which all code which wants to call
A.method()
has to have, and then make sure onlyB
andA
have that permission (this also means that no class hasAllPermission
).In
A
, you check this withSystem.getSecurityManager().checkPermission(new BMethodPermission())
, and inB
you call the method inside ofAccessController.doPrivileged(...)
.Of course, this requires that a security manager is installed (and it uses suitable policies) - if it isn't, all code is trusted and everyone can call everything (if necessary, with Reflection).
您可以考虑使用接口。如果您传入调用类,您可以确认该类属于适当的类型。
或者,如果您使用 Java,则可以使用“默认”或“包”级别访问(例如 void method() 与 public void method())。这将允许您的方法被包内的任何类调用,并且不需要您将该类传递给该方法。
You might consider using an interface. If you're passing in the calling class, you can confirm that the class is of the appropriate type.
Alternatively, if you're using Java, you can use "default" or "package" level access (e.g. void method() vs. public void method()). This will allow your method to be called by any class inside the package and does not require that you pass the class to the method.
在运行时进行确认的唯一方法是获取堆栈跟踪。即使它是私有的,您也可以通过反射访问该方法。
更简单的方法是检查 IDE 中的用法。 (前提是它不通过反射调用)
The only way to check for sure at run time is to take a stack trace. Even if its private you can access the method via reflections.
A simpler way to do this would be to check usages in your IDE. (provided its not called via reflections)
正如其他人提到的,使用堆栈跟踪是实现您正在寻找的功能的一种方法。一般来说,如果需要“阻止”调用者使用
public
方法,这可能是设计不佳的表现。根据经验,请使用尽可能限制范围的访问修饰符。然而,将方法设为包私有或受保护并不总是一种选择。有时,人们可能希望将某些类分组到一个单独的包中。在这种情况下,默认(包私有)访问限制太多,并且子类化通常没有意义,因此protected
也没有帮助。如果需要限制对某些类的调用,您可以创建一个如下方法:
使用它非常简单:只需指定哪些类可以调用该方法。例如,
如果
stop
方法被ShutdownHandler
之外的类调用,checkPermission
将抛出一个IllegalStateException
。您可能想知道为什么
checkPermission
被硬编码为使用堆栈跟踪的第四个元素。这是因为 Thread#getStackTrace() 使最近调用的方法成为第一个元素。因此,getStackTrace()[0]
将是对getStackTrace
本身的调用。getStackTrace()[1]
将调用checkPermission
。getStackTrace()[2]
将调用stop
。getStackTrace()[3]
将是调用stop
的方法。这就是我们感兴趣的。您提到您希望从特定类和方法中调用方法,但
checkPermission
仅检查类名。添加检查方法名称的功能只需要进行一些修改,因此我将其作为练习。As others have mentioned, using the stack trace is one way to implement the functionality that you are looking for. Generally, if one needs to "block" callers from a
public
method, it could be a sign of poor design. As a rule of thumb, use access modifiers that restrict the scope as much as possible. However, making a method package-private orprotected
is not always an option. Sometimes, one may want to group some classes in a separate package. In that case, the default (package-private) access is too restrictive, and it usually does not make sense to subclass, soprotected
is not helpful either.If restricting calling to certain classes is desired, you can create a method like:
Using it is very simple: just specify what class(es) can call the method. For example,
So, if the
stop
method gets called by a class other thanShutdownHandler
,checkPermission
will throw anIllegalStateException
.You may wonder why
checkPermission
is hard-coded to use the fourth element of the stack trace. This is becauseThread#getStackTrace()
makes the most recently called method the first element. So,getStackTrace()[0]
would be the call togetStackTrace
itself.getStackTrace()[1]
would be the call tocheckPermission
.getStackTrace()[2]
would be the call tostop
.getStackTrace()[3]
would be the method that calledstop
. This is what we are interested in.You mentioned that you want methods to be called from a specific class and method, but
checkPermission
only checks for class names. Adding the functionality to check for method names requires only a few modifications, so I'm going to leave that as an exercise.正确使用
protected
Make proper use of
protected
在java中执行此操作的标准方法是将B类和A类放在同一个包中(可能是当前应用程序的子包)并使用默认可见性。
默认的 java 可见性是“包私有”,这意味着该包中的所有内容都可以看到您的方法,但该包之外的任何内容都无法访问它。
另请参阅:
有没有办法模拟C++ Java中的“朋友”概念?
The standard way to do this in java is to put Class B and Class A in the same package (maybe a subpackage of your current application) and use the default visibility.
The default java visibility is "package-private" which means everything in that package can see your method, but nothing outside that package can access it.
See Also:
Is there a way to simulate the C++ 'friend' concept in Java?
假设您只需将此限制应用于项目中的类,静态分析可能适合您 - 例如 ArchUnit 测试:
Assuming that you only need to apply this restriction to classes within your project, static analysis could work for you - for example an ArchUnit test:
您可以通过使用注释和反射来做到这一点。我将报告一个类似的情况,即您可以让该方法仅由外部类中的特定方法调用的情况。假设必须通过对其公共方法的任何调用来“保护”的类是
Invoked
,而Invoker
是具有启用方法来调用一个或多个方法的类。来自Invoked
的更多方法。然后,您可以执行如下所示的操作。Invoker
类是注意,启用调用的方法是用
CanInvoke
注解进行注解的。您所请求的情况与此类似。您注释无法调用公共方法的类/方法,然后仅当该方法/类未注释时才将
canExecute
变量设置为true
。You can do it by using annotations and reflection. I will report a similar case, i.e. the case where you can let the method being called only by specific methods from extenal classes. Suppose that the class that must be "protected" by a whatsoever invocation of the its public methods is
Invoked
, whileInvoker
is the class tha has a method enabled to invoke one or more methods fromInvoked
. Then, you can do something like reported in the following.and the
Invoker
class isNote that the method that is enabled to invoke is annotated with the
CanInvoke
annotation.The case that you requested is similar. You annotate the classes/method that cannot call the public method and then you set to
true
thecanExecute
variable only if the method/class is not annotated.您可以使用 Macker 之类的工具并将其添加到您的构建过程中以检查某些规则是否得到遵守,例如
它将不会阻止您编写错误的代码,但如果您使用 Maven 或其他构建系统,它可能会在构建过程中引发错误。
该工具在“类”级别工作,而不是在“方法”级别工作,但我不认为有必要阻止从某个类仅调用一种方法......
You can use a tool like Macker and add it to your build process to check some rules are respected, like
It will NOT prevent you from writing wrong code but if you use Maven or another build system it can raise an error during your build process.
This tools work at a "class" level not at a "method" level but I do not see the point of preventing the call of only one method from a certain class ...