Spring AOP之Schema-based与AspectJ通知

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_42681787/article/details/102642531

(一)Spring AOP

1.AOP:中文名称面向切面编程

2.英文名称:(Aspect Oriented Programming)

3.正常程序执行流程都是纵向执行流程

3.1 又叫面向切面编程,在原有纵向执行流程中添加横切面

3.2 不需要修改原有程序代码

3.2.1 高扩展性

3.2.2 原有功能相当于释放了部分逻辑.让职责更加明确.

4.面向切面编程是什么?

4.1 在程序原有纵向执行流程中,针对某一个或某一些方法添加通 知,形成横切面过程就叫做面向切面编程.

5.常用概念

5.1 原有功能: 切点, pointcut

5.2 前置通知: 在切点之前执行的功能. before advice

5.3 后置通知: 在切点之后执行的功能,after advice

5.4 如果切点执行过程中出现异常,会触发异常通知.throws advice

5.5 所有功能总称叫做切面.

5.6 织入: 把切面嵌入到原有功能的过程叫做织入 

6.spring 提供了 2 种 AOP 实现方式

6.1 Schema-based

6.1.1 每个通知都需要实现接口或类

6.1.2 配置 spring 配置文件时在配置

6.2 AspectJ

6.2.1 每个通知不需要实现接口或类

6.2.2 配置 spring 配置文件是在的子标签 <aop:config>中配置

(二) Schema-based实现方法

1.导入jar包

 

注意:aopalliance.jar与aspectjweaver.jar需要单独下载

2.新建通知类 

2.1新建前置通知类

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class BeforeAdvice implements MethodBeforeAdvice {

	@Override
	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
		System.out.println("前置通知");
		//arg0:切点方法(即在哪个方法前后加入通知)对象
		System.out.println("Method arg0:"+arg0.toString());
		//arg1:切点方法的参数
		for(Object obj:arg1) {
			System.out.println("Object arg1:"+obj);
		};
		//切点方法所在的类的对象
		System.out.println("Object aeg2:"+arg2.toString());
	}

}

2.2新建后置通知类

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class AfterAdvice implements AfterReturningAdvice{

	@Override
	public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
	System.out.println("后置通知");
	//arg0:切点方法返回值
	System.out.println("Object arg0:"+arg0.toString());
	//arg1:切点方法对象
	System.out.println("Method arg1:"+arg1.toString());
	//arg2:切点方法参数
	for(Object obj:arg2) {
		System.out.println("Object[]arg2:"+obj);
	};
	//arg3:切点方法所在的类的对象
	System.out.println("Object arg3:"+arg3.toString());
	}

}

2.3配置ApplicationContext.xml文件

2.3.1 引入 aop 命名空间

2.3.2 配置通知类的

2.3.3 配置切面

2.3.4 * 通配符,匹配任意方法名,任意类名,任意一级包名

2.3.5 如果希望匹配任意方法参数 (..)

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<!-- 绑定通知类的对象 -->
<bean id="before" class="cn.edu.aop.BeforeAdvice"></bean>
<bean id="after" class="cn.edu.aop.AfterAdvice"></bean>
<!-- 配置切面 -->
<aop:config>
<!-- 配置切点 -->
<aop:pointcut expression="execution(* cn.edu.pojo.Demo.demo2(..))" id="point"></aop:pointcut>
<aop:advisor advice-ref="before" pointcut-ref="point"/>
<aop:advisor advice-ref="after" pointcut-ref="point"/>
</aop:config>
<bean id="demo" class="cn.edu.pojo.Demo"></bean>
</beans>

3.编写测试类 

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.edu.pojo.Demo;

public class AopTest {
public static void main(String[] args) {
	ApplicationContext ac=new ClassPathXmlApplicationContext("ApplicationContext03.xml");
	Demo demo=ac.getBean(Demo.class);
	demo.demo0();
	demo.demo1();
	demo.demo2("abc",12);
	demo.demo3();
}
}
public class Demo {
	/*public void demo0() {
		System.out.println("demo01方法");
	};*/
	public void demo1() {
		System.out.println("demo1方法");
	};
	public String  demo2(String ad,int b) {
		System.out.println("demo2方法");
		return "hpu";
		};
	 public  void demo3() {
		// int a=5/0;
		System.out.println("demo3方法");
	};

}

(三) AspectJ实现异常通知

1. 只有当切点报异常才能触发异常通知

2. 在 spring 中有 AspectJ 方式提供了异常通知的办法.

2.1 如果希望通过 schema-base 实现需要按照特定的要求自己编 写方法.

3. 实现步骤:

3.1 新建异常通知类,在类写任意名称的方法

public class AspectJException {
	public void myexception(Exception e1) {
		System.out.println("执行异常类!"+e1.getMessage());
	};

}

