文章目录
一、spring是什么
Spring是一个开源框架,它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。
1、spring的主要功能:
1) 方便解耦,简化开发
Spring就像一个大工厂,可以将所有对象的创建和依赖关系的维护交给Spring管理
这里提及了耦合和解耦合的概念:什么是耦合和解耦合呢?
- 在软件工程中,对象之间的耦合度就是对象之间的依赖性。对象之间的耦合越高,维护成本越高,因此对象的设计应使类和构件之间的耦合最小。
- 降低耦合度即可以理解为解耦,模块间有依赖关系必然存在耦合,理论上的绝对零耦合是做不到的,但可以通过一些现有的方法将耦合度降至最低。
2)方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如Struts2、Hibernate、MyBatis)的直接支持。
3)降低Java EE API 的使用难度
Spring对Java EE 开发中非常难用的一些API(JDBC、JavaMail、远程调用等)都提供了封装,使这些API应用的难度大大降低。
4)方便程序的测试
Spring支持JUnit4,可以直接通过注解方便的测试Spring程序
5)AOP编程的支持
Spring提供面向切面编程,可以方便的实现对程序进行权限拦截和运行监控等功能。
6)声明事务的支持
只需要通过配置就可以完成对事务的管理,而不用我们去手动编程。
二、spring的核心模块
1、核心模块(Core Container)
spring的核心模块实现了IoC的功能,它将类和类之间的依赖从代码中脱离出来,用配置的方式进行依赖关系描述。由Ioc容器负责类的创建,管理,获取等。BaenFactory接口是spring框架的核心接口,是西安了容器很多核心的功能。
2、AOP模块
spring AOP模块提供了满足AOP Alliance规范的实现,还整合了AspectJ这种语言级框架。通过AOP能降低耦合。
3、数据访问集成模块
该模块包括了JDBC、ORM、OXM、JMS和事务管理:
-
事务模块:该模块用于Spring管理事务,只要是Spring管理对象都能得到Spring管理事务的好处,无需在代码中进行事务控制了,而且支持编程和声明性的事务管理。
-
JDBC模块:提供了一个JBDC的样例模板,使用这些模板能消除传统冗长的JDBC编码还有必须的事务控制,而且能享受到Spring管理事务的好处。
-
ORM模块:提供与流行的“对象-关系”映射框架的无缝集成,包括hibernate、JPA、MyBatis等。而且可以使用Spring事务管理,无需额外控制事务。
-
OXM模块:提供了一个对Object/XML映射实现,将Java对象映射成XML数据,或者将XML数据映射成java对象,Object/XML映射实现包括JAXB、Castor、XMLBeans和XStream。
-
JMS模块:用于JMS(Java Messaging Service),提供一套 “消息生产者、消息消费者”模板用于更加简单的使用JMS,JMS用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
4、Web模块
该模块建立在ApplicationContext模块之上,提供了Web应用的功能,如文件上传、FreeMarker等。
5、测试模块
spring可以用非容器依赖的编程方式进行几乎所有的测试工作,支持Junit和TestNG等测试框架。
三、IoC容器和DI
IoC(控制反转:Inverse of Control): 是Spring容器的核心,但是IoC这个概念却比较晦涩,让人不太容易望文生义。
DI(依赖注入:Dependency Injection): 让第三方来实现注入,以移除我们类与需要使用的类之间的依赖关系。
区别: IoC是目的,DI是手段,创建对象的过程往往意味着依赖的注入。我们为了实现IoC,让生成对象的方式由传统方式(new)反转过来,需要创建相关对象时由IoC容器帮我们注入(DI)。
1、常见的几种属性注入方式
这里我先准备一个Student类和School类:
public class Student {
private String name;
private int age;
private School school;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name, int age, School school) {
System.out.println("====有参构造====");
this.name = name;
this.age = age;
this.school = school;
}
public School getSchool() {
return school;
}
public void setSchool(School school) {
this.school = school;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", school=" + school +
'}';
}
}
public class School {
private String name;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "School{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
- 有参构造方法注入,在bean标签中使用constructor-arg标签来进行有参方法的注入:
<!--声明Student对象-->
<bean id="myStudent" class="com.wenhua.ba05.Student" >
<constructor-arg name="name" value="张三丰"/>
<constructor-arg name="age" value="20"/>
<constructor-arg name="school" ref="mySchool"/>
</bean>
- 属性注入(set注入),在bean标签中使用property标签来进行有参方法的注入:
<!--声明School对象-->
<bean id="mySchool" class="com.wenhua.ba05.School">
<property name="name" value="人民大学"/>
<property name="address" value="海淀区"/>
</bean>
在Student类中由name,age,school三个属性,其中school是引用类型。普通类型和引用类型使用的属性标签是不一样的:
1)简单类型的有参注入
<bean id="xx" class="yyy">
<property name="属性名字" value="此属性的值"/>
一个property只能给一个属性赋值
<property ...>
</bean>
2)引用类型的有参注入
<bean id="xx" class="yyy">
<property name="属性名称" ref="bean的id(对象名称)"/>
</bean>
当该类的某一属性是简单类型或者String类型时,属性赋值使用的value。而当属性是简单类型或者String类型时,属性赋值是用的ref。
2、集合属性注入方式
这里先做了一个Collections类
public class Collections {
private List<String> list;
private Set<String> set;
private Map<Integer,String> map;
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<Integer, String> map) {
this.map = map;
}
}
集合注入:
<bean id="aCollections" class="com.wenhua.ba02.Collections">
<!--给List注入,可以存在相同对象-->
<property name="list">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>![在这里插入图片描述](https://img-blog.csdnimg.cn/ef26d69a232e4e13be04a3091db82fff.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAd2VuaHVhYnV6aGlqaWFu,size_13,color_FFFFFF,t_70,g_se,x_16#pic_center)
<!--给Set注入,不可以存在相同对象-->
<property name="set">
<set>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</set>
</property>
<!--给Map注入,只要map中的key值不一样就可以装配value-->
<property name="map">
<map>
<entry key="1" value="map元素1" />
<entry key="2" value="map元素1" />
</map>
</property>
</bean>
四、Bean的作用域
这里来演示一下第一种(singleton)和第二种():
1、scope=“singleton”(默认):
//XML文件配置
<bean id="myStudent" class="com.wenhua.ba02.Student" scope="singleton">
<property name="name" value="李四"/>
<property name="age" value="20"/>
<property name="school" ref="mySchool" />
</bean>
//测试
@Test
public void test02(){
String config = "ba02/applicationContext.xml";
//2.创建spring容器的对象,ApplicationContext
//ApplicationContext就是表示spring容器,通过这个容器获取对象
//ClassPathXmlApplicationContext表示从类路径种加载spring的配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//从容器中获取student对象
Student myStudent1 = (Student) ac.getBean("myStudent");
Student myStudent2 = (Student) ac.getBean("myStudent");
System.out.println(myStudent1);
System.out.println(myStudent2);
}
/**Output
com.wenhua.ba02.Student@757942a1
com.wenhua.ba02.Student@757942a1
**/
从输出可以看出是同一个对象
2、scope=“prototype”:
//XML文件配置
<bean id="myStudent" class="com.wenhua.ba02.Student" scope="prototype">
<property name="name" value="李四"/>
<property name="age" value="20"/>
<property name="school" ref="mySchool" />
</bean>
//测试
@Test
public void test02(){
String config = "ba02/applicationContext.xml";
//2.创建spring容器的对象,ApplicationContext
//ApplicationContext就是表示spring容器,通过这个容器获取对象
//ClassPathXmlApplicationContext表示从类路径种加载spring的配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//从容器中获取student对象
Student myStudent1 = (Student) ac.getBean("myStudent");
Student myStudent2 = (Student) ac.getBean("myStudent");
System.out.println(myStudent1);
System.out.println(myStudent2);
}
/**Output
com.wenhua.ba02.Student@757942a1
com.wenhua.ba02.Student@4a87761d
**/
从输出可以看出是两个不一样的对象
五、生命周期
-
实例化BeanFactoryPostProcessor,调用postProcessBeanFactory()方法对工厂定义信息进行后处理。
-
调用者通过getBean向容器请求bean时,如果容器注册了InstantiationAwareBeanPostProcessor接口,在实例化Bean之前,会调用接口的postProcessBeforeInstantiation()方法。
-
根据配置文件调用Bean的构造方法或者工厂方法实例化Bean。
-
实例化Bean后,调用InstantiationAwareBeanPostProcessor接口的postProcessAfterInstantiation()方法,在这里可以对已经实例化的对象进行一些修饰。
-
如果Bean配置了属性信息,在设置每个属性之前将先调用InstantiationAwareBeanPostProcessor的postProcessPropertyValues()方法。
-
调用Bean的属性设置方法为Bean设置属性。
-
如果Bean实现了BeanNameAware接口,会调用该接口的setBeanName()方法,将配置文件中该Bean对应的名称设置到Bean中。
-
如果Bean实现了BeanFactoryAware接口,会调用该接口的setBeanFactory方法,将BeanFactory容器实例设置到Bean中。
-
如果BeanFactory装配了BeanPostProcessor后处理器,将调用Object postProcessBeforeInitialization(Object bean,String beanName)方法对Bean进行加工操作,bean是当前处理的Bean,beanName是当前bean的配置名,返回的对象是加工处理后的Bean。用户可以使用该方法对Bean进行特殊处理。BeanPostProcessor在Spring框架中占有重要地位,AOP等都通过该BeanPostProcessor实施。
-
如果Bean实现了InitializingBean接口,会调用该接口的afterPropertiesSet()方法。
-
如果<bean>中配置了init-method属性,执行指定的初始化方法。
-
调用BeanPostProcessor后处理器的Object postProcessAfterInitialization(Object bean,String beanName)方法再次获得对Bean的加工处理机会。
-
将Bean返回给调用者。
-
当容器关闭时, 如果Bean实现了DisposableBean接口,调用接口的destory方法。
-
如果<bean>设置了destory-method属性,执行指定的销毁方法。
六、Bean的两种方式
我们前面的<bean>都是普通bean,Spring利用反射机制通过bean的class属性来实例化Bean。如果有的Bean属性特别多,我们就需要编写大量的配置信息。
Spring提供了一个FactoryBean接口。我们可以通过实现该接口来返回特定的Bean,该接口定义了三个方法:
- T getObject():返回由FactoryBean创建的Bean实例。
- boolean isSingleton():确定创建的Bean的作用域是singleton还是prototype
- Class< ? > getObjectType():返回FactoryBean创建Bean的类型
// 我们创建一个类实现FactoryBean接口
public class UserFactory implements FactoryBean<User> {
//返回对象
public User getObject() throws Exception {
User user=new User();
user.setAge(14);
user.setName("tom");
return user;
}
//返回bean的类型
public Class<User> getObjectType() {
return User.class;
}
public boolean isSingleton() {
// TODO Auto-generated method stub
return false;
}
}
//XML配置
<bean id="userfactory" class="com.wenhua.ba02.UserFactory"></bean>
//测试
@Test
public class Test {
public void test(){
ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
User user=(User) ac.getBean("userfactory");
user.say();
}
}
/*output:
tom:14
*/