Spring aop 原理及各种应用场景

标签: spring aop 原理 | 发表时间:2015-01-05 17:16 | 作者:yingmu3630
出处:http://www.iteye.com
AOP是Aspect Oriented Programing的简称,面向切面编程。AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理、缓存、对象池管理以及日志记录。AOP将这些分散在各个业务逻辑中的代码通过横向切割的方式抽取到一个独立的模块中。AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类,其中静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。
代理对象的方法 = 增强处理 + 被代理对象的方法
Spring AOP 则采用运行时生成 AOP 代理类,因此无需使用特定编译器进行处理。由于 Spring AOP 需要在每次运行时生成 AOP 代理,因此性能略差一些。

AOP使用场景

AOP用来封装横切关注点,具体可以在下面的场景中使用
Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging  调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence  持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务

AOP相关概念

方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的 Advisor或拦截器实现。
连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出
通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice
切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上
引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。Spring中要使用Introduction, 可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口
目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO
AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

日志应用:
实现登陆和日志管理(使用Spring AOP

1)LoginService   LogService   TestMain

2)用Spring 管理  LoginService 和 LogService 的对象

3)确定哪些连接点是切入点,在配置文件中

4)将LogService封装为通知

5)将通知植入到切入点

6)客户端调用目标



    <aop:config>
        <aop:pointcut expression="execution(* cn.com.spring.service.impl.*.*(..))" id="myPointcut"/>
        <!--将哪个-->
        <aop:aspect id="dd" ref="logService">
          <aop:before method="log" pointcut-ref="myPointcut"/>
        </aop:aspect>
    </aop:config>

execution(* * cn.com.spring.service.impl.*.*(..))

1)* 所有的修饰符

2)* 所有的返回类型

3)* 所有的类名

4)* 所有的方法名

5)* ..所有的参数名


1.ILoginService.java


    package cn.com.spring.service;

    public interface ILoginService {
        public boolean login(String userName, String password);
    }

2.LoginServiceImpl.java


    package cn.com.spring.service.impl;

    import cn.com.spring.service.ILoginService;

    public class LoginServiceImpl implements ILoginService {

        public boolean login(String userName, String password) {
            System.out.println("login:" + userName + "," + password);
            return true;
        }

    }

3.ILogService.java


    package cn.com.spring.service;

    import org.aspectj.lang.JoinPoint;

    public interface ILogService {
        //无参的日志方法
        public void log();
        //有参的日志方法
        public void logArg(JoinPoint point);
        //有参有返回值的方法
        public void logArgAndReturn(JoinPoint point,Object returnObj);
    }


4.LogServiceImpl.java


    package cn.com.spring.service.impl;

    import org.aspectj.lang.JoinPoint;

    import cn.com.spring.service.ILogService;

    public class LogServiceImpl implements ILogService {

        @Override
        public void log() {
            System.out.println("*************Log*******************");
        }
       
        //有参无返回值的方法
        public void logArg(JoinPoint point) {
            //此方法返回的是一个数组,数组中包括request以及ActionCofig等类对象
            Object[] args = point.getArgs();
            System.out.println("目标参数列表:");
            if (args != null) {
                for (Object obj : args) {
                    System.out.println(obj + ",");
                }
                System.out.println();
            }
        }

        //有参并有返回值的方法
        public void logArgAndReturn(JoinPoint point, Object returnObj) {
            //此方法返回的是一个数组,数组中包括request以及ActionCofig等类对象
            Object[] args = point.getArgs();
            System.out.println("目标参数列表:");
            if (args != null) {
                for (Object obj : args) {
                    System.out.println(obj + ",");
                }
                System.out.println();
                System.out.println("执行结果是:" + returnObj);
            }
        }
    }

5.applicationContext.java


    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:p="http://www.springframework.org/schema/p"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

        <bean id="logService" class="cn.com.spring.service.impl.LogServiceImpl"></bean>
        <bean id="loginService" class="cn.com.spring.service.impl.LoginServiceImpl"></bean>

        <aop:config>
            <!-- 切入点 -->
            <aop:pointcut
                expression="execution(* cn.com.spring.service.impl.LoginServiceImpl.*(..))"
                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:aspect>
        </aop:config>
    </beans>

6.TestMain.java


    public class TestMain {
    public static void testSpringAOP(){
            ApplicationContext ctx = new ClassPathXmlApplicationContext("app*.xml");
           
            ILoginService loginService = (ILoginService)ctx.getBean("loginService");
            loginService.login("zhangsan", "12344");
    }
    public static void main(String[] args) {
    testSpringAOP();
    }
    }

7.输出结果:

    login:zhangsan,12344
    目标参数列表:
    zhangsan,
    12344,

    执行结果是:true

