Spring框架知识简介

来源:互联网 发布:php广告管理系统源码 编辑:程序博客网 时间:2024/05/17 15:35
一. Spring有配置文件,一般命名为applicationContext.xml.spring加载配置文件的原理:spring是一个容器,是一个大管家,它的配置文件通过容器来加载。容器有ApplicationContext和BeanFactory.
ApplicationContext和Beanactory的异同:
相同点:
两者加载配置文件后都是通过getBean()方法获取对象
不同点:
(1)ApplicationContext有三种加载配置文件的方式:
a、配置文件在类路径下
AppliactionContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
b、配置文件在本地磁盘上:
ApplicationContext applicationContext = new
FileSystemXmlApplicationContext("d:applicationContext.xml");
c、配置文件在项目根路径下:
ApplicaionContext applicationContext = new
FileSystemXmlApplicationContext("applicationContext.xml");

(2)、BeanFactory是ApplicationContext的父类,它的实现类是XmlBeanFactory,spring的配置文件以资源Resource的形式出现在XmlBeanFactory的构造函数中,Resource是一个接口,它的实现类是ClassPathResource和FileSystemResource
BeanFactory beanFactory = new XmlBeanFactory(new
ClassPathResource("applicationContext.xml"));
BeanFactory beanFactory = new XmlBeanFactory(new
FileSystemResource("applicationContext.xml"));
根据源代码详解:
一 Spring中配置文件的解析
XmlBeanFactory继承自DefaultListableBeanFactory,XmlBeanFactory与DefaultListableBeanFactory最主要的区别是XmlBeanFactory有自定义的XML读取器XmlBeanDefinitionReader,XmlBeanFactory是DefaultListableBeanFactory类的扩展,主要用于从XML文档中读取BeanDefinition,对于注册和获取bean都使用父类的方法,而它与父类唯独不同的是增加了XmlBeanDefinitionReader的reader()属性,主要通过该方法对资源文件进行读取和注册,实际上最主要的方法是loadBeanDefinitions(resource);由于Spring的配置文件以资源Resource的形式出现在XmlBeanFactory的构造函数中,Resource是一个接口,它的实现类有ClassPathResource和FileSystemResource,通过ClassPathResource加载资源文件的实现方式是调用class或classLoader的底层方法this.clazz.getResourceAsStream(),通过FileSystemResource加载资源文件的实现方式是直接使用FileInputStream();就以ClassPathResource加载资源文件为例说明一下:ClassPathResource加载资源文件整个逻辑是,当进入到XmlBeanDefinitionReader后先使用EncodResource对资源文件进行封装,然后通过InputStream或去输入流封装成InputSource,最后执行最重要doLoadBeanDefinitions(InputSource,encodeResource.getResource()),其中EncodResource主要是对资源文件进行编码设定,doLoadBeanDefinitions开始真正处理XML文件,根据以上所述对BeanFactory beanFactory = new XmlBeanFactory(new
ClassPathResource("applicationContext.xml"));的总结就是XmlBeanFactory中的参数实际上是一个Resource,我们通过Resource的实现类ClassPathResource(当然还有其他的实现类)对资源文件进行封装,封装完之后资源文件的读取就全权交给XmlBeanDefinitionReader来处理,封装主要是通过调用class或classLoader的底层方法,而读取时最主要的方法是loadBeanDefinitions(resource),在执行loadBeanDefinitions(resource)方法之前先使用EncodResource岁资源文件进行编码设置,然后通过InputStream获取输入流InputSource,最终执行真正的读取配置文件的方法doLoadBeanDefinitions(InputSource,encodeResource.getResource()),在该方法中主要做了三件事:1.获取XML文件的验证模式,2.加载XML文件(通过SAX解析),通过documentLoader.loadDocument()得到对应的Document(DocumentLoader实际上是一个接口,真正调用的是DefultDocumentLoader) 3.根据返回的Document注册bean信息
(1)常用的验证模式有两种XSD和DTD,验证之后如果包含DOCTYPE就是DTD,否则就是XSD
(2)SAX解析XML文件的步骤:
a.创建DocumentBuilderFactory,
b.创建DocumentBuilder
c.解析inputStream,返回Document对象
(3)注册Bean
a.先解析profile,profile的用法是:
在开发环境中<param-name>Spring.profiles.active</param-name>
<param-value>dev</param-value>
用Spring解析之后就会变成<beans profile="dev">
b.执行doRegisterBeanDefinitions方法进行解析,XML文件中bean的声明采用不同的方式,解析方
式也不一样,咱们可以通过node.getNamespaceURI()判断bean的声明方式
Spring中默认标签的解析,以bean标签的解析为例:
(1)bean标签的解析在processBeanDefinition()方法中进行,先使用
parseBeanDefinitionElement(ele)对bean标签解析,解析完后返回BeanDefinitionHolder实例,
该实例中包含bean的id,name,class,alias属性
(2)如果bean标签的子节点中含有自定义的属性,再在decorateBeanDefinitionIfRequired()中进行解
(3)所有节点解析完后,在registerBeanDefinition()方法中对BeanDefinitionHolder实例进行注册
(4)注册完后,通知相关的监听器,这个bean已经加载完了,使用fireComponentRegistered()方法