3.2 在 spring 配置文件中配置

3.2.1 的 ref 属性表示:方法在哪个类中.

3.2.2 表示什么通知

3.2.3 method: 当触发这个通知时,调用哪个方法

3.2.4 throwing: 异常对象名,必须和通知中方法参数名相同(可以不在通知中声明异常对象)  

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<bean id="demo" class="cn.edu.pojo.Demo"></bean>
<bean id="exception" class="cn.edu.aop.AspectJException"></bean>
<aop:config>
<aop:aspect ref="exception">
<aop:pointcut expression="execution(* cn.edu.pojo.Demo.demo3())" id="mypoint"/>
<aop:after-throwing method="myexception" pointcut-ref="mypoint" throwing="e1"/>
</aop:aspect>
</aop:config>
</beans>
package cn.edu.pojo;

public class Demo {
	/*public void demo0() {
		System.out.println("demo01方法");
	};
	public void demo1() {
		System.out.println("demo1方法");
	};
	public String  demo2(String ad,int b) {
		System.out.println("demo2方法");
		return "hpu";
		};
*/
	 public  void demo3() {
		int a=5/0;
		System.out.println("demo3方法");
	};

}

(四)Schema-based实现异常通知 

1. 新建一个类实现 throwsAdvice 接口

1.1 必须自己写方法,且必须叫 afterThrowing

1.2 有两种参数方式

1.2.1 必须是 1 个或 4 个

1.3 异常类型要与切点报的异常类型一致

import org.springframework.aop.ThrowsAdvice;

public class SchemaException implements ThrowsAdvice{
	public void afterThrowing(Exception e) {
		System.out.println("Schema的异常通知!");
	};

}

2.配置ApplicationContext.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<bean id="demo" class="cn.edu.pojo.Demo"></bean>
</aop:config>
<aop:config>
<aop:pointcut expression="execution(* cn.edu.pojo.Demo.demo3())" id="demo3"/>
<aop:advisor advice-ref="schEx" pointcut-ref="demo3"/>
</aop:config>
<bean id="schEx" class="cn.edu.aop.SchemaException"></bean>
</beans>

3.测试

package cn.edu.pojo;

public class Demo {
	/*public void demo0() {
		System.out.println("demo01方法");
	};
	public void demo1() {
		System.out.println("demo1方法");
	};
	public String  demo2(String ad,int b) {
		System.out.println("demo2方法");
		return "hpu";
		};
*/
	 public  void demo3() {
		int a=5/0;
		System.out.println("demo3方法");
	};

}

(五) Schema-based实现环绕通知

1. 把前置通知和后置通知都写到一个通知中,组成了环绕通知

2. 实现步骤

2.1 新建一个类实现 MethodInterceptor

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MyArround implements MethodInterceptor {

	@Override
	public Object invoke(MethodInvocation arg0) throws Throwable {
		System.out.println("环绕通知方式-前置通知");
		Object result=arg0.proceed();//放行
		//System.out.println(result);
		System.out.println("环绕通知方式-后置通知");
		return result;
	}

}

2.2 配置 applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<bean id="demo" class="cn.edu.pojo.Demo"></bean>

<aop:config>
<aop:pointcut expression="execution(* cn.edu.pojo.Demo.demo0())" id="arroundPoint"/>
<aop:advisor advice-ref="arround" pointcut-ref="arroundPoint"/>
</aop:config>
<bean id="arround" class="cn.edu.aop.MyArround"></bean>
</beans>

3.编写测试类

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.edu.pojo.Demo;

public class AopTest {
public static void main(String[] args) {
	ApplicationContext ac=new ClassPathXmlApplicationContext("ApplicationContext03.xml");
	Demo demo=ac.getBean(Demo.class);
	demo.demo0();
	//demo.demo1();
	//demo.demo2("abc",12);
	//demo.demo3();
}
}
package cn.edu.pojo;

public class Demo {
	public void demo0() {
		System.out.println("demo01方法");
	};
	public void demo1() {
		System.out.println("demo1方法");
	};
	public String  demo2(String ad,int b) {
		System.out.println("demo2方法");
		return "hpu";
		};
	 public  void demo3() {
		int a=5/0;
		System.out.println("demo3方法");
	};

}

 

(六) AspectJ实现前后置、异常、环绕通知

1. 新建类,不用实现

1.1 类中方法名任意

import org.aspectj.lang.ProceedingJoinPoint;

public class AspectJNotice {
	
