传统Javaweb开发困惑及解决办法
开发遇到的问题:
①如图
- service层的实现需要获得dao层对象,每次都new,代码耦合度高
- 事务问题
- 用户行为信息的一个日志记录
- 解决思路:程序代码不要手动new,通过第三方获取需要的Bean对象。
IOC、DI和AOP思想的提出:
- IOC思想:Inversion of Control,控制反转,强调的是原来在程序中创建Bean的权利反转给第三方。
- DI思想:Dependency Injection,依赖注入,强调的Bean之间的关系,这种概念性第三方负责去设置。
- AOP思想:Aspect Oriented Programing ,面向切面编程,功能的横向抽取,主要实现方式就是Proxy。
框架概念的思想提出:
- 基本特点:基于基础技术之上,从众多业务抽取出通用解决方案;是个半成品,使用框架规定的语法开发提高效率;框架内部使用大量的设计模式、算法、底层代码操作技术,如反射、内省、xml解析、注解解析等;具备一定扩展性;
- 基础框架:如MyBtis、Spring、SpringMvc、Struct2、Hibernate等;
- 服务框架:特定领域的框架,一般还可以对外提供服务的框架,如MQ、ES、Nacos等;
SPring框架的诞生:
① 概述:
Spring是一个开源的Java开发应用框架,可以简化企业家应用开发。Spring解决了开发者在JavaEE开发中常遇到的问题,提供了强大IOC、AOP及Web MVC等功能。Spring的生态及其完善,不论哪个领域的解决方案都是依附在SpringFramework基础框架的。
[Spring官网]: www.spring.io “Spring官网”
②框架历史发展:
- Jsp默默扛下所有
- MVC+三层架构分工明确,但开发成本极高
- EJB重量级框架出现,走出一个困境,又走进另一个困境
- Spring春天来到,随之,SSH风生水起,称霸武林;
- Spring稳坐江湖大哥,SSM开始上位
- Spring本着“拿来主义”思维快速发展,生态不断健全
- Spring又一里程碑崛起,把“约定大于配置”思想玩的炉火纯青
- SpringCloud打包了众多解决方案,应对互联网项目更加easy!
③Spring Framework技术栈

引入Context包会自动引入Beans、Core、SpEL包
④BeanFactory快速入门
1 | //创建BeanFactory |
注意:
- 实现DI依赖注入
- 定义userDao
- userService添加setUserDao(UserDao dao)用于接收注入的对象
- 修改application.xml文件,在userServiceImpl中的
标签中嵌入 配置注入
④ApplicationContext快速入门
ApplicationContext称为Spring容器,内部封装的了BeanFactory,比BeanFactory功能更加丰富强大,使用ApplicationContext进行开发时,xml配置文件习惯写成applicationContext.xml。
1 | //创建context |
与ApplicationContext的关系:
1)BeanFactory是Spring的早期接口,称为Spring的bean工厂;ApplicationContext是后期的高级接口,称之为Spring容器。
2)ApplicationContext在BeanFactory基础上做了很多扩展。例如国际化、监听功能等;BeanFactory的API更偏向底层。
3)Bean的创建主要逻辑封装在BeanFactory中,ApplicationContext不仅继承了BeanFatory,其内部还维护着BeanFactory的引用。既有继承关系又有融合关系。
4)Bean的初始化时机不同,原始BeanFactory是在首次调用getBean时进行Bean的创建,而ApplicationContext在是加载配置文件,容器一创建就将Bean实例化并初始化好。(即BeanFactory为延迟加载)
BeanFactory的继承体系:

具体实现为DefaultListableBeanFactory,而AppicationContext内部维护的便是它。
ApplicationContext的继承体系:

