1 Spring Bean的生命周期(概念重点)
对象的生命周期:对象从生到死的过程。
Spring工厂中Bean的生命周期并不像想象的那么简单,Spring对工厂中的Bean的生命周期进行了细致的划分,并允许开发者通过编码或配置的方式定制Bean在生命周期的各个阶段的操作。
- 初始化阶段:在对象创建后,进行一些初始化操作,比如对属性值的检查
- 销毁阶段:在回收对象之前,执行一些销毁操作,比如资源的释放
1.1 初始化阶段
在Spring工厂创建对象并为属性赋值后就进入到了Bean的初始化阶段,Spring会自动调用Bean的初始化方法。在初始化方法中可以做一些初始化操作,比如对刚创建的对象进行检查操作。而初始化方法可以由开发者实现接口或者配置的方式定义。
实现InitializingBean接口
public class XxxServiceImpl implements XxxService, InitializingBean {
private XxxDao xxxDao;
public XxxServiceImpl() {
System.out.println("XxxServiceImpl()");
}
public XxxDao getXxxDao() {
return xxxDao;
}
public void setXxxDao(XxxDao xxxDao) {
System.out.println("XxxServiceImpl.setXxxDao");
this.xxxDao = xxxDao;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("XxxServiceImpl.afterPropertiesSet");
if(xxxDao == null){
throw new RuntimeException("xxxDao must not be null");
}
}
...
}
通过Bean标签的init-method属性
public class XxxServiceImpl implements XxxService, InitializingBean {
private XxxDao xxxDao;
public XxxServiceImpl() {
System.out.println("XxxServiceImpl()");
}
public XxxDao getXxxDao() {
return xxxDao;
}
public void setXxxDao(XxxDao xxxDao) {
System.out.println("XxxServiceImpl.setXxxDao");
this.xxxDao = xxxDao;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("XxxServiceImpl.afterPropertiesSet");
if(xxxDao == null){
throw new RuntimeException("xxxDao must not be null");
}
}
private void initMethod(){
System.out.println("XxxServiceImpl.init");
}
...
}
<bean id="xxxDao" class="com.bz.dao.impl.XxxDaoImpl"/>
<bean id="xxxService" class="cbzl" init-method="initMethod">
<property name="xxxDao" ref="xxxDao"/>
</bean>
InitializingBean接口和init-method属性的应用场景的区别:
接口方式:开发简单,一次实现,该类型的所有Bean配置都自动生效。
init-method属性:对于旧代码而言无需修改,即可完成对旧代码的配置。
实战中,二者一般不会同时出现,如果同时出现执行顺序:先接口中的方法,后init-method属性配置的方法。
1.2 销毁阶段
在关闭Spring工厂时,Spring会销毁其创建的对象,此时就进入到了Bean的销毁阶段。在此阶段,Spring会自动调用Bean的销毁方法,在对象销毁前执行一些操作,比如释放资源。
实现DisposableBean接口
public class XxxServiceImpl implements XxxService, InitializingBean, DisposableBean{
...
@Override
public void destroy() throws Exception {
System.out.println("XxxServiceImpl.destroy");
}
}
@Test
public void testInitializingBean(){
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
ctx.close();
}
通过Bean标签的destroy-method属性
public class XxxServiceImpl implements XxxService, InitializingBean, DisposableBean{
...
@Override
public void destroy() throws Exception {
System.out.println("XxxServiceImpl.destroy");
}
public void destroyMethod(){
System.out.println("XxxServiceImpl.destroyMethod");
}
}
<bean id="xxxService" class="cbzl" init-method="initMethod" destroy-method="destroyMethod">
<property name="xxxDao" ref="xxxDao"/>
</bean>
实战中,二者一般不会同时出现,如果同时出现执行顺序:先接口中的方法,后destroy-method属性配置的方法。
2 BeanPostProcessor(后置Bean处理)
BeanPostProcessor可以对Spring工厂中所有的Bean对象实例化后,对Bean对象再次加工处理。
编码
User.java
public class User implements Serializable, InitializingBean {
private Integer userId;
private String username;
private String password;
public User() {
}
public User(Integer userId, String username, String password) {
this.userId = userId;
this.username = username;
this.password = password;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("User.afterPropertiesSet");
}
public void initMethod(){
System.out.println("User.initMethod");
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
MyBeanPostProcessor.java
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
//在初始化阶段前调用
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("MyBeanPostProcessor.postProcessBeforeInitialization");
if (bean.getClass() == User.class) {
User user = (User)bean;
user.setPassword("654321");
}
return bean;
}
@Override
//在初始化阶段后调用
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("MyBeanPostProcessor.postProcessAfterInitialization");
return bean;
}
}
配置
<bean id="u" class="com.bz.entity.User" init-method="initMethod">
<property name="userId" value="1"/>
<property name="username" value="xiaohei"/>
<property name="password" value="123456"/>
</bean>
<bean class="com.bz.factory.MyBeanPostProcessor"/>
BeanPostProcessor可以对Spring中配置的所有Bean进行统一处理,并且Spring中可以配置多个BeanPostProcessor。
3 Spring AOP的实现原理
AOP:面向切面编程,灵活的动态的将额外功能添加到现有的原始对象上。
3.1 Spring AOP的实现方式
Spring AOP采用动态代理,将额外功能添加到原始对象中。
Spring的动态代理底层:JDK动态代理(默认)+CGLib动态代理。
3.2 JDK动态代理
JDK动态代理:面向接口编程
JDK动态代理要求原始类型必须实现的有接口。
@Test
public void testJDKProxy(){
// 创建原始对象
StudentService studentService = new StudentServiceImpl();
// 定义额外功能
java.lang.reflect.InvocationHandler handler = new java.lang.reflect.InvocationHandler(){
/*
proxy:生成的代理对象
method: 原始方法
args: 调用原始方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//定义额外功能
System.out.println("前置通知");
Object result = null;
try {
//调用原始对象的方法
result = method.invoke(studentService, args);
System.out.println("后置通知");
}catch(Exception e){
System.out.println("异常通知");
throw e;
}finally {
System.out.println("最终通知");
}
return result;
}
};
// 创建代理对象
/*
classLoader:类加载器
interfaces: 原始类型实现的接口
handler: 额外功能
*/
StudentService proxy = (StudentService)Proxy.newProxyInstance(StudentService.class.getClassLoader(), studentService.getClass().getInterfaces(), handler);
proxy.showStudents();
System.out.println(proxy.getClass());
System.out.println(proxy.getClass().getSuperclass());
}
类加载器的解释:
JDK生成的动态代理类型,实现了原始类型的接口,但同时继承了Proxy父类,只支持为面向接口编程的原始类型生成代理。
3.3 CGLib动态代理
CGLib支持面向接口和面向继承的代理。
@Test
public void testCGLib(){
// 创建原始对象
BookServiceImpl bookService = new BookServiceImpl();
// 定义额外功能
org.springframework.cglib.proxy.InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//定义额外功能
System.out.println("前置通知");
Object result = null;
try {
//调用原始对象的方法
result = method.invoke(bookService, args);
System.out.println("后置通知");
}catch(Exception e){
System.out.println("异常通知");
throw e;
}finally {
System.out.println("最终通知");
}
return result;
}
};
// 生成代理对象
Enhancer enhancer = new Enhancer();
//设置代理类的夫类型
enhancer.setSuperclass(BookServiceImpl.class);
//设置额外功能
enhancer.setCallback(handler);
BookServiceImpl proxy = (BookServiceImpl)enhancer.create();
proxy.showBooks();
System.out.println(proxy.getClass());
System.out.println(proxy.getClass().getSuperclass());
}
Spring默认使用JDK动态代理,如何设置Spring使用CGLib?
<aop:config proxy-target-class="true">
<aop:pointcut id="servicePointcut" expression="execution(* com.bz.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut"/>
</aop:config>
3.4 BeanPostProcessor技术
Spring何时使用动态代理生成代理对象?
Spring使用BeanPorstProcessor后置处理Bean,具体讲:就是在Spring工厂创建对象之后,对这个对象再次加工处理的过程。
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("before");
System.out.println("bean = [" + bean + "], beanName = [" + beanName + "]");
if (bean.getClass() == User.class) {
User user = (User)bean;
user.setPassword("654321");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("after");
System.out.println("bean = [" + bean + "], beanName = [" + beanName + "]");
return bean;
}
}
Spring中可以配置多个BeanPostProcessor.在内置BeanPostProcessor中根据切点和切面为特定的原始对象生成代理对象。
4 Spring事务控制的实现原理
4.1 JDBC中事务控制
第1次获取连接后将连接保存到线程对象中,接下来的多次获取直接从线程对象中获取同1个连接。保证service和dao使用同1连接,从而保证事务控制。
4.2 Spring事务控制的原理
Spring保证控制事务的连接和mybatis中执行sql的连接是同1个。
TransactionManager: 获取连接,把连接保存到线程中。
MyBaits集成Spring后:sqlSession内部获取连接,优先从线程中获取。
保证了控制事务(Spring)的连接和执行SQL(MyBaits)的连接是同1个。