3、Spring的AOP详解和案例

AOP(Aspect Oriented Programming),即面向切面编程。

1、OOP回顾

在介绍AOP之前先来回顾一下大家都比较熟悉的OOP(Object Oriented Programming)。OOP主要是为了实现编程的重用性、灵活性和扩展性。它的几个特征分别是继承、封装、多态和抽象。OOP重点体现在编程架构,强调的是类之间的层次关系。

2、OOP缺陷

为了更好的说明OOP的概念,我们接下来讲一个OOP的实例,重点分析OOP存在哪些缺陷,以便更好的理解AOP的相关内容。

先看如下的一张图:

上面这张图有三个类:Dog,Cat和Duck,他们都有一个方法run。按照OOP的设计理念,我们很容易就会想到抽象出一个Animal父类,同时让这三个子类继承Animal父类。这样的设计可以用如下的图示表示:

在OOP思想中,我们会使用大量的类似上图的编程方式,对类进行抽象、继承、封装和多态来实现编程的重用性、灵活性和扩展性。但是这样的编程仍然有一定的局限性,有时候,OOP并不能很好解决我们再实际开发中遇到的问题。为了说明这个问题,看下面的图示:

看到上面的图,我们暂时还不能发现有什么问题。为了大家便于理解,接下来我来给大家讲解一下上面类图的实现过程。描述如下:马戏团有一条表演的小狗,这条小狗可以跑和跳,但是它完成跑和跳两个动作之前必须是在接到驯兽师发出的命令后,同时完成跑和跳的动作之后,驯兽师会给与响应的奖励,比如一块肉。

了解了实现过程之后,我们在来看一下具体的代码。

    public class Dog {
        public void run() {
            System.out.println("驯兽师发出命令!")
            System.out.println("小狗开始跑!");
            System.out.pringln("驯兽师给与奖励");
        }
        public void jump() {
            System.out.println("驯兽师发出命令!")
            System.out.println("小狗开始跳!");
            System.out.pringln("驯兽师给与奖励");
        }
    }

    仔细看上面的代码,我们可以看出在run方法和jump方法中,存在一些相同的内容(驯兽师发出命令和给与奖励),这些内容并不能完全进行抽象,即不能按照OOP编程思想进行处理。类似这样的情况同样会出现在我们编程中的很多地方,例如:日志记录、性能统计、安全控制、事务处理、异常处理等等。但是这样的情况该如何解决呢?这就引入了AOP编程思想。

3、AOP简介

AOP为Aspect Oriented Programming的缩写,即面向切面编程(也叫面向方面),是一种可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。

4、AOP实现实例

为了大家更好的理解AOP如何实现,接下来我们优化一下上述代码。

首先是Dog类

    public interface Animal {
        public void run();
        public void jump();
    }

    public class Dog implements Animal{
        public void run(){
            System.out.println("小狗开始跑!");
        }
        public void jump(){
            System.out.println("小狗开始跳!");
        }
    }

对比之前的代码我们可以明显看出,我们将关于驯兽师的相关内容从run和jump中进行了抽取,接下来,我们如何在程序运行中将关于驯兽师的动作加入到程序中呢?这就是我们这次用到的AOP实现的核心技术动态代理(Dynamic Proxy)。具体代码如下:

    public class MyProxy  implements InvocationHandler{
        private Object targetObject;
        public Object createProxyInstance(Object targetObject) {
            this.targetObject = targetObject;
            return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                                   targetObject.getClass().getInterfaces(),
                                   this);
        }

        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            command();
            Object ret = method.invoke(targetObject, args);
            award();
            return ret;
        }

        private void command() {
            System.out.println("驯兽师发出命令!");
        }

        private void award(){
            System.out.println("驯兽师给与奖励!");
        }
    }

  上述代码实现完成之后,我们改如何调用呢?参考代码如下:

    public class Client {
        public static void main(String[] args) {
            MyProxy hander = new MyProxy();
            Animal dog = (Animal)hander.createProxyInstance(new Dog());
            dog.run();
            dog.jump();
        }
    }

  执行结果如下:

关于AOP编程的实例演示就完成了,接下来重新回顾一下AOP与OOP的相关概念。

