Bean的生命过程可以借鉴Servlet的生命过程,了解其生命过程对于不管是思想还是以后的使用都很有帮助;
Bean可以通过两种方式进行加载,分别是使用BeanFactory 和 applicationContext, 下边就这两种方式进行Bean的声明周期总结:
applicationContext:
先用一种生命周期流程图来概括;
1:Bean的建立:
容器寻找Bean的定义信息并将其实例化,也就是new一个对象,。
2:属性注入:
使用依赖注入,Spring按照Bean定义信息配置Bean所有属性,相当于调用set方法进行属性set操作
3. 如果这个Bean实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的是Spring配置文件中Bean的ID
4. 如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(),传递的是Spring工厂本身(可以用这个方法获取到其他Bean)
5. 如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文,该方式同样可以实现步骤4,但比4更好,以为ApplicationContext是BeanFactory的子接口,有更多的实现方法
6. 如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用After方法,也可用于内存或缓存技术
7.如果设置了initializingBean接口,则会调用实现的afterPropertiesSet()方法;
8. 如果这个Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法【相当于定制方法】
9. 如果这个Bean关联了BeanPostProcessor接口,将会调用postAfterInitialization(Object obj, String s)方法
注意:以上工作完成以后就可以用这个Bean了,那这个Bean是一个single的,所以一般情况下我们调用同一个ID的Bean会是在内容地址相同的实例
10. 容器关闭,当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean接口,会调用其实现的destroy方法,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法
BeanFactory:
下边对BeanFactory的生命周期过程进行分析,BeanFactory的生命周期相对于ApplicationContext来说相对简化了一些;
下面用图进行概括:
下面以BeanFactory为例,说明一个Bean的生命周期活动
如果使用ApplicationContext来维护一个Bean的生命周期,则基本上与上边的流程相同,只不过在执行BeanNameAware的setBeanName()后,若有Bean类实现了org.springframework.context.ApplicationContextAware接口,则执行其setApplicationContext()方法,然后再进行BeanPostProcessors的processBeforeInitialization() 实际上,ApplicationContext除了向BeanFactory那样维护容器外,还提供了更加丰富的框架功能,如Bean的消息,事件处理机制等
结果展示:
①ApplicationContext 接口继承BeanFactory接口,Spring核心工厂是BeanFactory ,BeanFactory采取延迟加载,第一次getBean时才会初始化Bean, ApplicationContext是会在加载配置文件时初始化Bean。
②ApplicationContext是对BeanFactory扩展,它可以进行国际化处理、事件传递和bean自动装配以及各种不同应用层的Context实现
开发中基本都在使用ApplicationContext, web项目使用WebApplicationContext ,很少用到BeanFactory
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
IHelloService helloService = (IHelloService) beanFactory.getBean("helloService");
helloService.sayHello();
1)使用类构造器实例化(默认无参数)
<bean id="bean1" class="cn.itcast.spring.b_instance.Bean1"></bean>
2)使用静态工厂方法实例化(简单工厂模式)
//下面这段配置的含义:调用Bean2Factory的getBean2方法得到bean2
<bean id="bean2" class="cn.itcast.spring.b_instance.Bean2Factory" factory-method="getBean2"></bean>
3)使用实例工厂方法实例化(工厂方法模式)
//先创建工厂实例bean3Facory,再通过工厂实例创建目标bean实例
<bean id="bean3Factory" class="cn.itcast.spring.b_instance.Bean3Factory"></bean>
<bean id="bean3" factory-bean="bean3Factory" factory-method="getBean3"></bean>
(1)bean定义
在配置文件里面用<bean></bean>来进行定义。
(2)bean初始化
有两种方式初始化:
A.在配置文件中通过指定init-method属性来完成
B.实现org.springframwork.beans.factory.InitializingBean接口
(3)bean调用
有三种方式可以得到bean实例,并进行调用
(4)bean销毁
销毁有两种方式
A.使用配置文件指定的destroy-method属性
B.实现org.springframwork.bean.factory.DisposeableBean接口
##作用域
singleton
当一个bean的作用域为singleton, 那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。
prototype
Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean() 方法)时都会创建一个新的bean实例。根据经验,对所有有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用 singleton作用域
request
在一次HTTP请求中,一个bean定义对应一个实例;即每次HTTP请求将会有各自的bean实例, 它们依据某个bean定义创建而成。该作用 域仅在基于web的Spring ApplicationContext情形下有效。
session
在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
global session
在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于 web的Spring ApplicationContext情形下有效。
spring支持构造器注入和setter方法注入
构造器注入,通过 <constructor-arg> 元素完成注入
setter方法注入, 通过<property> 元素完成注入【开发中常用方式】
BeanFactory:产生一个新的实例,可以实现单例模式
BeanWrapper:提供统一的get及set方法
ApplicationContext:提供框架的实现,包括BeanFactory的所有功能
使用”
com.mchange.v2.c3p0.ComboPooledDataSource
”数据源来配置数据库驱动。示例如下:
<!-- 配置数据库数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClassName}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- 初始化连接大小 -->
<property name="initialPoolSize" value="${initialSize}"/>
<!-- 最小连接池数量 -->
<property name="minPoolSize" value="${minActive}" />
<!-- 连接池最大数量 -->
<property name="maxPoolSize" value="${maxActive}"/>
<property name="autoCommitOnClose" value="true"/>
</bean>
ContextLoaderListener是一个ServletContextListener, 它在你的web应用启动的时候初始化。缺省情况下, 它会在WEB-INF/applicationContext.xml文件找Spring的配置。
你可以通过定义一个<context-param>元素名字为”contextConfigLocation”来改变Spring配置文件的 位置。示例如下:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/xyz.xml</param-value>
</context-param>
</listener-class>
</listener>
Spring使用ThreadLocal解决线程安全问题【博客有一节专门对于TreadLocal的分析】
我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。
在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
事务就是对一系列的数据库操作(比如插入多条数据)进行统一的提交或回滚操作,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作。这样可以防止出现脏数据,防止数据库数据出现问题。
开发中为了避免这种情况一般都会进行事务管理。Spring中也有自己的事务管理机制,一般是使用TransactionMananger进行管 理,可以通过Spring的注入来完成此功能。spring提供了几个关于事务处理的类:
TransactionDefinition //事务属性定义
TranscationStatus //代表了当前的事务,可以提交,回滚。
PlatformTransactionManager这个是spring提供的用于管理事务的基础接口,其下有一个实现的抽象类 AbstractPlatformTransactionManager,我们使用的事务管理类例如 DataSourceTransactionManager等都是这个类的子类。
一般事务定义步骤:
TransactionDefinition td =newTransactionDefinition();
TransactionStatus ts = transactionManager.getTransaction(td);
try{
//do sth
transactionManager.commit(ts);
}catch(Exception e){
transactionManager.rollback(ts);
}
spring提供的事务管理可以分为两类:编程式的和声明式的。
编程式的,比较灵活,但是代码量大,存在重复的代码比较多;
编程式主要使用transactionTemplate。省略了部分的提交,回滚,一系列的事务对象定义,需注入事务管理对象.【这里使用了模板模式】
void add(){
transactionTemplate.execute(newTransactionCallback(){
pulic Object doInTransaction(TransactionStatus ts){
//do sth
}
}
}
声明式:声明式的比编程式的更灵活。
使用TransactionProxyFactoryBean:PROPAGATION_REQUIRED PROPAGATION_REQUIRED PROPAGATION_REQUIRED,readOnly
围绕Poxy的动态代理 能够自动的提交和回滚事务
org.springframework.transaction.interceptor.TransactionProxyFactoryBean
PROPAGATION_REQUIRED–支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS–支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY–支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW–新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED–以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER–以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED–如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与 PROPAGATION_REQUIRED类似的操作。
1. 说出Spring的通知类型有哪些?
spring共提供了五种类型的通知:
通知类型 | 接口 | 描述 |
---|---|---|
Around 环绕通知 | org.aopalliance.intercept.MethodInterceptor | 拦截对目标方法调用 |
Before 前置通知 | org.springframework.aop.MethodBeforeAdvice | 在目标方法调用前调用 |
After 后置通知 | org.springframework.aop.AfterReturningAdvice | 在目标方法调用后调用 |
Throws 异常通知 | org.springframework.aop.ThrowsAdvice | 当目标方法抛出异常时调用 |
<bean id="myMethodBeforeAdvice" class="com.cdtax.aop.MyMethodBeforeAdvice"></bean>
<!-- 配置后置通知 -->
<bean id="myAfterReturningAdvice" class="com.cdtax.aop.MyAfterReturningAdvice"></bean>
<!-- 配置代理对象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口集 -->
<property name="proxyInterfaces">
<list>
<value>com.cdtax.aop.TestServiceInter</value>
<value>com.cdtax.aop.TestServiceInter2</value>
</list>
</property>
<!-- 把通知织入到代理对象 | 拦截器名集-->
<property name="interceptorNames">
<list>
<!-- 相当于把 MyMethodBeforeAdvice前置通知和代理对象关联起来,我们也可以把通知看成拦截器,struts2核心就是拦截器 -->
<value>myMethodBeforeAdvice</value>
<!-- 织入后置通知 -->
<value>myAfterReturningAdvice</value>
</list>
</property>
<!-- 配置被代理对象,可以指定 -->
<property name="target" ref="test1Service">
</property>
</bean>
</beans>
2. 谈谈目标对象实现接口与目标对象不实现接口有什么区别?
3. 请描述JDK动态代理和CGLI代理的区别?
jdk动态代理是目标类实现了接口,而不能针对类;
CGLI动态代理是目标类没有实现接口。主要是对指定的类生成一个子类,覆盖其中的方法。
4. 简述ProxyFactoryBean的作用是什么?
5. 叙述Spring中的自动代理的原理?
5. 写出创建代理对象需指定的三要素是什么?
(1)设定目标对象 (2)设定代理接口 (3)设定拦截器的名字
6. 写出代理的两种方式分别是什么?
7. 请简述:什么是AOP?简述AOP核心?
9. 请叙述AOP事务的含义?
1. 请简述Spring的工作机制?
2. 请回答你为什么用Spring的工作机制?
3. 请简述Spring是什么?
4. 简述spring的组成?
5.简述Spring容器提供了哪些功能?
6. 在Spring中,bean的注入有几种方式,各是什么?
7. 请简述:Spring bean的作用域?
8. 请叙述设值注入的优点?
9. 请叙述构造注入的优点?
10. 说出bean工厂创建bean的三种方式? 11. 请写出bean的生命周期的方法?
12. 请简述你对IOC的理解?
13. 请回答:IoC最大的好处是什么?
14. 简述IoC的类型?
15. Spring中依赖注入与传统编程之间的差别是什么?