Spring 源码学习 (-)

1、单例bean 、 单例模式、单例池之间的联系和底层原理
2、BeanFactory 于 Application之间的联系和底层架构设计
3、FactoryBean的作用和底层工作原理
4、spring 整合mybaits 的核心底层原理
5、@mapperScan注解的底层工作原理
6、MapperScannerConfigurer底层工作原理
7、ImportBeanDefintionRegistarar 和FactoryBean的经典应用实战
8、MapperFactory和sqlsessionFactoryBean的作用和底层源码分析

1、创建一个项目例子
在这里插入图片描述

mapper类

public interface UserMapper {
}

service类

@Component
public class UserService {

    @Autowired
    private UserMapper userMapper; // userMapper bean对象

    public void test(){
        System.out.println("打印"+userMapper);
    }
}

appliction类

@ComponentScan("com.spring") // 扫描包下面有 @Component、@service 等的bean
public class Application {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 容器
        applicationContext.register(Application.class); //  配置spring
        applicationContext.refresh(); // 刷新
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.test();
    }
}

创建了 application 、service 、mapper 类
启动main函数 报错
在这里插入图片描述
1、为什么会报错?
原因: userService 没有扫描到bean . 创建这个bean 报错 ,因为userservice 使用到了usermapper .,usermapper 没有注入spring 容器的bean .spring 构建bean 时,会自动给bean 的属性附值,这里报错的原因是附值过程中,出现了错误. spring -> bean的构造方法得到对象-对象属性需要附值-附值成功后,才是一个bean .而对象的属性值需要从spring 的容器中来.

@Autowired 先 byType 在byName 找到spring 容器的bean

@Mapper 在spring 中是用不到的,在 springboot 中才能用到.

bean一定是对象 ,但是对象不一定是bean
单例bean :在spring容器中,不只会存在一种bean类型的对象.会存在多个,多个之间名字是不一样的,同一个名字bean 对象是一样的.
单例模式: 获取单例bean 的模式
单例池 : 用来实现单例bean 的,得到同一个对象, 主要用map.

spring 容器的bean 是单例的,但是名字有可能不同,获取bean 先获取 类型,在根据名字来获取

2、如何解决报错?

