AOP概述
AOP(Aspect Oriented Program,面向切面编程)是现在比较热门的话题。AOP的历史可以追溯到1990年,当时面向对象编程(OOP)已经趋于成熟,并应用于软件开发。但是来自PARC研究中心的研究人员发现,在使用面向对象编程的过程中会产生局限性,他们对这种局限性做了深入的分析后,提出了一种新的编程思想,这种编程思想就是今天的AOP。
Spring AOP是继Spring IoC(Inversion of Control)之后的Spring框架的又一大特性,它也是Spring框架的核心内容。AOP是一种思想,所有符合AOP思想的技术,都可以看作AOP的实现。AOP是建立在Java的代理机制之上,Spring框架已经基本实现了AOP思想。在众多的AOP实现技术当中,Spring AOP做得最好,也是最为成熟的。
Spring AOP的实现是基于Java的代理机制,从JDK 1.3开始就支持代理功能,但是性能成为一个很大问题,为了解决JDK代理性能问题,出现了CGLIB代理机制。它可以生成字节码,所以它的性能会高于JDK代理。Spring支持这两种代理方式。但是,随着JVM(Java虚拟机)性能的不断提高,这两种代理性能的差距会越来越小。
Spring AOP术语
切面(Aspect)
由于平行四边形拦截了程序流程,Spring形象地把它叫作切面,所谓“面向切面编程”正是指的这个。以后提到的“切面”是形象地指这个“平行四边形”。实际上“切面”是一段程序代码,这段代码将被“植入”到程序流程中。在配置文件通过< bean >元素指定
连接点(Join Point)
对象操作过程中的某个阶段点。在程序流程上的任意一点,都可以是连接点。它实际上是对象的一个操作,方法调用。例如,对象调用某个方法、读写对象的实例或者某个方法抛出了异常等。
切入点(Point cut)
切入点是连接点的集合。切面与程序流程的“交叉点”便是程序的切入点。确切地说,它是“切面注入”到程序中的位置。换句话说,“切面”是通过切入点被“注入”的。在程序中可以有很多个切入点。
通知(Advice)
通知是某个切入点被横切后,所采取的处理逻辑。也就是说,在“切入点”处拦截程序后,通过通知来执行切面
目标对象(Target)
所有被通知的对象(也可以理解为被代理的对象)都是目标对象。目标对象被AOP所关注,它的属性的改变会被关注,它行为的调用也会被关注,它的方法传参的变化仍然会被关注。AOP会注意目标对象的变动,随时准备向目标对象“注入切面”。
织入(Weaving)
织入是将切面代码插入到目标对象上,从而生成代理对象的过程。由代理工厂创建一个代理对象,这个代理可以为目标对象执行切面功能。
Proxy(代理)
将通知应用到目标对象之后,被动态创建的对象。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
AOP入门小案例
JDK动态代理
UserDao.java
package beginner.jdk;
public interface UserDao {
public void addUser();
public void deleteUser();
}
目标类:UserdaoImpl.java
package beginner.jdk;
//目标类
public class UserdaoImpl implements UserDao {
@Override
public void addUser() {
// TODO Auto-generated method stub
System.out.println("添加用户");
}
@Override
public void deleteUser() {
// TODO Auto-generated method stub
System.out.println("删除用户");
}
}
切面类MyAspect.java
package beginner.asp;
//切面类:可以存在多个通知advice
public class MyAspect {
public void check_Permissions() {
System.out.println("模拟检查权限...");
}
public void log() {
System.out.println("模拟记录日志...");
}
}
JDK代理类JdkProxy.java
package beginner.jdk;
//JDK代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import beginner.asp.MyAspect;
public class JdkProxy implements InvocationHandler {
// 声明目标类接口
private UserDao userDao;
// 创建代理方法
public Object createProxy(UserDao userDao) {
this.userDao = userDao;
ClassLoader classLoader = JdkProxy.class.getClassLoader(); // 1、类加载器
Class[] clazz = userDao.getClass().getInterfaces(); // 2、被代理对象实现的所有接口
return Proxy.newProxyInstance(classLoader, clazz, this); // 3、使用代理类,进行增强,返回的是代理后的对象
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
// 声明切面
MyAspect myAspect = new MyAspect();
// 执行业务前增强
myAspect.check_Permissions();
// 在目标类上调用方法,并传入参数
Object obj = method.invoke(userDao, args);
// 方法执行后增强
myAspect.log();
return obj;
}
}
测试类JdkTest.java
package beginner.jdk;
public class JdkTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 创建代理对象
JdkProxy jdkProxy = new JdkProxy();
// 创建目标对象
UserDao userDao = new UserdaoImpl();
// 从代理对象中获取增强后的目标对象
UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);
userDao1.addUser();
userDao1.deleteUser();
}
}
总体布局:
运行结果如图:
可见实现了动态代理和AOP思想
CGLIB代理
目标类UserDao.java
package com.beginner.cglib;
public class UserDao {
public void addUser() {
System.out.println("添加用户");
}
public void deleteUser() {
System.out.println("删除用户");
}
}
切面类MyAspect.java
package com.beginner.cglib;
//切面类:可以存在多个通知advice
public class MyAspect {
public void check_Permissions() {
System.out.println("模拟检查权限...");
}
public void log() {
System.out.println("模拟记录日志...");
}
}
代理类CglibProxy.java
package com.beginner.cglib;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
//代理类
public class CglibProxy implements MethodInterceptor {
// 代理方法
public Object createProxy(Object target) {
// 创建一个动态对象
Enhancer enhancer = new Enhancer();
// 确定需要增强的类,设置其父类
enhancer.setSuperclass(target.getClass());
// 添加回调函数
enhancer.setCallback(this);
// 返回创建的代理类
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// TODO Auto-generated method stub
MyAspect myAspect = new MyAspect();
myAspect.check_Permissions();
Object obj = methodProxy.invokeSuper(proxy, args);
myAspect.log();
return obj;
}
}
测试类CglibTest.java
package com.beginner.cglib;
public class CglibTest {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
UserDao userDao = new UserDao();
UserDao userDao2 = (UserDao) cglibProxy.createProxy(userDao);
userDao2.addUser();
userDao2.deleteUser();
}
}
总体布局:
运行结果如下:
小型面试题
1、面试官:使用Spring框架的好处是什么?
应聘者:
(1)轻量:Spring是轻量的。
(2)控制反转IoC:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。
(3)面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。
(4)容器:Spring包含并管理应用中对象的生命周期和配置。
(5)MVC框架:Spring的Web框架是个精心设计的框架,是Web框架的一个很好的替代品。
(6)事务管理:Spring提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。
(7)异常处理:Spring提供方便的API把具体技术相关的异常(比如由JDBC、Hibernate或JDO抛出的)转换为一致的unchecked 异常。
面试官:什么是Bean的自动装配?
应聘者:Spring容器能够自动装配相互合作的Bean,这意味着容器不需要< constructor-arg >和< property >配置,能通过Bean工厂自动处理Bean之间的协作。
3、面试官:解释AOP。
应聘者:面向切面的编程或AOP,是一种编程技术,允许程序模块化横向切割关注点,或横切典型的责任划分,如日志和事务管理。
面试官:在Spring AOP中,关注点和横切关注的区别是什么?
应聘者:关注点是应用中一个模块的行为,一个关注点可能会被定义成一个我们想实现的功能。横切关注点是一个关注点,此关注点是整个应用都会使用的功能,并影响整个应用,比如日志、安全和数据传输,几乎应用的每个模块都需要的功能。因此这些都属于横切关注点。