总述
spring可以做很多事情,它为企业级开发提供了丰富的功能,这些功能的底层都依赖于它的两个特性,依赖注入(dependency injection ,DI)和面向切面编程(aspect-oriented programming,AOP)(干货:spring的底层依赖于它的两个核心特性,DI和AOP)
spring的目标是:全方位简化java开发,为达到这一目标,spring采取了4种关键策略(干货:spring的目标和策略)
1、基于pojo的轻量级和最小侵入性编程;
2、通过依赖注入和面向接口实现松耦合;
3、基于切面和惯例进行声明式编程;
4、通过切面和模板减少样板式代码;
几乎spring所做的所有事情都可以追溯到上述的一条或多条策略
首先我们先基于POJO的最小侵入性编程开始,在spring构建的应用中,它的类通常没有任何痕迹表明你使用的是spring,例如如下代码
package com.platform_manager.t_role.pojo.po;
public class HelloWordBean {
public String sayHello(){
return "Hello word";
}
}
可以看到,这就是一个普通的java类pojo,并看不出它和spring有什么关联,看不出它是一个spring组件。这种非侵入编程模型意味着这个类在spring应用和非spring应用中都可以发挥同样的应用,spring竭力避免因自身的API而弄乱你的应用程序。
尽管该类看起来很简单,但是spring同样可以赋予它魔力,其中一个大招就是DI(干货:依赖注入是一种编程技巧和设计模式理念,它可以使应用对象彼此之间保持松散耦合)
那DI是如何实现的呢?
依赖注入
任何一个程序都是由两个或更多的类组成,这些类相互协作来完成特点的业务逻辑,按照我们传统做法的话,每个对象负责管理与自己相互协作的对象的引用,这将会导致高度耦合和难以测试的代码,举个例子如下:
小张骑士执行杀龙冒险(传统写法)
package com.platform_manager.knight;
/**
* 探秘任务接口
* @version 2016年12月9日 下午2:17:47
* @since JDK 1.6
*/
public interface Quest {
/**
* 执行探秘任务
* @version 2016年12月9日 下午2:19:04
* @since JDK 1.6
*/
public void embark();
}
package com.platform_manager.knight;
/**
* 杀龙冒险活动类
* @version 2016年12月9日 下午2:23:55
* @since JDK 1.6
*/
public class KillLongQuest implements Quest{
@Override
public void embark() {
// TODO Auto-generated method stub
System.out.println("执行杀龙冒险");
}
}
package com.platform_manager.knight;
/**
* 小张骑士
* @version 2016年12月9日 下午2:14:08
* @since JDK 1.6
*/
public class MisZhangKnight {
private KillLongQuest killLongQuest;
public MisZhangKnight(){
killLongQuest=new KillLongQuest();//与killLongQuest紧耦合
}
/**
* 杀龙开始
* @version 2016年12月9日 下午2:21:27
* @since JDK 1.6
*/
public void KnightQuest(){
killLongQuest.embark();
}
}
可以看到杀龙任务紧密的和小张耦合到了一起,因此极大的限制了小张骑士执行探险的能力。如果这个时候有个少女需要救援或者更美好的事情,小张只能爱莫能助;
耦合具有两面性
一方面:紧密耦合的代码难以测试,难以复用,难以理解,并且典型地表现出打地鼠式的bug特性(修复一个bug,将会出现一个或更多新的bug);
)另一方面:一定程度的耦合又是必须的——完全没有耦合的代码什么都做不了;(总而言之,耦合是必须的,但应该被小心谨慎地管理);
(干货:依赖注入会将所依赖的关系自动交给目标对象,而不是对象自己去获取依赖)
为了不让小张再次错失机会,我们采用DI赋予小张万所不能的力量
package com.platform_manager.knight;
/**
* 小张骑士
* @version 2016年12月9日 下午2:14:08
* @since JDK 1.6
*/
public class MisZhangKnight {
private Quest quest;
public MisZhangKnight(Quest quest){//Quest被注入进来
this.quest=quest;
}
/**
* 杀龙开始
* @version 2016年12月9日 下午2:21:27
* @since JDK 1.6
*/
public void KnightQuest(){
quest.embark();
}
}
构造器注入:可以看到不同于之前的代码小张骑士需要new杀龙任务,才能执行杀龙任务,而是在构造的时候将杀龙任务作为参数传入,这就是依赖注入的一种方式;
松耦合:小张有执行任务的能力,执行什么任务都可以;
创建应用组件之间协作的行为通常称为装配。Spring有多种装配Bean的方式,XML配置、Java配置。(干货:Spring组件装配的两种方式)
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
">
<!-- 定义小张对象 -->
<bean id="knight" class="com.platform_manager.knight.MisZhangKnight">
<constructor-arg ref="quest"/>
</bean>
<!-- 定义杀龙任务 -->
<bean id="quest" class="com.platform_manager.knight.KillLongQuest" />
</beans>
小张开始杀龙
package com.platform_manager.knight;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class KnightText {
public static void main(String[] args) {
//通过Spring应用上下文转载装配对象
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("quest.xml");
//从容器中获取MisZhangKnight对象
MisZhangKnight knight=context.getBean(MisZhangKnight.class);
//执行杀龙任务
knight.KnightQuest();
}
}
在装配文件中,我们声明了小张与任务之间的关系,那么我们紧接着观察它们是如何运转起来的,如下图
AOP
DI能够将相互协作的组件进行松散耦合,而面向切面编程允许你把遍布各个组件的功能分离出来形成可重用的组件。
场景:日志、安全控制、事务
容器
Spring容器包括Bean工厂和应用上下文,虽然两者我们都可以使用,但Bean工厂对大多数应用来说太低级了,应用上下文比Bean工厂更受欢迎。