欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring整合Mybatis實操分享

 更新時間:2022年04月28日 10:54:44   作者:程序員周瑜?  
這篇文章主要介紹了Spring整合Mybatis實操分享,文章首先通過介紹Mybatis的工作原理展開Spring整合Mybatis的詳細內容,需要的小伙伴可以參考一下

在介紹Spring整合Mybatis原理之前,我們得先來稍微介紹Mybatis的工作原理。

Mybatis的基本工作原理

Mybatis中,我們可以使用一個接口去定義要執(zhí)行sql,簡化代碼如下: 定義一個接口,@Select表示要執(zhí)行查詢sql語句。

 public interface UserMapper { 
 @Select("select * from user where id = #{id}") 
 User selectById(Integer id);
 }

執(zhí)行代碼:

 InputStream inputStream = Resources.getResourceAsStream("mybatis.xml" ); 
 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder(). build(inputStream);
 SqlSession sqlSession = sqlSessionFactory.openSession(); 
 // 以下使我們需要關注的重點 
 UserMapper mapper = sqlSession.getMapper(UserMapper.class); 
 Integer id = 1; 
 User user = mapper.selectById(id);

Mybatis的目的是:使得程序員能夠以調用方法的方式執(zhí)行某個指定的sql,將執(zhí)行sql的底層邏輯進行了封裝。 這里重點思考以下mapper這個對象,當調用SqlSession的getMapper方法時,會對傳入的接口生成一個 代理對象,而程序要真正用到的就是這個代理對象,在調用代理對象的方法時,Mybatis會取出該方法所對應的sql語句,然后利用JDBC去執(zhí)行sql語句,最終得到結果。

分析需要解決的問題

Spring和Mybatis時,我們重點要關注的就是這個代理對象。因為整合的目的就是:把某個Mapper的代理 對象作為一個bean放入Spring容器中,使得能夠像使用一個普通bean一樣去使用這個代理對象,比如能 被@Autowire自動注入。 比如當Spring和Mybatis整合之后,我們就可以使用如下的代碼來使用Mybatis中的代理對象了:

 @Component 
 public class UserService { 
 @Autowired 
 private UserMapper userMapper; 

 public User getUserById(Integer id) { 
 return userMapper.selectById(id); 
 } 
 }

UserService中的userMapper屬性就會被自動注入為Mybatis中的代理對象。如果你基于一個已經完成整合的項目去調試即可發(fā)現(xiàn),userMapper的類型為: org.apache.ibatis.binding.MapperProxy@41a0aa7d。證明確實是Mybatis中的代理對象。 好,那么現(xiàn)在我們要解決的問題的就是:如何能夠把Mybatis的代理對象作為一個bean放入Spring容器中?要解決這個,我們需要對Spring的bean生成過程有一個了解。

Spring中Bean的產生過程

Spring啟動過程中,大致會經過如下步驟去生成bean

  • 掃描指定的包路徑下的class文件
  • 根據(jù)class信息生成對應的BeanDefinition
  • 在此處,程序員可以利用某些機制去修改BeanDefinition
  • 根據(jù)BeanDefinition生成bean實例
  • 把生成的bean實例放入Spring容器中

假設有一個A類,假設有如下代碼: 一個A類

@Component
 public class A { 
 }

一個B類,不存在@Component注解

 public class B { 
 }

執(zhí)行如下代碼:

AnnotationConfigApplicationContext context = new AnnotationConfigAppl icationContext(AppConfig.class); 
 System.out.println(context.getBean("a"));

輸出結果為:com.luban.util.A@6acdbdf5 A類對應的bean對象類型仍然為A類。但是這個結論是不確定的,我們可以利用BeanFactory后置處理器來 修改BeanDefinition,我們添加一個BeanFactory后置處理器:

 @Component 
 public class LubanBeanFactoryPostProcessor implements BeanFactoryPost Processor { 
 @Override 
public void postProcessBeanFactory(ConfigurableListableBeanFactor y beanFactory)
 throws BeansException { 
 BeanDefinition beanDefinition = beanFactory.getBeanDefinition 
("a"); 
beanDefinition.setBeanClassName(B.class.getName()); 
} 
 }

這樣就會導致,原本的A類對應的BeanDefiniton被修改了,被修改成了B類,那么后續(xù)正常生成的bean對 象的類型就是B類。此時,調用如下代碼會報錯:

 context.getBean(A.class);

但是調用如下代碼不會報錯,盡管B類上沒有@Component注解:

 context.getBean(B.class);

并且,下面代碼返回的結果是:

com.luban.util.B@4b1c1ea0

 AnnotationConfigApplicationContext context = new AnnotationConfigAppl icationContext(AppConfig.class); 
 System.out.println(context.getBean("a"));

之所以講這個問題,是想說明?個問題:在Spring中,bean對象跟class沒有直接關系,跟 BeanDefinition才有直接關系。 那么回到我們要解決的問題:如何能夠把Mybatis的代理對象作為一個bean放入Spring容器中? 在Spring中,如果你想生成一個bean,那么得先生成一個BeanDefinition,就像你想new一個對象實 例,得先有一個class。

