(1) Spring是什么?
spring的基本框架主要包含六大模块:DAO、ORM、AOP、JEE、WEB、CORE
Spring DAO:Spring提供了对JDBC的操作支持:JdbcTemplate模板工具类 。
Spring ORM:Spring可以与ORM框架整合。例如Spring整合Hibernate框架,其中Spring还提供HibernateDaoSupport工具类,简化了Hibernate的操作 。
Spring WEB:Spring提供了对Struts、Springmvc的支持,支持WEB开发。与此同时Spring自身也提供了基于MVC的解决方案 。
Spring AOP:Spring提供面向切面的编程,可以给某一层提供事务管理,例如在Service层添加事物控制 。
Spring JEE:J2EE开发规范的支持,例如EJB 。
Spring Core:提供IOC容器对象的创建和处理依赖对象关系 。
什么是依赖倒置原则?假设我们设计一辆汽车:先设计轮子,然后根据轮子大小设计底盘,接着根据底盘设计车身,最后根据车身设计好整个汽车。这里就出现了一个“依赖”关系:汽车依赖车身,车身依赖底盘,底盘依赖轮子。但这种设计维护性很低。
换一种思路:我们先设计汽车的大概样子,然后根据汽车的样子来设计车身,根据车身来设计底盘,最后根据底盘来设计轮子。这时候,依赖关系就倒置过来了:轮子依赖底盘,底盘依赖车身,车身依赖汽车。
控制反转就是依赖倒置原则的一种代码设计的思路。具体采用的方法就是所谓的依赖注入。这几种概念的关系大概如下
由此我们可以看到,仅仅是为了修改轮胎的构造函数,这种设计却需要修改整个上层所有类的构造函数!在软件工程中,这样的设计几乎是不可维护的——在实际工程项目中,有的类可能会是几千个类的底层,如果每次修改这个类,我们都要修改所有以它作为依赖的类,那软件的维护成本就太高了。
所以我们需要进行控制反转(IoC),即上层控制下层,而不是下层控制着上层。我们用依赖注入(Dependency Injection)这种方式来实现控制反转。所谓依赖注入,就是把底层类作为参数传入上层类,实现上层类对下层类的“控制”。这里我们用构造方法传递的依赖注入方式重新写车类的定义:
这里我只需要修改轮胎类就行了,不用修改其他任何上层类。这显然是更容易维护的代码。
这里我们采用的构造函数传入的方式进行的依赖注入。其实还有另外两种方法:Setter传递和接口传递,核心思路都是一样的,都是为了实现控制反转。
那什么是控制反转容器(IoC Container)呢?其实上面的例子中,对车类进行初始化的那段代码发生的地方,就是控制反转容器。
因为采用了依赖注入,在初始化的过程中就不可避免的会写大量的new。这里IoC容器就解决了这个问题。这个容器可以自动对你的代码进行初始化,你只需要维护一个Configuration(可以是xml,也可以是一段代码),而不用每次初始化一辆车都要亲手去写那一大段初始化的代码。这是引入IoC Container的第一个好处。
IoC Container的第二个好处是:我们在创建实例的时候不需要了解其中的细节。在上面的例子中,我们自己手动创建一个车instance时候,是从底层往上层new的。
(1) 面向切面编程(AOP)完善spring的依赖注入(DI),面向切面编程在spring中主要表现为两个方面 1.面向切面编程提供声明式事务管理 2.spring支持用户自定义的切面 。
(2)面向切面编程(aop)是对面向对象编程(oop)的补充, 面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面。
(3)AOP从程序运行角度考虑程序的结构,提取业务处理过程的切面,oop是静态的抽象,aop是动态的抽象, 是对应用执行过程中的步骤进行抽象,,从而获得步骤之间的逻辑划分。
想象下面的场景,开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?显然,没有人会靠“复制粘贴”吧。在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。
AOP 要达到的效果是,保证开发者不修改源代码的前提下,去为系统中的业务组件添加某种通用功能。AOP 的本质是由 AOP 框架修改业务组件的多个方法的源代码,看到这其实应该明白了,AOP 其实就是前面一篇文章讲的代理模式的典型应用。
按照 AOP 框架修改源代码的时机,可以将其分为两类:
首先添加
创建一个接口 IBuy.java
package com.sharpcj.aopdemo.test1;
public interface IBuy {
String buy();
}
Boy 和 Gril 两个类分别实现了这个接口:Boy.java
package com.sharpcj.aopdemo.test1;
import org.springframework.stereotype.Component;
@Component
public class Boy implements IBuy {
@Override
public String buy() {
System.out.println("男孩买了一个游戏机");
return "游戏机";
}
}
Girl.java
package com.sharpcj.aopdemo.test1;
import org.springframework.stereotype.Component;
@Component
public class Girl implements IBuy {
@Override
public String buy() {
System.out.println("女孩买了一件漂亮的衣服");
return "衣服";
}
}
配置文件, AppConfig.java
package com.sharpcj.aopdemo;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackageClasses = {com.sharpcj.aopdemo.test1.IBuy.class})
public class AppConfig {
}
测试类, AppTest.java
package com.sharpcj.aopdemo;
import com.sharpcj.aopdemo.test1.Boy;
import com.sharpcj.aopdemo.test1.Girl;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AppTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Boy boy = context.getBean("boy",Boy.class);
Girl girl = (Girl) context.getBean("girl");
boy.buy();
girl.buy();
}
}
运行结果:
这里运用SpringIOC里的自动部署。现在需求改变了,我们需要在男孩和女孩的 buy 方法之前,需要打印出“男孩女孩都买了自己喜欢的东西”。
我们看到,结果与我们需求一致,我们并没有修改 Boy 和 Girl 类的 Buy 方法,也没有修改测试类的代码,几乎是完全无侵入式地实现了需求。这就是 AOP 的“神奇”之处。
此外还可以通过注解配置 Spring AOP等,有兴趣的小伙伴可以去寻找一些例子,我的主页里有常用的注解用法。
以上是我个人的理解,如有不对请多指教。