Spring 是最受欢迎的企业级 Java 应用程序开发框架,数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。
Spring 是分层的 Java SE/EE full-stack 轻量级开源框架,以 IoC(Inverse of Control,控制反转)和 AOP(Aspect Oriented Programming,面向切面编程)为内核,使用基本的 JavaBean 完成以前只可能由 EJB 完成的工作,取代了 EJB 臃肿和低效的开发模式。
Spring 是一个轻量级的应用框架,具有很高的凝聚力和吸引力,其基础版本只有 2 MB 左右的大小。
Spring 框架的核心特性是可以用于开发任何 Java 应用程序,但是在 Java EE 平台上构建 web 应用程序是需要扩展的。 Spring 框架的目标是使 J2EE 开发变得更容易使用,通过启用基于 POJO 编程模型来促进良好的编程实践。
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 框架采用分层架构,根据不同的功能被划分成了多个模块,这些模块大体可分为 Data Access/Integration、Web、AOP、Aspects、Messaging、Instrumentation、Core Container 和 Test,如图 1 所示。
图 1 中包含了 Spring 框架的所有模块,这些模块可以满足一切企业级应用开发的需求,在开发过程中可以根据需求有选择性地使用所需要的模块。
数据访问/集成层包括 JDBC、ORM、OXM、JMS 和 Transactions 模块,具体介绍如下。
Spring 的 Web 层包括 Web、Servlet、Struts 和 Portlet 组件,具体介绍如下。
Spring 的核心容器是其他模块建立的基础,由 Beans 模块、Core 核心模块、Context 上下文模块和 Expression Language 表达式语言模块组成,具体介绍如下。
Spring的其他模块还有 AOP、Aspects、Instrumentation 以及 Test 模块,具体介绍如下。
Spring的核心框架是其他模块建立的基础,由Spring-core、Spring-beans、Spring-context、Spring-context-support和Spring-expression(Spring表达式语言)等模块组成。
核心就是管理资源组件及其依赖关系。包括IoC(Inversion of Control 控制反转)/ DI (Dependency Injection依赖注入),Aop(Aspect Oriented Programming面向切面编程)。
依赖注入或控制反转的定文中,调用者不负责被调用者的实例创建工作,该工作由Spring框架中的容器米负责,它通过开发者的配置来判断实例类型,创建后再注入调用者。由于Spring容器负责被调用者实例,实例创建后又负责将该实例注入调用者,因此称为依赖注入。而被调用者的实例创建工作不两由调用者来创建而是由Spring来创建,控制权由应用代码转移到了外部容器,控制权发生了反转,因此称为控制反转。
Aop(Aspect Oriented Programming面向切面编程):我们知道OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分,如果在应用开发过程中,如果产生横切性问题,比如日志记录,权限验证,监控性能,异常处理等,这个时候AOP就上场了,AOP是通过预编译方式或者运行期动态代理实现的一种方式,AOP可以对业务逻辑的各个部分进行隔离,有效减少了系统间的重复代码,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
名称 | 说明 |
---|---|
Joinpoint(连接点) | 指那些被拦截到的点,在 Spring 中,可以被动态代理拦截目标类的方法。 |
Pointcut(切入点) | 指要对哪些 Joinpoint 进行拦截,即被拦截的连接点。 |
Advice(通知) | 指拦截到 Joinpoint 之后要做的事情,即对切入点增强的内容。 |
Target(目标) | 指代理的目标对象。 |
Weaving(植入) | 指把增强代码应用到目标上,生成代理对象的过程。 |
Proxy(代理) | 指生成的代理对象。 |
Aspect(切面) | 切入点和通知的结合。 |
Bean 生命周期的整个执行过程描述如下。
1)根据配置情况调用 Bean 构造方法或工厂方法实例化 Bean。
2)利用依赖注入完成 Bean 中所有属性值的配置注入。
3)如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。
4)如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。
5)如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。
6)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。
7)如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。
8)如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。
9)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。
10)如果在 中指定了该 Bean 的作用范围为 scope=“singleton”,则将该 Bean 放入 Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用范围为 scope=“prototype”,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。
11)如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的 Bean 销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。
Spring 为 Bean 提供了细致全面的生命周期过程,通过实现特定的接口或 的属性设置,都可以对 Bean 的生命周期过程产生影响。虽然可以随意配置 的属性,但是建议不要过多地使用 Bean 实现接口,因为这样会导致代码和 Spring 的聚合过于紧密。
Spring 容器在初始化一个 Bean 的实例时,同时会指定该实例的作用域。Spring3 为 Bean 定义了五种作用域,具体如下。
单例模式,使用 singleton 定义的 Bean 在 Spring 容器中只有一个实例,这也是 Bean 默认的作用域。
< bean id=“person” class=“class=“com.swjd.bean.Person”” scope=“singleton”/>
原型模式,每次通过 Spring 容器获取 prototype 定义的 Bean 时,容器都将创建一个新的 Bean 实例。
< bean id=“person” class=“class=“com.swjd.bean.Person”” scope=“prototype”/>
在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。
在一次 HTTP Session 中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。
在一个全局的 HTTP Session 中,容器会返回该 Bean 的同一个实例。该作用域仅在使用 portlet context 时有效。
首先在新建好的Maven项目中找到pom.xml导入相应的依赖(< dependencies>内)
org.springframework
spring-context
4.3.7.RELEASE
org.springframework
spring-core
4.3.7.RELEASE
org.springframework
spring-beans
4.3.7.RELEASE
org.springframework
spring-context-support
4.3.7.RELEASE
org.springframework
spring-expression
4.3.7.RELEASE
①File(右击resources也可)—New—XML Configuration File—Spring Config(pom.xml中的依赖全下载好Spring Config才会显示)—applicationContext.xml
注意:配置文件官方推荐的命名为—applicationContext.xml,首字母不用大写
②输入applicationContext.xml后点击OK,最上方会出现一条黄色的线,点击Configure application context,在跳出的页面点击OK则真正创建成功
//创建实体类
public class Person {
private int id;
private String name;
private String sex;
public Person() {
System.out.println("用无参构造方法创建");
}
public Person(int id, String name, String sex) {
System.out.println();
this.id = id;
this.name = name;
this.sex = sex;
}
<!--Spring容器 管理创建对象-->
<!--配置对象 id表示对象名 class表示对象从person类中创建(要写全路径),name可以有多个,不同对象不可重复 -->
<!--方式一:无参构造方法创建对象-->
<bean id="p1" name="person" class="com.swjd.bean.Person">
<!--DI注入 本质调用set的方法赋值-->
<property name="id" value="957"></property>
<property name="name" value="貂蝉"></property>
<property name="sex" value="女"></property>
</bean>
public static void main(String[] args) {
//以前的方式
/*Person person=new Person();
System.out.println(person);*/
//用Spring容器(IOC控制反转)
//1.启动并加载spring框架(可创建多个 名字不同就可)
ClassPathXmlApplicationContext context=
new ClassPathXmlApplicationContext("applicationContext.xml");
//2.拿Person对象---测试单例
//测试无参的构造方法创建对象
Person person=context.getBean("p1",Person.class);
System.out.println(person);
person.setName("妲己");
System.out.println(person);
id:是bean对象的唯一标识(表示对象名),不能添加特别字符,一个对象只能有一个id
class:指定bean对应类的全路径,表示对象从person类中创建。如:
<bean id="p1" class="com.swjd.bean.Person"></bean>
name:是bean对应对象的一个标识,一个对象只能有一个id,但是可以拥有很多name,不同实体对象的name亦不可重复。
若name值一样,则在测试类引用实体对象时程序无法识别你引用的到底是p1对象还是p2对象。
property:< bean>元素的子元素,用于调用 Bean 实例中的 Set 方法完成属性赋值,从而完成依赖注入。该元素的 name 属性指定 Bean 实例中的相应属性名
< bean id="p1" name="person" class="com.swjd.bean.Person">
< property name="id" value="957">< /property>
< /bean>
scope:执行bean对象创建模式和生命周期,表示创建的容器的作用域是单例模式还是多例模式(默认为单例模式)。
//单例模式,若更改值,改的便是同一实体
scope="singleton"
//表示创建的对象是多例(每次使用对象的时候会额外创建一个新的)
scope="prototype"
若更改值,实体不会受影响。
Scope="request"表示再一次请求范围内,Spring容器中的对象保持一个,公用同一个。
Scope="session"表示再一次会话范围内,Spring容器中的对象保持一个,公用同一个。
注意:后两个只能用在web项目里
首先要在实体类中添加初始化的方法
//初始化
public void init(){
System.out.println("对象创建完,就调用");
}
Init-method:对象初始化方法,表示对象创建完成后调用的方法。
//使用
init-method="init"
首先要在实体类中添加销毁的方法
//销毁
public void destroy(){
System.out.println("已销毁");
}
Destroy-method:对象销毁方法,对象销毁后调用的方法。
//使用
destroy-method="destroy"
constructor-arg:< bean>元素的子元素,可以使用此元素传入构造参数进行实例化。该元素的 index 属性指定构造参数的序号(从 0 开始),type 属性指定构造参数的类型
<bean id="p1" name="person" class="com.swjd.bean.Person">
<constructor-arg index="0" type="int" value="34522"/>
<constructor-arg index="1" type="java.lang.String" value="妲己"/>
</bean>
ref:< property> 和 < constructor-arg> 等元素的子元索,该元素中的 bean 属性用于指定对 Bean 工厂中某个 Bean 实例的引用
<bean id="p1" name="person" class="com.swjd.bean.Person">
<constructor-arg index="2" type="int" value="3"/>
<!--引用注入对象二-->
<constructor-arg index="3" type="com.swjd.bean.Dog" ref="d2"/>
</bean>
value:< property> 和 < constractor-arg> 等元素的子元素,用于直接指定一个常量值
list:用于封装 List 或数组类型的依赖注入
set:用于封装 Set 类型属性的依赖注入
map:用于封装 Map 类型属性的依赖注入
entry: 元素的子元素,用于设置一个键值对。其 key 属性指定字符串类型的键值,ref 或 value 子元素指定其值
注意:DI依赖注入,要保留实体类中get/set方法和空的构造方法。
<bean id="m1" class="com.swjd.bean.Man">
<!--方式一:调用set方法注入-->
<property name="id" value="34521"/>
<property name="name" value="李白"/>
<property name="age" value="2"/>
</bean>
<bean id="m2" class="com.swjd.bean.Man">
<!--方式二:调用带参的构造方法注入-->
<constructor-arg index="0" type="int" value="34522"/>
<constructor-arg index="1" type="java.lang.String" value="妲己"/>
<constructor-arg index="2" type="int" value="3"/>
</bean>
<!--方式三:P命名空间注入(本质依旧是set注入)-->
<bean id="m3" class="com.swjd.bean.Man"
p:id="34523" p:name="八戒" p:age="1">
</bean>
<bean id="m4" class="com.swjd.bean.Man">
<!--方式四:Spel表达式注入-->
<property name="id" value="#{m1.id}"/>
<property name="name" value="#{m2.name}"/>
<property name="age" value="#{m3.age}"/>
</bean>
<!--方式一:无参构造方法创建对象-->
<bean id="p1" name="person" class="com.swjd.bean.Person">
<!--DI注入 本质调用set的方法赋值-->
<property name="id" value="957"></property>
<property name="name" value="貂蝉"></property>
<property name="sex" value="女"></property>
</bean>
<!--方式二:有参构造方法创建对象-->
<bean id="p3" class="com.swjd.bean.Person">
<constructor-arg index="0" type="int" value="9528"></constructor-arg>
<constructor-arg index="1" type="java.lang.String" value="德玛西亚"></constructor-arg>
<constructor-arg index="2" type="java.lang.String" value="男"></constructor-arg>
</bean>
//静态工厂
public class PersonFactory {
//创建person的静态方法
public static Person createPerson(){
System.out.println("通过静态工厂创建对象");
return new Person(914,"红灰太狼","男");
}
}
<!--方式三:用静态工厂创建对象-->
<bean id="p4" class="com.swjd.beanFactory.PersonFactory" factory-method="createPerson"></bean>
//非静态工厂
public class NoStaticPersonFactory {
//创建person的非静态方法
public Person createPerson(){
System.out.println("通过非静态工厂创建对象");
return new Person(914,"红灰小狼","男");
}
}
<!--方式四:用非静态工厂创建对象 不可直接拿对象-->
<bean id="noStaticFactory" class="com.swjd.beanFactory.NoStaticPersonFactory"></bean>
<bean id="p5" factory-bean="noStaticFactory" factory-method="createPerson"></bean>
例:人要养一条狗,建一个狗的实体类,然后在Man实体类中封装一个狗的对象,get/set方法toString都要有,接下来开始四种不同依赖注入对象
实体类:
public class Student {
private String name;
private String[] hobbies; //爱好
private List<String> subject; //科目
private Map<String,Object> map;
public Student() {
}
<!--复杂类型的注入-->
<bean id="s1" class="com.swjd.bean.Student">
<property name="name" value="小许鸭"/>
<!--注入数组-->
<property name="hobbies">
<array>
<value>喜欢你</value>
<value>喜欢我</value>
<value>喜欢大家</value>
</array>
</property>
</bean>
<bean id="s1" class="com.swjd.bean.Student">
<!--注入集合 list、set相同-->
<property name="subject">
<list>
<value>MyBatis</value>
<value>Spring</value>
<value>Oracle</value>
</list>
</property>
</bean>
<bean id="s1" class="com.swjd.bean.Student">
<!--注入Map-->
<property name="map">
<map>
<entry key="CN">
<value>中国</value>
</entry>
<entry key="HG">
<value>韩国</value>
</entry>
<entry key="RU">
<value>俄罗斯</value>
</entry>
</map>
</property>
</bean>