解決問題

繼續(xù)回到我們的問題,我們現(xiàn)在想自己生成一個bean,那么得先生成一個BeanDefinition,只要有了 BeanDefinition,通過在BeanDefinition中設置bean對象的類型,然后把BeanDefinition添加給 Spring,Spring就會根據(jù)BeanDefinition?動幫我們?成?個類型對應的bean對象。

所以,現(xiàn)在我們要解決兩個問題:

  • Mybatis的代理對象的類型是什么?因為我們要設置給BeanDefinition
  • 我們怎么把BeanDefinition添加給Spring容器?

注意:上文中我們使用的BeanFactory后置處理器,他只能修改BeanDefinition,并不能新增一個 BeanDefinition。我們應該使用Import技術來添加一個BeanDefinition。后面再詳細介紹如果使用Import 技術來添加一個BeanDefinition,可以先看一下偽代碼實現(xiàn)思路。

假設:我們有一個UserMapper接口,他的代理對象的類型為UserMapperProxy。 那么我們的思路就是這樣的,

偽代碼如下:

 BeanDefinitoin bd = new BeanDefinitoin();
 bd.setBeanClassName(UserMapperProxy.class.getName()); 
 SpringContainer.addBd(bd);

但是,這里有一個嚴重的問題,就是上文中的UserMapperProxy是我們假設的,他表示一個代理類的類 型,然而Mybatis中的代理對象是利用的JDK的動態(tài)代理技術實現(xiàn)的,也就是代理對象的代理類是動態(tài)生成的,我們根本方法確定代理對象的代理類到底是什么。 所以回到我們的問題:Mybatis的代理對象的類型是什么? 本來可以有兩個答案: 1. 代理對象對應的代理類 2. 代理對象對應的接口 那么答案1就相當于沒有了,因為是代理類是動態(tài)生成的,那么我們來看答案2:代理對象對應的接口如果我們采用答案2,那么我們的思路就是:

BeanDefinition bd = new BeanDefinitoin(); 
 // 注意這?,設置的是UserMapper 
 bd.setBeanClassName(UserMapper.class.getName()); 
 SpringContainer.addBd(bd);

但是,實際上給BeanDefinition對應的類型設置為一個接口是行不通的,因為Spring沒有辦法根據(jù)這個 BeanDefinition去new出對應類型的實例,接口是沒法直接new出實例的。 那么現(xiàn)在問題來了,我要解決的問題:Mybatis的代理對象的類型是什么? 兩個答案都被我們否定了,所以這個問題是無解的,所以我們不能再沿著這個思路去思考了,只能回到最 開始的問題:如何能夠把Mybatis的代理對象作為一個bean放入Spring容器中?

總結上文的推理:我們想通過設置BeanDefinition的class類型,然后由Spring自動的幫助我們去生成對應的bean,但是這條路是行不通的。 終極解決方案 那么我們還有沒有其他辦法,可以去生成bean呢?并且生成bean的邏輯不能由Spring來幫我們做了,得 由我們自己來做。 FactoryBean 有,那就是Spring中的FactoryBean。我們可以利用FactoryBean去自定義我們要生成的bean對象,比如

 @Component
 public class LubanFactoryBean implements FactoryBean { 
 @Override 
public Object getObject() throws Exception { 
 Object proxyInstance = Proxy.newProxyInstance(LubanFactoryBe an.class.
getClassLoader(), new Class[]{UserMapper.class}, new Invoca tionHandler() { 
 @Override 
 public Object invoke(Object proxy, Method method, Object [] args) throws Throwable { 
 if (Object.class.equals(method.getDeclaringClass())) { 
 return method.invoke(this, args); 
 } else { 
 // 執(zhí)?代理邏輯 
 return null; 
 } 
 } 
 }); 
 return proxyInstance; 
 } 
 @Override 
 public Class<?> getObjectType() { 
return UserMapper.class;
 } 
 }

我們定義了一個LubanFactoryBean,它實現(xiàn)了FactoryBean,getObject方法就是用來自定義生成bean 對象邏輯的。 執(zhí)行如下代碼:

 public class Test { 
 public static void main(String[] args) { 
 AnnotationConfigApplicationContext 
context = new AnnotationCo nfigApplicationContext(AppConfig.class); 
 System.out.println("lubanFactoryBean: " + context.getBean
("lu banFactoryBean")); 
 System.out.println("&lubanFactoryBean: " + context.getBean
("& lubanFactoryBean")); 
 System.out.println("lubanFactoryBean-class: " + 
context.getBe an("lubanFactoryBean").getClass()); 
 } 
}

將打?。?lubanFactoryBean: com.luban.util.LubanFactoryBean1@4d41cee &lubanFactoryBean: com.luban.util.LubanFactoryBean@3712b94 lubanFactoryBean-class: class com.sun.proxy.Proxy20 從結果我們可以看到,從Spring容器中拿名字為"lubanFactoryBean"的bean對象,就是我們所自定義的 jdk動態(tài)代理所生成的代理對象。

