Spring Aop demo

来源:互联网 发布:修真淘宝大户顶点 编辑:程序博客网 时间:2024/06/18 10:05

前言

AOP(AspectOriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

术语

1.通知(Advice):
通知定义了切面是什么以及何时使用。描述了切面要完成的工作和何时需要执行这个工作。
2.
连接点(Joinpoint):
程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法被调用时、异常被抛出时等等。
3.
切入点(Pointcut)
通知定义了切面要发生的“故事”和时间,那么切入点就定义了“故事”发生的地点,例如某个类或方法的名称,spring中允许我们方便的用正则表达式来指定
4.
切面(Aspect)
通知和切入点共同组成了切面:时间、地点和要发生的“故事”
5.
引入(Introduction)
引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)
6.
目标(Target)
即被通知的对象,如果没有AOP,那么它的逻辑将要交叉别的事务逻辑,有了AOP之后它可以只关注自己要做的事(AOP让他做爱做的事)
7.
代理(proxy)
应用通知的对象,详细内容参见设计模式里面的代理模式
8.
织入(Weaving)
把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:
(1)
编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器
(2)
类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码
(3)
运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术

Demo1:采用注解方式

整体结构图如下:


配置文件

applicationContext.xml

<?xml version="1.0"encoding="UTF-8"?><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.springframework.org/schema/beans"  xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"  xmlns:tx="http://www.springframework.org/schema/tx"xmlns:cache="http://www.springframework.org/schema/cache"  xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="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    http://www.springframework.org/schema/context     http://www.springframework.org/schema/context/spring-context.xsd    http://www.springframework.org/schema/tx    http://www.springframework.org/schema/tx/spring-tx.xsd    http://www.springframework.org/schema/cache    http://www.springframework.org/schema/cache/spring-cache.xsd">     <aop:aspectj-autoproxy />    <!-- 切面类 -->    <bean id="peopleHelp"class="com.hys.demo.aop.help.PeopleHelp"></bean>    <!-- 目标对象 -->    <bean id="peopleImp"class="com.hys.demo.aop.imp.PeopleImp"></bean>   </beans>

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd">  <modelVersion>4.0.0</modelVersion>  <groupId>hys.web.project</groupId>  <artifactId>hys_demo_springAop01</artifactId>  <version>0.0.1-SNAPSHOT</version>  <packaging>jar</packaging>  <name>hys_demo_springAop01</name>  <url>http://maven.apache.org</url>   <properties>    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  </properties>   <dependencies>    <dependency>      <groupId>junit</groupId>      <artifactId>junit</artifactId>      <version>4.10</version>      <scope>test</scope>    </dependency>     <!-- Spring-4.2.0 -->    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-web</artifactId>      <version>4.2.0.RELEASE</version>   </dependency>     <dependency>        <groupId>org.springframework</groupId>      <artifactId>spring-orm</artifactId>      <version>4.2.0.RELEASE</version>    </dependency>       <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-expression</artifactId>      <version>4.2.0.RELEASE</version>   </dependency>     <dependency>      <groupId>org.aspectj</groupId>      <artifactId>aspectjrt</artifactId>      <version>1.8.9</version>   </dependency>     <dependency>       <groupId>org.aspectj</groupId>       <artifactId>aspectjweaver</artifactId>       <version>1.8.9</version>       </dependency>  </dependencies> </project>

源代码:

01.创建接口:People.java和Clock.java

