《Spring实战》学习笔记(1)——Spring介绍
最近准备重头开始再系统地学习一遍Spring框架,使用《Spring实战》这本书为了加深印象并方便之后高效的复习,所以有了写这个文章的打算。如果有哪里写的不好还请各位大神批评指正。
Spring是为了解决企业级应用开发的复杂性而创建的一个开源框架。采取了以下四种策略来降低Java开发复杂性:
- 基于POJO的轻量级和最小侵入性编程
- 通过依赖注入和面向接口实现松耦合
- 基于切面和惯例进行声明式编程
- 通过切面和模板减少样板式代码
一、非侵入编程模型:
Spring不会强制我们去实现Spring规范的接口或类,最多需要我们去使用注解。对于一个普通的Java类,我们不需要使用侵入性的编程去破坏它。
二、依赖注入(DI)
用以下几个类做例子:
+---java
| +---enemy
| | ArtilleryCorps.java 炮车兵
| | Enemy.java 敌人接口
| | MeleeCreeps.java 近战兵
| | RemoteSoldier.java 远程兵
| |
| \---hero
| ADHero.java AD英雄
| APHero.java AP英雄
| Hero.java 英雄接口
|
\---resources
注意Hero与Enemy的关系:
public class APHero implements Hero {
private MeleeCreeps enemy;
public APHero() {
this.enemy = new MeleeCreeps(); // 与MeleeCreeps(近战兵)高耦合
}
public void killEnemy() {
enemy.dead();
}
}
可以看到,APHero在构造函数中创建了MeleeCreeps,使得它与MeleeCreeps类耦合度很高,导致APHero只能击杀(killEnemy方法)MeleeCreeps,如果想要击杀其他小兵就无法做到了。
而使用依赖注入,不需要Hero再来创建或管理对Enemy的依赖关系了,Spring将会自动将Enemy交给Hero。
所以代码将可以变成这样:
public class ADHero implements Hero{
private Enemy enemy;
public ADHero(Enemy enemy) {
this.enemy = enemy;
}
public void killEnemy() {
enemy.dead();
}
}
对比之前的APHero,ADHero并没有自己创建enemy,而是将其作为构造方法的参数传入进来,并且传入的类型是Enemy,所以不论传入的是什么类型的小兵,ADHero都能将其击杀。这种注入方式称为构造器注入。
既然ADHero可以接收任意一种Enemy的实现类了,那就要考虑如何将Enemy传给他。
这是将要注入到ADHero中的MeleeCreeps类
public class MeleeCreeps implements Enemy {
public void dead() {
System.out.println("近战兵被击杀");
}
}
创建应用组件之间协作的行为通常称为装配(wiring)。
Spring有主要三种装配方式:
- 在Java中显式配置
- 在XML中显式配置
- 隐式bean发现机制和自动装配
暂时只介绍前两种
1.在Java中显式配置
@Configuration
public class HeroConfig {
// 创建MeleeCreeps bean
@Bean
public Enemy enemy(){
return new MeleeCreeps();
}
// 注入MeleeCreeps到ADHero中
@Bean
public Hero hero(){
return new ADHero(enemy());
}
}
2.在XML中显式配置
<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">
<!-- 创建MeleeCreeps bean -->
<bean id="meleeCreeps" class="enemy.MeleeCreeps">
</bean>
<!-- 注入MeleeCreeps到ADHero中 -->
<bean id="adHero" class="hero.ADHero">
<constructor-arg ref="meleeCreeps"></constructor-arg>
</bean>
</beans>
三、面向切面编程(AOP)
如果要增加一个新闻播报功能,在英雄击杀敌人前后给出提示,该怎么样实现呢?
public class Broadcast {
// killEnemy方法之前调用
public void promptBeforeKill(){
System.out.println("英雄准备击杀敌人了");
}
// killEnemy方法之后调用
public void promptAfterKill(){
System.out.println("英雄击杀了敌人");
}
}
使用之前的依赖注入修改ADHero,如下:
public class ADHero implements Hero{
private Enemy enemy;
private Broadcast broadcast;
public ADHero(Enemy enemy,Broadcast broadcast) {
this.enemy = enemy;
this.broadcast = broadcast;
}
public void killEnemy() {
broadcast.promptBeforeKill();
enemy.dead();
broadcast.promptAfterKill();
}
}
这样实现乍一看似乎没有问题,但仔细想想,Broadcast不该由ADHero去管理。播报本身就是Broadcast该做的事情,不需要由ADHero去控制它来做事。
所以要解决这个问题,就需要使用AOP,将Broadcast抽象成一个切面。
<!-- 创建MeleeCreeps bean -->
<bean id="meleeCreeps" class="enemy.MeleeCreeps">
</bean>
<!-- 注入MeleeCreeps到ADHero中 -->
<bean id="adHero" class="hero.ADHero">
<constructor-arg ref="meleeCreeps"></constructor-arg>
</bean>
<!-- 创建Broadcast bean -->
<bean id="broadcast" class="broadcast.Broadcast"></bean>
<!-- AOP配置 -->
<aop:config>
<!-- 将 Broadcast bean声明为切面 -->
<aop:aspect ref="broadcast">
<!-- 定义切点 -->
<aop:pointcut id="killEnemy" expression="execution(* *.killEnemy(..))"/>
<!-- 前置通知 -->
<aop:before method="promptBeforeKill" pointcut-ref="killEnemy"></aop:before>
<!-- 后置通知 -->
<aop:before method="promptAfterKill" pointcut-ref="killEnemy"></aop:before>
</aop:aspect>
</aop:config>
在killEnemy方法前调用的,称为前置通知,在killEnemy方法后调用的,称为后置通知。
对比之前的方式,ADHero不需要再去管理Broadcast,可以说ADHero完全不知道Broadcast,而且我们不需要去改动Broadcast的代码,只是在xml里进行配置就可以达到抽象成切面的目的。
四、使用模板消除样板式代码
比如编写JDBC代码进行查询数据库操作时,会出现大量的样板式代码:创建数据库连接,创建语句对象,查询之后还要关闭连接等等。而使用Spring的JdbcTemplate可以避免这样的重复代码。
五、关于Bean
在Spring应用中,对象都生存在Spring容器中。Spring要创建对象,装配对象并管理对象的整个生命周期。
bean的生命周期:

六、总结
这一章对Spring的功能特点有了整体的认识了解,核心还是DI和AOP。之后会依次对每个知识点展开学习。

浙公网安备 33010602011771号