模拟Mybatis"无实现类"调用接口
2021-02-21 10:19:17 828
Mybatis让我们通过接口(interface)就能调用到对应sql, 看起来好像没有实现类, 貌似不符合"常识"
通过这个简易demo, 带你大致了解mybatis背后所做的事
先建立maven工程
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
application.yml
server:
port: 8088
启动类
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
数据对象
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class User {
private Long id;
private String username;
private String password;
}
定义一个接口
UserDAO
public interface UserDAO {
User getOne();
List<User> getAll();
}
以下是重要的部分
实现FactoryBean接口
重写getObject方法, 该方法返回的是代理后的对象
实现InvocationHandler接口
重写invoke方法, 编写代理逻辑
public class UserDaoFactoryBean<T> implements FactoryBean<T>, InvocationHandler {
@Override
public T getObject() throws Exception {
@SuppressWarnings("unchecked")
T t = (T)Proxy.newProxyInstance(UserDAO.class.getClassLoader(), new Class[]{UserDAO.class}, this);
return t;
}
@Override
public Class<?> getObjectType() {
return UserDAO.class;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
if ("getOne".equals(method.getName())) {
return new User(1L, "zhangsan", "123456");
} else if ("getAll".equals(method.getName())) {
List<User> userList = new ArrayList<>(2);
userList.add(new User(1L, "zhangsan", "123456"));
userList.add(new User(2L, "lisi", "09876"));
return userList;
}
throw new NoSuchMethodException();
}
}
}
动态注册bean
实现BeanDefinitionRegistryPostProcessor接口并加入容器
@Component
public class InterfaceBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinitionBuilder definitionBuilder =
BeanDefinitionBuilder.genericBeanDefinition(UserDAO.class);
AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
//将beanClass替换为UserDaoFactoryBean, spring在生成对象时就会获取到UserDaoFactoryBean#getObject生成的代理对象
beanDefinition.setBeanClass(UserDaoFactoryBean.class);
registry.registerBeanDefinition("userDAO", beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
启动测试
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
UserDAO userDAO;
@GetMapping("/getAll")
public List<User> getAll() {
return userDAO.getAll();
}
@GetMapping("/getOne")
public User getOne() {
return userDAO.getOne();
}
}
访问两个接口
getAll返回
[
{
"id": 1,
"username": "zhangsan",
"password": "123456"
},
{
"id": 2,
"username": "lisi",
"password": "09876"
}
]
getOne返回
{
"id": 1,
"username": "zhangsan",
"password": "123456"
}
实际上mybatis的生成代理过程更为复杂, 这里只是简略写出, 有兴趣的可以翻翻源码