×

beandefinition

beandefinition(如何手动创建Spring Bean Definition File)

admin admin 发表于2023-03-13 18:58:17 浏览64 评论0

抢沙发发表评论

本文目录

如何手动创建Spring Bean Definition File


BeanFactory beanFactory = new ClassPathXmlApplicationContext(“chapter2/instantiatingBean.xml“); HelloApi bean1 = beanFactory.getBean(“bean1“, HelloApi.class); bean1.sayHello();写
,

spring中bean是怎么解析的


bean定义的解析通过XmlBeanDefinitionReader来完成,在解析前先做一些准备工作。
1、设置环境变量(Environment)用来匹配在bean配置文件可能出现的一些占位符。
2、设置资源定位器用来定位bean的xml定义文件。
3、设置xml实体处理器辅助xml的解析工作。

Spring加载配置文件(org.springframework.beans.factory.BeanDefinitionStoreException)


1、首先手动加载Spring配置文件有两个类,分别是ClassPathXmlApplicationFileSystemXmlApplicationContext;两个类的区别。

2、然后就是“classpath:” 是可以缺省的。如果是绝对路径,就需要加上 “file:” 前缀,注意的是不可缺省FileSystemXmlApplicationContext的。

3、最后就是通过 @PropertySource 注解,这时候就是实现配置文件加载用法示例:在java类文件中使用 PropertySource 即可。


spring 配置文件的bean自动注入失败的解决方法是什么


一个Spring注入问题,首先看一个普通Spring Bean,

public class Foo {
@Autowired
Bar bar;
public void doSomething(){
bar.doSomething();
}
}

Spring配置一:

《bean id=“bar“ class=“com.test.Bar“》《/bean》
《bean id=“foo“ class=“com.test.Foo“》《/bean》

单元测试:

@Test
public void test_doSomthing(){
ApplicationContext ctx = new ClassPathXmlApplicationContext(“applicationContext-test.xml“);
Foo foo = ctx.getBean(Foo.class);
foo.doSomething();
}

执行上述测试方法,报错

java.lang.NullPointerException
at com.test.Foo.doSomething(Foo.java:15)
at com.test.FooTest.test_doSomthing(FooTest.java:13)

即foo bean中的bar并未注入。

Spring配置二:

《context:component-scan base-package=“com.test“》《/context:component-scan》

当改成配置二后执行上述单元测试方法便能成功通过。经分析日志及查看源代码,发现使用配置二时供装载了6个bean,如下所示:

DEBUG org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:216)  Loaded 6 bean definitions from location pattern [applicationContext-test.xml]
DEBUG org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:530)  Bean factory for org.springframework.context.support.ClassPathXmlApplicationContext@3c4e80d3: org.springframework.beans.factory.support.DefaultListableBeanFactory@14cc51c8: defining beans [bar,foo,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor]; root of factory hierarchy

而使用配置一时只有两个bean,如下所示:

DEBUG org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:216)  Loaded 2 bean definitions from location pattern [applicationContext-test.xml]
DEBUG org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:530)  Bean factory for org.springframework.context.support.ClassPathXmlApplicationContext@45ebbb93: org.springframework.beans.factory.support.DefaultListableBeanFactory@18481697: defining beans [bar,foo]; root of factory hierarchy

配置二执行单元测试通过的原因似乎就在于多出的这几个bean。是不是只要有context:component-scan元素在自动就会有这几个bean的产生?验证此假设

在配置一中添加一个无实际意义的context:component-scan元素,如下所示:

《context:component-scan base-package=“com.nonexist“》《/context:component-scan》

这时执行单元测试能通过,同配置二一样也会装载6个bean。那么这6个bean中到底哪个对注入bar到Foo中起了作用呢?

经过断点调试发现是AutowiredAnnotationBeanPostProcessor bean起了作用,见输出日志:

