在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码。经典应用:事务管理、性能监视、安全检查、缓存 、日志等,Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入。
aop底层采用代理机制进行实现。
如果目标对象实现了接口默认情况下会使用 jdk 的动态代理Proxy实现AOP,也可以强制使用cglib。操作如下: (1)添加cglib库,SPRING_HOME/cglib/*.jar (2)在spring配置文件中加入如下配置:
<aop:aspectj-autoproxy proxy-target-class="true"/>如果目标对象没有实现接口,就必须使用cglib字节码增强,Spring 会自动在动态代理和CGLIB之间转换。
1、原理的区别: jdk动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。 cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
2、生成的区别: jdk动态代理只能对实现了接口的类生成代理,而不能针对类 cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final
1、target:目标类,需要被代理的类。例如:UserService 2、Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法。例如:所有的方法 3、PointCut (切入点):已经被增强的连接点。例如:addUser() 4、advice (通知/增强):增强代码。例如:after、before 5、 Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程. 6、proxy 代理类 7、Aspect(切面): 是切入点pointcut和通知advice的结合 一个线是一个特殊的面。 一个切入点和一个通知,组成成一个特殊的面。
示例:
目标类:
public interface UserService { public void addUser(); public void updateUser(); public void deleteUser(); }切面类:
public class MyAspect { public void before(){ System.out.println("开启事务"); } public void after(){ System.out.println("提交事务"); } }jdk动态代理工厂:
public class MyBeanFactory { public static UserService createService() { // 1 目标类 final UserService userService = new UserServiceImpl(); // 2切面类 final MyAspect myAspect = new MyAspect(); /* * 3 代理类:将目标类(切入点)和 切面类(通知) 结合 --> 切面 Proxy.newProxyInstance * 参数1:loader ,类加载器,动态代理类 运行时创建,任何类都需要类加载器将其加载到内存。 * 一般情况:当前类.class.getClassLoader(); 目标类实例.getClass().get... * 参数2:Class[] interfaces 代理类需要实现的所有接口 * 方式1:目标类实例.getClass().getInterfaces() ;注意:只能获得自己接口,不能获得父元素接口 * 方式2:new Class[]{UserService.class} 例如:jdbc 驱动 --> DriverManager * 获得接口 Connection 参数3:InvocationHandler 处理类,接口,必须进行实现类,一般采用匿名内部 提供 * invoke 方法,代理类的每一个方法执行时,都将调用一次invoke 参数31:Object proxy :代理对象 * 参数32:Method method : 代理对象当前执行的方法的描述对象(反射) 执行方法名:method.getName() * 执行方法:method.invoke(对象,实际参数) 参数33:Object[] args :方法实际参数 * */ UserService proxService = (UserService) Proxy.newProxyInstance(MyBeanFactory.class.getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 前执行 myAspect.before(); // 执行目标类的方法 Object obj = method.invoke(userService, args); // 后执行 myAspect.after(); return obj; } }); return proxService; } }cglib代理工厂:
public class MyBeanFactory { public static UserServiceImpl createService() { // 1 目标类 final UserServiceImpl userService = new UserServiceImpl(); // 2切面类 final MyAspect myAspect = new MyAspect(); // 3.代理类 ,采用cglib,底层创建目标类的子类 // 3.1 核心类 Enhancer enhancer = new Enhancer(); // 3.2 确定父类 enhancer.setSuperclass(userService.getClass()); /* * 3.3 设置回调函数 , MethodInterceptor接口 等效 jdk InvocationHandler接口 * intercept() 等效 jdk invoke() 参数1、参数2、参数3:以invoke一样 参数4:methodProxy * 方法的代理 * * */ enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // 前 myAspect.before(); // 执行目标类的方法 Object obj = method.invoke(userService, args); // * 执行代理类的父类 ,执行目标类 (目标类和代理类 父子关系) methodProxy.invokeSuper(proxy, args); // 后 myAspect.after(); return obj; } }); // 3.4 创建代理 UserServiceImpl proxService = (UserServiceImpl) enhancer.create(); return proxService; } }测试
@Test public void demo01(){ UserService userService = MyBeanFactory.createService(); userService.addUser(); userService.updateUser(); userService.deleteUser(); }AspectJ是一个基于Java语言的AOP框架 Spring2.0以后新增了对AspectJ切点表达式支持 @AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面,新版本Spring框架,建议使用AspectJ方式来开发AOP 主要用途:自定义开发
1、execution() 用于描述方法
语法:execution(修饰符 返回值 包.类.方法名(参数) throws异常) 修饰符,一般省略 public 公共方法 * 任意 返回值,不能省略 void 返回没有值 String 返回值字符串 * 任意 包,[省略] com.itheima.crm 固定包 com.itheima.crm.*.service crm包下面子包任意 (例如:com.itheima.crm.staff.service) com.itheima.crm.. crm包下面的所有子包(含自己) com.itheima.crm.*.service.. crm包下面任意子包,固定目录service,service目录任意包 类,[省略] UserServiceImpl 指定类 *Impl 以Impl结尾 User* 以User开头 * 任意 方法名,不能省略 addUser 固定方法 add* 以add开头 *Do 以Do结尾 * 任意 (参数) () 无参 (int) 一个整型 (int ,int) 两个 (..) 参数任意 throws ,可省略,一般不写。 综合1 execution(* com.itheima.crm.*.service..*.*(..)) 综合2 <aop:pointcut id="myPointCut" expression="execution(* com.itheima.*WithCommit.*(..)) || execution(* com.itheima.*Service.*(..))" />2、within:匹配包或子包中的方法(了解) within(com.itheima.aop..*)
3、this:匹配实现接口的代理对象中的方法(了解) this(com.itheima.aop.user.UserDAO)
4、target:匹配实现接口的目标对象中的方法(了解) target(com.itheima.aop.user.UserDAO)
5、args:匹配参数格式符合标准的方法(了解) args(int,int)
6、bean(id) :对指定的bean所有的方法(了解) bean(‘userServiceId’)