解析:1.先调用了login()方法System.out.println("login:" + userName + "," + password);

     2.再调用了logArgAndReturn()方法输出了日志,并且返回了login()方法是否成功


    System.out.println("目标参数列表:");
            if (args != null) {
                for (Object obj : args) {
                    System.out.println(obj + ",");
                }
                System.out.println();
                System.out.println("执行结果是:" + returnObj);
            }

权限控制
首先定义一个用户:
Java代码  收藏代码

    public class User { 
        private String username; 
     
        public String getUsername() { 
            return username; 
        } 
        public void setUsername(String username) { 
            this.username = username; 
        } 
    } 


用户有三种人:未注册用户,注册用户,与管理员
注册用户可以可以发表,回复帖子
管理员除了可以发表,回复帖子,还可以删除帖子!
下面定义TestCommunity接口:
Java代码  收藏代码

    public interface TestCommunity { 
      public void answerTopic(); 
      public void deleteTopic(); 
    } 



实现上面接口的TestCommunityImpl类:
Java代码  收藏代码

    public class TestCommunityImpl implements TestCommunity { 
        //注册用户与管理员拥有的功能 
        public void answerTopic() { 
            System.out.println("可以发表,回复帖子"); 
        } 
        //管理员拥有的功能 
        public void deleteTopic() { 
            System.out.println("可以删除帖子!"); 
        } 
    } 



下一步,建立一下依赖注入的实现类TestResultImpl:
Java代码  收藏代码

    public class TestResultImpl { 
        private TestCommunity test; 
     
        public void setTest(TestCommunity test) { 
            this.test = test; 
        }    
         public void answerTopic() 
         { 
             test.answerTopic(); 
         } 
          public void deleteTopic() 
          { 
              test.deleteTopic(); 
          } 
    } 



接下来,就是最重要的一个类,拦截器,Around处理类型的,类TestAuthorityInterceptor:
Java代码  收藏代码

    import org.aopalliance.intercept.MethodInterceptor; 
    import org.aopalliance.intercept.MethodInvocation; 
     
    //创建Around处理应该实现MethodInterceptor接口 
    public class TestAuthorityInterceptor implements MethodInterceptor { 
        private User user; 
     
        public User getUser() { 
            return user; 
        } 
        public void setUser(User user) { 
            this.user = user; 
        } 
     
        // invoke方法返回调用的结果 
        public Object invoke(MethodInvocation invocation) throws Throwable { 
            String methodName = invocation.getMethod().getName(); 
     
            if (user.getUsername().equals("unRegistedUser")) { 
                System.out.println("你的身份是未注册用户,没有权限回复,删除帖子!"); 
                return null; 
            } 
            if ((user.getUsername().equals("user")) 
                    && (methodName.equals("deleteTopic"))) { 
                System.out.println("你的身份是注册用户,没有权限删除帖子"); 
                return null; 
            } 
            // proceed()方法对连接点的整个拦截器链起作用,拦截器链中的每个拦截器都执行该方法,并返回它的返回值 
            return invocation.proceed(); 
        } 
     
    } 



配置文件:
Java代码  收藏代码

    <?xml version="1.0" encoding="UTF-8"?> 
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> 
    <beans> 
        <bean id="authTarget" class="org.test.lighter.TestCommunityImpl" /> 
     
        <!-- 其中的username可以写为admin,user,和unRegistedUser --> 
        <bean id="user" class="org.test.lighter.User"> 
            <property name="username" value="user" /> 
        </bean> 
     
        <!-- 配置拦截器 --> 
        <bean id="TestAuthorityInterceptor" 
            class="org.test.lighter.TestAuthorityInterceptor"> 
            <property name="user" ref="user" /> 
        </bean> 
     
        <!-- 配置代理工厂bean --> 
       <bean id="service" 
            class="org.springframework.aop.framework.ProxyFactoryBean"> 
            <property name="proxyInterfaces"> 
                <value>org.test.lighter.TestCommunity</value> 
            </property> 
            <property name="target" ref="authTarget"/> 
            <property name="interceptorNames"> 
                <list> 
                    <value>TestAuthorityInterceptor</value> 
                </list> 
            </property> 
        </bean> 
     
        <bean id="testResult" class="org.test.lighter.TestResultImpl"> 
            <property name="test" ref="service" /> 
        </bean> 
    </beans> 



再写一个执行文件BeanTest:
Java代码  收藏代码

    import org.springframework.context.ApplicationContext; 
    import org.springframework.context.support.FileSystemXmlApplicationContext; 
     
    public class BeanTest { 
      public static void main(String[] args) throws Exception 
      { 
          ApplicationContext ctx = new FileSystemXmlApplicationContext("src/bean.xml"); 
          TestResultImpl test = (TestResultImpl)ctx.getBean("testResult"); 
          test.answerTopic(); 
          test.deleteTopic(); 
      } 
    } 



