Spring AOP
插说一句话:注解方式的出现目的是为了取代xml配置文件的开发方式
spring提供AOP技术了,重点是要学会怎么配置使用生成代理对象,以及增强代码怎么写
1.AOP的概述
1.1 AOP技术
AOP为Aspect Oriented Programming的缩写,面向切面编程
AOP最早由AOP联盟的组织提出的,制定了一套规范.Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范
通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
1.2. AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
1.3. 为什么要学习AOP
可以在不修改源代码的前提下,对程序进行增强.
2.Spring框架的AOP的底层实现
2.1. Srping框架的AOP技术底层也是采用的代理技术,代理的方式提供了两种
2.1.1. 基于JDK的动态代理
必须是面向接口的,只有实现了具体接口的类才能生成代理对象
2.1.2 基于CGLIB动态代理
对于没有实现了接口的类,也可以产生代理,产生这个类的子类的方式
2.2 Spring的传统AOP中根据类是否实现接口,来采用不同的代理方式
2.2.1. 如果实现类接口,使用JDK动态代理完成AOP
2.2.2. 如果没有实现接口,采用CGLIB动态代理完成AOP
3.JDK的动态代理
1. 使用Proxy类来生成代理对象的代码如下:
/** * 使用JDK的方式生成代理对象 */ public class MyProxyUtils { public static UserDao getProxy(final UserDao dao) { // 使用Proxy类生成代理对象 UserDao proxy = (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(), new InvocationHandler() { // 代理对象方法一直线,invoke方法就会执行一次 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("save".equals(method.getName())){ System.out.println("记录日志..."); // 开启事务 } // 提交事务 // 让dao类的save或者update方法正常的执行下去 return method.invoke(dao, args); } }); // 返回代理对象 return proxy; } }
4.CGLIB动态代理
public static OrderDaoImpl getProxy(){ // 创建CGLIB核心的类 Enhancer enhancer = new Enhancer(); // 设置父类 enhancer.setSuperclass(OrderDaoImpl.class); // 设置回调函数 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { if("save".equals(method.getName())){ // 记录日志 System.out.println("记录日志了..."); } return methodProxy.invokeSuper(obj, args); } }); // 生成代理对象 OrderDaoImpl proxy = (OrderDaoImpl) enhancer.create(); return proxy; }
5.AOP的相关术语
1. Joinpoint(连接点) -- 例如UserDaoIml这个类中的所有方法,save delete update
2. Pointcut(切入点) -- 拦截哪些方法(对哪些方法做增强)例如:save update
3. Advice(通知/增强) -- 具体做什么功能(例如:记录日志).通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
4. Target(目标对象) -- 代理的目标对象
5. Weaving(织入) -- 是指把增强应用到目标对象来创建新的代理对象的过程
6. Proxy(代理) -- 一个类被AOP织入增强后,就产生一个结果代理类
7. Aspect(切面) -- 切入点+通知 组合成为切面
通知需要自己写,切入点需要配置
6.AOP的配置(XML开发方式)
在Spring的配置文件中,引入AOP的约束,加上aop的配置如下
定义切面类 <bean id="myAspectXml" class="com.fff.demo.MyAspectXml"/> 配置文件中aop的配置 <aop:config> <!-- 引入切面类 --> <aop:aspect ref="myAspectXml"> <!-- 定义通知类型:切面类的方法和切入点的表达式 --> <aop:before method="log" pointcut="execution(public * com.fff.demo.CustomerDaoImpl.save(..))"/> </aop:aspect> </aop:config>
7.AOP的注解方式开发
1.创建Spring的配置文件,引入AOP的schema约束
2.在配置文件中开启自动注解代理
<aop:aspectj-autoproxy/>
3.将目标类配置到Spring中
<bean id="customerDao" class="com.fff.demo.CustomerDaoImpl"/>
4.定义切面类
添加切面和通知的注解
@Aspect -- 定义切面类的注解
通知类型(注解的参数是切入点的表达式)
@Before -- 前置通知
@AfterReturing -- 后置通知
@Around -- 环绕通知(目标方法执行前后都执行通知方法,目标对象方法默认不执行,需要手动执行)
@After -- 最终通知
@AfterThrowing -- 异常抛出通知
具体的代码如下
@Aspect public class MyAspectAnno { @Before(value="execution(public void com.fff.demo.CustomerDaoImpl.save())") public void log(){ System.out.println("记录日志..."); } }
5.在配置文件中定义切面类(例如Spring的事务类,spring已经提供)
<bean id="myAspectAnno" class="com.fff.demo.MyAspectAnno"/>
动态代理:
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。
如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。