(一)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);
}
}