Reputation: 4693
Searching for an explanation for why my static initialization block wouldn't execute, I found that a static initializer in a class never executes if it references a final.
So I tried to work around this by removing the 'final' from MYWS_PROPS:
public class HibernateUtil {
public static String MYWS_PROPS = "/myws.properties";
private static final Logger LOG = Logger.getLogger(HibernateUtil.class.getName());
private static final SessionFactory sessionFactory = buildSessionFactory();
private static Properties sProps;
static {
try {
sProps = new Properties();
sProps.load(ServiceUtils.class.getResourceAsStream(MYWS_PROPS));
LOG.info("Loaded (from " + MYWS_PROPS + ") connection url: " + sProps.getProperty("dburl"));
}
catch (IOException e) {
LOG.severe("Cannot find properties file " + MYWS_PROPS);
}
}
private static SessionFactory buildSessionFactory() {
try {
Configuration config = new Configuration();
config = config.configure("resources/hibernate.cfg.xml");
config.setProperty("hibernate.connection.url", sProps.getProperty("dburl")); // <== NullPointerException!
SessionFactory session = config.buildSessionFactory();
return session;
}
catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
}
But that static block still wouldn't execute!
What is the proper way to guarantee execution of a static block?
Update: All answers below mentioned the requirement to reference the class so that the static initialization will run. So I looked again at my stack trace and verified that the class is indeed referenced (otherwise, why would it throw an exception in that very same class method?)
Caused by: java.lang.ExceptionInInitializerError
at com.corp.dept.proj.myws.HibernateUtil.buildSessionFactory(HibernateUtil.java:76)
at com.corp.dept.proj.myws.HibernateUtil.<clinit>(HibernateUtil.java:38)
at com.corp.dept.proj.myws.ServicePortImpl.<init>(ServicePortImpl.java:82)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:126)
... 70 more
Caused by: java.lang.NullPointerException
at com.corp.dept.proj.myws.HibernateUtil.buildSessionFactory(HibernateUtil.java:67)
... 77 more
But to be on the safe side, I tried the Class.forName suggestion:
Class.forName("com.corp.dept.proj.myws.HibernateUtil");
mProps.load(ServiceUtils.class.getResourceAsStream(HibernateUtil.MYWS_PROPS));
And that static block still wouldn't execute.
So I tried to explicitly instantiate it:
HibernateUtil justExecTheDarnStaticBlock = new HibernateUtil();
mProps.load(ServiceUtils.class.getResourceAsStream(HibernateUtil.MYWS_PROPS));
But that static block still wouldn't execute!
What am I missing here?
Upvotes: 2
Views: 1146
Reputation: 6188
If you create an instance of the class the static block will certainly execute.
EDIT: The actual solution which was found in the comments is the following:
The buildSessionFactory()
method is used in the initialization of sessionFactory
. This depends on the static block having been called already but this isn't the case. If you call sessionFactory = buildSessionFactory();
in the static block rather than in its declaration and after sProps
has been initialized the problem will disappear.
Upvotes: 1
Reputation: 2108
When dealing with static initalisation, order matters.
Take the following example:
public class StaticTest {
private static final String someString = initSomeString();
private static final String someOtherString = initSomeOtherString();
private static final String whoops;
static {
System.out.println("static initializer");
whoops = "whoops";
}
public static void main(String[] args) {
System.out.println(someOtherString);
}
private static String initSomeString() {
System.out.println("initSomeString");
return "Initialised";
}
private static String initSomeOtherString() {
System.out.println("initSomeOtherString");
return whoops + "y daisy";
}
}
The result of running this class is the following:
initSomeString
initSomeOtherString
static initialiser
nully daisy
Static initialisations occur in the order in which they are declared, meaning that initSomeOtherString()
is called before the static initialiser block that initialises whoops
. As a result, whoops
is still null
at that point, so the resulting String
is nully daisy
and not whoopsy daisy
.
If I rearrange the class so that I initialise someOtherString
after the static initialiser, I get what I wanted:
public class StaticTest {
private static final String someString = initSomeString();
private static final String whoops;
static {
System.out.println("static initialiser");
whoops = "whoops";
}
private static final String someOtherString = initSomeOtherString();
public static void main(String[] args) {
System.out.println(someOtherString);
}
private static String initSomeString() {
System.out.println("initSomeString");
return "Initialised";
}
private static String initSomeOtherString() {
System.out.println("initSomeOtherString");
return whoops + "y daisy";
}
}
Prints
initSomeString
static initialiser
initSomeOtherString
whoopsy daisy
This is the behaviour that you're running into and is causing your problem. You're initialising sessionFactory
by calling buildSessionFactory()
, which relies upon sProps
being initialised:
config.setProperty("hibernate.connection.url", sProps.getProperty("dburl")
^ This hasn't been initialised yet
The solution? Move the declaration of sessionFactory
to below your static initialiser block.
Upvotes: 3
Reputation: 8386
The static block will be executed when the class is referenced the first time. (Instantiation or a reference to a static member)
Upvotes: 2
Reputation: 444
If you use the class, e.g. calling it's static method, creating an instance of it, the static block will be executed.
Another way is to load the class explicitely by calling Class.forName
. This is useful when you only know the name (as a string). Sometimes you have to scan the jar file to get the name and use Class.forname
to load it.
Upvotes: 0
Reputation: 15109
The ClassLoader needs to load the class before it the static block executes. This happens when the class is referenced for the first time.
You could force this by using Class.forName("");
. With this, you can use final
variables
Upvotes: 0
Reputation: 16364
The question you link to is just talking about compile-time constants; referencing them does not load the class. But anything you do to load the class -- for example, Class.forName("HibernateUtil")
-- will necessarily run the static initializer.
Also, since the static
variable you are dealing with is not a primitive type or a String
, it can't be a compile-time constant anyway.
Upvotes: 1