在了解JPA的Repository原理之前瀑罗,先了解一下EntityManager
的應(yīng)用骤坐,通過它可以開啟一個(gè)事務(wù)队萤,從而令之后的操作在保持在事務(wù)中。
public class JpaTest {
EntityManagerFactory factory;
@Before
public void before() {
// 從META-INF的persistence.xml獲取持久化單元
factory = Persistence.createEntityManagerFactory("hibernateJPA");
}
// 立即查詢
@Test
public void testR() {
EntityManager em = factory.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer customer = em.find(Customer.class, 1L);
System.out.println("========================");
System.out.println(customer);
tx.commit();
}
}
以Hibernate實(shí)現(xiàn)的JPA為例勾扭,通過JDK提供的動(dòng)態(tài)代理機(jī)制毡琉,生成JPA的Repository接口的實(shí)現(xiàn)類,從如下的單元測(cè)試作為切入點(diǎn)妙色,我們可以進(jìn)一步跟蹤JPA生成的動(dòng)態(tài)代理是如何進(jìn)行數(shù)據(jù)庫操作的
@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringdataJpaTest {
// jdk動(dòng)態(tài)代理的實(shí)例
@Autowired
CustomerRepository repository;
@Test
public void testR() {
Optional<Customer> byId = repository.findById(20L);
System.out.println(byId.orElse(null));
}
}
通過斷點(diǎn)桅滋,可以看到注入的CustomerRepository
是一個(gè)動(dòng)態(tài)代理對(duì)象$Proxy
,創(chuàng)建動(dòng)態(tài)代理所需的接口InvocationHandler
實(shí)現(xiàn)類采用org.springframework.aop.framework.JdkDynamicAopProxy
類
進(jìn)一步通過斷點(diǎn)到JdkDynamicAopProxy
類中,這是Spring提供的一個(gè)用于生成動(dòng)態(tài)代理對(duì)象的工廠類丐谋,可以看到這個(gè)類中存在獲取具體代理對(duì)象的方法芍碧,同時(shí)生成的動(dòng)態(tài)代理對(duì)象調(diào)用目標(biāo)方法的時(shí)候,也會(huì)相應(yīng)的觸發(fā)這個(gè)類中的攔截方法invoke
号俐,相關(guān)的代碼如下:
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
// ...
// 通過如下兩個(gè)方法可以獲取到當(dāng)前類需要?jiǎng)?chuàng)建的代理對(duì)象
@Override
public Object getProxy() {
return getProxy(ClassUtils.getDefaultClassLoader());
}
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}
// ...
// 當(dāng)代理對(duì)象觸發(fā)方法的調(diào)用時(shí)泌豆,會(huì)進(jìn)入到這個(gè)方法,Spring在這里會(huì)構(gòu)成一個(gè)調(diào)用鏈吏饿,
// 并通過遞歸的方式踪危,不斷地觸發(fā)調(diào)用鏈上的每一個(gè)方法
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ...
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
} else {
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
// ...
}
}
仔細(xì)觀察JdkDynamicAopProxy
類中的targetSource
屬性,內(nèi)部封裝了org.springframework.data.jpa.repository.support.SimpleJpaRepository
類找岖,這是spring-data-jpa提供的通用Repository陨倡,類似上文舉過的單元測(cè)試?yán)恿沧蹋鼉?nèi)部封裝了EntityManager
许布,通過后者就能夠獲取到當(dāng)前項(xiàng)目中實(shí)現(xiàn)了JPA規(guī)范的Hibernate的SessionImpl對(duì)象,從而完成數(shù)據(jù)庫操作绎晃,SimpleJpaRepository
類如下:
@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
// ...
@Override
public Optional<T> findById(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
Class<T> domainType = getDomainClass();
if (metadata == null) {
return Optional.ofNullable(em.find(domainType, id));
}
LockModeType type = metadata.getLockModeType();
Map<String, Object> hints = new HashMap<>();
getQueryHints().withFetchGraphs(em).forEach(hints::put);
return Optional.ofNullable(type == null ? em.find(domainType, id, hints)
: em.find(domainType, id, type, hints));
}
}