Spring AOP面向切面编程

出处:http://yangfei520.blog.51cto.com/1041581/1273069

前两天,在给新入职的同事做技术介绍时,讲到spring的AOP。使我又一次认识到,对于AOP,特别是spring AOP的理解,虽然大家都能说上来几句,但是许多人认识并不太全面,甚至可以说是一知半解----即使是对于那些已经有过几年开发经验的工程师也是如此。所以,回来之后,我干脆对这块东西做了个肤浅的小结,以便再有类似任务时,直接拿来给大家借鉴。

AOP(Aspect-Oriented Programming)其实是OOP(Object-Oriented Programing)思想的补充和完善。我们知道,OOP引进"抽象"、"封装"、"继承"、"多态"等概念,对万事万物进行抽象和封装,来建立一种对象的层次结构,它强调了一种完整事物的自上而下的关系。但是具体细粒度到每个事物内部的情况,OOP就显得无能为力了。比如日志功能。日志代码往往水平地散布在所有对象层次当中,却与它所散布到的对象的核心功能毫无关系。对于其他很多类似功能,如事务管理、权限控制等也是如此。这导致了大量代码的重复,而不利于各个模块的重用。
   而AOP技术则恰恰相反,它利用一种称为"横切"的技术,能够剖解开封装的对象内部,并将那些影响了多个类并且与具体业务无关的公共行为 封装成一个独立的模块(称为切面)。更重要的是,它又能以巧夺天功的妙手将这些剖开的切面复原,不留痕迹的融入核心业务逻辑中。这样,对于日后横切功能的编辑和重用都能够带来极大的方便。
AOP技术的具体实现,无非也就是通过动态代理技术或者是在程序编译期间进行静态的"织入"方式。下面是这方面技术的几个基本术语:
1、join point(连接点):是程序执行中的一个精确执行点,例如类中的一个方法。它是一个抽象的概念,在实现AOP时,并不需要去定义一个join point。
2、point cut(切入点):本质上是一个捕获连接点的结构。在AOP中,可以定义一个point cut,来捕获相关方法的调用。
3、advice(通知):是point cut的执行代码,是执行“方面”的具体逻辑。
4、aspect(方面):point cut和advice结合起来就是aspect,它类似于OOP中定义的一个类,但它代表的更多是对象间横向的关系。
5、introduce(引入):为对象引入附加的方法或属性,从而达到修改对象结构的目的。有的AOP工具又将其称为mixin。

所有AOP技术基本上都是基于以上这些概念实现的。

太抽象了,还是赶快上例子吧。下面,我写了一个用spring AOP实现的记录方法调用的日志功能的应用实例:目的是记录 系统登录功能 的执行情况,技术框架简单采用Spring+Struts。

1.登录页面:


1

2

3

4

5

<form action="login.do" method="post">

username:<input type="text" name="username" /><br>

password:<input type="password" name="password"><br>

<input type="submit" value="login">

</form>

2.表单类:


1

2

3

4

5

6

7

8

9

10

11

12

/**

*

* <p>[描述信息:登陆表单类]</p>

*

* @author bruce.yang

* @version 1.0 Created on 2013-8-13 下午2:04:09

*/

public class LoginActionForm extends ActionForm {

private String username;

private String password;

//setter,getter方法

}

3.Action类:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

/**

*

* <p>[描述信息:登陆Action]</p>

*

* @author bruce.yang

* @version 1.0 Created on 2013-8-13 下午2:04:38

*/

public class LoginAction extends Action {

private ILoginService loginService;

@Override

public ActionForward execute(ActionMapping mapping, ActionForm form,

HttpServletRequest request, HttpServletResponse response)

throws Exception {

LoginActionForm laf = (LoginActionForm) form;

String username = laf.getUsername();

String password = laf.getPassword();

boolean ret = loginService.login(username, password);

if (ret) {

return mapping.findForward("success");

}

return mapping.findForward("fail");

}

//loginService的setter,getter方法

}

4.service层接口:


1

2

3

4

5

6

7

8

public interface ILoginService {

public boolean login(String userName, String password);

}