5、AOP与OOP的关系

OOP针对业务处理过程的实体(Dog、Cat、Duck)及其属性和行为(run)进行抽象封装,以获得更加清晰高效的逻辑单元划分。而AOP则是针对业务处理过程中(run或jump)的切面(command和award)进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。

一、AOP案例如下:

1、创建如下项目结构

2、在com.entity包下创建User.java

 1 package com.entity;
 2
 3 public class User {
 4     private Integer id; // 用户ID
 5     private String username; // 用户名
 6     private String password; // 密码
 7     private String email; // 电子邮件
 8
 9     // getter & setter
10     public Integer getId() {
11         return id;
12     }
13
14     public void setId(Integer id) {
15         this.id = id;
16     }
17
18     public String getUsername() {
19         return username;
20     }
21
22     public void setUsername(String username) {
23         this.username = username;
24     }
25
26     public String getPassword() {
27         return password;
28     }
29
30     public void setPassword(String password) {
31         this.password = password;
32     }
33
34     public String getEmail() {
35         return email;
36     }
37
38     public void setEmail(String email) {
39         this.email = email;
40     }
41
42     @Override
43     public String toString() {
44         return "User [email=" + email + ", id=" + id + ", password=" + password
45                 + ", username=" + username + "]";
46     }
47
48
49
50 }

User.java

3、在com.dao包下创建IUserDao.java

1 package com.dao;
2
3 import com.entity.User;
4
5 public interface IUserDao {
6     public void save(User user);
7 }

IUserDao.java

4、在com.dao.impl包下创建UserDaoImpl.java

 1 package com.dao.impl;
 2
 3 import com.dao.IUserDao;
 4 import com.entity.User;
 5 /**
 6  * 用户DAO类,实现IDao接口,负责User类的持久化操作
 7  */
 8 public class UserDaoImpl implements IUserDao{
 9      /**
10       * 保存
11       */
12     public void save(User user) {
13         // 这里并未实现完整的数据库操作,仅为说明问题
14         System.out.println("保存用户信息到数据库");
15     }
16 }

UserDaoImpl.java

5、在com.biz包下创建IUserBiz.java

1 package com.biz;
2
3 import com.entity.User;
4
5 public interface IUserBiz {
6     public void addNewUser(User user);
7 }

IUserBiz.java

6、在com.biz.impl包下创建UserBizImpl.java

 1 package com.biz.impl;
 2
 3 import com.biz.IUserBiz;
 4 import com.dao.impl.UserDaoImpl;
 5 import com.entity.User;
 6
 7
 8 /**
 9  * 用户业务类,实现对User功能的业务管理
10  */
11 public class UserBizImpl implements IUserBiz {
12
13     // 声明接口类型的引用,和具体实现类解耦合
14     private UserDaoImpl dao;
15
16
17     public void addNewUser(User user) {
18         //调用用户dao的方法保存用户信息
19         dao.save(user);
20
21     }
22
23     public UserDaoImpl getDao() {
24         return dao;
25     }
26
27     public void setDao(UserDaoImpl dao) {
28         this.dao = dao;
29     }
30
31
32     public UserBizImpl() {
33     }
34
35     public UserBizImpl(UserDaoImpl dao) {
36         this.dao = dao;
37     }
38
39
40
41
42 }

UserBizImpl.java

7、在com.aop包下创建LoggerBefore.java

 1 package com.aop;
 2
 3 import java.lang.reflect.Method;
 4 import java.util.Arrays;
 5
 6 import org.apache.log4j.Logger;
 7 import org.springframework.aop.MethodBeforeAdvice;
 8
 9 /**
10  * 通过MethodBeforeAdvice实现前置增强
11  */
12 public class LoggerBefore implements MethodBeforeAdvice {
13     private static final Logger log = Logger.getLogger(LoggerBefore.class);
14
15     public void before(Method method, Object[] arguments, Object target)
16             throws Throwable {
17         // Arrays.toString()数组内容转换为字符串
18         log.info("调用 " + target + "的" + method.getName() + "方法。方法传入参数:"
19                 + Arrays.toString(arguments));
20     }
21
22 }