二 bean加载
使用FactoryBean时,bean的装配是在调用getBean(beanName)时才进行的,调用该方法后,底
层的执行过程是:
1、转换beanName
因为传入的beanName并不一定是我们想要的bean的id,也可能是FactoryBean的beanName
或者是bean的alias别名,所以在这里要进行解析
2、尝试从缓存中加载单例(只有scope=singleton时,bean才会加入到缓存中)
因为单例模式的bean在Spring中只创建一次,创建好之后会将bean放入到缓存中,所以我们会
先尝试从缓存中加载,如果缓存中加载不到再尝试从singletonFactories中加载。因为在创建单
例bean的时候存在依赖注入的情况,所以Spring加载bean的原则是不等bean创建好,就会将
bean的ObjectFactory加入到缓存中,一旦下一个bean创建时要用到上一个bean则直接使用
ObjectFactory.(Spring有三级缓存,第一级是singletonObject,指单例对象的缓存,第二级是
earlySingletonObject,指提前曝光的单例对象的缓存,第三级是singletonFactories,指单例对
象工厂的缓存,里面是ObjectFactory类型的。从缓存中获取bean时从第一级缓存开始,如果
没有的话,再逐级递增,但二级缓存和三级缓存是冲突的,如果从三级缓存中获取到bean,则要
把该singletonFactory从三级缓存中删除,放入到二级缓存。这个依赖关系是这样的,如果对象
A的field或者setter依赖了对象B,同时B的某个field或者setter依赖了A的实例对象”,A首先完成
了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,
发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初
始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯
定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓
存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过
ObjectFactory.getObject拿到A对象,B拿到A对象后顺利完成了初始化阶段1、2、3,完全
初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺
利完成自己的初始化阶段2、3,最终A也完成了初始化完成,进去了一级缓存singletonObjects)
3、bean的实例化
4、原型模式的依赖检查
5、检测parentBeanFactory
6、将存储XML配置文件的GernericBeanDefinition转换为RootBeanDefinition
7、寻找依赖
8、针对不同的scope进行bean的创建
9、类型转换
首先介绍一下FactoryBean
1、FactoryBean是一个接口,其中有三个方法
(1)Object getObject(),
(2)Class<T> getObjectType(),
(3)boolean isSingleton()//该方法用来判断bean是否是单例
当一个工厂bean实现了FactoryBean后,如果配置文件中的bean指向该工厂bean,则通过
getBean(beanName)获取bean时,返回的并不是工厂bean的实例,而是工厂bean代理的bean实
例,在这里使用了反射机制,当执行getBean()方法时,会执行FactoryBean的getObjectType()方
法,在该方法中定义了实例bean,如果要返回代理bean的实例,可以这样写getBean(&beanName)

(3)、它们是两个不同的容器,所以对于对象的装配时机不同:
a.ApplicationContext会在容器对象初始化时将对象一次性装配好,当使用对象的时候直接从内存中拿就好,这样执行效率高,但比较耗内存;而BeanFactory是在使用该对象的时候才会装配对象,即调用getBean()方法时,才会装配对象,占用内存较少,但执行效率较低

二. bean的装配:spring容器根据代码创建对象,并将对象传递给代码的过程叫做bean的装配
bean的装配方式:
(1)默认装配方式:
调用getBean()方法后,会调用bean的无参构造器创建空值的实例对象
(2)使用动态工厂bean装配
a.将动态工厂bean作为普通bean使
用(这个工厂bean是程序员自己写的,然后将这个类注册到配置文件中)
applicationContext.xml文件中配置factory,在测试类中通过获取该工厂创建bean实例,例如:
配置文件:
<bean id="factory" class="...ServiceFactory"/>
实例类:
public class ServiceFactory{
public IUserService getUserSerive(){
return new UserServiceImpl();
}
}
测试类:
public void test{
ApplicationContext context = new
ClassPathXmlApplicationContext("applicationContext.xml");
SerivceFactory factory = (SerivceFactory) context.getBean("factory");
IUserService service = factory.getUserService();
service.doSome();
}

