大家好,又见面了,我是你们的朋友全栈君。
一、问题描述
在mybatis中,mapper通常是一个接口,但是我们却可以直接通过这个接口调用方法。按道理来说接口是不能直接调用方法的,只有实现类才能调用接口。但在下面的代码中,我们直接调用applicationContext.getBean(TestMapper.class).list(“”),就可以查询我们的数据库。
也就是说applicationContext.getBean(TestMapper.class)拿到的是一个代理对象并存在spring容器中,只不过这些都由@MapperScan这个注解帮我们实现了。接下来我们自己写一个简单的scan,拿到sql语句。
public interface TestMapper {
@Select("SELECT * FROM test")
public List<Map<String,Object>> list(String str);
}
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
applicationContext.getBean(TestMapper.class).list("");
}
@Configuration
@ComponentScan("com.stay")
@MapperScan("com.stay.dao")
public class AppConfig {
@Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("xxx");
dataSource.setUrl("xxx");
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setLogImpl(Log4jImpl.class);
sqlSessionFactoryBean.setConfiguration(configuration);
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
}
二、分析
第一步:将TestMapper接口变成一个对象。
第二步:这个对象必须实现了TestMapper接口。
第三步:把这个对象放入spring容器中。
第一、二步可以使用我们的动态代理,jdk动态代理基于接口,符合我们的需求。
第三步需要把我们的动态代理对象放到spring容器中,这里需要把一个class转成bd,再注册到spring容器中。
1、如果将TestMapper直接注入的话,spring容器是创建不出来的,它是一个接口。
2、怎样才能把我们生成的动态代理给注册到spring容器中。
这里我们通过实现ImportBeanDefinitionRegistrar接口,动态注册给spring,并修改beanDefinition的BeanClass让它变成一个factoryBean,在getObject方法中,返回我们的代理对象。(这里有个知识点FactoryBean,大家可以去了解下)。
三、代码实现
1、自定义注解TestScan
@Retention(RetentionPolicy.RUNTIME)
@Import(MyImportBeanDefinitionRegistrar.class)
public @interface TestScan {
}
2、实现ImportBeanDefinitionRegistrar接口,动态注册beanDefinition到spring容器。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(TestMapper.class);
GenericBeanDefinition beanDefinition = (GenericBeanDefinition)builder.getBeanDefinition();
//因为factoryBean.getObject方法返回的是代理对象,所以我们需要将TestMapper.class传进去
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(TestMapper.class.getName());
//动态修改beanClass为FactoryBean
beanDefinition.setBeanClass(MyFactoryBean.class);
//将beanDefinition注册到spring容器中
registry.registerBeanDefinition("testMapper",beanDefinition);
}
}
3、实现FactoryBean和InvocationHandler接口。FactoryBean实际会创建2个bean,一个是MyFactoryBean对象本身,通过&+beanName来获取,一个是getObject返回的对象,通过当前的beanName来获取。而当我们执行testMapper的list方法的时候,实际上是执行了实现InvocationHandler 接口的invoke方法。
public class MyFactoryBean implements FactoryBean,InvocationHandler {
Class clazz;
public MyFactoryBean(Class clazz){
this.clazz = clazz;
}
public Object getObject() throws Exception {
Class[] clazzs = new Class[]{clazz};
//jdk动态代理
Object proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), clazzs, this);
return proxy;
}
public Class<?> getObjectType() {
return clazz;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy");
Method method1 = proxy.getClass().getInterfaces()[0].getMethod(method.getName(), String.class);
Select select = method1.getDeclaredAnnotation(Select.class);
//打印sql语句
System.out.println(select.value()[0]);
System.out.println("执行sql查询.....");
return null;
}
}
4、将MapperScan换成TestScan
@Configuration
@ComponentScan("com.stay")
@TestScan
public class AppConfig {
@Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("xxx");
dataSource.setUrl("xxx");
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setLogImpl(Log4jImpl.class);
sqlSessionFactoryBean.setConfiguration(configuration);
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
}
5、输出结果
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/138914.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...