@Autowired (required = false) 默认是true 必须获取 userMapper 的bean 但 改完
false 之后就不会报错,但是会报空指针异常,获取的userMapper 中的方法时.
但这不是解决问题的根本方法.
3、怎么得到userMapepr 的bean 对象?
usermapper 是个接口,如何获取这个对象. 两种方案: 1、实现userMapper 的接口
根据实现的接口获取一个对象,2、获取 userMapper 的代理对象. 这里主要用第二种方案.
UserMapper 的bean 对象不是有spring 容器产生的,它是有mybatis 产生的一个代理对象. 通过jdk 动态代理产生的, 附值给 userMapper
4、如何把 mybatis 的代理对象放入spring 容器中?
首先 需要将代理对象 成功一bean .
4.1 如何产生bean ?
1、声明式: 注解 @Transan、 xml、 @Component 、@Service 等等
2、编程式 : 代码逻辑 TranSanManager 、BeanDefinition 方法
在这里插入图片描述
4.2 FactoryBean
在这里插入图片描述

 上面的方式定义 接口类型的Bean 是实现不了,; 
 FactoryBean 特殊的Bean  会有两个对象
 1、当前定义的FactoryBean 对象  bean 的名字 &xxx 
 2、FactoryBean 接口中返回的对象 bean的名字 xxx
 实现 将mybatis 的代理对象 生成bean 加载 到spring 容器中来, 通过 mybatis 生成bean 也是通过jdk 动态代理生成的.
  public class UserFactoryBean implements FactoryBean {
     /**
     *  获得一个对象
     * @return
     * @throws Exception
     */
    @Override
    public Object getObject() throws Exception {
        // Mybatis userMapper的代理对象 也是通过jdk动态代理的
         Object instance= Proxy.newProxyInstance(UserFactoryBean.class.getClassLoader(), new Class[]{UserMapper.class}, new InvocationHandler() {
            // 代理逻辑
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method.getName()); // 代理对象的方法
                return null;  //  userMapper 调用时,会toString() 返回 null
            }
        });
         return instance;
    }
    /**
     *  获取对象的类型
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        return UserMapper.class;
    }
}

在次调用时,就不会报错了

@ComponentScan("com.spring") // 扫描包下面有 @Component、@service 等的bean
public class Application {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 容器
        applicationContext.register(Application.class); //  配置spring
        // 定义bean
        AbstractBeanDefinition beanDefinition =BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        beanDefinition.setBeanClass(UserFactoryBean.class);
        // 这里需要产生一个UserMapper的对象,但是userMapper 是一个接口,没有对象的默认构造方法,需要用到特殊的bean 动态代理
        applicationContext.registerBeanDefinition("userFactory",beanDefinition );
        applicationContext.refresh(); // 刷新
        System.out.println(applicationContext.getBean("userFactory")); // factory 返回bean 的名字
        System.out.println(applicationContext.getBean("&userFactory")); // factory bean 的名字
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.test();
    }
}

mybaits-spring jar包的作用 将mybatis的代理对象放到spring 容器中去 .
使用多个mapper 类动态代理到 Spring 容器中,上面的使用方法就不是很合理.上面一次只能代理一个类.
改造
1、UserService 获取两个bean

@Component
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private OrderMapper orderMapper;

    public void test(){
        System.out.println("打印"+userMapper+orderMapper);
    }
}

2、FactoryBean 改造

public class FactoryBean implements org.springframework.beans.factory.FactoryBean {


    private Class mapperClass; // 接受:接口 、java 类 ,附值什么接口类,就返回什么代理对象

    public FactoryBean(Class mapperClass) {
        this.mapperClass = mapperClass;
    }

    @Override
    public Object getObject() throws Exception {
        Object instance = Proxy.newProxyInstance(FactoryBean.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
            // 代理逻辑
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method.getName()); // 代理对象的方法
                return null;
            }
        });
        return instance;
    }
    /**
     * 获取对象的类型
     *
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        return mapperClass;
    }
}

3、appplication

@ComponentScan("com.spring") // 扫描包下面有 @Component、@service 等的bean
public class Application {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 容器
        applicationContext.register(Application.class); //  配置spring

        // 定义bean1
        AbstractBeanDefinition beanDefinition1 =BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();;
        beanDefinition1.setBeanClass(FactoryBean.class);
         // 利用bean 对象的构成方法 将需要代理的类附值进去
        beanDefinition1.getConstructorArgumentValues().addGenericArgumentValue(UserMapper.class);
        applicationContext.registerBeanDefinition("xx1",beanDefinition1);

        // 定义bean2
        AbstractBeanDefinition beanDefinition2 =BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();;
        beanDefinition2.setBeanClass(FactoryBean.class);
        // 利用bean 对象的构成方法 将需要代理的类附值进去
        beanDefinition2.getConstructorArgumentValues().addGenericArgumentValue(OrderMapper.class);
        applicationContext.registerBeanDefinition("xx2",beanDefinition2);
        applicationContext.refresh(); // 刷新
        System.out.println(applicationContext.getBean("xx1"));
        System.out.println(applicationContext.getBean("xx2"));
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.test();
    }
}

结论 改造 factoryBean 通过使用factoryBean 的构造方法,将需要代理的mybatis 对象依次传入,就可以得传入时的代理对象.

ImportBeanDefinitionRegistrar : 注册多个beanDefinition

改造
1、注册类

 /**
 *
 *  BeanDefinition注册
 */
public class SpringImportBeanDefinitionRegistarer implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        // 定义bean1
        AbstractBeanDefinition beanDefinition1 =BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();;
        beanDefinition1.setBeanClass(FactoryBean.class);
        // 利用bean 对象的构成方法产生一个bean
        beanDefinition1.getConstructorArgumentValues().addGenericArgumentValue(UserMapper.class);
        beanDefinitionRegistry.registerBeanDefinition("xx1",beanDefinition1);

        // 定义bean2
        AbstractBeanDefinition beanDefinition2 =BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();;
        beanDefinition2.setBeanClass(FactoryBean.class);
        // 利用bean 对象的构成方法产生一个bean
        beanDefinition2.getConstructorArgumentValues().addGenericArgumentValue(OrderMapper.class);
        beanDefinitionRegistry.registerBeanDefinition("xx2",beanDefinition2);


        // 定义bean3
        AbstractBeanDefinition beanDefinition3 =BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();;
        beanDefinition3.setBeanClass(FactoryBean.class);
        // 利用bean 对象的构成方法产生一个bean
        beanDefinition3.getConstructorArgumentValues().addGenericArgumentValue(MeberMapper.class);
        beanDefinitionRegistry.registerBeanDefinition("xx3",beanDefinition3);
    }
}

2、application 改造

@ComponentScan(“com.spring”) // 扫描包下面有 @Component
@service 等的bean
@Import(SpringImportBeanDefinitionRegistarer.class)
public class Application {

public static void main(String[] args) {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 容器
    applicationContext.register(Application.class); //  配置spring
    applicationContext.refresh(); // 刷新
    System.out.println(applicationContext.getBean("xx1"));
    System.out.println(applicationContext.getBean("xx2"));
    System.out.println(applicationContext.getBean("xx3"));

    UserService userService = applicationContext.getBean("userService", UserService.class);
    userService.test();
}

}
改造之后跟上面定义多个 BeanDefinition是一样的道理,通过将定义方法放入ImportBeanDefinitionRegistarer 中,然后Import 到Application中,代码看起更简洁.
改造 ImportBeanDefinitionRegistarer
spring 默认扫描 @Component注解的类 ,不会扫描接口,
这里需要定义一个类来扫描
继承 ClassPathBeanDefinitionScanner
扫描
1、扫描路径
2、重写扫描方法
1、 定义扫描器

/**
 * 扫描器 扫描得到的BeanDefinition 注册 BeanDefinitionRegistry
 */
public class SpringMapperScanner extends ClassPathBeanDefinitionScanner {
    /**
     * spring 创建扫描器 只关系类,不关心接口
     *
     * @param registry
     */
    public SpringMapperScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }


    /**
     * 重写方法
     * 判断是不是一个bean
     * 改为扫描接口
     *
     * @param beanDefinition
     * @return
     */
    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        //  return super.isCandidateComponent(beanDefinition); 默认判断类返回true 
        return beanDefinition.getMetadata().isInterface();//  判断是一个接口返回 true
    }

    /**
     *  重写 doScan 扫描到相关的类
     * @param basePackages
     * @return
     */
    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        //return super.doScan(basePackages);
        Set<BeanDefinitionHolder> beanDefinitionHolders=super.doScan(basePackages);
        // 此处逻辑将spring 扫描的bean 转为mybatis接口 通过代理对象 使用构造方法生成的bean
        // 将mybatis 的bean 转为Spring 需要的bean 
         for(BeanDefinitionHolder beanDefinitionHolder:beanDefinitionHolders){
             BeanDefinition beanDefinition =beanDefinitionHolder.getBeanDefinition();
             beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
             beanDefinition.setBeanClassName(FactoryBean.class.getName());
         }
        return beanDefinitionHolders;
    }
}

2、改造 注册BeanDifinition 方法
SpringImportBeanDefinitionRegistarer

/**
 *
 *  BeanDefinition注册
 */