LoggerBefore.java

8、在com.aop包下创建LoggerAfterReturning.java

 1 package com.aop;
 2
 3 import java.lang.reflect.Method;
 4 import org.apache.log4j.Logger;
 5 import org.springframework.aop.AfterReturningAdvice;
 6 /**
 7  * 通过AfterReturningAdvice实现后置增强
 8  */
 9 public class LoggerAfterReturning implements AfterReturningAdvice {
10     private static final Logger log = Logger.getLogger(LoggerAfterReturning.class);
11
12     public void afterReturning(Object returnValue, Method method,
13             Object[] arguments, Object target) throws Throwable {
14         log.info("调用 " + target + "的" + method.getName() + " 方法方法返回值为"
15                 + returnValue);
16     }
17
18 }

LoggerAfterReturning.java

9、在src下创建applicationContext.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:aop="http://www.springframework.org/schema/aop"
 5     xsi:schemaLocation="http://www.springframework.org/schema/beans
 6     http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
 7     http://www.springframework.org/schema/aop
 8     http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
 9     <!-- 实例化接口对象 -->
10     <bean id="userDao" class="com.dao.impl.UserDaoImpl"></bean>
11
12     <!-- 实例化业务对象 -->
13     <bean id="biz" class="com.biz.impl.UserBizImpl">
14         <!-- 注入方式1:设置注入接口对象 -->
15         <!--
16         <property name="dao" ref="userDao"/>
17         -->
18         <!-- 注入方式2:构造注入对象 -->
19         <!-- type表示参数的类型,index表示参数的位置索引 -->
20         <constructor-arg index="0" ref="userDao" />
21     </bean>
22
23      <!-- 实例化日志前置增强对象 -->
24     <bean id="loggerBefore" class="com.aop.LoggerBefore"></bean>
25
26     <!-- 实例化日志后置增强对象 -->
27     <bean id="loggerAfterReturning" class="com.aop.LoggerAfterReturning"></bean>
28
29     <!--
30       面向切面编程的配置即将某个功能动态的配置到某个流程中,而不改变后台代码
31       实现业务代码和日志代码是完全分离的,经过配置后,不做任何代码的修改,就在addNewUser方法前后实现了日志输出
32     -->
33     <aop:config>
34         <!-- 切入点的配置 -->
35         <aop:pointcut id="pointcut"
36             expression="execution(public void addNewUser(com.entity.User))" />
37        <!-- 将增强处理和切入点结合, -->
38         <aop:advisor pointcut-ref="pointcut" advice-ref="loggerBefore" />
39         <aop:advisor pointcut-ref="pointcut" advice-ref="loggerAfterReturning" />
40     </aop:config>
41 </beans>

applicationContext.xml

10、在src下创建log4j.properties

1 # rootLogger是所有日志的根日志,修改该日志属性将对所有日志起作用
2 # 下面的属性配置中,所有日志的输出级别是info,输出源是console
3 log4j.rootLogger=info,console
4 # 定义输出源的输入位置是控制台
5 log4j.appender.console=org.apache.log4j.ConsoleAppender
6 # 定义输出日志的布局采用的类
7 log4j.appender.console.layout=org.apache.log4j.PatternLayout
8 # 定义日志输出布局
9 log4j.appender.console.layout.ConversionPattern=%d %p [%c]%n - %m%n

log4j.properties

11、在com.test包下创建Test.java

 1 package com.test;
 2
 3 import org.springframework.context.ApplicationContext;
 4 import org.springframework.context.support.ClassPathXmlApplicationContext;
 5
 6 import com.biz.IUserBiz;
 7 import com.entity.User;
 8
 9 public class Test {
10
11     public static void main(String[] args) {
12         //读取和加载xml配置文件
13         ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
14         //获取配置文件的bean的实例
15         IUserBiz biz = (IUserBiz) ctx.getBean("biz");
16
17         //实例化User对象
18         User user = new User();
19         user.setId(1);
20         user.setUsername("test");
21         user.setPassword("123456");
22         user.setEmail("[email protected]");
23
24         //添加信息
25         biz.addNewUser(user);
26     }
27
28 }

Test.java

12、运行结果如下:

时间: 2024-10-23 18:52:01

3、Spring的AOP详解和案例的相关文章

Spring的AOP详解

Spring的AOP详解 一.AOP基础 1.1AOP是什么 考虑这样一个问题:需要对系统中的某些业务做日志记录,比如支付系统中的支付业务需要记录支付相关日志,对于支付系统可能相当复杂,比如可能有自己的支付系统,也可能引入第三方支付平台,面对这样的支付系统该如何解决呢? 传统解决方案 1.日志部分定义公共类LogUtils,定义logPayBegin方法用于记录支付开始日志, logPayEnd用于记录支付结果 logPayBegin(long userId,long money) logPay

Spring Framework AOP详解

此前对于AOP的使用仅限于声明式事务,除此之外在实际开发中也没有遇到过与之相关的问题.最近项目中遇到了以下几点需求,仔细思考之后,觉得采用AOP来解决.一方面是为了以更加灵活的方式来解决问题,另一方面是借此机会深入学习SpringAOP相关的内容.本文是权当本人的自己AOP学习笔记,以下需求不用AOP肯定也能解决,至于是否牵强附会,仁者见仁智者见智. 对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用情况 监控部分重要函数,若抛出指定的异常,需要以短信或邮件方式通知相关人员 金

Spring框架Aop详解

一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnblogs.com/xrq730/p/4919025.html AOP AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善.OOP引入封装.继承.多态等概念来建立一种对象层次

(三)Spring 之AOP 详解

第一节:AOP 简介 AOP 简介:百度百科: 面向切面编程(也叫面向方面编程):Aspect Oriented Programming(AOP),是软件开发中的一个热点,也是Spring框架中的一个重要内容.利用AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率. 主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等. -------- 这里我先开头讲一个例子代码程序: T.java: 1 package c

spring之aop详解

面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率. 原文地址:https://www.cnblogs.com/nidegui/p/11069477.html

Spring AOP详解(转载)

此前对于AOP的使用仅限于声明式事务,除此之外在实际开发中也没有遇到过与之相关的问题.最近项目中遇到了以下几点需求,仔细思考之后,觉得采用AOP 来解决.一方面是为了以更加灵活的方式来解决问题,另一方面是借此机会深入学习Spring AOP相关的内容.本文是权当本人的自己AOP学习笔记,以下需求不用AOP肯定也能解决,至于是否牵强附会,仁者见仁智者见智. 对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用情况 监控部分重要函数,若抛出指定的异常,需要以短信或邮件方式通知相关人员

Spring AOP详解(转载)所需要的包

上一篇文章中,<Spring Aop详解(转载)>里的代码都可以运行,只是包比较多,中间缺少了几个相应的包,根据报错,几经百度搜索,终于补全了所有包. 截图如下: 在主测试类里面,有人怀疑,没有main方法,是怎么运行的.这是用的junit,结合spring来进行的测试类. Spring AOP详解(转载)所需要的包,布布扣,bubuko.com

spring AOP详解〇

AOP正在成为软件开发的下一个圣杯.使用AOP,你可以将处理aspect的代码注入主程序,通常主程序的主要目的并不在于处理这些aspect.AOP可以防止代码混乱. 为了理解AOP如何做到这点,考虑一下记日志的工作.日志本身不太可能是你开发的主程序的主要任务.如果能将"不可见的".通用的日志代码注入主程序中,那该多好啊.AOP可以帮助你做到. Spring framework是很有前途的AOP技术.作为一种非侵略性的,轻型的AOP framework,你无需使用预编译器或其他的元标签,

Spring AOP 详解 【转】

此前对于AOP的使用仅限于声明式事务,除此之外在实际开发中也没有遇到过与之相关的问题.最近项目中遇到了以下几点需求,仔细思考之后,觉得采用AOP 来解决.一方面是为了以更加灵活的方式来解决问题,另一方面是借此机会深入学习Spring AOP相关的内容.本文是权当本人的自己AOP学习笔记,以下需求不用AOP肯定也能解决,至于是否牵强附会,仁者见仁智者见智. 对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用情况 监控部分重要函数,若抛出指定的异常,需要以短信或邮件方式通知相关人员