	public AspectJNotice() {
		super();
		
	}
	public void mybefore0() {
		System.out.println("AspectJ无参前置通知");
	};
	public void mybefore1(String id,int age) {
		System.out.println("AspectJ前置通知(有参数):"+id+">"+age);
	};
	public void myafter0() {
		System.out.println("AspectJ实现After后置通知");
	};
	public void myafter1() {
		System.out.println("AspectJ实现After-returning后置通知!");
	};
	public void mythrow() {
		System.out.println("这里出现异常!AspectJ实现异常通知!");
	};
	public void myArround(ProceedingJoinPoint p) throws Throwable {
		System.out.println("AspectJ实现环绕前置通知!");
		Object result=p.proceed();//放行
		System.out.println("已放行"+result);
		System.out.println("AspectJ实现环绕后置通知");
	};

}

1.2 配置 spring 配置文件

1.2.1 后置通知,是否出现异常都执行

1.2.2 后置通知,只有当切点正确执行时 执行

1.2.3 和 和 执行顺序和配置顺序有关

1.2.4 execution() 括号不能扩上 args

1.2.5 中间使用 and 不能使用&& 由 spring 把 and 解析成&&

1.2.6 args(名称) 名称自定义的.顺序和 demo1(参数,参数)对应

1.2.7 arg-names=” 名 称 ” 名 称 来 源 于 expression=”” 中 args(),名称必须一样

1.2.7.1 args() 有几个参数,arg-names 里面必须有几个参数

1.2.7.2 arg-names=”” 里面名称必须和通知方法参数名对应

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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">

<!-- aspectj实现通知 -->

<bean id="notice" class="cn.edu.aop.AspectJNotice"></bean>
<bean id="test" class="cn.edu.pojo.MyTest"></bean>
<!-- 实现前置无参数的通知 -->
<aop:config>
<aop:aspect ref="notice">
<aop:pointcut expression="execution(* cn.edu.pojo.MyTest.test2())" id="point0"/>
<aop:before method="mybefore0" pointcut-ref="point0" />
</aop:aspect>
</aop:config>
<!-- 实现有参数传递的前置通知 -->
<aop:config >
<aop:aspect ref="notice">
<!--  args中的参数名必须与下面的arg-names相同-->
<aop:pointcut expression="execution(* cn.edu.pojo.MyTest.test1(String,int)) and args(id,age)" id="point1"/>
<!--  arg-names必须与通知类中的方法形参名相同 -->
<aop:before method="mybefore1" pointcut-ref="point1" arg-names="id,age"/>
</aop:aspect>
</aop:config> 
<!-- 实现后置通知 -->
<aop:config>
<aop:aspect ref="notice">
<aop:pointcut expression="execution(* cn.edu.pojo.MyTest.test2())" id="point2"/>
<aop:after method="myafter0" pointcut-ref="point2"/>
</aop:aspect>
</aop:config> 
<!-- After-returning实现后置通知(after与after-returning的区别在于有没有异常after都会通知,如果切点出现异常,after-returning会被阻断) -->
<aop:config>
<aop:aspect ref="notice">
<aop:pointcut expression="execution(* cn.edu.pojo.MyTest.test2())" id="point3"/>
<aop:after-returning method="myafter1" pointcut-ref="point3"/>
</aop:aspect>
</aop:config>
<!-- 实现异常通知 -->
<aop:config>
<aop:aspect ref="notice">
<aop:pointcut expression="execution(* cn.edu.pojo.MyTest.test2())" id="point4"/>
<aop:after-throwing method="mythrow" pointcut-ref="point4"/>
</aop:aspect>
</aop:config>
<!-- 实现环绕通知 -->
<aop:config>
<aop:aspect ref="notice">
<aop:pointcut expression="execution(* cn.edu.pojo.MyTest.test3())" id="point5"/>
<aop:around method="myArround" pointcut-ref="point5"/>
</aop:aspect>
</aop:config>
</beans>
public class MyTest {
	public MyTest() {
		super();
		
	}
	public void test1(String id,int age) {
		System.out.println("test1方法"+">>"+id+">>"+age);
	};
	public void test2() {
		//int error=3/0;
		System.out.println("test2方法");
	};
	public void test3() {
		System.out.println("test3方法");
	};

}

3.测试类

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.edu.pojo.Demo;
import cn.edu.pojo.MyTest;


public class AopTest {
public static void main(String[] args) {
	ApplicationContext ac=new ClassPathXmlApplicationContext("ApplicationContext03.xml");
	//Demo demo=ac.getBean(Demo.class);
	//demo.demo0();
	//demo.demo1();
	//demo.demo2("abc",12);
	//demo.demo3();
	MyTest t=ac.getBean(MyTest.class);
	t.test1("3118",20 );
	t.test2();
	t.test3();
	//t.test1("100",213);
}
}

猜你喜欢

转载自blog.csdn.net/qq_42681787/article/details/102642531