看过之前的猿思考系列文章,相信你对java方面的基础有了一定的认识。经过之前的进化和思考的锻炼,也该是时候像模像样的做一些事情了。比如框架的学习。
猿蜕变同样是一个原创系列文章,帮助你从一个普通的小白,开始掌握一些行业内通用的框架技术知识以及锻炼你对系统设计能力的提升,完成属于你的蜕变,更多精彩内容,敬请大家关注公主号猿人工厂,点击猿人养成获取!
Spring 是于 2003 年兴起的一个轻量级的Java 开发框架,创始人是Rod Johnson,它是为了解决企业应用开发的复杂性问题,随着时代的发展,spring可以说已经成为了一个非标准J2EE规范的事实标准,它主要有这些特点:
方便解耦,简化开发(高内聚低耦合),可以将对象依赖关系的维护交给Spring管理。
IOC(Inversion of Control)控制反转,对象的创建由spring完成,并将创建好的对象注入给使用者。这是一种非侵入式的编程,通过接口来控制实现类。可以轻松的替换实现,控制业务变动导致代码变动带来的影响。
AOP(Aspect Orient Programming)编程的支持,面向切面编程,可以将一些日志,事务等操作从业务逻辑的代码中抽取出来,这样子业务逻辑代码就更加纯净了,并且可以增强日志和事务复用性。声明式事务的支持,只需要通过配置就可以完成对事务的管理,而无需手动编程。
粘合剂一样的特性,方便集成各种优秀框架,其内部提供了对很多优秀框架的直接支持,其实发展到今天是很多框架主动拥抱了它,事实标准不支持,你就很难推销你的框架。非侵入式的编程,使得spring api一般也不会显式调用,假如项目不使用spring了,那么可以很方便的移植到其他框架上。
现在我们使用spring的方式来编写第一个程序吧。
首先我们在pom.xml中增加依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
待会我们会使用测试用例的方式来调用程序,所以我们再加一个junit的依赖。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
第二步,创建和编写接口以及实现类。
package com.pz.study.frame.spring.service;
import com.pz.study.frame.spring.domain.TravelRoute;
/**
* 线路Service
*/
publicinterface TravelRouteService {
/**
* 根据id查询
* @param rid
* @return
*/
public TravelRoute findTravelRouteById(StringtravelRouteId);
}
package com.pz.study.frame.spring.service.impl;
import com.pz.study.frame.spring.domain.TravelRoute;
import com.pz.study.frame.spring.service.TravelRouteService;
publicclass TravelRouteServiceImpl implements TravelRouteService {
@Override
public TravelRoutefindTravelRouteById(String travelRouteId) {
System.out.println("哈哈哈,我在使用"+ travelRouteId+"的方式在调用程序噢");
return new TravelRoute();
}
}
第三步,在项目的Resources目录中新增一个文件ApplicationContext.xml(文件名可以随意取名,只要是xml文件就好,这里我使用的是ApplicationContext.xml)。加入以下内容:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="travelRouteService" class="com.pz.study.frame.spring.service.impl.TravelRouteServiceImpl"/>
</beans>
编写测试用例运行下:
package com.pz.study.frame.spring.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
importorg.springframework.context.support.ClassPathXmlApplicationContext;
importcom.pz.study.frame.spring.service.TravelRouteService;
importcom.pz.study.frame.spring.service.impl.TravelRouteServiceImpl;
publicclass TestTravelRouteService {
@Test
public void testFindTravelRouteByIdOld(){
//JDK的原生方式,需要创建实现类的对象,代码上需要importcom.pz.study.frame.spring.service.impl.TravelRouteServiceImpl
TravelRouteServicetravelRouteService = new TravelRouteServiceImpl();
travelRouteService.findTravelRouteById("JDK");
}
@Test
public void testFindTravelRouteByIdSpring(){
//Sping的方式获取对象 代码上只用依赖接口com.pz.study.frame.spring.service.TravelRouteService
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");
TravelRouteServicetravelRouteService=(TravelRouteService) applicationContext.getBean("travelRouteService");
travelRouteService.findTravelRouteById("Spring");
}
}
大家可以回想以下,我们如果使用JDK的原生方式调用TravelRouteService,那么在程序中必然会由两个依赖:TravelRouteService和TravelRouteServiceImpl这样子使用程序,代码的依赖程度比较高,其实很多时候我们使用接口,仅仅想依赖接口,而不想依赖实现类。我们使用Spring来做到了这一点,将创建TravelRouteService接口实现类的工作,交给了Spring容器,我们再使用TravelRouteService接口的时候,在代码里只用依赖TravelRouteService就好了,程序的依赖减少了,耦合度降低了,对于这一类事情我们叫做解耦。而且在这种框架下编写代码,大家轻松的统一了施工标准,代码的可维护性这方面,不知不觉就提高了噢。
想知道sping是如何来管理这些对象的吗?ApplicationContext和BeanFactory你需要了解一下了。在spring的老版本里,BeanFactory里存放了被spring管理的对象,AplicationContext实际上是BeanFactory的子接口,随着版本的升级AplicationContext承担着托管对象的重要职责。简单点来说ApplicationContext就是spring提供的一个容器,被spring管理的对象,都将被存放在这里。
注入又是怎么做到的,欢迎你回过头去看看,原理嘛,猿人工厂君已经给你讲早就讲清楚了(猿思考系列4——一文学会java的斗转星移),框架的使用,你掌握起来会很轻松的。
spring,作为一个IOC容器是怎样完成依赖注入的,我们可以形象地将容器创建对象并将创建的对象传递给使用者的过程,叫做“装配”。
Spring的注入方式主要有以下几种:
默认方式
Spring IOC容器,默认会使用类的无参构造方法的方式来实例化对象,我们务必要保证使用的类存在无参构造方法!在java中如果一个类不写构造方法,会给类默认存在一个无参构造方法,所以我们的第一个程序没有编写无参构造方法也能运行。
实例工厂
这种方式需要编写一个创建实例的工厂类并且要求类的方法不能是静态的。
package com.pz.study.frame.spring.service.factory;
import com.pz.study.frame.spring.service.TravelRouteService;
import com.pz.study.frame.spring.service.impl.TravelRouteServiceImpl;
public class ServiceBeanFactory {
public TravelRouteService createTravelRouteService(){
return new TravelRouteServiceImpl();
}
}
我们在Spring中配置并使用它。修改applicationContext配置文件,增加以下内容:
<bean id="serviceFactory"class="com.pz.study.frame.spring.service.factory.ServiceBeanFactory"/>
<bean id="serviceFactory" class="com.pz.study.frame.spring.service.factory.ServiceBeanFactory"/>
<bean id="travelRouteServiceByFactory" class="com.pz.study.frame.spring.service.impl.TravelRouteServiceImpl"factory-bean="serviceFactory"factory-method="createTravelRouteService"/>
注意,这种方式需要配置工厂类和工厂方法。factory-bean:使用哪个工厂类的实例。factory-method:使用哪个工厂类的实例的哪个方法。
编写测试用例
@Test
publicvoid testServiceBeanFactory(){
ApplicationContext applicationContext= new ClassPathXmlApplicationContext("applicationContext.xml");
TravelRouteServicetravelRouteService=(TravelRouteService) applicationContext.getBean("travelRouteServiceByFactory");
travelRouteService.findTravelRouteById("Spring");
}
静态工厂:这种方式也需要定义一个工厂类,并且要求提供的创建对象的方法是static修饰的,编写一个静态工厂类:
package com.pz.study.frame.spring.service.factory;
import com.pz.study.frame.spring.service.TravelRouteService;
import com.pz.study.frame.spring.service.impl.TravelRouteServiceImpl;
public class ServiceBeanStaticFactory {
public static TravelRouteService createTravelRouteService() {
return new TravelRouteServiceImpl();
}
}
修改applicationContext配置文件,增加以下内容:
<bean id="serviceStaticFactory"class="com.pz.study.frame.spring.service.factory.ServiceBeanFactory"/>
<bean id="travelRouteServiceByStaticFactory" class="com.pz.study.frame.spring.service.impl.TravelRouteServiceImpl"factory-bean="serviceStaticFactory"factory-method="createTravelRouteService"/>
编写测试用例
@Test
public void testServiceBeanStaticFactory(){
ApplicationContext applicationContext= new ClassPathXmlApplicationContext("applicationContext.xml");
TravelRouteServicetravelRouteService=(TravelRouteService) applicationContext.getBean("travelRouteServiceByStaticFactory");
travelRouteService.findTravelRouteById("Spring");
}
bean的作用域这几个字,看起来又点让人难以理解,我们可以可以简单的理解为IOC容器创建实例的方式。默认是单例的,就是在一个Spring IOC容器里只有一个对象。以下是Spring IOC种Bean的作用域:
singleton:单例模式。Spring IOC容器默认的模式,就是一个bean在一个Spring IOC容器里只有一个实例,也就是一个对象。在bean标签中,不做scope的设置或者将scope属性设置为singleton时,就是单例模式!
prototype:原型模式。每次调用从Spring IOC容器提供的getBean方法,都会新创建一个bean实例,使用时需要在bean标签中将scope属性设置为prototype。
request:针对每一个http请求,都会创建一个新的实例,一般在web应用中使用,使用时需要在bean标签中将scope属性设置为request。
session:针对每一个 HTTP session,都会创建一个新的实例,一般在web应用中使用,使用时需要在bean标签中将scope属性设置为session。
application:在一个web应用中只会创建一个实例!,一般在web应用中使用,使用时需要在bean标签中将scope属性设置为application。
由于各种原因,singleton和prototype的方式使用得相对较多,接下来我们编写个验证程序试一下。至于各种都有哪些呢?以后再告诉你了。
修改配置文件ApplicationContext.xml增加prototype的配置:
<bean id="travelRouteServicePrototype"class="com.pz.study.frame.spring.service.impl.TravelRouteServiceImpl"scope="prototype"/>
编写一个测试用例:
@Test
public void testServicePrototype(){
ApplicationContext applicationContext= new ClassPathXmlApplicationContext("applicationContext.xml");
TravelRouteServicetravelRouteService1=(TravelRouteService) applicationContext.getBean("travelRouteServiceByStaticFactory");
TravelRouteServicetravelRouteService2=(TravelRouteService) applicationContext.getBean("travelRouteServiceByStaticFactory");
TravelRouteServicetravelRouteServicePrototype1=(TravelRouteService) applicationContext.getBean("travelRouteServicePrototype");
TravelRouteServicetravelRouteServicePrototype2=(TravelRouteService) applicationContext.getBean("travelRouteServicePrototype");
System.out.println(travelRouteService1);
System.out.println(travelRouteService2);
System.out.println(travelRouteService1==travelRouteService2);
System.out.println(travelRouteServicePrototype1);
System.out.println(travelRouteServicePrototype2);
System.out.println(travelRouteServicePrototype1==travelRouteServicePrototype2);
}
运行测试用例观察控制台输出:
com.pz.study.frame.spring.service.impl.TravelRouteServiceImpl@3ea909c7
com.pz.study.frame.spring.service.impl.TravelRouteServiceImpl@3ea909c7
true
com.pz.study.frame.spring.service.impl.TravelRouteServiceImpl@4bc107f4
com.pz.study.frame.spring.service.impl.TravelRouteServiceImpl@6df14b06
false
我们可以看出,prototype方式的对象每次地址都不同,说明不是同一个对象噢!
生命周期,会无数次的出现在你的java学习路径上,生命周期总是不时的跳出来套路一下你。无它,只因为了解他们,你才能说得上是了解了一些原理。
bean的生命周期是指对象在容器中从建立到销毁的一个过程。Spring IOC容器中的bean的完整生命周期如下图所示:
总结起来可以分为下面几个阶段:
1、实例化一个Bean
2、按照Spring上下文对实例化的Bean进行配置,也就是IOC注入
3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)
4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(BeanFactory),传递的是Spring工厂自身
5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文
6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,
BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;
7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。
8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法
9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;
10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
其中,我们比较常见的是在创建和销毁程序时加入自己的逻辑处理,我们来演示下。
在TravelRouteServiceImpl类中添加下面方法
public void init() {
System.out.println("我是初始方法init我被执行了");
}
public void destroy() {
System.out.println("我是销毁方法destroy我被执行了");
}
编写验证程序
@Test
public void testServiceDestory(){
ClassPathXmlApplicationContextapplicationContext = newClassPathXmlApplicationContext("applicationContext.xml");
TravelRouteService travelRouteService1=(TravelRouteService)applicationContext.getBean("travelRouteServiceDestory");
applicationContext.destroy();
}
修改applicationContext.xml配置文件,加入配置:
<bean id="travelRouteServiceDestory" class="com.pz.study.frame.spring.service.impl.TravelRouteServiceImpl"init-method="init"destroy-method="destroy"/>
运行测试用例,查看控制台输出
我是初始方法init我被执行了
postProcessAfterInitialization====注意观察,我是在创建对象后被调用的
我是销毁方法destroy我被执行了
关注公号,转发文章到朋友圈的朋友还有一个福利,每月月底工厂君会根据后台记录筛选转发文章前三位的朋友奖励,第一名100元,第二名50元,第三名30元的现金奖励。行动力超强的你,赶紧动起来吧!
我建了一个群,群里有很多高手,欢迎大家入群探讨。