Spring AOP及动态代理
AOP 人气:0一. Spring AOP
面向切面编程(Aspect Oriented Programming,AOP)是软件编程思想发展到一定阶段的产物,是对面向对象编程(Object Oriented Programming,OOP)的有益补充, 目前已成为一种比较成熟的编程方式。AOP适用于具有横向逻辑的场所,如访问控制、事务管理、性能监测等。
1. 传统问题:
在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然OOP可以通过组合或者继承的方式来达到代码的重用,但是比如实现日志记录时,代码还是会分散到不同的方法中。这样就会存在一个问题,如果想要关闭某个功能或者修改时,就必须要修改所有的相关方法。这不单单增加了开发人员的工作量,而且提高了代码的出错率。
2. 问题的解决策略:
为了解决这个问题,AOP思想随之产生。AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。这种采用横向抽取机制的方式,是传统的OOP思想无法办到的,因为OOP只能实现父子关系的纵向的重用。虽然AOP是一种新的编程思想,却不是OOP的替代品,它只是OOP的延申和补充。
3. AOP优点:
AOP的使用让开发人员在编写业务逻辑时可以专心于核心业务,而不用过多地关注于其他业务逻辑的实现,这不但提高了开发效率,而且增强了代码的可维护性。
在AOP思想中,类于切面的关系如下图所示。我们可以看出,通过Aspect(切面)分别在Class1和Class2的方法中加入了事务、日志、权限和异常等功能。
二. 动态代理
通过学习我们知道了AOP中的代理就是由AOP框架动态生成的一个对象,该对象可以作为目标对象使用,对于面向切面编程,简单地说,就是在不改变原程序的基础上为代码段增加新的功能,对代码段进行增强处理。它的设计思想来源于代理设计模式,通常情况下调用对象的方法如下图。
在代理模式中可以为该对象设置一个代理对象,代理对象为function()提供一个代理方法,当通过代理对象的function()方法调用原对象的function()方法时,就可以在代理方法中添加新的功能,即增强处理。增强的功能既可以插到原对象的function()前面,也可以插到其后面(如虚线)
1. JDK动态代理
JDK动态代理是通过java.lang.reflect.Proxy类来实现的,可以调用Proxy类的newProxyInstance()方法来创建代理对象。对于使用业务接口的类,Spring框架会默认使用JDK动态代理来实现AOP。通过一个案例来演示。
1. UserDao.java
package dao; public interface UserDao { public void addUserDao(); public void deleteUser(); }
2. UserDaoImpl.java
package dao; public class UserDaoImpl implements UserDao{ @Override public void addUserDao() { System.out.println("添加用户"); } @Override public void deleteUser() { System.out.println("删除用户"); } }
3. MyAspect.java
package aspect; public class MyAspect { public void check_permission(){ System.out.println("----模拟检查访问----"); } public void log(){ System.out.println("----模拟记录日记----"); } }
4. JdkProxy.java
package jdk; import aspect.MyAspect; import dao.UserDao; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * Jdk代理类 */ public class JdkProxy implements InvocationHandler { //声明目标类接口 private UserDao userdao; // 创建代理方法 public Object createProxy(UserDao userdao){ this.userdao=userdao; //类加载器 ClassLoader classLoader=JdkProxy.class.getClassLoader(); //被代理对象实现的所有接口 Class[] clazz=userdao.getClass().getInterfaces(); //使用代理类进行增强,返回的是代理后的对象 return Proxy.newProxyInstance(classLoader,clazz,this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //声明切面 MyAspect myAspect=new MyAspect(); //前增强 myAspect.check_permission(); //在目标上调用方法,并传入参数 Object obj=method.invoke(userdao,args); //后增强 myAspect.log(); return obj; } }
5. Test.java
@Test public void shouldAnswerWithTrue() { JdkProxy jdkProxy=new JdkProxy(); UserDao userDao=new UserDaoImpl(); UserDao userDao1=(UserDao) jdkProxy.createProxy(userDao); userDao1.addUserDao(); System.out.println("\n-----------------------------分割线------------------------------------\n"); userDao1.deleteUser(); }
结果:
2. CGLIB代理
JDK 动态代理的使用非常简单,但它具有一定的局限性(使用动态代理的对象必须实现一个或多个接口)如果要对没有实现接口的类进行代理,那么可以使用CGLIB代理。
CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。在Spring框架的核心包中已经集成了CGLIB所需要的包,所以开发中不需要另外导入jar包。
1. BookDao.java
package dao; public class BookDao { public void addBook(){ System.out.println("添加书本"); } public void deleteBook(){ System.out.println("删除书本"); } }
2. CglibProxy.java
package jdk; import aspect.MyAspect; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; 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 { MyAspect myAspect=new MyAspect(); myAspect.check_permission(); Object o1=methodProxy.invokeSuper(proxy,args); myAspect.log(); return o1; } }
结果:
加载全部内容