public class SpringImportBeanDefinitionRegistarer implements ImportBeanDefinitionRegistrar {
    /**
     *
     * @param annotationMetadata
     * @param beanDefinitionRegistry 注册表
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {

         String path ="com.spring.mapper";
         SpringMapperScanner springMapperScanner=new SpringMapperScanner(beanDefinitionRegistry);
         //  增加一个过滤器这里先不管 ,返回的true 
         springMapperScanner.addIncludeFilter(new TypeFilter() {
            @Override
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                return true;
            }
        });


         int scan=springMapperScanner.scan(path); // 扫描路径  .scan 方法默认回去调用 doScan 方法
         System.out.println(scan);


    }
}

3、application 类

@ComponentScan("com.spring") // 扫描包下面有 @Component、@service 等的bean
@Import(SpringImportBeanDefinitionRegistarer.class)
public class Application {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 容器
        applicationContext.register(Application.class); //  配置spring

        applicationContext.refresh(); // 刷新
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.test();
    }
}

总结: 现在有了spring 的扫描器 、beandefinition 注册器、factoryBean 构造方法. 在定义多个mybatis 的mapper 接口类就不会报错. 会自动扫描 - 注册- 定义为spring bean, 在spring 的容器就能拿到相关的bean.

问题? 怎么拿到mybatis 的代理对象 ? 怎么动态扫描路径?
怎么拿到mybatis 代理对象,目前是jdk 代理对象?
1、改造factoryBean 类
拿到mybatis 的代理对象

  /**
 * 通用 factoryBean 类
 */
public class FactoryBean implements org.springframework.beans.factory.FactoryBean {


    private Class mapperClass; // 接受:接口 、java 类 ,附值什么接口类,就返回什么代理对象

    private SqlSession sqlSession; // mybatis 代理对象


    public FactoryBean(Class mapperClass) {
        this.mapperClass = mapperClass;
    }
    // set 注入 需要spring容器首先要一个SqlSession bean ,然而这个bean 来自于SqlSessionactory;
    @Autowired
    public void setSqlSession(SqlSessionFactory sqlSessionFactory) {
        // 将每一个mapper 加到配置中去
        sqlSessionFactory.getConfiguration().addMapper(mapperClass);
        this.sqlSession = sqlSessionFactory.openSession();
    }
    @Override
    public Object getObject() throws Exception {
//        Object instance = Proxy.newProxyInstance(FactoryBean.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
//            // 代理逻辑
//            @Override
//            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//                System.out.println(method.getName()); // 代理对象的方法
//                return null;
//            }
//        });
        //       return instance;
        return sqlSession.getMapper(mapperClass);

    }
    /**
     * 获取对象的类型
     *
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        return mapperClass;
    }
}

2、 改造 application 拿到mybatis 的SqlSessionactory

@ComponentScan("com.spring") // 扫描包下面有 @Component、@service 等的bean
@Import(SpringImportBeanDefinitionRegistarer.class)
public class Application {

    /**
     * 构建数据库配置bean  sqlSessionFactory
     *
     * @return
     * @throws IOException
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("xx.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        return sqlSessionFactory;
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 容器
        applicationContext.register(Application.class); //  配置spring
        applicationContext.refresh(); // 刷新
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.test();
    }
}

修改 factoryBean 取消 SqlSession set 方法的 @Autowired 注解

1、取消注解

 	**
 * 通用 factoryBean 类
 */
public class FactoryBean implements org.springframework.beans.factory.FactoryBean {


    private Class mapperClass; // 接受:接口 、java 类 ,附值什么接口类,就返回什么代理对象

    private SqlSession sqlSession; // mybatis 代理对象


    public FactoryBean(Class mapperClass) {
        this.mapperClass = mapperClass;
    }
    // set 注入 需要spring容器首先要一个SqlSession bean ,然而这个bean 来自于SqlSessionactory;
    //@Autowired
    public void setSqlSession(SqlSessionFactory sqlSessionFactory) {
        // 将每一个mapper 加到配置中去
        sqlSessionFactory.getConfiguration().addMapper(mapperClass);
        this.sqlSession = sqlSessionFactory.openSession();
    }

