Spring实践之1:开始使用Spring
来源:互联网 发布:知乎 撩妹技巧 编辑:程序博客网 时间:2024/06/05 23:47
- 简介
- 从一个小程序开始
- 创建工程
- 实现功能
- 运行程序
- 使用Spring
- 工欲善其事必先利其器
- IOCDIAOP
- 改造
- 遗留问题
简介
此“Spring实践”系列是笔者在开发过程中对Spring相关使用经验的简单总结,欢迎各位读者与笔者讨论或斧正。
从一个小程序开始
假设我们需要开发一个程序,主要功能是向Product Manager询问某个product的开发进度,而Product Manager又转而询问Software Engineer。
创建工程
首先我们使用maven创建一个简单的Java工程:
$ mvn archetype:generate[INFO] Scanning for projects...[INFO][INFO] ------------------------------------------------------------------------[INFO] Building Maven Stub Project (No POM) 1[INFO] ------------------------------------------------------------------------[INFO][INFO] >>> maven-archetype-plugin:2.4:generate (default-cli) > generate-sources @ standalone-pom >>>[INFO][INFO] <<< maven-archetype-plugin:2.4:generate (default-cli) < generate-sources @ standalone-pom <<<[INFO][INFO] --- maven-archetype-plugin:2.4:generate (default-cli) @ standalone-pom ---[INFO] Generating project in Interactive mode[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)Choose archetype:1: remote -> am.ik.archetype:maven-reactjs-blank-archetype (Blank Project for React.js)2: remote -> am.ik.archetype:msgpack-rpc-jersey-blank-archetype (Blank Project for Spring Boot + Jersey)3: remote -> am.ik.archetype:mvc-1.0-blank-archetype (MVC 1.0 Blank Project)……1895: remote -> us.fatehi:schemacrawler-archetype-plugin-command (-)1896: remote -> us.fatehi:schemacrawler-archetype-plugin-dbconnector (-)1897: remote -> us.fatehi:schemacrawler-archetype-plugin-lint (-)Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 1007:Choose org.apache.maven.archetypes:maven-archetype-quickstart version:1: 1.0-alpha-12: 1.0-alpha-23: 1.0-alpha-34: 1.0-alpha-45: 1.06: 1.1Choose a number: 6: 6Define value for property 'groupId': : practice.springDefine value for property 'artifactId': : spriceDefine value for property 'version': 1.0-SNAPSHOT: : 1.0Define value for property 'package': practice.spring: : practice.spring.spriceConfirm properties configuration:groupId: practice.springartifactId: spriceversion: 1.0package: practice.spring.sprice Y: : y[INFO] ----------------------------------------------------------------------------...[INFO] BUILD SUCCESS...
这里笔者使用了“mvn archetype:generate”没有附带参数的用法,所以maven进入了Interactive Mode,然后我们选择了“maven-archetype-quickstart”这个archetype,指定groupId,artifactId,version,package分别为practice.spring,sprice,1.0和practice.spring.sprice。
自动生成的App是一个HelloWorld程序,在这个基础上,我们开始增加需要的功能。
sprice即“spring practice”
实现功能
在这个程序里,笔者定义了两个interface:Manager和Engineer,并使用两个class:ProductManager和SoftwareEngineer来分别实现它们。
Manager.java
package practice.spring.sprice;public interface Manager { String queryProgress(String productName);}
Engineer.java
package practice.spring.sprice;public interface Engineer { String reportProgress(String productName);}
ProductManager.java
package practice.spring.sprice;public class ProductManager implements Manager { private Engineer engineer = new SoftwareEngineer(); @Override public String queryProgress(String productName) { return this.engineer.reportProgress(productName); }}
SoftwareEngineer.java
package practice.spring.sprice;import org.apache.commons.math3.random.RandomDataGenerator;public class SoftwareEngineer implements Engineer { @Override public String reportProgress(String productName) { int progress = (new RandomDataGenerator()).nextInt(1, 100); return String.format("%d%%", progress); }}
可以看到ProductManager的queryProgress()调用了SoftwareEngineer的reportProgress(),而后者从1到100中随机了一个值作为工作进度返回。
修改App.java,调用这些新的模块。
App.java
package practice.spring.sprice;public class App { public static void main(String[] args) { (new App()).run(); } public void run() { String productName = "3D-Map"; Manager manager = new ProductManager(); String progress = manager.queryProgress(productName); System.out.println(progress); }}
由于代码中引用了“org.apache.commons.math3.random.RandomDataGenerator”,所以需要在pom.xml中增加dependency:
pom.xml
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-math3</artifactId> <version>3.6.1</version></dependency>
编译这个工程:
$ mvn clean package...
编译完成后,在target文件夹下会生成一个.jar文件:
$ ls ./target/classes/ maven-archiver/ maven-status/ sprice-1.0.jar surefire-reports/ test-classes/
此时这个sprice-1.0.jar还不能直接使用“java -jar”命令运行,因为:
1. Manifest中没有主清单属性。
2. 依赖的“org.apache.commons.math3.random.RandomDataGenerator”在.jar文件中不存在。
所以接下来我们需要对pom.xml做适当更改,以使程序运行起来。
运行程序
关于如何创建可运行的fat-jar,请参考maven打包可运行的fat-jar的简单方法。
修改之后,就可以编译运行了:
$ mvn clean package...
$ ls ./target/archive-tmp/ classes/ maven-archiver/ maven-status/ sprice-1.0.jar sprice-1.0-jar-with-dependencies.jar surefire-reports/ test-classes/$ java -jar ./target/sprice-1.0-jar-with-dependencies.jar29%
使用Spring
接下来就是使用Spring来改造sprice这个小程序了。但在这之前,还是要先探讨一下,为什么要使用Spring来搭建程序呢?
工欲善其事必先利其器
软件设计里面有一个重要原则:强内聚,低耦合。在此基础上,重用性、鲁棒性、可维护性等等是评价一个软件设计是否优秀的重要指标。
在软件设计过程中,有很多种对应的方法来达成这些目标。Spring遵循的是“控制反转/依赖注入+面向切面编程”(IOC/DI+AOP)。
IOC/DI+AOP
关于“IOC/DI+AOP”这些概念的定义,在网上可以搜索到很多。如果列举它到底要做什么,那么大概有以下几点:
1. Framework扮演了Container(容器)的角色。
2. 使用者负责定义各个组件以及它们的配置方法。
3. Container负责创建、装配组件和管理组件的生命周期。
4. 以编织(weaving)的方式扩展组件功能。
以sprice中的ProductManager为例,ProductManager对SoftwareEngineer对象的使用存在两个问题:
1. Engineer对象成为了ProductManager的一个组成部分(即组合关系)。
2. ProductManager与Engineer的子类SoftwareEngineer形成了强耦合。
所以接下来使用Spring对sprice这个小程序做一些改造,以达到强内聚、低耦合的目的。
改造
首先把ProductManager与SoftwareEngineer解耦并增加Setter:
package practice.spring.sprice;public class ProductManager implements Manager { private Engineer engineer; @Override public String queryProgress(String productName) { return this.engineer.reportProgress(productName); } public void setEngineer(Engineer engineer) { this.engineer = engineer; }}
然后增加一个Spring Context的配置文件“ApplicationContext.xml”:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="softwareEngineer" class="practice.spring.sprice.SoftwareEngineer"> </bean> <bean id="productManager" class="practice.spring.sprice.ProductManager"> <property name="engineer" ref="softwareEngineer" /> </bean></beans>
在“ApplicationContext.xml”中我们配置为在容器中创建两个bean“softwareEngineer”和“productManager”,然后在“App.java”里面就可以使用这个xml创建ApplicationContext并获得bean:
注:ClassPathXmlApplicationContext会加载jar包中指定路径的文件。若配置文件在jar包之外,则可以使用FileSystemXmlApplicationContext,文件路径可以是绝对路径或相对路径(这个相对路径不是配置文件相对于jar包的路径,而是配置文件相对于运行java命令之工作目录的路径)。
package practice.spring.sprice;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class App { public static final String PRODUCT_NAME = "3D-Map"; public static void main(String[] args) { String xmlPath = "context/ApplicationContext.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); Manager manager = (Manager)applicationContext.getBean("productManager"); String progress = manager.queryProgress(PRODUCT_NAME); System.out.println(progress); }}
在“pom.xml”中添加对“org.springframework.context”的依赖:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.9.RELEASE</version></dependency>
此时整个项目的文件目录结构为:
│ pom.xml│└─src ├─main ├─java │ └─practice │ └─spring │ └─sprice │ App.java │ Engineer.java │ Manager.java │ ProductManager.java │ SoftwareEngineer.java │ └─resources └─context ApplicationContext.xml
编译运行:
$ mvn clean package...$ java -jar ./target/sprice-1.0-jar-with-dependencies.jar一月 15, 2017 6:55:20 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@17a7cec2: startup date [Sun Jan 15 18:55:20 CST 2017]; root of context hierarchy一月 15, 2017 6:55:21 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions信息: Loading XML bean definitions from class path resource [context/ApplicationContext.xml]51%
遗留问题
在改造过程中,虽然ProductManager与SoftwareEngineer解耦了,但是它们仍然是聚合关系,这其实是不符合现实中产品经理与软件工程师的真实关系的。
这里仅提出一种解决方案:增加一个类Group(产品小组),Group聚合Engineer,而Manager关联Group和Engineer。
- Spring实践之1:开始使用Spring
- 开始spring boot 实践第一节
- 开始使用Spring Boot
- 开始使用spring
- spring学习笔记1——开始spring之旅
- spring入门(1)---开始spring之旅
- 开始使用 Spring Data JPA(1)
- Spring学习之路--开始学spring
- spring学习笔记:开始使用spring
- 开始使用 Spring Data JPA
- 数据处理---Spring Batch之实践
- Spring REST实践之HATEOAS
- 从 Spring Cloud 开始,聊聊微服务架构的实践之路
- Spring Security3.1实践
- Spring Security3.1实践
- Spring Security3.1实践
- 开始spring
- 开始spring
- 开始写博客
- 模板方法模式
- hadoop学习之MapReduce笔记
- 质数无上界的证明
- 213. House Robber II
- Spring实践之1:开始使用Spring
- 关于Struts2基础案例总是出现404错误的解决方法
- Source Insight基本使用和快捷键
- mysql基础学习(1)
- 将Eclipse中的项目提交到GitHub上
- POJ2352
- leetcode --18. 4Sum
- hiho一下 第163周 希尔伯特曲线
- 每日一则JavaWeb---Spring的OncePerRequestFilter的作用