您的位置:首页 > 编程语言 > Java开发

Spring Aop demo

2017-01-09 17:16 441 查看

前言

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;

@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.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;

@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:火锅
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: