首先总结一下,总结一下Spring框架包含哪些内容
1.Spring框架的基本概念
2.IoC控制反转
3.AOP面向切面编程
4.Spring的DAO以及其事务
Spring整合SSH/SSM就不在这个范畴了。
用目录来看,就是这样的了。
后面的AOP和事务我就放在后面的博客文章里面了。
Spring的定义:
Spring是一个容器,管理系统里面的所有bean和依赖关系。这个框架是用来降低耦合度,它对不同的代码采用不同的解耦方式,对于主业务逻辑之间的交叉耦合,使用IoC来实现。对于系统级别的逻辑交叉,使用AOP来实现解耦。
Spring体系结构图:
现在在spring官方网站已经看不到这个图了,不知道是不是我没有找到的原因。
Spring的特点
①非侵入式
②容器 管理对象的生命周期,对象与对象之间的关系。可以通过配置文件,来定义对象, 以及配置与其他的依赖关系。
③ IoC 控制反转,即创建被调用者的实例不是有调用者完成的,而是由Spring容器完成。
④AOP 面向切面编程,是一种编程思想。是对OOP的补充。分离应用的业务逻辑与系统级服务进行开发,应用对象只实现它们应该做的完成业务逻辑而已。
IoC
控制反转(IoC) 指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。
IoC 是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现方式有两种:
依赖注入和依赖查找。依赖注入方式应用更为广泛。
Ø 依赖查找:Dependency Lookup ,DL,容器提供回调接口和上下文环境给组件,程序代
码则需要提供具体的查找方式。比较典型的是依赖于 JNDI 系统的查找。
依赖注入:Dependency Injection,DI,程序代码不做定位查询,这些工作由容器自行完
成。
依赖注入 DI 是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建
被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。
Spring 的依赖注入对调用者与被调用者几乎没有任何要求,完全支持 POJO 之间依赖关系的管理。
依赖注入是目前最优秀的解耦方式。依赖注入让 Spring 的 Bean 之间以配置文件的方式组织在一起,而不是以硬编码的方式耦合在一起的。
Spring第一个程序
下面这个程序就拿传统的new对象的方式和使用spring的方式做了一个比较
项目结构图:
新建一个java project
导入jar
首先定义接口
public interface UserService {
public void add();
public void update();
}
再写实现类
public class UserServiceImpl implements UserService {
@Override
public void add() {
}
@Override
public void update() {
}
}
测试类:
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
/**
* 传统的方式如果想要调用add update方法的话就必须new一个对象出来 如下
*/
UserService userService=new UserServiceImpl();
userService.update();
userService.add();
}
}
这就是传统的方式了,要调用UserServiceImpl里面的update和add方法就必须new一个对象。再来看看spring的实现方式
创建一个java project
导入上面的jar到lib 并将jar 添加到环境里面去。
接口:
public interface ISomeService {
String doFirst();
void duSecond();
}
实现类:
public class SomeServiceImpl implements ISomeService {
public SomeServiceImpl() {
System.out.println("对象创建了。。。。。");
}
@Override
public String doFirst() {
System.out.println("执行doFirst方法");
return "abcd";
}
@Override
public void duSecond() {
System.out.println("执行读second方法");
}
}
测试类:
public class MyTest {
@Test
public void test01() {
ISomeService service = new SomeServiceImpl();
service.doFirst();
service.duSecond();
}
@Test
public void test02() {
//创建容器对象
//这里的配置文件存放在项目的src下
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
ISomeService service=(ISomeService)ac.getBean("SomeService");
service.doFirst();
service.duSecond();
}
public void test03() {
//这里的配置文件存放在项目的根下 具体的路径 如:d:applicationContext.xml
ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml");
ISomeService service=(ISomeService)ac.getBean("SomeService");
service.doFirst();
service.duSecond();
}
@SuppressWarnings("deprecation")
@Test
public void test04() {
//这个容器中的对象不是在容器初始化时创建的,而是在真正使用时创建的。
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
ISomeService service=(ISomeService)bf.getBean("SomeService");
service.doFirst();
service.duSecond();
}
}
applicationContext.xml
执行的效果就不粘贴上来了,自己复制代码跑一遍就知道了。
总结一下
/**
* 使用BeanFactory创建bean和使用ClassPathXmlApplicationContext、FileSystemXmlApplicationContext
* 创建bean方式的比较:
* 1.BeanFactory是在真正要使用的时候再创建对象,相对于内存的消耗来说要好,但执行效率慢
* 2.ClassPathXmlApplicationContext、FileSystemXmlApplicationContext在容器初始化的时候就创建了,内存消耗高,但执行效率快
* 相对来说现在我们的内存很大,而web项目对执行效率有要求。
*/
下一个知识点就是Spring的bean的装配,也就是对象的创建
Bean 的装配,即 Bean 对象的创建。容器根据代码要求创建 Bean 对象后再传递给代码
的过程,称为 Bean 的装配。
首先我们知道,我们在java代码里面
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
通过这一句,我们就完成了spring容器的初始化,那么这一句spring容器到底是怎么进行初始化的呢?底层做了哪些事情?
下面是bean的类图
从该继承体系可以看出:
1. BeanFactory 是一个 bean 工厂的最基本定义,里面包含了一个 bean 工厂的几个最基本的方 法,getBean(…) 、 containsBean(…) 等 ,是一个很纯粹的bean工厂,不关注资源、资源位置、事件等。 ApplicationContext 是一个容器的最基本接口定义,它继承了 BeanFactory, 拥有工厂的基本方法。同时继承了 ApplicationEventPublisher 、 MessageSource 、 ResourcePatternResolver 等接口, 使其 定义了一些额外的功能,如资源、事件等这些额外的功能。
2. AbstractBeanFactory 和 AbstractAutowireCapableBeanFactory 是两个模 板抽象工厂类。AbstractBeanFactory 提供了 bean 工厂的抽象基类,同时提供 了 ConfigurableBeanFactory 的完整实现。AbstractAutowireCapableBeanFactory 是继承 了 AbstractBeanFactory 的抽象工厂,里面提供了 bean 创建的支持,包括 bean 的创建、依赖注入、检查等等功能,是一个 核心的 bean 工厂基类。
3.ClassPathXmlApplicationContext之 所以拥有 bean 工厂的功能是通过持有一个真正的 bean 工厂DefaultListableBeanFactory 的实例,并通过 代理 该工厂完成。
容器初始化过程:
我们知道在spring中BeanDefinition很重要。因为Bean的创建是基于它的。容器AbstractApplicationContext中有个方法是refresh()这个里面包含了整个Bean的初始化流程实现过程,如果我们需要自己写一个ClassPathXmlApplicationContext类的话我们可以继承这个类,下面贴段这个方法的代码:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
beanFactory.destroySingletons();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
如上代码加上图片就是整个Bean的初始化过程。我们知道Bean有可以配置单列以及懒加载形式。在初始化的过程中,我们也能很好的观察到这个过程的实现。
在AbstractBeanFactory中定义了getBean()方法,而它又调用doGetBean().
GetBean 的大概过程:
1. 先试着从单例缓存对象里获取。
2.从父容器里取定义,有则由父容器创建。
3. 如果是单例,则走单例对象的创建过程:在 spring 容器里单例对象和非单例对象的创建过程是一样的。都会调用父 类 AbstractAutowireCapableBeanFactory 的 createBean 方法。 不同的是单例对象只创建一次并且需要缓 存起来。 DefaultListableBeanFactory 的父类 DefaultSingletonBeanRegistry 提供了对单例对 象缓存等支持工作。所以是单例对象的话会调用 DefaultSingletonBeanRegistry 的 getSingleton 方法,它会间 接调用AbstractAutowireCapableBeanFactory 的 createBean 方法。
如果是 Prototype 多例则直接调用父类 AbstractAutowireCapableBeanFactory 的 createBean 方法。
Bean的作用域
Scope
Prototype:原型模式,容器初始化的时候并不会创建bean对象,而在getBean的时候创建, 如果多次getBean,则会创建多个bean对象。
Singleton:单例模式,
lazy-init为false(默认是false,所以可以不用设置),则 ApplicationContext启动的时候就实例化该Bean,并且将实例化的Bean放在一个map结构的缓存中,下次再使用该Bean的时候, 直接从这个缓存中取
lazy-init为true,则该Bean的实例化是在第一次使用该Bean的时候进行实例化
request:对于每次 HTTP 请求,都将会产生一个不同的 Bean 实例。
session:对于每个不同的 HTTP session,都将产生一个不同的 Bean
Bean的装配过程
①默认装配方式:调用无参构造器的方式由spring容器创建的
②动态工厂Bean:由工厂创建出来的
③静态工厂Bean:由静态工厂创建出来的
下面的案例就讲讲动态工厂bean和静态工厂bean的两种方式。虽然这两种很少用到,但万一哪一天碰到了呢
第一种:动态工厂bean
定义接口:
public interface ISomeService {
String doFirst();
void duSecond();
}
实现类:
public class SomeServiceImpl implements ISomeService {
public SomeServiceImpl() {
System.out.println("对象创建了。。。。。");
}
@Override
public String doFirst() {
System.out.println("执行doFirst方法");
return "abcd";
}
@Override
public void duSecond() {
System.out.println("执行读second方法");
}
}
工厂类:
/**
* 动态工厂
* @author Administrator
*
*/
public class ServiceFactory {
public ISomeService getSomeService(){
return new SomeServiceImpl();
}
}
测试类:
public class MyTest {
@Test
public void test01() {
String resource="com/liuz/ba02/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
ISomeService service=(ISomeService)ac.getBean("someService");
service.doFirst();
service.duSecond();
}
}
applicationContext.xml
运行效果:
在这个例子中对象就是通过动态工厂ServiceFactory类创建的。
第二种 静态工厂bean
接口:
public interface ISomeService {
String doFirst();
void duSecond();
}
实现类:
public class SomeServiceImpl implements ISomeService {
public SomeServiceImpl() {
System.out.println("对象创建了。。。。。");
}
@Override
public String doFirst() {
System.out.println("执行doFirst方法");
return "abcd";
}
@Override
public void duSecond() {
System.out.println("执行读second方法");
}
}
工厂类:
/**
* 静态工厂
* @author Administrator
*
*/
public class ServiceFactory {
Public static ISomeService getSomeService(){
Return new SomeServiceImpl();
}
}
测试类:
public class MyTest {
@Test
public void test01() {
String resource="com/liuz/ba02/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
ISomeService service=(ISomeService)ac.getBean("someService");
service.doFirst();
service.duSecond();
}
}
applicationContext.xml
这2中创建bean的方式就到这里就结束了。
Bean后处理器
在目标 Bean 初始化完毕之前与之后执行。它们的返回值为:功能被扩展或增强后的 Bean 对象。(这个功能在项目中用得比较少),在这里就不做介绍了,具体看文档。
定制Bean的生命始末
可以为 Bean 定制初始化后的生命行为,也可以为 Bean 定制销毁前的生命行为。也即是说在初始化完毕之后,销毁之前,执行自己定义的方法。
实现的方式分三个步骤
第一步:在你的bean类里面定义初始化之后和销毁之前的两个方法,例如
第二步:在配置文件的
init-method:指定初始化方法的方法名
destroy-method:指定销毁方法的方法名
注意,若要看到 Bean 的 destroy-method 的执行结果,需要满足两个条件:
(1)Bean 为 singleton,即单例,spring默认的获取bean的方式就是单例的。
(2)要确保容器关闭。接口 ApplicationContext 没有 close()方法,但其实现类有。所以,要在调用bean对象的地方
将 ApplicationContext 强转为其实现类对象,或直接创建的就是实现类对象。
Bean的生命周期
这个知识点,可以说是一个重点,虽然说bean的生命周期一共有11个步骤,不能记得很清楚,但大概的印象还是要有的。
第一步:调用无参构造方法
第二步:调用所有属性的set方法
第三步:如果类实现了BeanNameAware 则可以获取到bean的id 执行setBeanName()
第四步:如果类实现了BeanFactoryAware,则可以获取到beanFactory对象 执行setBeanFactory()
第五步:如果类实现了BeanFactoryAware 则执行bean后处理器的before方法 postProcessBeforeInitialization()
第六步:如果类实现了InitializingBean接口,则执行afterPropertiesSet()这个方法,这个方法属于一个标志性的方法--属性设置结束了,标志着bean初始化完毕了
第七步:如果xml的bean配置了init-method,则执行初始化之后的方法
第八步:如果类实现了BeanFactoryAware 则执行bean后处理器的after方法 postProcessAfterInitialization()
第九步:执行自己的业务方法 add update delete select
第十步:如果类实现了DisposableBean接口,并且的调用的地方写了((ClassPathXmlApplicationContext)ac).close();则执行destroy()方法 准备销毁
第十一步:如果xml的bean里面配置了destroy-method则调用这里的方法销毁
为了说明这11个步骤,下面写了一个测试案例:
新建一个java project jar包我就不说了,跟上面的一样
定义接口:
public interface ISomeService {
String doFirst();
void duSecond();
}
实现类:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class SomeServiceImpl implements ISomeService,BeanNameAware,BeanFactoryAware,InitializingBean,DisposableBean {
private String userDao;
private String deptDao;
public void setUserDao(String userDao) {
System.out.println("step2:执行setUserDao");
this.userDao = userDao;
}
public void setDeptDao(String deptDao) {
System.out.println("step2:执行setDeptDao");
this.deptDao = deptDao;
}
public SomeServiceImpl() {
System.out.println("step1:对象创建了。。。。。");
}
@Override
public String doFirst() {
System.out.println("step9:执行主业务方法");
return "abcd";
}
@Override
public void duSecond() {
System.out.println("执行读second方法");
}
@Override
public void setBeanName(String name) {
System.out.println("step3:beanName="+name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("step4:获取到beanFactory="+beanFactory);
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("step6:属于一个标志性的方法--属性设置结束了,标志着bean初始化完毕了");
}
public void initAfter(){
System.out.println("step7:初始化之后");
}
@Override
public void destroy() throws Exception {
System.out.println("step10:准备开始执行销毁");
}
public void preDestory(){
System.out.println("step11:执行销毁");
}
}
Bean后处理器
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcess implements BeanPostProcessor {
/**
* bean:当前调用执行bean后处理器的bean对象
* beanName:当前调用执行bean后处理器的bean对象的id
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("step5:执行bean后处理器before");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("step8:执行bean后处理器after");
return bean;
}
}
测试类:
public class MyTest {
@Test
public void test01() {
String resource="com/liuz/ba07/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
ISomeService service=(ISomeService)ac.getBean("SomeService");
service.doFirst();
((ClassPathXmlApplicationContext)ac).close();
//service.duSecond();
}
}
applicationContext.xml
运行效果:
bean的生命周期到这里就结束了。
下面就是关于DI的内容了。
基于XML的DI
在这里要解释一下DI,很多人都知道DI是注入,但到底注入什么?把什么注入到什么?为什么一说道IoC大家第一反应想到的事DI
第一:DI是IoC目前最好的一种方式,还有一种方式叫DL(依赖查找)
第二:所谓的注入是将属性值注入到bean对象身上,前面所有的bean对象的获取,都是为了这一步,给bean对象赋值。当然了对于service dao这种bean也许没有属性,自然用不到注入什么属性值,但对于自己定义的bean对象,类似于model,是一定要用到注入的。
第一种:设值注入
设值注入就是靠在xml中给对象设置值,在做POI导出的时候就用到了。
还是java project
定义Student和School类:
public class Student {
private String name;
private Integer age;
private School school;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setSchool(School school) {
this.school = school;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", school=" + school
+ "]";
}
}
public class School {
private String sname;
public void setSname(String sname) {
this.sname = sname;
}
@Override
public String toString() {
return "School [sname=" + sname + "]";
}
}
applicationContext.xml
测试类:
public class MyTest {
@Test
public void test01() {
String resource="com/liuz/di01/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
Student student=(Student)ac.getBean("myStudent");
System.out.println(student);
}
}
运行结果:
第二种:构造注入
构造注入不需要无参构造器就可以了。但一定要有全部属性的带参构造器,因为这种方式调用的就是带参的构造器。
案例就不列举了。具体的实现请下载下面的源码。
第三种:p命名空间设置注入
底层调用的set方法,所有对象的属性一定要有set方法。
注意:在applicationContext.xml中一定要加上约束
xmlns:p="http://www.springframework.org/schema/p",具体实现看下载下来的代码
第四种: c命名空间构造注入
底层用的是构造方法,所以一定要把构造方法写出来。
第五种:集合属性注入 具体看源码(很简单)
第六种:byName方式域属性自动注入
byName自动注入,是将域属性名作为id到容器中查找相同名称的bean自动注入
第七种: byType方式域属性自动注入
根据域属性的类型到容器中查找具有 is a关系的bean自动注入。
要求:相同类型的bean不能多余1个
基于注解的DI
主要使用到的3个注解
@Component("mySchool")表明它是一个组件,后面括号里面给组件起个名 相当于xml配置方式注册bean的id
@Value("清华大学") 注入值
域属性的注入:域属性的复制有2种方式 byType 和byName
@Autowired就是属于byType
项目案例:
新建maven项目:
引入这些依赖包
org.springframework
spring-aop
4.2.1.RELEASE
org.springframework
spring-beans
4.2.1.RELEASE
junit
junit
4.9
test
org.springframework
spring-core
4.2.1.RELEASE
org.springframework
spring-context
4.2.1.RELEASE
org.springframework
spring-expression
4.2.1.RELEASE
org.apache.commons
com.springsource.org.apache.commons.logging
1.1.1
org.apache.log4j
com.springsource.org.apache.log4j
1.2.15
项目结构图
School.java
@Component("mySchool")
public class School {
@Value("清华大学")
private String sname;
public void setSname(String sname) {
this.sname = sname;
}
@Override
public String toString() {
return "School [sname=" + sname + "]";
}
}
Student.java
@Component("myStudent")
public class Student {
@Value("张三")
private String name;
@Value("23")
private Integer age;
@Autowired
private School school;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setSchool(School school) {
this.school = school;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", school=" + school
+ "]";
}
}
applicationContext.xml
测试类:
public class MyTest {
@Test
public void test01() {
String resource="com/liuz/di01/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
Student student=(Student)ac.getBean("myStudent");
System.out.println(student);
}
}
注入成功!
总结一下实现注解注入的步骤:
①在实体类上加上@Component 后面跟上括号,并在括号里面写上bean的id
②在每个属性上加上@Value("清华大学")后面括号里面放value
如果是域属性对象,则可以使用byType方式和byName注入
@Autowired:就是byType方式
③在spring的配置文件加入扫包的配置
<context:component-scan base-package="com.liuz.di01"/>
base-package="com.liuz.di01"会扫描自己以及其子包
base-package="com.liuz.di01.*"扫描所有的子包
在这里补充说明一下byType方式和byName方式注入
@autowire是 byType的方式,就等同于xml的byType注入方式,在applicationContext.xml的bean后面配置的autowire=”byType”,会根据域属性类型到容器中查找is a 关系的bean自动注入。使用byType的方式的要求是相同的类型的bean只能有一个。
@Qualifier("mySchool") 是byName的方式,是要求你要注入的域属性的bean的id要和域属性名相同,spring会根据域属性名去找域属性对象的bean。使用byName方式的时候是
@autowire+@Qualifier("mySchool") 连着一起用
与@Component 具有相同功能不同意义的注解还有3个
@Repository:注解在dao实现类上
@Service:注解在service上
@Controller 注解在controller
总结spring里面涉及到的注解以及功能解释
功能:定义组件
@Component:定义组件
@Repository:注解在dao实现类上
@Service:注解在service上
@Controller 注解在controller
实现域属性注入
第一种:@autowire 根据is a关系自动注入
第二种:@Resource 要求jre1.6以及以上
第一种:@Autowired
@Qualifier("mySchool")
两个一起用
第二种:@Resource(name="mySchool") 括号里面放域属性bean的id
实现定义bean生命始末
@PostConstruct 声明初始化完毕后的方法
@PreDestroy 声明销毁前的方法。
注意:执行这2个方法的前提是手动关闭容器
((ClassPathXmlApplicationContext)ac).close();
在调用bean的地方加上这一行代码。
@Resource 自动的根据byType的方式来注入,如果后面跟着value,就使用byName的方式。
@Configuration 声明这个类将作为spring容器来使用了。
@javaConfig定义一个java类,这个类将来就是容器
使用Spring的Junit测试
基于注解和xml方法共同使用
配置文件的优先级高于注解的优先级
好处:当项目里面用到了注解,并且bean的属性给了set器,如果要改以前设置的值,则改xml就可以了。