执行结果:大家猜一下啦
Java代码  收藏代码

    1、如果是管理员,打印出: 
    可以发表,回复帖子 
    可以删除帖子! 
     
    2、如果是注册用户: 
    可以发表,回复帖子 
    你的身份是注册用户,没有权限删除帖子 
     
    3、未注册用户: 
    你的身份是未注册用户,没有权限回复,删除帖子! 

Spring为我们提供了,根据beanName匹配后进行自动代理的解决方法

mark from http://blog.csdn.net/sd0902/article/details/8393770
http://lighter.iteye.com/blog/42673


已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [spring aop 原理] 推荐:

Spring AOP 实现原理与 CGLIB 应用

- - 博客 - 伯乐在线
来源: IBM Developerworks. 简介: AOP(Aspect Orient Programming),也就是面向方面编程,作为面向对象编程的一种补充,专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在 Java EE 应用中,常常通过 AOP 来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等.

Spring AOP详解

- - Java - 编程语言 - ITeye博客
        最近项目中遇到了以下几点需求,仔细思考之后,觉得采用AOP来解决. 一方面是为了以更加灵活的方式来解决问题,另一方面是借此机会深入学习Spring AOP相关的内容. 例如,以下需求不用AOP肯定也能解决,至于是否牵强附会,仁者见仁智者见智. 1.对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用情况.

Spring AOP动态代理原理与实现方式 (转)

- - 开源软件 - ITeye博客
AOP:面向切面、面向方面、面向接口是一种横切技术. 1.事务管理: (1)数据库事务:(2)编程事务(3)声明事物:Spring AOP-->声明事物   . 3.安全验证: Spring AOP---OOP升级  . 静态代理原理:目标对象:调用业务逻辑    代理对象:日志管理. 表示层调用--->代理对象(日志管理)-->调用目标对象.

Spring aop 原理及各种应用场景

- - 开源软件 - ITeye博客
AOP是Aspect Oriented Programing的简称,面向切面编程. AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理、缓存、对象池管理以及日志记录. AOP将这些分散在各个业务逻辑中的代码通过横向切割的方式抽取到一个独立的模块中. AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类,其中静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强.

Spring AOP监控SQL执行

- - CSDN博客架构设计推荐文章
         对数据库连接池Proxool比较熟悉的读者,都知道Proxool可以记录SQL执行内容和时间等信息日志. 我们可以将该日志记录专门的SQL日志文件,对于查找执行特别耗时的SQL起了不小的作用. 对于一些其他连接池,没有该特性时,本文介绍Spring AOP切面方法来记录SQL日志.

Spring AOP 代理机制 JDK&CGLIB

- - 开源软件 - ITeye博客
Spring AOP使用JDK动态代理或者CGLIB来为目标对象创建代理. (建议优先使用JDK的动态代理). 如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理. 所有该目标类型实现的接口都将被代理. 若该目标对象没有实现任何接口,则创建一个CGLIB代理. 如果你希望强制使用CGLIB代理,(例如:希望代理目标对象的所有方法,而不只是实现自接口的方法) 那也可以.

使用spring AOP获得session的思路

- - RSS - IT博客云
由于Spring 的AOP面向切面编程,与Servlet容器没有任何关联,所以想要获得Session会话比较麻烦. 当然Struts2同样不依赖Servlet容器,可以在Spring AOP中可以使用 com.opensymphony.xwork2.ActionContext,就可以获得 Session.

Spring AOP + Redis缓存数据库查询

- - 编程语言 - ITeye博客
我们希望能够将数据库查询结果缓存到Redis中,这样在第二次做同样的查询时便可以直接从redis取结果,从而减少数据库读写次数. 必须要做到与业务逻辑代码完全分离. 从缓存中读出的数据必须与数据库中的数据一致. 如何为一个数据库查询结果生成一个唯一的标识. Key),能唯一确定一个查询结果,同一个查询结果,一定能映射到同一个.

基于 Annotation 拦截的 Spring AOP 权限验证方法

- - 企业架构 - ITeye博客
转自: http://www.ibm.com/developerworks/cn/java/j-lo-springaopfilter/index.html. 使用 Annotation 可以非常方便的根据用户的不同角色,分配访问 Java 方法的权限. 在 Java Web 开发中,使用这种方法,可以提高系统的松耦合度,方便维护.

Spring aop--几种不同的使用方式

- - CSDN博客推荐文章
        最近听老师讲关于AOP关于容器的课,突然觉得之前对aop的理解都不是很到位,先不说理解就是应用都很少,最近也写了几篇关于AOP的博客,都是一些简单的demo,今天在这里再说一下关于Spring AOP的几种实现方式.         我们经常会用到的有如下几种.         1、基于代理的AOP.