springboot继承抽象类构造函数注⼊_Spring实战1:Spring初
现在的Java程序员赶上了好时候。在将近20年的历史中,Java的发展历经沉浮。尽管有很多为⼈诟病的产品,例如applets、EJB、Java Data Object(JDO)和数不清的⽇志框架,Java还是发展为⼀个庞⼤且丰富的开发平台,很多企业级应⽤都是基于JVM平台构建。Spring是JVM开发平台中的⼀颗明珠。
Spring最开始出现的⽬的是替代企业级开发框架EJB,相⽐EJB,Spring提供更轻量和更易⽤的编程模型。Spring的重要特点是⾮侵⼊式增强POJO(plain old java object)的能⼒。
在后续的发展过程中,EJB也效仿Spring的做法提供了简单的以POJO为中⼼的编程模型,现在的EJB框架也拥有依赖注⼊(DI)和⾯向切⾯编程(AOP)能⼒,可以论证是受Spring成功的影响。
尽管J2EE⼀直在追赶Spring的发展,但是Spring本⾝也没有停⽌进步。现在,Spring在⼀些J2EE刚刚涉⼊或者完全没有涉⼊的领域飞速发展:移动开发、社交API整合、NoSQL数据库、云计算和⼤数据。就⽬前来看,Spring的未来⼀⽚光明。
重要的事情再强调⼀遍:现在的Java程序员赶上了好时候。
这篇⽂章会从⼀个⽐较⾼的层次探索Spring,介绍Spring框架解决了哪些主要问题。
1.1 简化Java开发
Spring是⼀种开源框架,由Rod Johnson发明,并在其著作《Expert One-on-One:J2EE设计与开发》。Spring的初衷是降低企业级开发的复杂性,并试图通过POJO对象实现之前EJB这类重型框架才能实现的功能。Spring不仅仅对服务端开发有⽤,任何Java应⽤都可受益于Spring的简洁、易测试和低耦合等特性。
Spring框架中使⽤beans或JavaBeans来表⽰应⽤程序中的组件,但这并不意味着该组件必须严格满⾜Java Bean的规范。
Spring做了很多事情,但是归根到底是⼀些基本的思路,⽽所有这些思路最终都导向Spring的使命:简化Java开发。
Spring通过下列四种策略来简化Java开发:
基于POJO的轻量级、最⼩侵⼊式开发;
通过依赖注⼊和⾯向接⼝编程实现松耦合;
通过⾯向切⾯编程和惯例实现声明式编程;
通过⾯向切⾯编程和模板消除样板式代码(boierplate code)
⼏乎Spring的每条特性都可以追溯到这四条策略之⼀,接下来分别对这四条策略进⾏阐述,并给出具体的代码说明Spring如何简化Java开发。
1.1.1 激发POJO的能⼒
如果你做Java开发⾜够久,你应该遇到过很多会束缚程序员能⼒的开发框架,这些框架要求程序员继承框架提供的类或者实现它提供的接⼝,例如EJB框架中的session beans,另外,在EJB之前的很多框架中也有类似的侵⼊式编程模型,如Struts、WebWork、Tapestry等等。
Spring尽量避免让⾃⼰的API污染你的应⽤代码。Spring⼏乎不会强制要求开发⼈员实现某个Spring提供的接⼝或者继承某个Spring提供的类,在Spring应⽤中的Java类看起来和普通类⼀样,不过,Spring现在经常使⽤注解来修饰Java类,但是这个类还是⼀个POJO。
举个代码例⼦说明,看如下的HelloWorldBean
package com.spring.sample;public class HelloWorldBean { public String sayHello() { return "Hello World"; }}
可以看出,这就是⼀个简单的Java类-POJO,没有什么特殊的标志表明它是⼀个Spring组件。Spring这种⾮侵⼊式编程模型使得这个类在Spring和⾮Spring框架下具备相同的功能。
尽管形式⾮常简单,POJO的能⼒值却可能⾮常⾼,例如Spring可以通过依赖注⼊编织这些POJOs来激发POJO的能⼒。
1.1.2 依赖注⼊
依赖注⼊听起来⽐较吓⼈,貌似⼀种⾮常复杂的编程技术或者设计模式。实际上依赖注⼊并不复杂,通过在⼯程中应⽤依赖注⼊技术,可以
得到更简单、更容易理解和测试的代码。
How DI works
除了Hello-world级别的程序,稍微复杂⼀点的Java应⽤都需要多个类配合实现功能。⼀般⽽⾔,每个类⾃⼰负责获取它要合作的类对象的
引⽤,这会导致代码⾼度耦合且难以测试。
⾸先看如下代码:
package com.spring.sample.knights;public class DamselRescuingKnight implements Knight { private RescueDamselQuest quest; public DamselRescuingK
可以看出,DamselRescuingKnight在它的构造函数中创建了⾃⼰的Quest实例——RescueDamselQuest实例,这使
得DamselRescuingKnight与RescueDamselQuest紧密耦合,如果需要刺杀Damsel,则这个⼑可以使⽤,但是如果需要刺杀恐龙,则这
个⼑就派不上⽤场了。
更糟的是,给DamselRescuingKnight写单元测试很不⽅便,在这个测试中,你必须确认:当调⽤knight的emarkOnQuest函数
时,quest的embark函数也正确调⽤,但这并不容易。
耦合是⼀头双头怪:⼀⽅⾯,紧耦合的代码难以测试、难以复⽤并且难以理解,并且经常陷⼊“修复⼀个bug但引⼊⼀个新的bug”的开发
怪圈中;另⼀⽅⾯,应⽤程序必须存在适当的耦合,否则该应⽤⽆法完成任何功能。总之,耦合是必要的,但是应该控制组件之间的耦合程
度。
通过使⽤依赖注⼊(DI)技术,对象之间的依赖关系由Spring框架提供的容器进⾏管理,⽽不需要某个对象主动创建⾃⼰需要的引⽤,如下图
所⽰:
依赖注⼊的作⽤
再看⼀个BraveKnight类的例⼦:
package com.spring.sample.knights;public class BraveKnight implements Knight { private Quest quest; public BraveKnight(Quest quest) { // Quest实例被注⼊ this.
该对象不再局限于⼀种quest实例,在构造过程中利⽤构造函数的参数传⼊quest实例,这种类型的依赖注⼊称为构造注⼊。
还有⼀点需要注意,使⽤接⼝定义quest实例,这就是⾯向接⼝编程,使得BraveKnight不再局限于某种特定的Quest实现,这就是DI带来
的最⼤的好处——松耦合。
实现依赖注⼊
在上述例⼦代码可以看出,Spring相当于将依赖注⼊的位置从BraveKnight类中剥离出来,那么具体的依赖注⼊代码如何写呢?开发⼈员如
何规定给BraveKnight注⼊哪个Quest实现,例如SlayDragonQuest?
package com.spring.sample.knights;import java.io.PrintStream;public class SlayDragonQuest implements Quest { private PrintStream stream; public SlayD
在Spirng框架中,最通⽤的⽅法是通过写XML配置⽂件来定义组件之间的依赖关系,如下所⽰:
<?xml version="1.0" encoding="UTF-8"?>
在这个xml配置⽂件中分别定义了BraveKnight和SlayDragonQuest两个bean:在BraveKnight bean的定义中,通过构造器函数传⼊⼀
个SlayDragonQuest的引⽤;在SlayDragonQuest的定义中,通过SpEL语⾔将System.out传⼊它的构造函数。
Spring 3.0引⼊了JavaConfig,这种写法⽐xml⽂件的好处是具备类型安全检查,例如,上⾯XML配置⽂件可以这么写:
package com.spring.fig;import com.spring.sample.knights.BraveKnight;import com.spring.sample.knights.Knight;import com.spring.sam
不论是基于XML的配置还是基于Java⽂件的配置,都由Spring框架负责管理beans之间的依赖关系。
启动依赖注⼊
在Spring应⽤中,由application context负责加载beans,并将这些beans根据配置⽂件编织在⼀起。Spring框架提供了⼏种application context的实现,如果使⽤XML格式的配置⽂件,则使⽤ClassPathXmlApplicationContext;如果使⽤Java⽂件形式的配置⽂件,则使
⽤AnnotationConfigApplicationContext。
package com.spring.sample.knights;import com.spring.fig.KnightConfig;import t.ApplicationContext;import o
上述代码中,根据KnightConfig.java⽂件创建Spring应⽤上下⽂,可以把该应⽤上下⽂看成对象⼯⼚,来获取id knight的bean。
1.1.3 切⾯编程
依赖注⼊(DI)实现了模块之间的松耦合,⽽利⽤⾯向切⾯编程(AOP)可以将涉及整个应⽤的基础功能(安全、⽇志)放在⼀个可复⽤的模块
中。
AOP是⼀种在软件系统中实现关注点分离的技术。软件系统由⼏个模块构成,每个模块负责⼀种功能,
不过在系统中有些需求需要涉及到所有的模块,例如⽇志、事务管理和安全等。如果将这些需求相关的代码都分散在各个模块中,⼀⽅⾯是不⽅便维护、另⼀⽅⾯是与原来每个模块的业务逻辑代码混淆在⼀起,不符合单⼀职责原则。
实现系统级别处理的代码分散在多个⼦模块中,这意味着如果要修改这些处理代码,则要在每个模块中都进⾏修改。即使将这些代码封装到⼀个模块中,在没给个⼦模块中只保留对⽅法的调⽤,这些⽅法调⽤还是在各个模块中重复出现。
业务逻辑代码与⾮核⼼功能的代码混淆在⼀起。例如,⼀个添加address book的⽅法应该只关⼼如何添加address book,⽽不应该关⼼该操作是否安全或者是否能够实现事务处理。
下⾯这张图可以体现这种复杂性,左边的业务逻辑模块与右边的系统服务模块沟通太过密切,每个业务模块需要⾃⼰负责调⽤这些系统服务模块。
业务逻辑模块与系统服务模块过度交互
AOP可以模块化这些系统服务,然后利⽤声明式编程定义该模块需要应⽤到那些业务逻辑模块上。这使得业务模块更简洁,更专注于处理业务逻辑,简⽽⾔之,切⾯(aspects)确保POJO仍然是普通的Java类。
可以将切⾯想象为覆盖在⼀些业务模块上的毯⼦,如下图所⽰。在系统中有⼀些模块负责核⼼的业务逻辑,利⽤AOP可以为所有这些模块增加额外的功能,⽽且核⼼业务模块⽆需知道切⾯模块的存在。
切⾯就像毯⼦⼀样覆盖在⼏个核⼼业务模块之上
AOP实践
继续上⾯的例⼦,如果需要⼀个⼈记录BraveKnight的所作所为,下⾯代码是该⽇志服务:
package com.spring.sample.knights;import java.io.PrintStream;public class Minstrel { private PrintStream stream; public Minstrel(PrintStream stream) { this 然后在XML⽂件中定义Minstrel对应的切⾯:
<?xml version="1.0" encoding="UTF-8"?>
在这个配置⽂件中增加了aop配置名字空间。⾸先定义Minstrel的bean,然后利⽤标签定义aop相关的配置;然后在节点中引⽤minstrel,定义⽅⾯;aspect负责将pointcut和要执⾏的函数(before、after或者around)连接在⼀起。
还有⼀种更先进的写法,利⽤注解和Java配置⽂件,可以参考aop docs
Spring框架中的⼀些⼦模块也是基于AOP实现的,例如负责事务处理和负责安全的模块。
1.1.4 使⽤模板消除重复代码
在编程过程中有没有感觉经常需要写重复⽆⽤的代码才能实现简单的功能,最经典的例⼦是JDBC的使⽤,这些代码就是样板式代码(boilerplate code)。springboot aop
以JDBC的使⽤举个例⼦,这种原始的写法你⼀定见过:
public Employee getEmployeeById(long id) { Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; try { conn = Conne
可以看到,上⾯这么⼀坨代码中只有少数是真正⽤于查询数据(业务逻辑)的。除了JDBC的接⼝,其他JMS、JNDI以及REST服务的客户端API等也有类似的情况出现。
Spring试图通过模板来消除重复代码,这⾥所⽤的是模板设计模式。对于JDBC接⼝,Spring提供了JdbcTemplate模板来消除上⾯那个代码⽚段中的样板式代码,例⼦代码如下:
public Employee getEmployeeById(long id) { return jdbcTemplate.queryForObject( "select id, name from employee where id=?

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。