01-Spring实战(Spring简化java编程)
1 Spring简化java开发
为了降低Java开发的复杂性,Spring采取了以下4种关键策略:
(1)基于POJO的轻措级和最小侵入性编程;
(2)通过依赖注入和面向接口实现松耦合;
(3)基于切面和惯例进行声明式编程;
(4)通过切面和模板减少样板式代码。
1.1 基于POJO的轻措级和最小侵入性编程
POJO:POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称。
很多框架通过强迫应用继承它们的类或实现它们的接口从而导致应用与框架绑死。一个典型的例子是EJB 2时代的无状态会话bean。早期的EJB是一个很容易想到的例子,不过这种侵入式的编程方式在早期版本的Struts、WebWork、Tapestry以及无数其他的Java规范和框架中都能看到。
Spring竭力避免因自身的API而弄乱你的应用代码。Spring不会强迫你实现Spring规范的接口或继承Spring规范的类,相反,在基于Spring构建的应用中,它的类通常没有任何痕迹表明你使用了Spring。最坏的场景是,一个类或许会使用Spring注解,但它依旧是POJO。
Spring赋予POJO魔力的方式之一就是通过DI来装配它们。
1.2 依赖注入
DI是如何实现的?
任何一个有实际意义的应用(肯定比Hello World示例更复杂)都会由两个或者更多的类组成,这些类相互之间进行协作来完成特定的业务逻辑。按照传统的做法,每个对象负责管理 与自己相互协作的对象(即它所依赖的对象)的引用,这将会导致高度耦合和难以测试的代码。
Spring可以自动创建和管理对象之间的依赖关系。
定义一个类BraveKnight
package sia.knights;
public class BraveKnight implements Knight {
private Quest quest;
public BraveKnight(Quest quest) {
this.quest = quest;
}
public void embarkOnQuest() {
quest.embark();
}
}
。
勇敢的骑士依赖于任务,而这个任务时任意的,例如杀恶龙,拯救公主等
定义一个类SlayDragonQuest
package sia.knights;
import java.io.PrintStream;
public class SlayDragonQuest implements Quest {
private PrintStream stream;
public SlayDragonQuest(PrintStream stream) {
this.stream = stream;
}
public void embark() {
stream.println("Embarking on quest to slay the dragon!");
}
}
把杀恶龙的任务注入到勇敢的骑士,实现两者的写作的行为通常称为装配(wiring)。可以通过XML也可以通过注解完成装配。
通过XML完成装配。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<bean id="knight" class="sia.knights.BraveKnight">
<constructor-arg ref="quest" />
</bean>
<bean id="quest" class="sia.knights.SlayDragonQuest">
<constructor-arg value="#{T(System).out}" />
</bean>
</beans>
尽管BraveKnight依赖于Quest,但是它并不知道传递给它的是什么类型的Quest,也不知道这个Quest来自哪里。与之类似,SlayDragonQuest依赖于PrintStream,但是在编码时它并不需要知道这个PrintStream是什么样子的。只有Spring通过它的配置,能够了解 这些组成部分是如何装配起来的。这样的话,就可以在不改变所依赖的类的情况下,修改依赖关系。
DI是如何工作的?
Spring通过应用上下文(Application Context)装载bean的定义并把它们组装起来。Spring应用上下文全权负责对象的创建和组装。Spring自带了多种应用上下文的实现,它们之间主要的区别仅仅在于如何加载配置。
package sia.knights;
import org.springframework.context.support.
ClassPathXmlApplicationContext;
public class KnightMain {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext(
"META-INF/spring/knight.xml");
Knight knight = context.getBean(Knight.class);
knight.embarkOnQuest();
context.close();
}
}
这里的main()方法基于knights.xml文件创建了Spring应用上下文。随后它调用该应用上下文获取一个ID为knight的bean。得到Knight对象的引用后,只需简单调用embarkOnQuest() 方法就可以执行所赋予的探险任务了。注意这个类完全不知道我们的英雄骑士接受哪种探险任务,而且完全没有意识到这是由BraveKnight来执行的。只有knights.xml文件知道哪个骑士执行哪种探险任务。
对象的创建和装配(依赖)是交给spring管理的,依赖关系管理通过配置的XML(或注解)。
1.3 应用切面进行声明式编程
面向切面编程往往被定义为促使软件系统实现关注点的分离一项技术。系统由许多不同的组 件组成,每一个组件各负责一块特定功能。除了实现自身核心的功能之外,这些组件还经常 承担着额外的职责。诸如日志、事务管理和安全这样的系统服务经常融入到自身具有核心业 务逻辑的组件中去,这些系统服务通常被称为横切关注点,因为它们会跨越系统的多个组件。
(1)减少代码重复:实现系统关注点功能的代码将会重复出现在多个组件中。这意味着如果你要改变这些关 注点的逻辑,必须修改各个模块中的相关实现。即使你把这些关注点抽象为一个独立的 模块,其他模块只是调用它的方法,但方法的调用还是会重复出现在各个模块中。
(2)只关注自身业务逻辑:组件会因为那些与自身核心业务无关的代码而变得混乱。一个向地址簿增加地址条目的方法应该只关注如何添加地址,而不应该关注它是不是安全的或者是否需要支持事务。
AOP应用
每一个人都熟知骑士所做的任何事情,这是因为吟游诗人用诗歌记载了骑士的事迹并将其进 行传唱。假设我们需要使用吟游诗人这个服务类来记载骑士的所有事迹。程序清单1.9展示了我们会使用的Minstrel类。
package sia.knights;
import java.io.PrintStream;
public class Minstrel {
private PrintStream stream;
public Minstrel(PrintStream stream) {
this.stream = stream;
}
public void singBeforeQuest() {
stream.println("Fa la la, the knight is so brave!");
}
public void singAfterQuest() {
stream.println("Tee hee hee, the brave knight " +
"did embark on a quest!");
}
}
将Minstrel声明为一个切面
<?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="knight" class="sia.knights.BraveKnight">
<constructor-arg ref="quest" />
</bean>
<bean id="quest" class="sia.knights.SlayDragonQuest">
<constructor-arg value="#{T(System).out}" />
</bean>
<bean id="minstrel" class="sia.knights.Minstrel">
<constructor-arg value="#{T(System).out}" />
</bean>
<aop:config>
<aop:aspect ref="minstrel">
<aop:pointcut id="embark"
expression="execution(* *.embarkOnQuest(..))"/>
<aop:before pointcut-ref="embark"
method="singBeforeQuest"/>
<aop:after pointcut-ref="embark"
method="singAfterQuest"/>
</aop:aspect>
</aop:config>
</beans>
每个模块只用关系自身的业务逻辑,系统级业务逻辑通过切面的形式织入。
1.4 使用模板消除样板式代码
举个例子,如果你曾经用过JDBC,那么你或许会写出类似下面的代码。
首先你需要创建一个数据库连接,然后再创建一个语句对象,最后你才能进行查询。为了平息JDBC 可能会出现的怒火,你必须捕捉SQLException,这是一个检查型异常,即使它抛出后你也做不了太多事情。最后,毕竟该说的也说了,该做的也做了,你不得不清理战场,关闭数据库连接、语句和结 果集。同样为了平息JDBC可能会出现的怒火,你依然要捕捉SQLException。
使用Spring模板减少重复的代码之后
通过模板避免代码的重复编写 。
推荐阅读
-
01-Spring实战(Spring简化java编程)
-
spring之AspectJ面向切面编程 博客分类: java springbootaop
-
Java并发编程实战纠错与部分翻译优化 博客分类: 读书纠错 java
-
手写Servlet 到 Spring MVC 的简化之路 博客分类: java高级架构 springmvcjava
-
Java并发编程原理与实战视频课程 java
-
Java学习笔记-全栈-web开发-14-Spring入门&AOP编程&Spring注解开发&Spring事务
-
Java并发编程实战 读书笔记(一)
-
第1章—Spring之旅—简化Spring的java开发
-
使用Spring的JAVA Mail支持简化邮件发送功能
-
Java编程枚举类实战代码分享