Singleton:如何停止通过反射创建实例
我知道在Java中我们可以通过new、clone()、Reflection以及序列化和反序列化来创建类的实例序列化。
我创建了一个实现单例的简单类。
我需要一直停止创建我的类的实例。
public class Singleton implements Serializable{
private static final long serialVersionUID = 3119105548371608200L;
private static final Singleton singleton = new Singleton();
private Singleton() { }
public static Singleton getInstance(){
return singleton;
}
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("Cloning of this class is not allowed");
}
protected Object readResolve() {
return singleton;
}
//-----> This is my implementation to stop it but Its not working. :(
public Object newInstance() throws InstantiationException {
throw new InstantiationError( "Creating of this object is not allowed." );
}
}
在这个类中,我成功地通过 new、clone() 和序列化来停止类实例,但无法通过反射来停止它。
我创建对象的代码是
try {
Class<Singleton> singletonClass = (Class<Singleton>) Class.forName("test.singleton.Singleton");
Singleton singletonReflection = singletonClass.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
I know in Java we can create an instance of a Class by new
, clone()
, Reflection
and by serializing and de-serializing
.
I have create a simple class implementing a Singleton.
And I need stop all the way one can create instance of my Class.
public class Singleton implements Serializable{
private static final long serialVersionUID = 3119105548371608200L;
private static final Singleton singleton = new Singleton();
private Singleton() { }
public static Singleton getInstance(){
return singleton;
}
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("Cloning of this class is not allowed");
}
protected Object readResolve() {
return singleton;
}
//-----> This is my implementation to stop it but Its not working. :(
public Object newInstance() throws InstantiationException {
throw new InstantiationError( "Creating of this object is not allowed." );
}
}
In this Class I have managed to stop the class instance by new
, clone()
and serialization
, But am unable to stop it by Reflection.
My Code for creating the object is
try {
Class<Singleton> singletonClass = (Class<Singleton>) Class.forName("test.singleton.Singleton");
Singleton singletonReflection = singletonClass.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
我们可以使用静态嵌套类来打破它
请遵循下面的代码,它 100% 正确,我测试过
We can break it using static nested class
Please follow below code its 100% correct, i tested
完美的单例类,可以避免在序列化、克隆和反射过程中创建实例。
Perfect Singleton Class that can avoid instance creation during serialization, clone and reflection.
延迟初始化方法:
即使您决定在任何 getInstance 调用之前使用反射创建实例,此方法也有效
Approach with Lazy initialization:
This approach works even if you decide to create an instance using reflection before any getInstance invocation
通过在您的私有构造函数中添加以下检查
By adding below check inside your private constructor
像这样定义单例:
Define the singleton like this:
如何检查构造函数:
当然,这可能并不能真正阻止它 - 如果有人乱用反射来访问私有成员,他们可能可以自己将该字段设置为 null。你必须问自己到底想阻止什么,以及它是否值得。
(编辑:正如 Bozho 提到的,即使通过反射,最终字段也可能无法设置。如果有某种通过 JNI 等方式做到这一点,我不会感到惊讶......如果你给人们足够的访问,他们几乎可以做任何事情......)
How about checking in the constructor:
Of course, this may not really stop it - if someone is messing around with reflection to access private members, they may be able to set the field to null themselves. You have to ask yourself just what you're trying to prevent though, and how worthwhile it is.
(EDIT: As Bozho mentioned, final fields may not be settable even via reflection. I wouldn't be surprised if there were some way of doing it via JNI etc though... if you give folks enough access, they'll be able to do almost anything...)
您应该注意的另一件事是 readResolve(..) 方法,因为您的类实现了 Serialiable。在那里您应该返回现有实例。
但使用单例最简单的方法是通过枚举 - 你不用担心这些事情。
Another thing you should watch is the
readResolve(..)
method, because your class implementsSerialiable
. There you should return the existing instance.But the easiest way to use singletons is through enums - you don't worry about these things.
作为单例的替代方案,您可以查看单态模式。然后,类的实例化不再是问题,并且您不必担心您列出的任何场景。
在单态模式中,类中的所有字段都是静态的。这意味着该类的所有实例共享相同的状态,就像单例一样。而且,这一事实对于调用者来说是透明的;他们不需要了解像 getInstance 这样的特殊方法,他们只需创建实例并使用它们即可。
但是,就像单例一样,它是一种隐藏的全局状态;这非常糟糕。
As an alternative to the singleton, you could take a look at the monostate pattern. Then, instantiation of your class is not a problem anymore, and you don't have to worry about any of the scenarios you listed.
In the monostate pattern, all the fields in your class are
static
. That means that all instances of the class share the same state, just like with a singleton. Moreover, this fact is transparent to the callers; they don't need to know about special methods likegetInstance
, they simply create instances and work with them.But, just like with singleton, it's a form of hidden global state; which is very bad.
除了枚举解决方案外,所有其他解决方案都可以通过反射来解决
以下是有关如何解决 Dave G 解决方案的两个示例:
1:将变量 Singleton.singleton 设置为 null
输出:
2:不调用 getInstance
输出:
所以我可以认为如果您不想使用枚举,有两种方法:
第一个选项:使用 securityManager :
它可以防止使用未经授权的操作(从类外部调用私有方法....)
只需要在其他答案提出的单例构造函数中添加一行即可
,它的作用是防止调用 setAccessible(true)
因此,当您想调用它时:
会发生此异常:
java.security.AccessControlException: access returned ("java.lang.RuntimePermission" "createSecurityManager")
第二个选项:在单例构造函数中,测试是否通过反射进行调用:
我建议您参考另一个Stackoverflow 线程 获取调用者类或方法的最佳方法。
因此,如果我在 Singleton 构造函数中添加它:
并且我这样调用它:
输出将是:
jdk.internal.reflect.DelegatingConstructorAccessorImpl
但如果我定期调用它(实例化公共构造函数或调用没有反射的方法)打印调用方法的类的名称。例如,我有:
callerClassName将为
MainReflexion
,因此输出将为MainReflexion
。PS:如果建议的解决方案存在解决方法,请告诉我
Apart the enum solution, all the others can be workaround-ed via Reflexion
These are two examples on how to workaround the Dave G Solution :
1 : Setting the variable Singleton.singleton to null
Output :
2 : not calling the getInstance
Output :
So I can think of 2 ways if you don't want to go with an Enum :
1st option : Using the securityManager :
It prevent from using unauthorized operations (calling private methods from outside the class ....)
So you just need to add one line to the singleton constructor proposed by the other answers
what it does is that it prevents from calling
setAccessible(true)
So when you want to call it :
this exeption will occure :
java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "createSecurityManager")
2nd option : In the singleton constructor, test if the call is made via Reflexion :
I refer you to this other Stackoverflow thread for the best way to get the caller class or method.
So If I add this in the Singleton constructor :
And I call it like this :
the output will be :
jdk.internal.reflect.DelegatingConstructorAccessorImpl
but if I call it regularly (Instantiating a public constructor or calling a method without Reflexion) the name of the class of the calling method is printed. So for example I have :
the callerClassName will be
MainReflexion
and so the output will beMainReflexion
.PS : If workarounds exists for the proposed solutions please let me know
请注意,从 Java 8 开始,根据我的检查,只要单例具有私有构造函数,就无法通过反射实例化单例。
你会得到这个异常:
Just to note, that as of Java 8 and according to my check- you cannot instantiate a Singleton via Reflections as long as it has a private constructor.
You'll get this exception:
我的事情下面的代码将工作..
输出:单例设计模式
I Thing Below code will work ..
output : Singleton Design Pattern