spring 第一篇(1-1):让java开发变得更简单(下)
切面(aspects)应用
DI能够让你的软件组件间保持松耦合,而面向切面编程(AOP)能够让你捕获到在整个应用中可重用的组件功能。在软件系统中,AOP通常被定义为提升关注点分离的一个技术。系统由很多的组件组成,每个组件负责一部分的功能。但是这些组件往往除了核心功能外,还有些额外的责任。比如像日志,事务管理和安全这些系统服务会被引进到组件中。这些服务通常被称为横切关注点(cross-cutting-concerns),因为它们常常贯穿于多个组件中。
在多个组件中传播这些概念,会有两个复杂层面内容将被引进到你的代码中
- 实现系统级概念代码将会在多个组件中重复出现,这意味着如果你需要改变这些概念的话,你必须修改多个组件的内容。即使你将这些概念抽象到一个独立的方法中来使它成为一个单一的方法,但是方法的调用还是会出现在多个地方。
- 你的组件现在充斥着和核心代码无关的功能。
下图展示的它的复杂度。左边的业务对象和右边的系统服务紧密的关联在一起。
AOP能够使这些服务成为模块并且很方便的使用它们(声明方式),来达到在组件中消除这些影响。这样让组件更有粘合性,使它们只关注于自己具体的业务,完全忽略系统服务。简而言之,切面让POJOS保持简单。
你可以将切面设想为覆盖了很多组件的毛毯,如下图所示。一个应用由多个实现业务功能的模块组成。使用AOP,你可以使用这些功能来覆盖你的核心应用。这些层可以通过声明的方式来贯穿到你的应用中,而你核心代码根本不用知道它们的存在。这是一个强大的概念,因为它将应用核心业务和系统服务独立开来。
下面我们来看个具体的例子,现在是个骑士的故事,故事如何流传下来呢,当然是通过歌手的歌声来传递
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是个简单的类并有两个方法。singBeforeQuest()方法在骑士开始探索的之前被调用,而singAfterQuest()方法应当在骑士探索结束后调用。在这两个用例中,Minstrel通过在构造器中注入的PrintSteam来歌颂骑士的事迹。
现在你只需要注入BraveKnight就能使用,让我们微调下BraveKnight来使用Minstrel。下面展示如何将他们合并在一起
public class BraveKnight implements Knight {
private Quest quest;
private Minstrel minstrel;
public BraveKnight(Quest quest, Minstrel minstrel) {
this.quest = quest;
this.minstrel = minstrel;
}
public void embarkOnQuest() throws QuestException {
minstrel.singBeforeQuest();
quest.embark();
minstrel.singAfterQuest();
}
}
它应该能够运行,你要做的就是回到你的配置文件中,然后声明一个Minstrel bean并且注入到BraveKnight的构造器中。但是等等...是否需要在knight中来管理minstrel?minstrel类不是只要关注于自己的功能?因为knight需要知道minstrel存在。这将迫使你将Minstrel注入到BraveKnight中,这样不仅使BraveKnight的代码变得复杂,而且用户可能需要个没有minstrel的knight。如果Minstrel为空?是否需要进行空值逻辑检测?
但是如果你使用AOP,minstrel和knight只需要关心各自的事情。要将Minstrel转化成一个切面,你只需要在Spring配置文件中声明它。下面在原来的knights.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="knight" class="com.springinaction.knights.BraveKnight">
<constructor-arg ref="quest" />
</bean>
<bean id="quest" class="com.springinaction.knights.SlayDragonQuest">
<constructor-arg value="#{T(System).out}" />
</bean>
<bean id="minstrel" class="com.springinaction.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>
上面使用spring aop命名空间配置来声明Minstrel作为一个切面。首先你声明Minstrel作为一个bean。然后在<aop:aspect>元素中引用它。接着进一步对切面定义,你使用<aop:before>来声明在执行embarkOnQuest()执行前调用singBeforeQuest方法,这个叫做前置通知。使用<aop:after>来声明在执行embarkOnQuest()执行后调用singAfterQuest方法,这个叫做后置通知。在这两个用例中,pointcut-ref属性都引用了叫embark的切点。这个切点在前面的<ponitcut>元素中定义,并且它有个叫做expression属性,该属性用来选择那些地方需要使用通知。这个表达式语法是AspectJ的切点表达式语言。
现在先不要关心AspectJ表达式细节,以后的章节会详细谈论。现在你应该有点感知切面在spring中是如何使用的,是的只要一点点的XML配置,你就能将minstrel转成spring的切面。现在你应该从这个例子中能获取到两点认识。
- 首先,Minstrel仍然是一个POJO ---- 没有任何指示能表明它是一个切面。但实际上它却是spring context中的一个切面
- 其次,更重要的是,Minstrel可以被用到BraveKnight中,而不用BraveKnight明确的去调用。事实上,BraveKnight完全不关心Minstrel是否存在
这里需要指出的是,在使用Spring魔法将Minstrel转换成一个切面前,需要将它声明为Spring的bean。所以只要注入依赖就能就其他的spring bean也变成一个切面。当然这个例子只是简单的spring aop应用,后面我们将介绍它在声明式事务和安全方面中的应用。接下来我们再来看一种spring使java开发变得更简单的方式。
使用模板消除样板代码
样板代码指的是你经常一次次编写相似的代码来完成普通、简单的任务。有很多Java APIs存在着样板代码。一个常见的样板代码就是使用JDBC来查询数据库操作。如果你有用过JDBC来操作的话,你可能会经常写下类似下面的一段代码
public Employee getEmployeeById(long id) {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();
stmt = conn.prepareStatement(
"select id, firstname, lastname, salary from " +
"employee where id=?");
stmt.setLong(1, id);
rs = stmt.executeQuery();
Employee employee = null;
if (rs.next()) {
employee = new Employee();
employee.setId(rs.getLong("id"));
employee.setFirstName(rs.getString("firstname"));
employee.setLastName(rs.getString("lastname"));
employee.setSalary(rs.getBigDecimal("salary"));
}
return employee;
} catch (SQLException e) {
} finally {
if(rs != null) {
try {
rs.close();
} catch(SQLException e) {}
}
if(stmt != null) {
try {
stmt.close();
} catch(SQLException e) {}
}if(conn != null) {
try {
conn.close();
} catch(SQLException e) {}
}
}
return null;
}
如你所见,你只是想查询id为某个值的employee数据,而这里却要写一堆的JDBC操作。首先你要创建个connection,然后创建statement,接着获取查询结果。而且,你还要捕获SQLException异常。最终当所有操作完成后,你必须清除乱七八糟的东西,关闭connection,statement和result set。很显然,上面的代码你可能有经历过。查询employee只要一条语句,但是却要进行大量的JDBC样板操作。
JDBC不是唯一的样板代码,很多活跃的代码也存在类似的问题,比如JMS,JNDI和REST客服端。Spring通过将样板代码封装到模板中来消除这些重复代码。Spring的JDBCTemplate使不用传统的JDBC来操作数据库成为可能。
举个例子,使用Spring的SimpleJdbcTemplate(利用java 5特性来实现的一个JDBCTemplate),getEmployeeById()方法可以被重写为,如何获取employee数据而不用在考虑JDBC API操作。下面是更新后的代码
public Employee getEmployeeById(long id) {
return jdbcTemplate.queryForObject(
"select id, firstname, lastname, salary " +
"from employee where id=?",
new RowMapper<Employee>() {
public Employee mapRow(ResultSet rs,
int rowNum) throws SQLException {
Employee employee = new Employee();
employee.setId(rs.getLong("id"));
employee.setFirstName(rs.getString("firstname"));
employee.setLastName(rs.getString("lastname"));
employee.setSalary(rs.getBigDecimal("salary"));
return employee;
}
},id);
}
如你所见,这个版本的getEmployeeById()更加的简单,它只关注于如何进行数据库查询employee操作。模板的queryForObject()只需要一个SQL query语句,以及如何将数据封装到domain object的RowMapper,你看不到任何的JDBC样板代码,所有的样板代码都会在模板中进行处理。
这里我们使用spring DI、aspect、template来使复杂的java开发变得简单。上面我们举例说明如何配置bean和aspect。下面我们就来解释下这些文件如何加载以及将他们加载到什么地方去。
spring 第一篇(1-1):让java开发变得更简单(下)的更多相关文章
- spring 第一篇(1-1):让java开发变得更简单(下)转
spring 第一篇(1-1):让java开发变得更简单(下) 这个波主虽然只发了几篇,但是写的很好 上面一篇文章写的很好,其中提及到了Spring的jdbcTemplate,templet方式我之前 ...
- spring 第一篇(1-1):让java开发变得更简单(上)
1.释放POJOS能量 传统开发中是如何束缚POJOS呢,如果你开发过java很长时间,那你一定有接触过EJB的开发.那时候开发一个小小的功能都要扩展框架的类或者实现其接口.所以你很容易在早期的Str ...
- GVIM与模板——让FPGA开发变得更简单
还在使用FPGA开发环境自带的代码编辑器?还在逐个字母敲击冗长重复的代码?明德扬至简设计法让你快速提高代码编写效率!利用GVIM这一高效的编辑工具并添加自定义模板,通过简短的脚本命令即可自动生成所有常 ...
- .net敏捷开发框架 力软敏捷开发(learun) 让开发变的更简单
版本:6.1.6.2 体验地址:www.fishcmonkey.com 联系QQ:6539471
- .net敏捷开发框架 力软(learun) 让开发变的更简单
版本:6.1.6.2 体验地址:www.fishcmonkey.com 联系QQ:6539471
- spring 第一篇(1-3):鸟瞰spring蓝图
如你所见,spring框架的核心是关注于如何使用DI.AOP和模板来让企业级java开发变得更简单.spring确实也是这样做的,所以很值得你去使用它.不过spring内容可能比你所能看到的要多很多. ...
- 第一篇:微信公众平台开发实战Java版之了解微信公众平台基础知识以及资料准备
相信很多人或多或少听说了微信公众平台的火热.但是开发还是有一点门槛,鉴于挺多朋友问我怎么开发,问多了,自己平时也进行以下总结.所以下面给大家分享一下我的经验: 微信公众号是什么? 官网的介绍:再小的个 ...
- JVM学习第一篇思考:一个Java代码是怎么运行起来的-上篇
JVM学习第一篇思考:一个Java代码是怎么运行起来的-上篇 作为一个使用Java语言开发的程序员,我们都知道,要想运行Java程序至少需要安装JRE(安装JDK也没问题).我们也知道我们Java程序 ...
- 初学Java ssh之Spring 第一篇
之前虽然毕业前实习的工作是使用的C# .NET语言,但是,毕业后还是果断应聘Java.虽然自己对Java的理解不如C#深入,只是对基础知识比较熟悉,但还是义无返顾了··· 虽然应聘经历比较坎坷,但最终 ...
随机推荐
- HDU 3600 Simple Puzzle 归并排序 N*N数码问题
先介绍八数码问题: 我们首先从经典的八数码问题入手,即对于八数码问题的任意一个排列是否有解?有解的条件是什么? 我在网上搜了半天,找到一个十分简洁的结论.八数码问题原始状态如下: 1 2 3 4 5 ...
- u3d 模型ID配置
换模型为什么要改代码 0.0 你没有逻辑ID->模型的配置么. 做2个配置.. 分别是角色的ID对应模型路径. 然后是里面的动画名对应真实动画名. 比如 ID=1 path = "xx ...
- jQuery on(),live(),trigger()
jQuery on()方法是官方推荐的绑定事件的一个方法. $(selector).on(event,childSelector,data,function,map); 由此扩展开来的几个以前常见的方 ...
- html5压缩图片并上传
手机端图片有很大的,上传的时候很慢,这时候就要压缩一下了,有一个开源的js可以压缩图片的大小,开源地址如下:https://github.com/think2011/localResizeIMG3 代 ...
- extjs ajax请求与struts2进行交互
sencha extjs 5 增加一个struts2的配置,这样可以在设置好前台布局之后,与后台交互获取数据显示.现在有一个问题是struts2对于url的跳转action支 持比较良好,但是对于像E ...
- SpringMVC Controller介绍(转)
SpringMVC Controller 介绍 一.简介 在SpringMVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理 ...
- python logging 模块
我有几个项目中使用了 sentry 捕获 ERROR 级别的日志,现在遇到一个问题:本地调试的时候,日志设置中,所有的 handler(包括 root) 都只打到 console 上面,但是本地调试中 ...
- oracle系统包—-dbms_output用法
dbms_output包主要用于调试pl/sql程序,或者在sql*plus命令中显示信息(displaying message)和报表,譬如我们可以写一个简单的匿名pl/sql程序块,而该块出于某种 ...
- WebAPI2使用Autofac实现IOC属性注入完美解决方案
一.前言 只要你是.NETer你一定IOC,IOC里面你也会一定知道Autofac,上次说了在MVC5实现属性注入,今天实现在WebApi2实现属性注入,顺便说一下autofac的程序集的注入方式,都 ...
- BinaryWrite方法输出验证码
在创建网站中验证码是不可或缺的.可以利用BinaryWrite输出二进制图像的方法输出验证码. 在开发图形验证码时,首先生成验证码,然后绘制成图像,最后通过该方法输出到页面中.所以熟练地掌握该方法可以 ...