这一章节我们来介绍一下什么是AOP?为什么需要面向切面编程?
1.什么是AOP?(百度百科版)
AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
比如我们最常见的就是日志记录了,举个例子,我们现在提供一个查询学生信息的服务,但是我们希望记录有谁进行了这个查询。如果按照传统的OOP的实现的话,那我们实现了一个查询学生信息的服务接口(StudentInfoService)和其实现 类 (StudentInfoServiceImpl.java),同时为了要进行记录的话,那我们在实现类(StudentInfoServiceImpl.java)中要添加其实现记录的过程。
假如我们要实现的服务有多个呢?那就要在每个实现的类都添加这些记录过程。这样做就会有点繁琐,而且每个实现类都与记录服务日志的行为紧耦合,违反了面向对象的规则。那么怎样才能把记录服务的行为与业务处理过程中分离出来呢?看起来好像就是查询学生的服务自己在进行,但却是背后日志记录对这些行为进行记录,并且查询学生的服务不知道存在这些记录过程,这就是我们要讨论AOP的目的所在。
笔者认为是下面两个图:
在没有使用aop的时候,就像上面,每一个模块都跟日志、事务这些功能交织在一起,无法分离,而且每当我们修改日志的形式的时候,需要修改非常多的代码。
当我们使用了aop,就如上图,输入的数据先进过日志、事务等控制模块,然后再流向实际的业务模块,而日志、事务这些控制模块看起来就像一道切面,切在数据流的中间。
2.为什么需要面向切面编程?
(1)解耦
(2)重复利用代码
(3)便于装卸
(注意,由于代码比较多,而且我是使用maven形式构建项目,因此,具体的代码可以到我的github去查看,本文最底部有我的github地址)
在没有使用aop之前的代码:
package com.raylee.my_new_spring.my_new_spring.ch01.topic_1_2;
public class Song {
private String name;
public Song(String name) {
this.name = name;
}
@Override
public String toString() {
return "the song:" + name;
}
}
package com.raylee.my_new_spring.my_new_spring.ch01.topic_1_2;
public class Singer {
private Song song = null;
public Singer() {
}
public Singer(Song song) {
this.song = song;
}
private void beforeSing() {
System.out.println("beforeSing");
}
public void singTheSong() {
beforeSing();
System.out.println(song.toString());
afterSing();
}
private void afterSing() {
System.out.println("afterSing");
}
public static void main(String[] args) {
new Singer(new Song("my heart will go on")).singTheSong();
}
}
输出:
beforeSing
the song:my heart will go on
afterSing
当我们在千百个模块里面都需要这种操作前和操作后的日志需要修改,那个时候就是灾难。
使用aop之后:
package com.raylee.my_new_spring.my_new_spring.ch01.topic_1_2;
public class Singer {
private Song song = null;
public Singer() {
}
public Singer(Song song) {
this.song = song;
}
public void singTheSong() {
System.out.println(song.toString());
}
}
Singer类只需要删除了两个private方法即可
package com.raylee.my_new_spring.my_new_spring.ch01.topic_1_2;
public class Song {
private String name;
public Song(String name) {
this.name = name;
}
public Song() {
}
@Override
public String toString() {
return "the song:" + name;
}
}
Song类需要建立一个默认的构造器,这是由于使用aop,就必须使用cglib,由cglib决定
增加了一个Log类来模拟日志:
package com.raylee.my_new_spring.my_new_spring.ch01.topic_1_2;
public class Log {
public void before() {
System.out.println("beforeSing");
}
public void after() {
System.out.println("afterSing");
}
}
测试类:
package com.raylee.my_new_spring.my_new_spring.ch01.topic_1_2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
"/com/raylee/my_new_spring/my_new_spring/ch01/topic_1_2/ApplicationContext-test.xml" })
public class SingerTest {
@Autowired
private ApplicationContext applicationContext;
@Test
public void testJackSinger() {
Singer singer = (Singer) applicationContext.getBean("jack");
singer.singTheSong();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="jack"
class="com.raylee.my_new_spring.my_new_spring.ch01.topic_1_2.Singer">
<constructor-arg ref="song" />
</bean>
<bean id="song"
class="com.raylee.my_new_spring.my_new_spring.ch01.topic_1_2.Song">
<constructor-arg value="there will be" />
</bean>
<bean id="log"
class="com.raylee.my_new_spring.my_new_spring.ch01.topic_1_2.Log">
</bean>
<aop:config>
<aop:aspect ref="log">
<aop:pointcut
expression="execution(* com.raylee.my_new_spring.my_new_spring.ch01.topic_1_2.Singer.*(..))"
id="logPointcut" />
<aop:before method="before" pointcut-ref="logPointcut" />
<aop:after method="after" pointcut-ref="logPointcut" />
</aop:aspect>
</aop:config>
</beans>
主要是配置文件的修改,增加了aop配置类。
输出:
beforeSing
the song:there will be
afterSing
3.aop的优缺点:
优点:
(1)切面的定义放在xml里面,我们可以灵活的配置
(2)易于测试
(3)易于装卸
缺点:
(1)创建对象的流程麻烦了
(2)由于spring大部分采用反射机制来实现,因此性能一定是个问题
(3)由于对象的定义放在xml,对于使用eclipse来重构就会比较麻烦
总结:这一章节我们主要介绍了什么是AOP?为什么需要面向切面编程?
我的github:https://github.com/raylee2015/my_new_spring