所以,我們可以通過FactoryBean來向Spring容器中添加一個自定義的bean對象。上文中所定義的 LubanFactoryBean對應的就是UserMapper,表示我們定義了一個LubanFactoryBean,相當于把 UserMapper對應的代理對象作為一個bean放入到了容器中。 但是作為程序員,我們不可能每定義了一個Mapper,還得去定義一個LubanFactoryBean,這是很麻煩的 事情,我們改造一下LubanFactoryBean,讓他變得更通用,

比如:

 @Component 
public class LubanFactoryBean implements FactoryBean { 
// 注意這里 
 private Class mapperInterface;
 public LubanFactoryBean(Class mapperInterface) { 
 this.mapperInterface = mapperInterface; 
 } 
 @Override 
 public Object getObject() throws Exception { 
 Object proxyInstance = Proxy.newProxyInstance(LubanFactoryBe an.class.getClassLoader(), 
new Class[]{mapperInterface}, new Invocat ionHandler() { 
 @Override 
 public Object invoke(Object proxy, Method method, Object [] args) throws Throwable { 
if (Object.class.equals(method.getDeclaringClass())) { 
return method.invoke(this, args); 
 } else { 
 // 執(zhí)行代理邏輯 
 return null; 
 } 
 } 
 }); 

return proxyInstance; 
 } 
 @Override 
 public Class<?> getObjectType() { 
 return mapperInterface; 
 }
}

改造LubanFactoryBean之后,LubanFactoryBean變得靈活了,可以在構造LubanFactoryBean時,通 過構造傳入不同的Mapper接口。 實際上LubanFactoryBean也是一個Bean,我們也可以通過生成一個BeanDefinition來生成一個 LubanFactoryBean,并給構造方法的參數(shù)設置不同的值,比如偽代碼如下:

BeanDefinition bd = new BeanDefinitoin();
 // 注意一:設置的是LubanFactoryBean 
 bd.setBeanClassName(LubanFactoryBean.class.getName()); 
 // 注意二:表示當前BeanDefinition在生成bean對象時,會通過調用LubanFactoryBean
 的構造方法來生成,并傳入UserMapper 
bd.getConstructorArgumentValues().addGenericArgumentValue(UserMapper. class.getName()) 
 SpringContainer.addBd(bd)

特別說一下注意二,表示表示當前BeanDefinition在生成bean對象時,會通過調用LubanFactoryBean的 構造方法來生成,并傳入UserMapper的Class對象。那么在生成LubanFactoryBean時就會生成一個 UserMapper接口對應的代理對象作為bean了。 到此為止,其實就完成了我們要解決的問題:把Mybatis中的代理對象作為一個bean放入Spring容器中。

只是我們這是用簡單的JDK代理對象模擬的Mybatis中的代理對象,如果有時間,我們完全可以調? Mybatis中提供的方法區(qū)生成一個代理對象。這里就不花時間去介紹了。 Import 到這里,我們還有一個事情沒有做,就是怎么真正的定義一個BeanDefinition,并把它添加到Spring中, 上文說到我們要利用Import技術,比如可以這么實現(xiàn):

定義如下類:

public class LubanImportBeanDefinitionRegistrar implements ImportBea nDefinitionRegistrar { 
 @Override 
 public void registerBeanDefinitions(AnnotationMetadata importing
 ClassMetadata, BeanDefinitionRegistry registry) { 
 BeanDefinitionBuilder builder = BeanDefinitionBuilder.generi cBeanDefinition(); 
 AbstractBeanDefinition beanDefinition = builder.getBeanDefin ition(); 
 beanDefinition.setBeanClass(LubanFactoryBean.class); 
 beanDefinition.getConstructorArgumentValues().addGenericArgu mentValue(UserMapper.class);
 // 添加beanDefinition 
 registry.registerBeanDefinition("luban"+UserMapper.class.get SimpleName(),
 beanDefinition); 
 } 
 }

并且在AppConfig上添加@Import注解:

 @Import(LubanImportBeanDefinitionRegistrar.class) 
 public class AppConfig {

這樣在啟動Spring時就會新增一個BeanDefinition,該BeanDefinition會生成一個LubanFactoryBean對 象,并且在生成LubanFactoryBean對象時會傳入UserMapper.class對象,通過LubanFactoryBean內部 的邏輯,相當于會自動生產一個UserMapper接口的代理對象作為一個bean。

總結

總結一下,通過我們的分析,我們要整合Spring和Mybatis,需要我們做的事情如下:

  • 定義一個LubanFactoryBean
  • 定義一個LubanImportBeanDefinitionRegistrar
  • 在AppConfig上添加一個注解@Import(LubanImportBeanDefinitionRegistrar.class)

到此這篇關于Spring整合Mybatis實操分享的文章就介紹到這了,更多相關Spring整合Mybatis內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論