目录
1.Spring AOP基本概念
1.1AOP概念
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
OOP基本单元:类 AOP基本单元:切面
原理:AOP采取横向抽取机制,即将分散到各个方法中的重复代码提取出来,然后在程序编译或运行阶段将这些抽取出来的代码应用到需要执行的地方。
1.2AOP的术语
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等。
切面(ASPECT):横切关注点被模块化的特殊对象。即,它是一个类。
通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
目标(Target):被通知对象。
代理(Proxy):向目标对象应用通知之后创建的对象。
切入点(PointCut):切面通知执行的“地点”的定义。
连接点(JointPoint):与切入点匹配的执行点。
1.3需要导入的jar包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
2.AOP的实现
2.1 案例
2.2基于自定义类的AOP实现(AspectJ)
1.Student接口
public interface Student {
public void study();
public void homework();
}
2.学生核心业务(实现类)
public class StudentImpl implements Student {
@Override
public void study() {
System.out.println("--------------上面是学生学习(前置插入)--------------");
}
@Override
public void homework() {
System.out.println("--------------下面是学生做作业(后置插入)-------------");
}
}
3.自定义切面类
public class DiyPointCut {
public void before(){
System.out.println("学生打开书本预习。。。。");
System.out.println("学生听老师讲课,做笔记,完成课堂练习。。。。");
System.out.println("学生上课结束,收拾书包,带走垃圾。。");
}
public void after(){
System.out.println("学生准备做家庭作业,复习今天课堂所学得的知识。。");
System.out.println("学生打开作业本,写作业、检查改错、预习。。");
System.out.println("学生完成家庭作业,收拾书本。。。");
}
public void errorPrint(){
System.out.println("=====》这个是抛出异常切入《=====");
}
public void finalPrintLog(){
System.out.println("=====》无论正确都会执行《=====");
}
}
4.aop配置
<bean id="student" class="com.liu.demo.StudentImpl"/>
<bean id="diy" class="com.liu.demo.DiyPointCut"/>
<aop:config>
<!--自定义切面:ref 要引用的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point1" expression="execution(* com.liu.demo.StudentImpl.study(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="point1"/>
</aop:aspect>
<!--自定义切面:ref 要引用的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point2" expression="execution(* com.liu.demo.StudentImpl.homework(..))"/>
<!--通知-->
<aop:after method="after" pointcut-ref="point2"/>
</aop:aspect>
<aop:aspect ref="diy">
<!-- 配置异常通知:在切入点方法执行产生异常之后执行。它和后置通知永远只能执行一个-->
<aop:after-throwing method="errorPrint" pointcut-ref="point1"/>
<!-- 配置最终通知:无论切入点方法是否正常执行它都会在其后面执行-->
<aop:after method="finalPrintLog" pointcut-ref="point1"/>
</aop:aspect>
</aop:config>
5.测试
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student) context.getBean("student");
student.study();
student.homework();
}
2.3使用spring原生API接口实现
1.前置方法(类)
public class AddBeforeService implements MethodBeforeAdvice {
//加入前置业务
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("学生打开书本预习。。。。");
System.out.println("学生听老师讲课,做笔记,完成课堂练习。。。。");
System.out.println("学生上课结束,收拾书包,带走垃圾。。");
}
}
2.后置方法(类)
public class AddAfterService implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("学生准备做家庭作业,复习今天课堂所学得的知识。。");
System.out.println("学生打开作业本,写作业、检查改错、预习。。");
System.out.println("学生完成家庭作业,收拾书本。。。");
}
}
3.spring 实现aop切入
<!--注册bean-->
<bean id="student" class="com.liu.demo1.StudentImpl"/>
<bean id="addBeforeService" class="com.liu.demo1.AddBeforeService"/>
<bean id="addAfterService" class="com.liu.demo1.AddAfterService"/>
<aop:config>
<!--切入点:expression:表达式 execution(要执行的位置!)-->
<aop:pointcut id="pointcut1" expression="execution(* com.liu.demo1.StudentImpl.study(..))"/>
<aop:pointcut id="pointcut2" expression="execution(* com.liu.demo1.StudentImpl.homework(..))"/>
<!--执行环绕增强!-->
<aop:advisor advice-ref="addBeforeService" pointcut-ref="pointcut1"/>
<aop:advisor advice-ref="addAfterService" pointcut-ref="pointcut2"/>
</aop:config>
2.4使用注解开发AspectJ
1.类中直接用注释
@Aspect //标注这个类是一个切面
public class AnnotationPointCut {
@Before("execution(* com.liu.demo.StudentImpl.study(..))")
public void before(){
System.out.println("学生打开书本预习。。。。");
System.out.println("学生听老师讲课,做笔记,完成课堂练习。。。。");
System.out.println("学生上课结束,收拾书包,带走垃圾。。");
}
@After("execution(* com.liu.demo.StudentImpl.homework(..))")
public void after(){
System.out.println("学生准备做家庭作业,复习今天课堂所学得的知识。。");
System.out.println("学生打开作业本,写作业、检查改错、预习。。");
System.out.println("学生完成家庭作业,收拾书本。。。");
}
//在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点;
@Around("execution(* com.liu.demo.StudentImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable{
System.out.println("上课前");
Signature signature = jp.getSignature();// 获得签名
System.out.println("signature:"+signature);
Object proceed = jp.proceed(); //执行方法
System.out.println("放学后");
System.out.println(proceed);
}
}
2.xml中配置
<!--方式三:使用注解-->
<bean id="annotationPointCut" class="com.liu.demo.AnnotationPointCut"/>
<!--开启注解支持! JDK(默认是 proxy-target-class="false") cglib(proxy-target-class="true")-->
<aop:aspectj-autoproxy/>
3.Spring AOP小结
3.1运行结果
3.2小结
Spring AOP可以在不改变源代码的情况下对核心业务进行增强处理。AspectJ是一个AOP框架,这比基于代理类的AOP实现简单灵活的多。使用AspectJ实现spring AOP的方式有两种,一是基于XML配置开发AspectJ,而是基于注解开发AspectJ。两者各有各的好处,在工程量不是很大情况下用注解开发会更快捷、省时,可读性较好,但如若程序频繁修改,则注解就不适用了,反而需要每个改。所以两者合理运用才是最方便的。