【从零开始学Spring笔记】AOP的XML开发
大家可以关注作者的账号,关注从零开始学Spring笔记文集。也可以根据目录前往作者的博客园博客进行学习。本片文件将基于黑马程序员就业班视频进行学习以及资料的分享,并记录笔记和自己的看法。欢迎大家一起学习和讨论。
【从零开始学Spring笔记】Spring学习路线
什么是AOP
面向切面编程(Aspect Oriented Programming)通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
什么是OOP
面向对象程序设计(Object Oriented Programming)作为一种新方法,其本质是以建立模型体现出来的抽象思维过程和面向对象的方法。模型是用来反映现实世界中事物特征的。任何一个模型都不可能反映客观事物的一切具体特征,只能对事物特征和变化规律的一种抽象,且在它所涉及的范围内更普遍、更集中、更深刻地描述客体的特征。通过建立模型而达到的抽象是人们对客体认识的深化。
简单的说就是AOP是OOP的扩展和延伸,解决OOP开发遇到问题。
如下图:
如果想在数据存入数据库前加一个权限校验,需要先编写一个父类,再有众多需要校验的类继承这个父类,并调用校验方法。但是任然十分繁琐复杂,维护性不高。AOP采用的是横向抽取机制取代了传统纵向继承,可以在不改变源码的情况下为类提供权限校验、性能检测、日志编写等功能。
Spring底层的AOP实现原理
动态代理:
JDK 动态代理:只能对实现了接口的类产生代理。
Cglib动态代理(类似于Javassist第三方代理技术):对没有实现接口的类产生代理对象。生成子类对象。(不要用final修饰)
Spring可以自动更换动态代理的的切换,没有接口的使用Cglib,有接口的使用JDK。
AOP简介
AOP思想最早是由AOP联盟组织提出的。Spring是使用这种思想最好的框架。早期Spring的AOP有自己实现的方式,但是非常繁琐。而AspectJ是一个 优秀的AOP框架,后期Spring 引入AspectJ作为自身AOP的开发。
所以Spring两套AOP开发方式:
Spring 传统方式(弃用)。
Spring基于AspectJ的AOP的开发(使用)。
AOP开发的相关术语
创建项目测试
第一步:创建web项目,引入jar包
除了引入spring的基本开发包,还需要引入aop开发的相关jar包。
可以在以前下载解压后的libs文件夹中寻找,同时也上传到百度云了,下载即可。下载链接在本系列文集的第一篇【从零开始学Spring笔记】Spring4学习路线中寻找。
第二步:引入Spring的配置文件
在 src下创建applicationContext.xml
引入aop约束。
约束:spring framework-4.2.4.RELEASE\docs\spring framework-reference\htm|\xsd-configuration.html
观看本系列文集的【从零开始学Spring笔记】Spring配置,配置aop XML文件的提示。
第三步:编写目标类并配置
和之前一样,建立接口和实现类,并配置
<!-- 配置目标对象:被增强的对象 -->
<bean id = "productDao" class="com.tyust.spring.demo2.ProductDaoImpl"></bean>
第四步:编写测试
Spring与Junit整合
还是引入相应的testjar包
可以在以前下载解压后的libs文件夹中寻找,同时也上传到百度云了,下载即可。下载链接在本系列文集的第一篇【从零开始学Spring笔记】Spring4学习路线中寻找。
与Junit整合后,即可省去工厂类和主函数的创建,使用注解完成。
代码如下
package com.tyust.spring.demo2;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo3 {
@Resource(name = "productDao")
private ProductDao producDao;
@Test
public void demo1() {
producDao.save();
producDao.delete();
producDao.find();
producDao.update();
}
}
运行时点解方法名,右键,Junit运行即可。
输出结果
第五步:编写一个切面类,并交给Spring
package com.tyust.spring.demo2;
public class MyAspect {
public void checkPri() {
System.out.println("权限校验...");
}
}
<bean id = "myAspect" class="com.tyust.spring.demo2.MyAspect"></bean>
第六步:通过AOP的配置实现
配置文件
<!-- 通过AOP的配置完成对目标类产生代理 -->
<aop:config>
<!-- 表达式配置那些类的哪些方法需要进行增强 -->
<aop:pointcut
expression="execution(* com.tyust.spring.demo2.ProductDaoImpl.save(..))"
id="pointcut1" />
<!-- 配置切面 -->
<aop:aspect ref="myAspect">
<aop:before method="checkPri" pointcut-ref="pointcut1" />
</aop:aspect>
</aop:config>
输出结果
可以发现在没有修改代码的情况下,在保存用户前输出了权限校验。
通知的类型
1.前置通知:在目标方法执行之前进行操作
前置通知:获得切入点信息
2.后置通知:在目标方法执行之后进行操作
后置通知:获得方法的返回值
3.环绕通知:在目标方法执行之前和之后进行操作
4.异常抛出通知:在程序出现异常的时候,进行的操作
5.最终通知:无论代码是否有异常,总是会执行
6.引介通知(不用会)
示例:
<!-- 通过AOP的配置完成对目标类产生代理 -->
<aop:config>
<!-- 表达式配置那些类的哪些方法需要进行增强 -->
<aop:pointcut expression="execution(* com.tyust.spring.demo2.ProductDaoImpl.save(..))" id="pointcut1" />
<aop:pointcut expression="execution(* com.tyust.spring.demo2.ProductDaoImpl.delete(..))" id="pointcut2" />
<aop:pointcut expression="execution(* com.tyust.spring.demo2.ProductDaoImpl.update(..))" id="pointcut3" />
<aop:pointcut expression="execution(* com.tyust.spring.demo2.ProductDaoImpl.find(..))" id="pointcut4" />
<!-- 配置切面 -->
<aop:aspect ref="myAspect">
<aop:before method="checkPri" pointcut-ref="pointcut1" />
<aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result" />
<aop:around method="monitoring" pointcut-ref="pointcut3" />
<aop:after-throwing method="afterThrow" pointcut-ref="pointcut4" throwing="ex"/>
<aop:after method="after" pointcut-ref="pointcut4" />
</aop:aspect>
</aop:config>
package com.tyust.spring.demo2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect {
public void checkPri(JoinPoint joinPoint) {
System.out.println("权限校验==================" +joinPoint);
}
public void writeLog(Object result) {
System.out.println("日志记录=================="+result);
}
public Object monitoring(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("前");
Object obj = joinPoint.proceed();
System.out.println("后");
return obj;
}
public void afterThrow(Throwable ex)
{
System.out.println("异常=================="+ex.getMessage());
}
//相当于finally代码块中内容
public void after() {
System.out.println("最终==================");
}
}
Spring的切入点的写法
基于execution的函数完成的
语法:[访问修饰符] 方法返回值包名.类名.方法名(参数)
public void com.tyust.spring.demo2.ProductDaoImpl.save(..)
..
:任意参数
*
:任意,例如* *.*.*.*Dao.*(..)
。第一个星代表任意返回值,后三个星代表任意包下,第五个星代表所有的Dao类,第六个星代表,该类下的所有方法。(参数不能用*)
+
:当前类和其子类,例如* com.tyust.spring.demo2.ProductDaoImpl+.save(..)
..
:当前包和器子包,例如* com.tyust.spring.demo2.ProductDaoImpl+.save(..)