利用Spring提供的扩展点实现简单版本的@MapperScan

首先这里说说我知道的spring提供的扩展点:

1、实现BeanFactoryPostProcessor

干预BeanDefinition的信息

例如:设置作用域为prototype

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        BeanDefinition userDao = configurableListableBeanFactory.getBeanDefinition("userDao");
        userDao.setScope("prototype");
    }
}

2、实现BeanPostProcessor

干预bean的生命周期

例如:

@Component
public class testBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
         System.out.println("postProcessBeforeInitialization");
        /**
         * 这里我们就可以返回一个代理的bean了,所以spring是在这里完成代理的。
         */
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization");
        return bean;
    }

}

3、实现FactoryBean

这里实现的类就会注册Bean由spring管理,使用的时候加上"$"符号,然后还提供了一个方法getObject可以返回一个对象,也会注册Bean交由spring管理,不需要加上符号。
例如Mybatis里面的SqlSessionFactoryBean就实现了FactoryBean,然后getObject返回sqlSessionFactory对象。可以解决SqlSessionFactoryBean下的一些复杂的配置,不需要我们手动去xml里面进行一个配置。

4、实现ImportBeanDefinitionRegistrar

动态实现将一个类是否交由spring管理,可以使用方法registerBeanDefinition注册BeanDefinition到beanDefinitionmap中。

其他还有一个方法、注解,例如:@PostConstruct(){举个例子,布隆过滤器解决redis缓存穿透问题,提前找到为空的给返回了}、afterPropertiesSet方法、init方法等
Import有三种,这里是一种还有两种,一种是导入自己普通的类,普通的类即被component注释的类,第二种就是用importSelector可以实现动态的去开启某个功能,这个下篇文章讲。

@PostConstruct
public void init(){
    System.out.println("init");
}

5、开始案例:配置pom.xml

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
    <spring.version>5.0.2.RELEASE</spring.version>
</properties>

<dependencies>
    <!-- https://mvnrepository.com/artifact/dom4j/dom4j -->
    <dependency>
        <groupId>dom4j</groupId>
        <artifactId>dom4j</artifactId>
        <version>1.6.1</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.6</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.18</version>
    </dependency>
</dependencies>

6、实现一个基本的MapperScan

6.1、UserDao接口、UserDaoImpl 实现类

public interface UserDao {

    @Select("select * from student where name = #{name}")
    public void query(String name);
}

@Repository
public class UserDaoImpl implements UserDao {

    @Autowired
    UserDao userDao;
    @Override
    public void query(String name) {
        userDao.query(name);
        System.out.println("useDao·····");
    }
}

6.2、AppConfig类进行扫描

@Configuration
//Configuration,spring源码里面是个if/else,后4个注解使用add添加进行解析的
@ComponentScan("com.spring")
//@Component
//@Import
//@ImportResource("xxx.xml")
//@EnableAspectJAutoProxy
//@MapperScan("com.spring")
@MyMapperScan("com.spring")
public class Appconfig {

    @Bean
    @Autowired
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean
    public DataSource dataSource(){
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
        driverManagerDataSource.setUsername("root");
        driverManagerDataSource.setPassword("123456");
        driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/dayi?useUnicode=true&characterEncoding=utf8");
        driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        return driverManagerDataSource;
    }
}

6.3、自定一个自己的MyMapperScan注解

@Retention(RetentionPolicy.RUNTIME)
@Import(MyImportBeanDefinitionRegistrar.class)
public @interface MyMapperScan {
    public String value();
}

6.4、ImportBeanDefinitionRegistrar 的实现类读取bd并且注册bd

注意这里的构造方法添加构造类型beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(“com.spring.dao.UserDao”);,
可以给后续的Factory的实现类中的构造方法动态获取接口

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        /***
         * 得到bd
         */
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserDao.class);
        GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
        //设置构造方法接口类型
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue("com.spring.dao.UserDao");
        //添加beanclass
        beanDefinition.setBeanClass(MyFactoryBean.class);
        //注册beanDefinition存进map
        registry.registerBeanDefinition("userDao",beanDefinition);
    }
}

6.5、实现Factory进行注册Bean,返回代理对象

模拟Mapper,使用动态代理,返回代理对象。

public class MyFactoryBean implements FactoryBean {
    //构造方法动态获取接口
    Class clazz;
    public MyFactoryBean(Class clazz) {
        this.clazz = clazz;
    }

    @Override
    public Object getObject() throws Exception {
        Class[] claszz = new Class[]{clazz};
        //动态代理
        Object o = Proxy.newProxyInstance(this.getClass().getClassLoader(), claszz, new InvocationHandler() {
            @Override
            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 select = method1.getDeclaredAnnotation(Select.class);
                //得到注解信息得value值是个数组
                System.out.println(select.value()[0]);
                return proxy;
            }
        });
        return o;
    }

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

    @Override
    public boolean isSingleton() {
        return false;
    }
}

6.6、 最后是我们的启动类

public class Test02 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new
                AnnotationConfigApplicationContext(Appconfig.class);
        UserDao userDao = (UserDao) annotationConfigApplicationContext.getBean("userDaoImpl");
        userDao.query("zhangsan");
    }
}

5.7、控制台打印结果如下

返回了代理对象,知道了sql语句。

proxy
select * from student where name = #{name}
useDao·····

总结

ImportBeanDefinitionRegistrarFactory的使用有了进一步的了解。

你可能感兴趣的:(spring,mybatis)