Spring Aop demo
2017-01-09 17:16
441 查看
前言
AOP(AspectOriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(crosscutting),在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.javapackage 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; @Component public 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; @Aspect public 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.javapackage 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; @Component public 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:火锅
相关文章推荐
- SpringMVC+HibernateValidator,配置在properties文件中的错误信息回显前端页面出现中文乱码
- jdk绿色免安装版如何进行相关配置?
- 【java】将List中的实体按照某个字段进行分组的算法
- java基础知识查漏 四
- Java 理论与实践: 正确使用 Volatile 变量
- java基础知识查漏 四
- RxJava学习(一)
- [算法入门]快速排序非递归方法(Java实现),大家一起来找茬啊~
- Spring事务隔离级别(solation level)介绍及例子
- Maven搭建Springmvc+Spring+Hibernate+html
- java判断中文字符串长度
- java注解
- window 下安装jdk (java环境)
- Java高并发,如何解决,什么方式解决
- Java 枚举7常见种用法
- Java 四舍五入
- Debian 8 x86_64 Java 使用 NRSerialPort 开发串口通信
- jdbc入门(二)
- java初始结构
- spring使用JdbcTemplate