Hibernate use javassist to create dynamic proxies instead of concrete entities to populate fields of fetched entity that are referencing other persistent entity (or collection of persistent entities).
(note that if you mark the relationship as eagerly fetched : hibernate won't create proxies but concrete entities. This is NOT the default)
A major advantage of javassist over standard dynamic proxy mechanism is that it allows creation of dynamic proxy on concrete classes, not only interfaces.
The responsability of a proxy is to perform "transparently" a database read operation when required (i.e. when access to a proxied entity is required)
Proxies and first or second level cache aren't really linked concepts. We can just say that if you try to "resolve" a proxy when the entity holding it isn't attached to an open session (i.e. when the entity holding it isn't in the first level cache) it will raise a LazyInitializationException (simply because there is no way perform a database read in this situation)