技术要点
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Import(MapperScan.class)
//对应mybatis的@MapperScan,这里使用了@Import,后面必须配置MapperScan的导入规则
public @interface MyMapperScan {
/**
* 自定义组件扫描,默认类全名
* @return
*/
String[] value() default {};
}
@Target(value = {ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
//对应mybatis的@Select
public @interface MySelect {
String value() default "";
}
/**
* 用于注册,返回MyMapperScan的类全名
*/
public class MapperScan implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(MyMapperScan.class.getName());
return (String[])annotationAttributes.get("value");
}
}
/**
* 为Mapper生成代理对象
* @author authorZhao
* @param
*/
public class MyFactoryBean<T> implements FactoryBean<T> {
private Logger logger = LoggerFactory.getLogger(MyFactoryBean.class);
private Class<T> interfaceClass;
public MyFactoryBean(Class<T> interfaceClass) {
this.interfaceClass = interfaceClass;
}
@Override
public T getObject() throws Exception {
logger.info("正在为{}生成代理对象",interfaceClass.getName());
T t = (T)MapperProxy.getObject(interfaceClass);
if(t==null){
logger.error("{}代理对象,生成失败",interfaceClass.getName());
}
logger.info("{}代理对象,生成成功",interfaceClass.getName());
return t;
}
@Override
public Class<T> getObjectType() {
return interfaceClass;
}
@Override
public boolean isSingleton() {
return true;
}
}
public interface MyMapper {
@MySelect("select * from crm_gb_goods where id=#{id}")
Map getGoodsById(Integer id);
}
/**
* 这个类参考别人的,说一下 {@link BeanDefinitionRegistryPostProcessor}的作用,这个类继承了 {@link BeanFactoryPostProcessor}
* BeanFactoryPostProcessor主要有一个方法,在bean定义加载的时候做一些事情,但这时候是没有bean实例的,spring不推荐这个时候
* 进行bean的实例操作,这里采用工厂模式,必须提前注册工厂的bean定义
*/
public class MapperDefinitionRegistry implements BeanDefinitionRegistryPostProcessor {
private static final Logger logger = LoggerFactory.getLogger(MapperDefinitionRegistry.class);
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
//写死支持MyMapper,其实扫描应该用在这里,动态获取那些类需要代理,但是后面发现用错地方了
Class beanClazz = MyMapper.class;
/* //使用RootBeanDefinition
BeanDefinition beanDefinition = new RootBeanDefinition(Car.class);
//beanDefinition.setAttribute("car",car1);
//这里是我之前直接用spring上下文进行注册的方法
registry.registerBeanDefinition("car",beanDefinition);*/
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClazz);
GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition(); definition.getConstructorArgumentValues().addGenericArgumentValue(beanClazz);
definition.setBeanClass(MyFactoryBean.class);
//这里采用的是byType方式注入,类似的还有byName等
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
registry.registerBeanDefinition(beanClazz.getSimpleName(), definition);
logger.info("{}定义信息注册完毕",beanClazz.getSimpleName());
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String str = "开始注册bean";
System.out.println(str);
}
}
**
* 代理工具类,直接生成代理对象
* @author authorZhao
*/
public class MapperProxy {
private static final Logger logger = LoggerFactory.getLogger(MapperProxy.class);
private static class MyInvocationHandler implements InvocationHandler {
/**
* 接口名
*/
String interfaceName;
/**
* 构造函数
* @param interfaceName
*/
public MyInvocationHandler(String interfaceName) {
super();
this.interfaceName = interfaceName;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this,args);
}
//获取方法上注解信息的工具类,这里不贴了,想了解的可以话,上github
MySelect annotation = AnnotationUtil.getAnnotation(method, MySelect.class);
if(annotation==null)throw new RuntimeException("找不着select");
String sql = annotation.value();
//未支持spel表达式
sql = sql.replace("#{id}",String.valueOf(args[0]));
ResultSet resultSet = SqlQuery.querySql(sql);
//结果映射,还未实现
Object object = OrmUtil.convertResultSet(resultSet,method.getReturnType());
return null;
//return proxy;
}
}
public static Object getObject(final Class interfaceInfo) {
// 获取接口名
String interfaceName = interfaceInfo.getName();
ClassLoader classLoader = interfaceInfo.getClassLoader();
Class[] interfaces = { interfaceInfo };
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(interfaceName);
//创建代理对象
Object object = null;
try {
object = Proxy.newProxyInstance(classLoader, interfaces, myInvocationHandler);
}catch (Exception e){
System.out.println("获取不到代理对象");
e.printStackTrace();
}
return object;
}
}
/**
* jdbc封装类,目前只设置了查询方法
*/
public class SqlQuery {
private static final Logger logger = LoggerFactory.getLogger(SqlQuery.class);
public static ResultSet querySql(String sql)throws ClassNotFoundException, SQLException {
//1.加载驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
DataSource dataSource = MySpringContext.getBean(DataSource.class);
Connection conn = dataSource.getConnection();//2.获取连接
logger.info("正在执行的sql:{}",sql);
PreparedStatement prs = (PreparedStatement) conn.prepareStatement(sql);//3.创建ProparedStatement 对象
ResultSet res =prs.executeQuery();//4.执行sql语句并将查询结果返回
logger.info("返回的结果:{}",res);
while(res.next()) {//5.遍历查询结果
}
//6.关闭资源
return res;
}
}
public class Test1 {
@Test
public void test1(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SqlConfig.class);
//context.start();
MyMapper mapper = context.getBean(MyMapper.class);
Map goodsById = mapper.getGoodsById(8);
}
}
@SpringBootTest(classes = SqlApplication.class)
public class BootSqlTest {
@Autowired
private MyMapper myMapper;
@Test
public void test1(){
myMapper.getGoodsById(8);
}
}
@SpringBootApplication
@Import({SqlConfig.class})
public class SqlApplication {
public static void main(String[] args) {
SpringApplication.run(SqlApplication.class, args);
}
}
//这两个类放一起
@Configuration
@MyMapperScan({"com.git.sql.config.MapperDefinitionRegistry",
"com.git.sql.config.MyBatisBeanPostProcessor"
,"com.git.sql.util.MySpringContext"})
public class SqlConfig {
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF8&allowMultiQueries=true");
dataSource.setUsername("user");
dataSource.setPassword("password");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
return dataSource;
}
}
基本功能暂时就这样,后面会看看mybatis源码继续模仿,
想到之前瞎写ioc的实现真是笑死了,spring源码搭建都比我自己的实现时间长,本次及基本模拟了jdk动态代理,下一步将完成前面的bean定义注册的扫描特性
里面有dubbo和mq的demo,还有本次代码的
本次代码在provider的sql目录下面,其他的工具类都在里面
参考文章: https://blog.csdn.net/lichuangcsdn/article/details/89694363 这个是注册bean工厂的
本文为作者原厂,转载请注明出处