2015-04-25 20:23:09 DEBUG org.springframework.beans.factory.annotation.InjectionMetadata.《init》(InjectionMetadata.java:60)  Found injected element on class [com.test.Foo]: AutowiredFieldElement for com.test.Bar com.test.Foo.bar
2015-04-25 20:23:09 DEBUG org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:85)  Processing injected method of bean ’foo’: AutowiredFieldElement for com.test.Bar com.test.Foo.bar
2015-04-25 20:23:09 DEBUG org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:245)  Returning cached instance of singleton bean ’bar’
2015-04-25 20:23:09 DEBUG org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.registerDependentBeans(AutowiredAnnotationBeanPostProcessor.java:424)  Autowiring by type from bean name ’foo’ to bean named ’bar’
2015-04-25 20:23:09 DEBUG org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)  Finished creating instance of bean ’foo’

那么直接在配置一种显式添加AutowiredAnnotationBeanPostProcessor bean呢?如下所示:

《bean id=“bar“ class=“com.tcl.account.service.test.Bar“》《/bean》
《bean id=“foo“ class=“com.tcl.account.service.test.Foo“》《/bean》
《bean id=“autowiredAnnotationBeanPostProcessor“ class=“org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor“》《/bean》

测试会不会通过?会通过。见日志:

DEBUG org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:216)  Loaded 3 bean definitions from location pattern [applicationContext-test.xml]
DEBUG org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:530)  Bean factory for org.springframework.context.support.ClassPathXmlApplicationContext@7767d3c1: org.springframework.beans.factory.support.DefaultListableBeanFactory@1924ed52: defining beans [bar,foo,autowiredAnnotationBeanPostProcessor]; root of factory hierarchy
DEBUG org.springframework.beans.factory.annotation.InjectionMetadata.《init》(InjectionMetadata.java:60)  Found injected element on class [com.test.Foo]: AutowiredFieldElement for com.test.Bar com.test.Foo.bar
DEBUG org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:85)  Processing injected method of bean ’foo’: AutowiredFieldElement for com.test.Bar com.test.Foo.bar
DEBUG org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:245)  Returning cached instance of singleton bean ’bar’
DEBUG org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.registerDependentBeans(AutowiredAnnotationBeanPostProcessor.java:424)  Autowiring by type from bean name ’foo’ to bean named ’bar’
DEBUG org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)  Finished creating instance of bean ’foo’

那么为什么在配置文件中添加了context:    componet-scan元素后就会自动添加那另外4个bean呢?经过断点调试发现Spring隐式装载的4个bean是在如下方法中加载的:

Set《BeanDefinitionHolder》  org.springframework.context.annotation.AnnotationConfigUtils.registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, Object source)

其调用链如下所示:

补充一:

若仍用配置一,但单元测试改成如下形式也可以测试通过。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { “classpath:/applicationContext-test.xml“ })
public class FooTest2 {
@Autowired
private Foo foo;
@Test
public void test_doSomthing(){
foo.doSomething();
}
}

当然一点都不意外,这种方式也会隐式加载那4个bean,见日志:

DEBUG org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:216)  Loaded 2 bean definitions from location pattern [classpath:/applicationContext-test.xml]
INFO  org.springframework.context.support.AbstractApplicationContext.prepareRefresh(AbstractApplicationContext.java:500)  Refreshing org.springframework.context.support.GenericApplicationContext@51f3336e: startup date [Sun Apr 26 17:27:35 CST 2015]; root of context hierarchy
DEBUG org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:530)  Bean factory for org.springframework.context.support.GenericApplicationContext@51f3336e: org.springframework.beans.factory.support.DefaultListableBeanFactory@4f9d1352: defining beans [bar,foo,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor]; root of factory hierarchy

补充二,若使用的是@Value,效果同@Autowired,即也需要隐式加载AutowiredAnnotationBeanPostProcessor bean。

public class Foo {
@Value(“${bar}“)
String bar;
public void doSomething(){
System.out.println(bar);
}
}

补充三:

若使用配置一,@PostConstruct标注的方法也不会被执行,但此时需要隐式加载的Spring bean是:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor

补充四:

在配置一中添加如下配置

《context:annotation-config/》

也会隐式加载那4个bean


springMVC + mybatis 环境,本地正常 服务器报错,求解!BeanDefinitionStoreException