public interface ILogService {

public void log();

public void logArg(JoinPoint point);

public void logArgAndReturn(JoinPoint point, Object returnObj);

}

5.service层实现类:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

/**

*

* <p>[描述信息:登录Service类]</p>

*

* @author bruce.yang

* @version 1.0 Created on 2013-8-13 下午1:45:32

*/

public class LoginServiceImpl implements ILoginService {

/**

*

* <p>功能实现描述:登录处理逻辑(这里是假实现)</p>

*

* @see com.bruceyang.login.service.ILoginService#login(java.lang.String, java.lang.String)

* @author: bruce.yang

* @date: Created on 2013-8-13 下午1:45:32

*/

public boolean login(String userName, String password) {

StringBuffer sb=new StringBuffer();

sb.append("Target:").append("login:").append(userName).append(",").append(password);

System.out.println(sb.toString());

return true;

}

}


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

/**

*

* <p>[描述信息:操作日志记录Service]</p>

*

* @author bruce.yang

* @version 1.0 Created on 2013-8-13 下午1:45:32

*/

public class LogServiceImpl implements ILogService {

/**

*

* <p>功能实现描述:最简单的情况</p>

*

* @see com.bruceyang.login.service.ILogService#log()

* @author: bruce.yang

* @date: Created on 2013-8-13 下午1:46:17

*/

public void log() {

System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+" *******Log*********");

}

/**

*

* <p>功能实现描述:有参无返回值的方法</p>

*

* @see com.bruceyang.login.service.ILogService#logArg(org.aspectj.lang.JoinPoint)

* @author: bruce.yang

* @date: Created on 2013-8-13 下午1:43:43

*/

public void logArg(JoinPoint point) {

StringBuffer sb=new StringBuffer();

//获取连接点所在的目标对象

Object obj=point.getTarget();

//获取连接点的方法签名对象

String method=point.getSignature().getName();

//获取连接点方法运行时的入参列表

Object[] args = point.getArgs();

sb.append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())).append(" ");

sb.append(obj.toString().substring(0,obj.toString().indexOf(‘@‘)));

sb.append(".").append(method).append(" ");

sb.append("Args:[");

if (args != null) {

for(int i=0;i<args.length;i++){

Object o=args[i];

sb.append(o);

if(i<args.length-1){

sb.append(",");

}

}

}

sb.append("]");

System.out.println(sb.toString());

}

/**

*

* <p>功能实现描述:有参并有返回值的方法</p>

*

* @see com.bruceyang.login.service.ILogService#logArgAndReturn(org.aspectj.lang.JoinPoint, java.lang.Object)

* @author: bruce.yang

* @date: Created on 2013-8-13 下午1:43:17

*/

public void logArgAndReturn(JoinPoint point, Object returnObj) {

StringBuffer sb=new StringBuffer();

//获取连接点所在的目标对象

Object obj=point.getTarget();

//获取连接点的方法签名对象

String method=point.getSignature().getName();

//获取连接点方法运行时的入参列表

Object[] args = point.getArgs();

sb.append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())).append(" ");

sb.append(obj.toString().substring(0,obj.toString().indexOf(‘@‘)));

sb.append(".").append(method).append(" ");

sb.append("Args:[");

if (args != null) {

for(int i=0;i<args.length;i++){

Object o=args[i];

sb.append(o);

if(i<args.length-1){

sb.append(",");

}

}

}

sb.append("]").append(" ");

sb.append("Ret:[").append(returnObj).append("]");

System.out.println(sb.toString());

}

}

6.web.xml:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

<!-- spring config -->

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>classpath:config/spring/app*.xml</param-value>

</context-param>

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

<!-- struts config -->

<servlet>

<servlet-name>action</servlet-name>

<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>

<init-param>

<param-name>config</param-name>

<param-value>/config/struts/struts-config.xml</param-value>

</init-param>

<init-param>

<param-name>debug</param-name>

<param-value>2</param-value>

</init-param>

<init-param>

<param-name>detail</param-name>

<param-value>2</param-value>

</init-param>

<load-on-startup>2</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>action</servlet-name>