package com.hys.demo.aop.inter; public interface People {       public void sleep();       public void work(Stringname);       public void eat(Stringname, String s);       public void sing(Stringsong);       public void run(Stringname, String s);}
package com.hys.demo.aop.inter; public interface Clock {       //睡觉时间       public booleansleepClock(int t);}

02.创建实现类:PeopleImp.java和ClockImp.java

package com.hys.demo.aop.imp;import org.springframework.stereotype.Component; import com.hys.demo.aop.inter.People; @Componentpublic class PeopleImp implements People {        public void sleep() {              System.out.println("我正在睡觉。。。");       }             public void work(Stringname){              System.out.println(name+"正在上班。。。");       }             public void eat(Stringname, String s){              System.out.println(name+"正在吃"+s);       }             public void sing(Stringsong){              System.out.println("我正在唱"+song);       }             public void run(Stringname, String s){              System.out.println(name+"正在"+s);       }}
package com.hys.demo.aop.imp;import com.hys.demo.aop.inter.Clock; public class ClockImp implements Clock {        @Override       public booleansleepClock(int t) {                if(t >= 10){                        return true;              }else{                     return false;              }                   }}

03.创建切面类:PeopleHelp.java

package com.hys.demo.aop.help;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.DeclareParents;import org.aspectj.lang.annotation.Pointcut;import com.hys.demo.aop.inter.Clock; @Aspectpublic class PeopleHelp {             /**        * 为被通知的目标对象引入额外的接口,并透明地实现        * 简单的说,你可以把当前对象转型成另一个对象,你就可以调用另一个对象的方法了        */  @DeclareParents(value="com.hys.demo.aop.imp.PeopleImp",defaultImpl=com.hys.demo.aop.imp.ClockImp.class)       public Clock clock;             //------------------------切面1:无参测试-------------------------             @Pointcut("execution(**.sleep())")       public void sleepPoint(){};             //声明前置通知       @Before("sleepPoint()")       public void doBefore(){              System.out.println("------------------------切面1:无参测试-------------------------");              System.out.println("前置通知");       }          //声明后置通知     @AfterReturning(pointcut ="sleepPoint()", returning = "result")     public void doAfterReturning(String result){           System.out.println("后置通知");        System.out.println("---" + result + "---");     }      //声明例外通知 (发生异常时,执行此方法)    @AfterThrowing(pointcut ="sleepPoint()", throwing = "e")     public void doAfterThrowing(Exceptione) {         System.out.println("例外通知");        System.out.println(e.getMessage());     }           //声明最终通知       @After("sleepPoint()")       public void doAfter(){              System.out.println("最终通知");       }             //声明环绕通知    @Around("sleepPoint()")     public ObjectdoAround(ProceedingJoinPoint pjp) throws Throwable {         System.out.println("进入方法---环绕通知");         Object o =pjp.proceed();         System.out.println("退出方法---环绕通知");         return o;     }        //------------------------切面2:传参测试-----------------------------      @Before("execution(* *.work(..)) &&args(param)")     public void workBefore(String param){            System.out.println("------------------------切面2:传参测试-----------------------------");            System.out.println("传递一个参数");            System.out.println("参数:"+param);     }      @Before("execution(* *.eat(..)) && args(..)")     public void eatBefore(JoinPoint jp){            System.out.println("传递多个参数");            Object[] args = jp.getArgs();                      System.out.println("参数1:"+args[0]+"\n参数2:"+args[1]);     }         //--------------一个切点同时给多个通知传递参数----------------       @Pointcut("execution(**.sing(..)) && args(param)")       public voidsingPoint(String param){};           @Before(value ="singPoint(param)",argNames="param")       public voidsingBefore(String sing){              System.out.println("前置通知");              System.out.println("传递一个参数");              System.out.println("参数:"+sing);       }             @After(value ="singPoint(param)",argNames="param")       public voidsingAfter(String sing){              System.out.println("最终通知");              System.out.println("传递一个参数");              System.out.println("参数:"+sing);       }             @Pointcut("execution(**.run(..)) && args(..)")       public void runPoint(){};             @Before("runPoint()")       public voidrunBefore(JoinPoint jp){              System.out.println("传递多个参数");              Object[] args =jp.getArgs();                      System.out.println("参数1:"+args[0]+"\n参数2:"+args[1]);       }}

04.创建测试类:SleepTest.java

package com.hys.demo.aop.test;import org.junit.Before;import org.junit.Test;import org.springframework.context.ApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext;import com.hys.demo.aop.inter.Clock;import com.hys.demo.aop.inter.People; public class SleepTest {             private ApplicationContextcontext;             @Before       public void getContext(){              //获取上下文对象              this.context =newClassPathXmlApplicationContext("applicationContext.xml");       }             @Test       public void test_sleep(){              People people =(People)context.getBean("peopleImp");              Clock clock =(Clock)people;                           if(clock.sleepClock(18)){                     people.sleep();              }else{                     System.out.println("还没到睡觉时间呢!");              }                           people.work("张三");              people.eat("小明", "牛肉拉面");                people.sing("儿歌");              people.run("小明", "晨跑");       }}

测试结果:

一月 09, 2017 4:44:06 下午org.springframework.context.support.ClassPathXmlApplicationContextprepareRefresh信息: Refreshingorg.springframework.context.support.ClassPathXmlApplicationContext@1175e2db:startup date [Mon Jan 09 16:44:03 CST 2017]; root of context hierarchy一月 09, 2017 4:44:08 下午org.springframework.beans.factory.xml.XmlBeanDefinitionReaderloadBeanDefinitions信息: Loading XML bean definitions from class pathresource [applicationContext.xml]进入方法---环绕通知------------------------切面1:无参测试-------------------------前置通知我正在睡觉。。。退出方法---环绕通知最终通知------------------------切面2:传参测试-----------------------------传递一个参数参数:张三张三正在上班。。。传递多个参数参数1:小明参数2:牛肉拉面小明正在吃牛肉拉面前置通知传递一个参数参数:儿歌我正在唱儿歌最终通知传递一个参数参数:儿歌传递多个参数参数1:小明参数2:晨跑小明正在晨跑

Demo2:采用xml配置方式

整体结构图如下:


配置文件

<?xml version="1.0"encoding="UTF-8"?><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.springframework.org/schema/beans"  xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"  xmlns:tx="http://www.springframework.org/schema/tx"xmlns:cache="http://www.springframework.org/schema/cache"  xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="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    http://www.springframework.org/schema/context    http://www.springframework.org/schema/context/spring-context.xsd     http://www.springframework.org/schema/tx    http://www.springframework.org/schema/tx/spring-tx.xsd    http://www.springframework.org/schema/cache    http://www.springframework.org/schema/cache/spring-cache.xsd">       <aop:config>      <!-- 定义切面1 无参测试 -->      <aop:aspect id="peopleAspect01"ref="peopleHelp">        <!-- 定义切点 -->        <aop:pointcut id="sleepPointcut"expression="execution(* com.hys.demo.aop.imp.*.sleep())"/>              <!-- 前置通知 -->        <aop:before pointcut-ref="sleepPointcut"method="doBefore"/>        <!-- 后置通知 -->        <aop:after-returning pointcut-ref="sleepPointcut"returning="result" method="doAfterReturning"/>        <!-- 例外通知 -->        <aop:after-throwing pointcut-ref="sleepPointcut"throwing="e" method="doAfterThrowing"/>        <!-- 环绕通知 -->        <aop:around pointcut-ref="sleepPointcut"xsi:type="aop:basicAdviceType" method="doAround"/>        <!-- 最终通知 -->        <aop:after pointcut-ref="sleepPointcut"method="doAfter"/>        <!--                 为被通知的目标对象引入额外的接口,并透明地实现                 简单的说,你可以把当前对象转型成另一个对象,你就可以调用另一个对象的方法了         -->        <aop:declare-parents types-matching="com.hys.demo.aop.imp.PeopleImp"implement-interface="com.hys.demo.aop.inter.Clock" default-impl="com.hys.demo.aop.imp.ClockImp" />      </aop:aspect>           <!-- 定义切面2 有参测试-->      <aop:aspect id="peopleAspect02"ref="peopleHelp">        <!-- 获取一个参数值 -->       <aop:before pointcut="execution(*com.hys.demo.aop.imp.*.setAge(..)) and args(arg)" method="doWork" arg-names="arg"/>        <!-- 获取多个参数值 -->        <aop:before pointcut="execution(*com.hys.demo.aop.imp.*.eat(..)) and args(arg1,arg2)" method="eatBefore" arg-names="arg1,arg2"/>        <!-- 参数个数不确定 -->        <aop:after pointcut="execution(*com.hys.demo.aop.imp.*.eat(..)) and args(..)" method="eatAfter"/>       </aop:aspect>        </aop:config>       <!-- 切面类 -->   <bean id="peopleHelp" class="com.hys.demo.aop.help.PeopleHelp"></bean>   <!-- 目标对象 -->   <bean id="peopleImp" class="com.hys.demo.aop.imp.PeopleImp"></bean></beans>

源代码:

01.创建接口:People.java和Clock.java

package com.hys.demo.aop.inter; public interface People {       public void setAge(intage);       public String sleep();       public void eat(Stringname, String s);}
package com.hys.demo.aop.inter; public interface Clock {       //睡觉时间       public booleansleepClock(int t);}


02.创建实现类:PeopleImp.java和ClockImp.java

package com.hys.demo.aop.imp;import org.springframework.stereotype.Component; import com.hys.demo.aop.inter.People; @Componentpublic class PeopleImp implements People {        public void setAge(intage){              System.out.println("我已经"+age+"岁了");       }             public String sleep() {              System.out.println("张三正在睡觉。。。");              return "目标方法的返回值";       }             public void eat(Stringname, String s){              System.out.println(name+ ":"+"正在吃"+ s);       }}
packagecom.hys.demo.aop.imp;importcom.hys.demo.aop.inter.Clock; public class ClockImp implements Clock {    @Override   public boolean sleepClock(int t) {       if(t >= 10){         return true;      }else{        return false;      }       }}

03.创建切面类:PeopleHelp.java

package com.hys.demo.aop.help;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;public class PeopleHelp {              //------------------------peopleAspect01切面---------------------------       /**        * 声明前置通知        * 说明:在目标方法前执行此方法        */       public void doBefore(){              System.out.println("---------------切面1测试---------------");              System.out.println("前置通知");       }           /**        * 声明最终通知        * 说明:在目标方法返回值之前执行次方法        */       public void doAfter(){              System.out.println("最终通知");       }          /**     * 声明后置通知        * 说明:在目标方法返回值之后执行次方法     * 可以获取目标方法的返回值result     */    public voiddoAfterReturning(String result) {           System.out.println("后置通知");        System.out.println(result);     }       /**     * 声明例外通知     * 说明:目标方法发生异常时,执行此方法     */    public voiddoAfterThrowing(Exception e) {         System.out.println("例外通知");        System.out.println(e.getMessage());     }          /**        * 声明环绕通知          * 说明:在目标方法前后织入此方法        */    public ObjectdoAround(ProceedingJoinPoint pjp) throws Throwable {         System.out.println("进入方法---环绕通知");         Object o =pjp.proceed();         System.out.println("退出方法---环绕通知");         return o;     }     //------------------------peopleAspect02切面:如何获取目标方法的多个参数---------------------------    //获取一个参数值    public void doWork(int arg){          System.out.println("---------------切面2测试---------------");          System.out.println("参数:"+arg);    }       //获取多个参数值    public void eatBefore(Stringarg1, String arg2){          System.out.println("参数1:"+arg1+"\n参数2:"+arg2);    }       //参数个数不确定    public voideatAfter(JoinPoint jp){          Object[] args = jp.getArgs();          System.out.println("参数1:"+args[0]+"\n参数2:"+args[1]);    }}

04.创建测试类:SleepTest.java

package com.hys.demo.aop.test;import org.junit.Before;import org.junit.Test;import org.springframework.context.ApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext;import com.hys.demo.aop.inter.Clock;import com.hys.demo.aop.inter.People; public class SleepTest {             private ApplicationContextcontext;             @Before       public void getContext(){              //获取上下文对象              this.context =newClassPathXmlApplicationContext("applicationContext.xml");       }             @Test       public void test_sleep(){              People people =(People)context.getBean("peopleImp");              Clock clock =(Clock)people;                           //引用额外接口,判断是否到了睡觉时间              if(clock.sleepClock(10)){                     people.sleep();              }else{                     System.out.println("睡觉时间还没到呢!");              }                           people.setAge(21);              people.eat("张三", "火锅");       }}

测试结果:

一月 09, 2017 4:55:14 下午org.springframework.context.support.ClassPathXmlApplicationContextprepareRefresh信息: Refreshingorg.springframework.context.support.ClassPathXmlApplicationContext@1175e2db: startupdate [Mon Jan 09 16:55:14 CST 2017]; root of context hierarchy一月 09, 2017 4:55:14 下午org.springframework.beans.factory.xml.XmlBeanDefinitionReaderloadBeanDefinitions信息: Loading XML bean definitions from class pathresource [applicationContext.xml]---------------切面1测试---------------前置通知进入方法---环绕通知张三正在睡觉。。。最终通知退出方法---环绕通知后置通知目标方法的返回值---------------切面2测试---------------参数:21我已经21岁了参数1:张三参数2:火锅张三:正在吃火锅参数1:张三参数2:火锅

0 0
原创粉丝点击