b.使用spring的动态工厂bean(这个bean也是程序员自己写的,但是这样实现了测试类和工厂类
的解耦和)
配置文件:
<bean id="factory" class="..ServiceFactory"/>
<bean id="userService" factory-bean="factory" factory-method="getUserService"/>
实例类:
public class ServiceFactory{
public IUserService getUserSerive(){
return new UserServiceImpl();
}
测试类:
public void test{
ApplicationContxt context = new ClassPathXmlApplicationContext("applicationContext.xml");
IUserService service = (IUserService )context.getBean("userService");
service.doSome();
}

(3)静态工厂bean
静态工厂中由于方法是静态的,可以直接通过类来调用,所以不需要创建工厂bean
配置文件
<bean id="userSerivce" class="..ServiceFactory" factory-method="getUserService"/>
实例类:
public class ServiceFactory{
publicstaticIUserService getUserSerive(){
return new UserServiceImpl();
}
测试类:
public void test{
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
IUserService service = context.getBean("userService");
service.doSome();
}
三.容器中bean的作用域:
最常用的是singleton和prototype,singleton表示bean是单例的,只有一个实例,该bean是在容器被创建时就已经装配好了
prototype是多例的,当scope=prototype时,该bean是在代码使用它时才进行装配

四.bean后处理器
要使用bean后处理器,我们需要在程序中自己定义一个bean后处理器,该处理器实现BeanPostProcessor,实现它的两个方法postProcessBeforeInitialization(Object obj,String beanName)和postProcessAfterInitialization(Object obj,String beanName),这两个方法主要是在bean初始化完成之前和之后由容器自己调用的,因为在bean初始化之前和之后调用这两个方法,所以希望bean在初始化时需要完成的任务可以放在这里执行。并且,自己定义的这个bean后处理器需要在applicaitonContext.xml中配置

五.属性注解
(1)按类型注入域属性:使用@Autowired
@Component("mySchool")
public classSchool{
}
@Component("myStudent")
public class Student{
@Autowired
private String name;
@Autowired
private Integer age;
@Autowired
privateSchool school;
}
(2)按名称注入域属性:使用@Autowired和@Qualifier
@Component("mySchool")
public class School{
}
@Component("myStudent")
public class Student{
@Autowired
private String name;
@Autowired
private Integer age;
@Autowired
@Qualifier("mySchool")
private School school;
}
(3)域属性注解@Resource(该注解融合了前两种注入方式)
第一种方式:@Resource上不带任何参数时,相当于按类型注入
@Component("mySchool")
public classSchool{
}
@Component("myStudent")
public class Student{
@Autowired
private String name;
@Autowired
private Integer age;
@Resource
privateSchool school;
}
第二种方式:@Resource上带参数时,相当于按名称注入
@Component("mySchool")
public class School{
}
@Component("myStudent")
public class Student{
@Autowired
private String name;
@Autowired
private Integer age;
@Resource(name="mySchool")
private School school;
}

六.注解与XML同用
(1)XML的优先级要高于注解,是因为要修改某个bean时,只需要修改配置文件,重启服务器即可,但是此时bean要有setter方法和构造器;使用注解,修改bean后,还需要重新编译代码。

七.Spring与DAO
1.使用JDBC
(1)配置数据源
Spring中数据源有三种:
a.默认的DriverManagerDataSource
b.C3P0数据源
c.DBCP数据源
(2)读取配置文件获取数据库连接信息
(3)配置JDBC模板(使用JDBC模板,dao的实现类需要继承JdbcDaoSupport类)
(4)在JDBC中,增,删,改都是通过update()方法实现的,但查询不同,JDBC模板的查询结果都是以对象的形式返回,这个对象可以是简单对象(Integer,String,List<String>等),也可以是自定义对象(User);
对于简单对象查询,可以使用方法queryForObject(查询单个对象)和queryForList(结果是集合), 格式:
public T queryForObject(String sql,Class<T> type,Object... args)
public List<T> queryForList(String sql,Class<T> type,Object... args)
参数说明:sql,返回类型,参数
对于自定义对象查询,可以使用方法queryForObject(查询单个对象)和query(结果是集合),格式:
public T queryForObject(String sql,RowMapper<T> m,Object... args)
public List<T> query(String sql,RowMapper<T> m,Object... args)
参数说明:RowMapper是一个接口,需要自定义一个类实现mapRow(ResultSet rs,int rowNum)方法,rowNum表示当前行的行号,ResultSet表示当前行的结果集,例如:
private class UserMapper implments RowMapper<User>{
@Override
public User mapRow(ResultSet rs,int rowNum){
User user = new User();
user.setId(rs.getId);
user.setName(rs.getName);
return user;
}
}
public List<User> getUser(){
String sql = "select * from user";
this.getJdbcTemplate().query(sql,new UserowMapper());
}
或者带一个参数
public List<User> getUserById(int id){
String sql = "select * from user where id=?";
this.getJdbcTemplate().query(sql,new UserowMapper(),id);
}
注:jdbcTemplate模板是多例的,系统会为每一个使用jdbcTemplate模板的方法创建一个jdbcTemplate实例

2.spring事物的隔离级别:
读未提交,都已提交,可重复读,串行化;mysql默认的是可重复读,oracle默认的是读已提交
事物注解@Transactional的作用:
@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义
在改注解上可以添加事物的一些属性:隔离级别,事物传播的行为,发生异常时的回滚等等


原创粉丝点击