您的位置:首页 > 编程语言 > Java开发

Spring的AOP-面向切面编程

2017-01-21 15:36 686 查看
这篇文章主要是讲述:什么是Spring中的AOP面向切面编程,以及为什么要使用AOP。

1. 什么是AOP

AOP(百度百科):AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。

比如我们最常见的就是日志记录了,举个例子,我们现在提供一个登录的服务,但是我们希望记录有谁登录过系统了并且记录下详细的信息。如果按照传统的OOP的实现的话,那我们实现了一个用户登录的服务接口(LoginService)和其实现类(LoginServiceImpl.Java),同时为了要进行记录的话,那我们在实现类(LoginServiceImpl.java)中要添加其实现记录的过程。

假如我们要实现的服务有多个呢?那就要在每个实现的类都添加这些记录过程。这样做就会有点繁琐,而且每个实现类都与记录服务日志的行为紧耦合,违反了面向对象的规则。并且一旦我们需要修改日志信息,就要到每个实现类中去修改,这个工作量是非常巨大的。那么怎样才能把记录服务的日志行为与具体的业务处理过程中分离出来呢?看起来好像就是用户登录的服务自己在进行,但却是背后日志记录对这些行为进行记录,并且用户登录的服务不知道存在这些记录过程,这就是我们要讨论AOP的目的所在。

具体的来分析,可以用下面两个图图阐述:

1) 在没有使用AOP的时候,就像下面一样,每一个模块都跟日志、事务这些功能交织在一起,无法分离,而且每当我们修改日志的形式的时候,需要修改非常多的代码,重复的工作量巨大。



2)使用AOP之后,就如下图,输入的数据先经过日志、事务等控制模块,然后再流向实际的业务模块,而日志、事务这些控制模块看起来就像一道切面,切在数据流的中间。



2. 为什么使用AOP

从上面一节其实我们已经可以看出使用AOP的好处:

(1)解耦

(2)重复利用代码

(3)便于装卸

3. 测试实例

下面还是以歌唱者唱歌这个实际的事例来演示使用AOP和不使用AOP的区别所在。

不使用AOP的时候

Song类(不变)

package spring.ch1.topic3;

/**
* Created by louyuting on 17/1/20.
*/
public class Song {
private String name;

public Song(String name) {
this.name = name;
}

@Override
public String toString() {
return "The song : "+ this.name;
}
}


Singer类:

package spring.ch1.topic3;

/**
* Created by louyuting on 17/1/20.
*不使用AOP
*/
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 singSong(){
beforeSing();
System.out.println(song.toString());
afterSing();
}

private void afterSing() {
System.out.println("afterSing");
}

/**
* 没有面向使用AOP时候,模拟日志
* @param args
*/
public static void main(String[] args) {
new Singer(new Song("my heart will gon on")).singSong();
}
}


运行结果:

/***
// output:~
beforeSing
The song : my heart will gon on
afterSing
*/


当我们在千百个模块里面都需要这种操作前和操作后的日志时,如果需要修改日志信息格式,那个时候就是灾难。

上面完整代码的github地址

https://github.com/louyuting/nettyRPC/tree/master/Spring/spring/spring/ch1/topic3

使用AOP之后

Song类必须建立一个默认的构造器,这是由于使用AOP,就必须使用cglib,由cglib决定的。

package spring.ch1.topic4;

/**
* Created by louyuting on 17/1/20.
*
*/
public class Song {
private String name;

//默认构造器
public Song() {
}
//带参构造器
public Song(String name) {
this.name = name;
}

@Override
public String toString() {
return "The song : "+ this.name;
}
}


Singer类只需要删除了两个private方法即可:

package spring.ch1.topic4;

/**
* Created by louyuting on 17/1/20.
* 使用AOP
*/
public class Singer {
private Song song=null;
//默认构造器必须要有
public Singer() {
}

public Singer(Song song) {
this.song = song;
}

public void singSong(){
System.out.println(song.toString());
}
}


增加了一个Log类来模拟日志:

package spring.ch1.topic4;

/**
* Created by louyuting on 17/1/20.
* Log类模拟日志
*/
public class Log {
public void before() {
System.out.println("beforeSing");
}

public void after() {
System.out.println("afterSing");
}
}


Spring的配置文件:增加了aop的配置。

<?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="song1"
class="spring.ch1.topic4.Song">
<constructor-arg value="my heart will go on" />
</bean>

<bean id="song2"
class="spring.ch1.topic4.Song">
<constructor-arg value="there will be" />
</bean>

<bean id="jack"
class="spring.ch1.topic4.Singer">
<constructor-arg ref="song1" />
</bean>

<bean id="log"
class="spring.ch1.topic4.Log">
</bean>

<aop:config>
<aop:aspect ref="log">
<aop:pointcut id="logPointcut"
expression="execution(* spring.ch1.topic4.Singer.*(..))"/>

<aop:before method="before" pointcut-ref="logPointcut"/>
<aop:after method="after" pointcut-ref="logPointcut"/>
</aop:aspect>
</aop:config
</beans>


测试类:

package spring.ch1.topic4;

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;

/**
* Created by louyuting on 17/1/20
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/spring/ch1/topic4/ApplicationContext-test.xml"})
public class SingerTest {
@Autowired
private ApplicationContext applicationContext;

@Test
public void testSinger(){
Singer singer = (Singer)applicationContext.getBean("jack");
singer.singSong();
}
}


运行结果是:

/***
//output:~
beforeSing
The song : my heart will go on
afterSing
*/


上面完整代码的github地址

https://github.com/louyuting/nettyRPC/tree/master/Spring/spring/spring/ch1/topic4

4.aop的优缺点:

1)优点:

(1)切面的定义放在xml里面,我们可以灵活的配置

(2)易于测试

(3)易于装卸

2)缺点:

(1)创建对象的流程麻烦了

(2)由于spring大部分采用反射机制来实现,因此性能一定是个问题
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring aop