C:\apache-tomcat-6.0.24\webapps\hljjj-wechat-webapp\WEB-INF\classes\spring\context-global.xml
这个文件贴出来
服务器是linux?

org.springframework.beans.factory.parsing.BeanDefinitionParsingException 这是什么错误啊,急求


你的applicationContext.xml里引用了applicationContext-framework.xml文件,异常信息是说因为applicationContext-framework.xml不存在所以无法打开,所以请先检查applicationContext-framework.xml文件是否存在。
另外你贴的applicationContext.xml的代码里classpath被写成了classpayh,不知是你提问时写错的还是你文件里就是这么写的,请检查下文件,如果有错也一并改正。

注解@component定义的bean怎么注入一个通过xml定义的bean


由于项目的需要spring的业务相关的bean不是写在xml文件中,因为项目是一个模块一个模块提交的,提交的时候不想修改xml文件,因此就用到了spring的注解Service。
例如:
Java代码
1 @Service(“TestService“)
2 public class TestService {
3 }
这等同于:
1 《bean id=“TestService“ class=“TestService“/》
spring会在classpath里面扫描标记有TestService等标签的类,扫描组件的配置如下:
Xml代码
1 《!-- sdp-service主要是注入服务类 --》
2
3 《context:component-scanbase-package=“org.sdp“/》
加上以上的配置后,spring会自动的扫描org.sdp文件下的标记有注释的类。
以上的配置看似很“完美”,其实如果项目稍微大时就会出现问题,大家都知道spring的bean的id必须唯一,如果两个人同事写代码就有可以造成写同样的bean名称。
解决这个问题的一个思路是把bean的名称修改为 类的全路径,例如org.sdp.A 和com.bey.A 。
只要修改spring默认的bean命名策略就可以了。
AnnotationBeanNameGenerator是bean的默认命名策略,他实现了BeanNameGenerator接口。在Service里面,如果不写bean的名称,那么默认的名曾是类名,但是第一个字母是小写的。
例如:
Html代码
1 com.xyz.FooServiceImpl -》 fooServiceImpl
观察spring的源代码发现,buildDefaultBeanName方法首先了bean名称小写的作用。
1 protected String buildDefaultBeanName(BeanDefinition definition) {
2 String shortClassName = ClassUtils.getShortName(definition.getBeanClassName());
3 return Introspector.decapitalize(shortClassName);
4
5 }
因此,可以写一个类继承自AnnotationBeanNameGenerator,重写buildDefaultBeanName方法。
Java代码
1 public class SdpAnnotationBeanNameGenerator extends AnnotationBeanNameGenerator {
2 @Override
3 protected String buildDefaultBeanName(BeanDefinitiondefinition) {
4 return definition.getBeanClassName();
5 }
6 }
我的改下代码:
1 @Override
2 protected String buildDefaultBeanName(BeanDefinition definition) {
3 String className =definition.getBeanClassName();
4
5 className=className.substring(className.lastIndexOf(“.“)+1);
6 if(className.toLowerCase().endsWith(“impl“)){
7 className=className.substring(0, className.length()-4);
8 }
9 if((className.toLowerCase().endsWith(“service“)||className.toLowerCase().endsWith(“dao“))==false){
10 return super.buildDefaultBeanName(definition);
11 }
12
13
14 className=className.substring(0,1).toLowerCase() + className.substring(1);
15 return className;
16 }
在扫描配置中需要添加自己的命名策略类:
1 《!-- sdp-service主要是注入服务类 --》
2 《context:component-scan base-package=“org.sdp“ name-generator=“org.sdp.spring.SdpAnnotationBeanNameGenerator“ /》
通过以上配置,业务相关的bean不用写bean的名称了,ApplicationContext.getBean(“类的全路径“)就可以得到类的实例了。
如果是spring2.5则就结束了,但是spring3.0为完美提供了getBean(name,requiredType);使用了泛型,因此只要传入一个业务类的Class,getBean就返回此类的实例,而不用在
强制转换类型了。
1 public static 《T》T getService(String classFullName) throws ClassNotFoundException,BeansException{
2 Class《T》 requiredType=(Class《T》)Class.forName(classFullName);
3 return SdpContext.getContext().getBean(classFullName,requiredType); }
强制转换类型代码:
Java代码
1 TestService testService2=(TestService ) SdpContext.getContext().getBean(“TestService “);
2 System.out.println(testService2);
改进后的代码:
Java代码
1 @Service
2 public class TestService { }
1 TestService testService=SdpContext.getService(“org.sdp.context.TestService“);
2 System.out.println(testService);