<url-pattern>*.do</url-pattern>

</servlet-mapping>

7.spring配置信息:

applicationContext.xml:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

<?xml version="1.0" encoding="UTF-8"?>

<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-2.0.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop-2.0.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"

xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx">

<bean id="logService" class="com.bruceyang.login.service.impl.LogServiceImpl"></bean>

<bean id="loginService" class="com.bruceyang.login.service.impl.LoginServiceImpl"></bean>

<aop:config>

<!-- 切入点 -->

<aop:pointcut

expression="execution(* com.bruceyang.login.service.impl.Login*.*(..))"

id="myPointcut" />

<!-- 切面: 将哪个对象中的哪个方法,织入到哪个切入点 -->

<aop:aspect id="dd" ref="logService">

<!-- 前置通知

<aop:before method="log" pointcut-ref="myPointcut" />

<aop:after method="logArg" pointcut-ref="myPointcut"/>

<aop:after-returning method="logArgAndReturn" returning="returnObj" pointcut-ref="myPointcut"/>

-->

<aop:before method="log" pointcut-ref="myPointcut" />

<aop:after method="logArg" pointcut-ref="myPointcut"/>

<aop:after-returning method="logArgAndReturn" returning="returnObj" pointcut-ref="myPointcut"/>

</aop:aspect>

</aop:config>

</beans>

7.Struts配置信息:

struts-config.xml:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE struts-config PUBLIC

"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"

"http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">

<struts-config>

<form-beans>

<form-bean name="loginActionForm" type="com.bruceyang.login.web.form.LoginActionForm"/>

</form-beans>

<action-mappings>

<action path="/tologin"

forward="/login.jsp"

/>

<action

path="/login"

type="org.springframework.web.struts.DelegatingActionProxy"

name="loginActionForm"

scope="request"

input="/login.jsp">

<forward name="success" path="/success.jsp"/>

<forward name="fail" path="/login.jsp"/>

</action>

</action-mappings>

<plug-in

className="org.springframework.web.struts.ContextLoaderPlugIn">

<set-property property="contextConfigLocation"

value="/WEB-INF/classes/config/struts/action-servlet.xml" />

</plug-in>

</struts-config>

action-servlet.xml:


1

2

3

4

5

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

<import resource="login-action.xml" />

</beans>

login-action.xml:


1

2

3

4

5

6

7

8

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

<bean name="/login"

class="com.bruceyang.login.web.action.LoginAction">

<property name="loginService" ref="loginService" />

</bean>

</beans>

附:1.目录结构

2.运行结果

其实,spring为我们提供的事务管理等功能也是基于这个逻辑来实现的。并且我们还可以用这种方式实现更加复杂多样的AOP编程。以达到把所有所谓的横切关注点分离出来,一劳永逸的加以实现,以后集中精力解决核心关注点的实现。

本文出自 “夜狼” 博客,请务必保留此出处http://yangfei520.blog.51cto.com/1041581/1273069

时间: 2024-07-31 00:00:39

Spring AOP面向切面编程的相关文章

深入探索spring技术内幕(七): 配置Spring AOP面向切面编程

一. AOP一些概念 Aspect( 切面 ): 指横切性关注点的抽象即为切面, 它与类相似, 只是两者的关注点不一样, 类是对物体特征的抽象, 而切面横切性关注点的抽象. joinpoint( 连接点 ): 指那些被拦截到的点. 在spring中, 这些点指的是方法, 因为spring只支持方法类型的连接点, 实际上joinpoint还可以是field或类构造器) Pointcut( 切入点 ): 指我们要对那些joinpoint进行拦截的定义. Advice( 通知 ): 指拦截到joinp

浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~

简介 我们都知道,Spring 框架作为后端主流框架之一,最有特点的三部分就是IOC控制反转.依赖注入.以及AOP切面.当然AOP作为一个Spring 的重要组成模块,当然IOC是不依赖于Spring框架的,这就说明你有权选择是否要用AOP来完成一些业务. AOP面向切面编程,通过另一种思考的方式,来弥补面向对象编程OOP当中的不足,OOP当中最重要的单元是类,所以万物皆对象,万物皆是 对象类.而在AOP的模块单元中,最基础的单元是切面,切面对切点进行模块化的管理. 最后再提一句:Spring当