    @Override
    public Object getObject() throws Exception {
//        Object instance = Proxy.newProxyInstance(FactoryBean.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
//            // 代理逻辑
//            @Override
//            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//                System.out.println(method.getName()); // 代理对象的方法
//                return null;
//            }
//        });
        //       return instance;
        return sqlSession.getMapper(mapperClass);

    }

    /**
     * 获取对象的类型
     *
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        return mapperClass;
    }
}

2、修改 扫描器 中 beandefinition 的配置方法

/**
 * 扫描器 扫描得到的BeanDefinition 注册 BeanDefinitionRegistry
 */
public class SpringMapperScanner extends ClassPathBeanDefinitionScanner {
    /**
     * spring 创建扫描器 只关系类,不关心接口
     *
     * @param registry
     */
    public SpringMapperScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }


    /**
     * 重写方法
     * 判断是不是一个bean
     * 改为扫描接口
     *
     * @param beanDefinition
     * @return
     */
    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        //  return super.isCandidateComponent(beanDefinition); 默认判断类
        return beanDefinition.getMetadata().isInterface();//  判断是一个接口返回 true
    }

    /**
     *  重写 doScan 扫描到相关的类
     * @param basePackages
     * @return
     */
    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        //return super.doScan(basePackages);
        Set<BeanDefinitionHolder> beanDefinitionHolders=super.doScan(basePackages);
        // 此处逻辑将spring 扫描的bean 转为mybatis接口 通过代理对象 使用构造方法生成的bean
        // 将mybatis 的bean 转为Spring 需要的bean
         for(BeanDefinitionHolder beanDefinitionHolder:beanDefinitionHolders){
             //BeanDefinition beanDefinition =beanDefinitionHolder.getBeanDefinition();
             // 修改为 GenericBeanDefinition 取消 FactoryBean 中Autowire 注解 同时设置 AutowireMode
             GenericBeanDefinition beanDefinition= (GenericBeanDefinition) beanDefinitionHolder.getBeanDefinition();
             beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
             beanDefinition.setBeanClassName(FactoryBean.class.getName());
             beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
         }
        return beanDefinitionHolders;
    }
}

如果解决写死扫描的路径?
写一个注解
1、添加一个 SpringScan 注解
并将 Application Import 方法加到这个注解中来,Spring 启动中,会自动加载 这个注解中 SpringImportBeanDefinitionRegistarer这个类中的方法,我们在类中的方法就可以取到这个注解.

 /**
 *  注解
 *  扫描路径
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) // 写在类上面
@Import(SpringImportBeanDefinitionRegistarer.class)
public @interface SpringScan {
    String value();
}

2、 获得这个注解
改造 Beandefinition 获取扫描路径

/**
 *
 *  BeanDefinition注册
 */
public class SpringImportBeanDefinitionRegistarer implements ImportBeanDefinitionRegistrar {
    /**
     *
     * @param annotationMetadata
     * @param beanDefinitionRegistry 注册表
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {

        // 获得扫描路径 "com.spring.mapper";
        Map<String,Object> newannotationMetadata =annotationMetadata.getAnnotationAttributes(SpringScan.class.getName());
         String path = (String) newannotationMetadata.get("value");
          // 拿到扫描路径

         //String path ="com.spring.mapper";
         SpringMapperScanner springMapperScanner=new SpringMapperScanner(beanDefinitionRegistry);
         //  增加一个过滤器
         springMapperScanner.addIncludeFilter(new TypeFilter() {
            @Override
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                return true;
            }
        });
        int scan=springMapperScanner.scan(path); // 扫描路径  .scan 方法默认回去调用 doScan 方法
         System.out.println(scan);


    }
}

MapperScanConfiguration 扫描配置 代替 @MapperScan 和 @Configuration

spring的作用是代替 EJB 解偶.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值