spring 用什么数据结构来映射bean的


spring会解析XML把《bean》《/bean》解析成每一个BeanDefinition ,这个BeanDefinition 为spring内部的数据结构,最后使用HashMap注册起来被IOC容器使用,IOC容器的初始化就是BeanDefinition 的单位,载入,注册。。这三个过程,具体实现可以去看spring的源代码

基于接口的动态代理是如何实现的

什么叫动态代理

可以在运行期动态创建某个接口的实例;创建的这个代理实例可以帮我们完成很多事情,常见的比如:RPC框架中通过代理类帮助我们发送请求到远程服务器端,Mybatis中通过代理类帮助我们映射到xml中的statement,Spring中通过代理类实现事务的控制,日志的打印等等;

如何实现

1.基于 JDK 实现动态代理,通过jdk提供的工具方法

Proxy.newProxyInstance

动态构建全新的代理类;

2.基于CGlib 动态代理模式基于继承被代理类生成代理子类,不用实现接口;

3.基于 Aspectj 实现动态代理,在程序编译的时候 插入动态代理的字节码,不会生成全新的Class;

源码分析

下面以Mybatis为例子,分析一下XXXMapper是如何通过接口调用xml中的statement的,下面看一个常见的实例:

public class BlogMain {

public static void main(String args) throws IOException {

String resource = “mybatis-config-sourceCode.xml“;

InputStream inputStream = Resources.getResourceAsStream(resource);

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

SqlSession session = sqlSessionFactory.openSession();

try {

BlogMapper mapper = session.getMapper(BlogMapper.class);

// 常规方法

System.out.println(mapper.selectBlog(101));

} finally {

session.close();

}

}

}

BlogMapper是一个接口类,我们通过

session.getMapper

的时候获取的就是一个动态代理类,由此代理类去映射到xml中的statement;

如上代码中使用openSession创建的一个DefaultSqlSession类,此类中包含了执行了sql的增删改查等操作,另外还包含了getMapper方法:

此处的Configuration是关键,也是Mybatis的一个核心类,可以先简单理解为就是我们的配置文件mybatis-config.xml的一个映射类;继续往下走:

这里引出了MapperRegistry,所有的Mapper都在此类中注册,通过key-value的形式存放,key对应xx.xx.xxMapper,而value存放的是Mapper的代理类,具体如类MapperRegistry代码所示:

可以看到每次getMapper的时候其实都是去knownMappers获取一个MapperProxyFactory类,至于是何时往knownMappers中添加数据的,是在解析mybatis-config.xml配置文件的时候,解析到mappers标签的时候,如下所示:

继续解析里面的BlogMapper.xml,会把BlogMapper.xml中的namespace作为key,如下所示:

namespace是必填的,此值作为MapperRegistry中的knownMappers的key,而value就是此Mapper类的一个代理工厂类MapperProxyFactory,每次调用getMapper的时候都会newInstance一个实例,代码如下:

可以发现通过jdk自带的代理类Proxy.newProxyInstance(...)创建了一个代理类,设置MapperProxy作为InvocationHandler,在实例化MapperProxy时同时传入了一个methodCache对象,此对象是一个Map,存放的就是每个Mapper里面的方法,这里定义为MapperMethod;至此我们了解了getMapper的大致流程,下面继续看执行方法;

当我们执行上面的mapper.selectBlog(101)时会自动触发MapperProxy的invoke方法,此方法会帮我们映射到xml中的statement,然后执行里面配置的sql。

beandefinitionstoreexception什么异常


spring配置有错 问题一:你的jdbc文件加到Spring配置文件了吗 问题二:你的spring datasource配置的value是错误的,如果采用jdbc配置文件的东西,应使用{key} 而不是你展示的{value}