Spring AOP 面向切面编程

AOP 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率. 在Spring中提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例

spring AOP面向切面编程学习笔记

一.面向切面编程简介: 在调用某些类的方法时,要在方法执行前或后进行预处理或后处理:预处理或后处理的操作被封装在另一个类中.如图中,UserService类在执行addUser()或updateUser方法前开启事务,执行完后要提交事务:而几乎所有数据库操作都是如此,那么就可以将事务操作的方法提取出封装到一个类里.然后再利用代理类进行处理(目标类方法增强),返回代理类对象 二.AOP相关术语 Target:目标类,需要被增强的类. JoinPoint:连接点,目标类上需要被增强的方法.(这些方法

【Spring系列】Spring AOP面向切面编程

前言 接上一篇文章,在上午中使用了切面做防重复控制,本文着重介绍切面AOP. 在开发中,有一些功能行为是通用的,比如.日志管理.安全和事务,它们有一个共同点就是分布于应用中的多处,这种功能被称为横切关注点(cross-cutting concerns). DI(依赖注入)有助于应用对象之间的解耦,而AOP可以实现横切关注点与他们所影响的对象之间的解耦. 面向切面编程在Spring AOP中有4种类型的调用,方法调用的之前.后.异常增加其他方法,方法调用的前和后调用其他方法,将方法中的参数传递给其

十二.Spring AOP面向切面编程

什么是AOP? AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP的第一个案例 要求:使用AOP实现日志记录功能,核心模块和增强单独开发,运行时组装 1.创建接口HelloDao.HelloService. 创建接口类HelloDaoImpl.HelloServiceImpl并实现dao层接口 public interface IHolleDao { public void print

Spring AOP面向切面编程详解

前言 AOP即面向切面编程,是一种编程思想,OOP的延续.在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等等.在阅读本文前希望您已经对Spring有一定的了解 注:在能对代码进行添加注解方式实现AOP的话,并不推荐使用XML方式.换言之在XML方式配置更适用于不能对代码添加注解的情况下(注解配置方式推荐值>XML配置方式推荐值) AOP相关术语 1.通知(Advice):在切面的某个特定的连接点上执行的动作,即当程序到达一个执行点后会执行相对应的一段代码,也称为增强处理.通知

从源码入手,一文带你读懂Spring AOP面向切面编程

之前<零基础带你看Spring源码--IOC控制反转>详细讲了Spring容器的初始化和加载的原理,后面<你真的完全了解Java动态代理吗?看这篇就够了>介绍了下JDK的动态代理. 基于这两者的实现上,这次来探索下Spring的AOP原理.虽然AOP是基于Spring容器和动态代理,但不了解这两者原理也丝毫不影响理解AOP的原理实现,因为大家起码都会用. AOP,Aspect Oriented Programming,面向切面编程.在很多时候我们写一些功能的时候,不需要用到继承这么

Spring AOP(面向切面编程)

AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理.日志管理.权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性. 简单例子:在某个项目里,非管理员不能对某些业务进行操作,代码如下: 从上面的代码我们可以看出这种方式达到了权限验证的功能,但是如果有100个方法的话,就会显得冗余,代码不好维护,这是传统的硬编码方式 我们对代码进行改进(以注解的方式) 1,新建一个切面(Aspect) @Pointcut是切入点,我需要切入那

详细解读 Spring AOP 面向切面编程(一)

今天我要和大家分享的是 AOP(Aspect-Oriented Programming)这个东西,名字与 OOP 仅差一个字母,其实它是对 OOP 编程方式的一种补充,并非是取而代之.翻译过来就是"面向方面编程",可我更倾向于翻译为"面向切面编程".它听起有些的神秘,为什么呢?当你看完这篇文章的时候,就会知道,我们做的很重要的工作就是去写这个"切面" .那么什么是"切面"呢? 没错!就是用一把刀来切一坨面.注意,相对于面而言,