spring源码解读(一)

    科技2022-07-16  114

    bean注入的方式

    1:xml

    <bean id = "" class = ""/>

    2:@Bean     config配置文件

    3:@ComponentScan   @Controller   @Service   @Component......

    4:@import

     

    上述四种方式,导入bean会被spring识别成BeanDefinition对象,不是单例对象

    一般出现NoSuchBeanDefinitionException异常,代表该bean没有被spring管理

    .class===========================>BeanDefinition对象:bean定义解析环节,然后将beanDefinition对象放到一个BeanDefinitionMap<beanName,BeanDefinition>中保存

    BeanFactoryPostProcessor是用来修改beanDefinition对象的,子接口:BeanDefinitionRegistryPostProcessor的实现类来进行解析,相当于设计小组,而BeanFactoryPostProcessor的实现类来修改ben定义,相当于审核小组

    BeanDefinitionRegistryPostProcessor的接口实现类有三个执行级别PriorityOrdered、Ordered、普通三个接口,优先级依次降低,优先级高的越先执行,spring帮助我们实现了优先级最高的,它的实现类是ConfigurationClassPostProcessor

    BeanPostProcessor是用来修改bean定义(beanInstance)的

    Bean定义===>BeanFactoryPostProcessor=====>getBean()========>BeanPostProcessor=====>单例对象

    我们来看下下面案例

    用Component注解修饰接口为什么会出现异常?(NoSuchBeanDefinitionException)

    分析:

    我们先来看前面的图解就知道问题可能会出现在@Component注解扫描这块,我们来分析ClassBeanPathDefinitionScanner(类路径bean定义扫描器)源码

    再去看它的父类ClassPathScanningCandidateComponentProvider(类路径候选扫描组件提供者),看以下方法

    以上代码表示不能是抽象类也不能是接口,如果我们把以上代码做出如下注释里面的修改,那这个注解就可以扫描接口了

    修改后,我们继续运行下代码,发现又会出现以下错误

    很明显,报错的信息就是接口不能实例化

    spring底层是通过反射来实例化对象的

    Class clzz=Class.forName("xxx(路径)");

    因为是接口所以才会报以上的错误,路径就是bean定义的一个属性,是可以直接取到的,类似以下:

    class AccountMapperDB{

            beanClassName="xxxxx";

    }

    所以我们需要修改这个beanClassName这个属性,使他是一个代理对象的路径而不是接口的路径

    想要生成代理对象,可以实现FactoryBean<?>接口,实际返回的对象就是getObject方法返回的对象,并不是carFactoryBean对象

    我们来完成之前的accountMapper @Compoent注解生效

    @Component(value = "accountMapper") public interface AccountMapper { String queryById(Integer id); } public class AccountFactoryBean implements FactoryBean { @Override public Object getObject() throws Exception { return Proxy.newProxyInstance(AccountMapper.class.getClassLoader(),new Class[]{AccountMapper.class},new AccountMapperProxy()); } @Override public Class<?> getObjectType() { return null; } } class AccountMapperProxy implements InvocationHandler{ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Class<?> returnType = method.getReturnType(); String o = (String) returnType.newInstance(); o="select * from test"; return o; } }

     

    @Component public class AccountMapperProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { GenericBeanDefinition accountMapper = (GenericBeanDefinition) beanFactory.getBeanDefinition("accountMapper"); accountMapper.setBeanClass(AcountMapperFactoryBean.class); } } public class MainTest { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AccountConfig.class); AccountMapper accountMapper = (AccountMapper) applicationContext.getBean("accountMapper"); accountMapper.queryById(11); } }

    但这种方式修改上述spring源码扫描注解的地方,而且每加一个mapper都需要重写许多代码,所以会显得非常臃肿,如何优化这一点,我们在下一篇会详细说明。。。

    Processed: 0.009, SQL: 8