使用Spring框架的优点
使用Spring框架的优点:简化Java开发
Spring是为了解决企业级应用开发的复杂性而创建的,使用Spring可以让简单的JavaBean实现之前只有EJB才能完成的事情。但Spring不仅仅局限于服务器端开发,任何Java应用都能在简单性、可测试性和松耦合等方面从Spring中获益。
Spring通过以下四种策略降低Java开发的复杂性:
- 基于POJO的轻量级和最小侵入性编程
- 基于切面和惯例进行声明式编程;
- 通过依赖注入和面向接口实现松耦合;
- 通过切面和模板减少样板式代码。
一、基于POJO的轻量级和最小侵入性编程
Spring不会强迫应用继承它的类或实现它的接口从而导致应用与框架绑死,Spring尽量避免因自身的API而弄乱你的应用代码。相反,在基于Spring构建的应用中,它的类通常没有任何痕迹表明你使用了Spring。
如下面一段示例代码:
public class Person {
private String name;
public String getName() {
return name;
}
public Person setName(String name) {
this.name = name;
return this;
}
public void eat(){
System.out.println(name + "吃饭...");
}
}
在main方法中调用work()
方法:
public class Main {
public static void main(String[] args) {
//将Person对象的创建和依赖关系交由Spring管理
ApplicationContext ctx = new AnnotationConfigApplicationContext(AOPConfig.class);
//从Spring容器中获取Person对象
Person Jason = ctx.getBean(Person.class);
Jason.eat();
}
}
执行结果为:
在这段代码中,Person只是一个普通的Java类——POJO,没有任何地方表明它是一个Spring组件,除了main()
方法中初始化Spring容器和获取Person类对象实例的语句,完全看不出Spring的痕迹。
二、基于切面和惯例进行声明式编程;
面向切面编程(Aspect-Oriented Programming, AOP)一方面把功能分离出来形成可重用的组件,另一方面使得开发者更专注于核心业务。
如在Person类的对象吃饭前,需要买菜、烧菜、将菜端上桌,吃完后还要收拾碗筷,若在吃饭中途碗被打碎了,还要处理碎碗,那么Person的eat()
方法代码可能是这样:
public void eat(){
System.out.println("妈妈去菜市场买菜...");
System.out.println("妈妈回家煮饭、烧菜...");
System.out.println("妈妈将香喷喷的饭菜端到饭桌...");
System.out.println("======================");
//核心业务代码
System.out.println(name + "吃饭...");
if(Math.random() > 0.5){ //模拟吃饭时碗被打碎
System.out.println("挑拣碎碗...");
System.out.println("打扫地面...");
}
System.out.println("======================");
System.out.println("妈妈收拾碗筷...");
}
有经验可能是这样:
public void eat(){
prepare();
System.out.println(name + "吃饭...");
if(Math.random() > 0.5){ //模拟吃饭时碗被打碎
handleSmashedBowl();
}
afterEat();
}
private void prepare() {
System.out.println("妈妈去菜市场买菜...");
System.out.println("妈妈回家煮饭、烧菜...");
System.out.println("妈妈将香喷喷的饭菜端到饭桌...");
System.out.println("======================");
}
private void afterEat() {
System.out.println("======================");
System.out.println("妈妈收拾碗筷...");
}
private void handleSmashedBowl() {
System.out.println("挑拣碎碗...");
System.out.println("打扫地面...");
}
而在面向切面编程中是这样:
public void eat(){
System.out.println(name + "吃饭...");
if(Math.random() > 0.5){ //模拟吃饭时碗被打碎
throw new BowlSmashedException();
}
}
可以看到eat()
方法中只保留有核心业务代码,其他的工作则在切面中配置:
@Aspect
public class PersonAOP {
//@Pointcut中的字符串声明Person类的eat方法为一个切点
@Pointcut("execution(* zb.spring.aop.pojo.Person.eat(..))")
public void eat() {
}
//@Around中的值引用上面的eat()方法切点
@Around("eat()")
public void around(ProceedingJoinPoint jp) {
//吃饭前准备
prepare();
try {
//相当于执行Person类的eat方法
jp.proceed();
} catch (Throwable t) {
//处理异常
exceptionHandler(t);
}
//吃完后收拾
afterEat();
}
private void prepare() {
System.out.println("妈妈去菜市场买菜...");
System.out.println("妈妈回家煮饭、烧菜...");
System.out.println("妈妈将香喷喷的饭菜端到饭桌...");
System.out.println("======================");
}
private void afterEat() {
System.out.println("======================");
System.out.println("妈妈收拾碗筷...");
}
//处理异常
private void exceptionHandler(Throwable t) {
System.out.println(t.getMessage());
System.out.println("挑拣碎碗...");
System.out.println("打扫地面...");
}
}
在main()
方法中(有异常时)执行的结果为:
这就是Spring的魔法之一,也就是所谓的面向切面编程(Aspect-Oriented Programming, AOP)
,它可以将功能分离出来,让你从重复、繁琐的样板代码中解脱出来,只需要关注于真正的业务代码。如在实际的应用中,可以使用切面来检测用户登录状态、统一异常处理、处理数据库事务等。
三、通过依赖注入和面向接口实现松耦合;
任何有实际意义的应用都会由两个可者更多的类组成,这些类之前进行协作来完成特定的业务逻辑。按照传统的做法,每个对象负责管理与自己协作的对象的引用,这将导致代码的高度耦合和难以测试。而在Spring的理念中,这些依赖都交由Spring管理,程序员只需要关心真正的逻辑即可。
比如一个Person类的对象要吃饭,程序员不需要知道吃饭这个去作需要依赖于哪些类或对象,只要知道Person类可以吃饭就行了。
思考上面的例子,在main方法中调用Jason对象的eat()
方法时,为什么PersonAOP类的对象知道Jason对象的eat()
方法被调用了呢?(注:注解只是用来标注,并没有任何业务逻辑)
以下是将Person对象Jason和Person的切面对象personAOP交由Spring管理的代码:
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"zb.spring.aop.aspect"})
public class AOPConfig {
/**
* Person类对象的切面,定义
*/
@Bean
public PersonAOP personAOP(){
return new PersonAOP();
}
/**
* 此处代码意思是创建一个name为Jason的Person
* 类对象并将实例放入Spring容器中,具体含义不必深究
*/
@Bean
public Person person() {
return new Person().setName("Jason");
}
}
这样就将Person的对象和PersonAOP的对象交由Spring管理,Spring容器通过扫描Person对象的注解和PersonAOP的注解来读取两者之间的依赖关系。这样就完成了将依赖关系交给Spring容器的操作。(可能还有人问即既然注解只是用来标注的,那么AOPConfig中的配置信息又怎么被Spring发现的呢?还记得在main()方法中有个初始化容器的语句吗,new AnnotationConfigApplicationContext(AOPConfig.class)就是读取AOPConfig类中的配置信息并创建容器(应用上下文))
回到之前的提问,PersonAOP类的对象知道Jason对象的eat()方法被调用了呢?,Person类的对象Jason和PersonAOP的对象都被交由Spring容器,在PersonAOP中使用注解execution(* zb.spring.aop.pojo.Person.eat(..))
标注Person类中的eat方法为一个切点,@Around("eat()")
标注给eat()
方法添加环绕通知,Spring容器读出这些信息后就能在容器中的Jason对象的eat()
方法被执行时先来到被@Around()
标注的方法中。如果Jason对象不是交由Spring管理,而是在外部创建并调用eat()
方法,切面是无法对其产生作用的。
四、通过切面和模板减少样板式代码。
想想在之前开发的过程中是怎样进行数据库操作的?先要加载驱动,然后获取连接、定义预处理语句,然后才是对数据进行操作,而在这之后又要关闭资源,关闭资源时先要进行判空操作,然后再进行关闭,在关闭时甚至还要嵌套一个try catch语句,大量的样板代码,而利用切面,可以将这些操作都放入切面中,只需关注真正的数据操作。
- 感谢你赐予我前进的力量