只在Spring基础环境下,常用的有三个
ClassPathXmlApplicationContext————-加载类路径下的xml配置的ApplicationContext
FileSystemXmlApplicationContext———–加载磁盘路径下的xml配置的ApplicationContext
AnnocationConfigApplicationContext——-加载注解配置类的ApplicationContext
一、基于xml的Spring应用
1.SpringBean的配置详解
常用配置如下
Bean的id和全限定名配置(不设id则singletonObjects的map中KEY为全限定名) Bean的别名(存在aliasMap中,key为别名,value为bean的id) - 注意:在未配id时,singletonObjects的map中KEY为别名的第一个,没有id和别名则为全限定名
Bean的作用范围,BeanFactory作为容器时取值singleton和prototype Bean的实例化时机,是否延迟加载,BeanFactory作为容器时无效 Bean的实例化执行的初始化方法 Bean的实例销毁前的方法 设置自动注入模式,常用的有按类型byType和按名字byName 指定哪个工厂bean的哪个方法完成Bean的创建
Spring实例化方式主要如下两种:
构造方法实例化:底层通过构造方法对Bean进行实例化
- 在
标签中加入 实现有参构造。
- 在
工厂方式实例化:底层调用自定义的工厂方法进行Bean的实例化
静态工厂方法
1
2
3
4
5
6
7//自定义工厂
public class MyBeanFactory1{
public static UserDao getUserDao(){
//bean创建前可进行其他的业务逻辑操作
return new UserDaoImpl();
}
}1
<bean id="userDao1" class="com.truly.factory.MyBeanFactory1" factory-method="getUserDao"/>
会实例化userDao;
实例工厂方法
1
2
3
4
5
6
7//自定义工厂
public class MyBeanFactory2{
public UserDao getUserDao(){
//bean创建前可进行其他的业务逻辑操作,第三方jar包提供了工厂时,就这样获取bean
return new UserDaoImpl();
}
}1
2<bean id="myFactory2" class="com.truly.factory.MyBeanFactory2"/>
<bean id="userDao2" factory-bean="myFatory" factory-method="getUserDao"/>- 实现FactoryBean接口规范延迟实例化Bean
1 | //自定义工厂 |
1 | <bean id="userDao3" class="com.truly.factory.MyBeanFactory3"/> |
注意 在单步执行过程中,我发现在singletonObjects中存的key为userDao3,但value为MyBeanFactory3这个工厂对象,并不是我们想要的userDao3,但为什么还是能拿到对象呢?
在FactoryBeanObjectCache这个缓存map中有key为userDao3,value为对应userDao的实现
Bean的依赖注入主要如下两种:
- 通过Bean的set方法:
1 | <property name="userDao" ref="userDao"/> |
- 通过构造Bean的方法:
1 | <constructor-arg name="userDao" ref="userDao"/> |
注入数据类型有如下三种:
1)普通数据类型:如String,int等,通过value属性指定
2)引用数据类型:如UserDaoImpl等,通过ref属性指定
3)集合数据类型:如List、map、properties等。
- 如list为例
1 | <property name="stringList"> |
Bean的依赖注入配置:
1)byName:通过属性名自动装配,即去匹配setXXX与id=“XXX”或name=”XXX”是否一致
2)byType:通过Bean类型从容器中匹配,匹配出多个相同类型会报错。
- 如userServiceImpl中有setUserDao这个方法
1 | <bean id="userService" class="com.truly.UserServiceImpl" autowire="byName"/> |
Sping的其他配置标签:
1)
1 | <beans profile="dev"> |
可使用下列两种方法激活环境:
- 使用命令行动态参数,虚拟机参数位置加载-Dspring.profile.active=dev
- 使用代码的方式设置环境变量System.serProperty(“spring.profile.active”,”dev”)
2)
3
Bean实例化的流程
- Spring容器进行初始化时,解析xml配置中的
的信息并封装成一个BeanDefinition对象 -

-
- BeanDefinition又会存储到一个名为beanDefinitionMap的map集合中
- Spring框架对beanDefinitionMap进行遍历,使用反射创建Bean实例对象,创建好的Bean放入名为SingletonObjects(单例池)的map中
- 当你调用getBrean方法时,从SingletonObjects取出Bean实例对象
Spring的后处理器
Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程,以达到注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用,主要有如下两种:
BeanFactoryPostProcessor:Bean工厂后处理器,在BeanDefinition填充完毕后,Bean实例化之前。
可使用beanFactory.getBeanDefinition(Sting beanName)获取对应的对象。
Spring提供了BeanFactoryPostProcessor的子接口BeanDefinitionPostProcessor专门用于注册BeanDefinition的操作。
执行顺序如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws Execption{
System.out.println("BeanDefinition填充完毕后调用该方法")
BeanDefinition beanDefinition=beanFactory.getBeanDefinition("userService");
//此次将class路径修改
beanDefinition.setBeanClassName("com.truly.dao.impl.UserDaoImpl");
//还可以动态注入Bean
BeanDefinition beanDefinition2=new RootBeanDefinition();
beanDefinition2.setBeanClassName("com.truly.dao.personDaoImpl");
//强转成DefaultListableBeanFactory
DefaultListableBeanFactory defaultListableBeanFactory=(DefaultListableBeanFactory)beanFactory;
defaultListableBeanFactory.registerBeanDefinition("personDao",beanDefinition2);
}BeanPostProcessor:Bean后处理器,一般在Bean实例化后,填充到singletonObjects之前。
- 其中有两个default方法
- postProcessorBeforeInitialization(Obejct bean,String beanName)
- postProcessorAfterInitialization(Obejct bean,String beanName)
- 其中有两个default方法
以后者为例,模拟AOP的基本实现思想
@Override
public Object postProcessorAfterInitialization(Obejct bean,String beanName){
//使用动态代理,返回proxy对象,存到单例池
Object proxy=Proxy.newInstance(
bean.getClass.getClassLoader(),
bean.getClass.getInterfaces(),
(proxy,method,args)->{
//1.输出开始时间
System.out.println("方法"+method.getName()+"-开始时间:"+new Date())
//2.执行目标方法
Object result=method.invoke(bean,args);
//3.输出结束时间
System.out.println("方法"+method.getName()+"-结束时间:"+new Date())
return result;
}
);
return proxy;
}
最终执行流程如下
Spring的生命周期
- 大体上分为三个阶段:
Bean的实例化阶段:Spring框架取出BeanDefinition的信息进行判断范围是否为singleton的,是否不是延迟加载;是不是FactoryBean等,最终将普通的singleton的Bean通过反射实例化。
Bean的初始化阶段:Bean创建之后还是个半成品,还要对Bean实例进行填充,执行一些Aware接口方法,该阶段是最具技术含量的阶段,AOP、注解功能等
Bean实例的属性填充
注入普通属性:String、int或基本数据类型时;通过set方法反射设置
注入单项对象引用属性时:从容器getBean获取后再用set设置进去,如果容器内没有,则先创建对象Bean实例,再进行注入。
注入双向对象引用时(循环依赖):解决方案如下
如图,循环依赖会按如下方式循环执行下去
三级缓存
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { ..... //1.最终存储单例Bean成品的容器,及实例化和初始化都完成的Bean,称之为“一级缓存” private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256); //3.单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时通过工厂创建Bean,称之为“三级缓存” private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16); //2.早期Bean单例池,缓存半成品对象,且当前对象已被其他对象引用,称之为“二级缓存” private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16); .......... }
UserService和userDao循环依赖的过程结合三级缓存描述如下:
1.UserService实例化对象,但未初始化,将userService放入三级缓存中
2.UserService属性注入,需要UserDao,从一级到三级缓存取,没有则实例化userDao对象
3.UserDao实例化对象,但未初始化,将UserDao放入三级缓存
4.UserDao属性注入,需要UserService,从三级缓存中就找到,将UserService从三级缓存移到二级缓存
5.UserDao注入了UserService,执行其他生命流程,最终称为一个完整的Bean,放入一级缓存,删除二三级缓存
6.UserService注入UserDao
7.UserService执行其他生命流程,,最终称为一个完整的Bean,放入一级缓存,删除二三级缓存
Aware接口属性注入
BeanPostProcessor的before()方法回调
InitializingBean接口的初始化方法回调
自定义舒适化方法init回调
BeanPostProcessor的after()方法回调
Bean的完成阶段:存储到单例池中,完成SpringBean的生命周期。