AOP现有两个主要的流行框架,即Spring AOP和Spring+AspectJ

代理

AOP - 图1
编译期间的静态织入,又称为编译时增强织入(Weaving),指的是将切面代码和源业务代码链接起来的过程。AspectJ 就是这样一个面向切面的 Java 语言扩展,称呼其为语言的“扩展”,就是因为它扩展了 Java 语言的语法,需要特定的编译器来把 AspectJ 的代码编译成 JVM 可识别的 class 文件。
运行期间的动态代理,又称为运行时增强。这种方式是在程序运行时,依靠预先创建或运行时创建的代理类来完成切面功能的。比如 JDK 基于接口的动态代理技术,或 CGLib 基于类的代理对象生成技术就属于这一种。
动态代理的方式由于在运行时完成代理类或代理对象的创建,需要用到 Java 的拦截、反射和字节码生成等技术,因此运行时的性能表现往往没有静态织入好,功能也有较多限制,但是由于使用起来简便(不需要语言扩展,不需要特殊的编译器等),它的实际应用更为广泛。

CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。

1、JDK动态代理

利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

例子:

  1. package bean.proxy;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. interface Hello {
  6. public void toHelo(String str);
  7. }
  8. class HelloImpl implements Hello {
  9. public void toHelo(String str) {
  10. System.out.println("HelloImpl......"+str);
  11. }
  12. }
  13. class MyHandler implements InvocationHandler {
  14. //要代理的原始对象
  15. private Object obj;
  16. public MyHandler(Object obj) {
  17. super();
  18. this.obj = obj;
  19. }
  20. /**
  21. * 在代理实例上处理方法调用并返回结果
  22. *
  23. * @param proxy 代理类
  24. * @param method 被代理的方法
  25. * @param args 该方法的参数数组
  26. */
  27. public Object invoke(Object proxy, Method method, Object[] args)
  28. throws Throwable {
  29. before();
  30. Object result = method.invoke(obj, args);
  31. after();
  32. return result;
  33. }
  34. private void before() {
  35. System.out.println("before......");
  36. }
  37. private void after() {
  38. System.out.println("after......");
  39. }
  40. }
  41. public class ProxyDemo {
  42. public static void main(String[] args) {
  43. Hello hello = new HelloImpl();
  44. InvocationHandler invocaionHandler = new MyHandler(hello);
  45. Hello proxy = (Hello) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), invocaionHandler);
  46. proxy.toHelo("proxy代理对象实现toHello方法");
  47. }
  48. }

运行结果:

  1. before......
  2. HelloImpl......proxy代理对象实现toHello方法
  3. after......

2、CGLiB动态代理

利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

  1. public class UserDao {
  2. public void save(){
  3. System.out.println("保存");
  4. }
  5. public void modify(){
  6. System.out.println("修改");
  7. }
  8. public void delete(){
  9. System.out.println("删除");
  10. }
  11. }
  12. /**
  13. * 创建CGLIB代理对象
  14. * 重写inttercept方法
  15. */
  16. public class Cglib implements MethodInterceptor {
  17. /**
  18. *创建代理方法,生成CGLIB代理对象
  19. * target是目标对象,需要增强的对象
  20. * 返回目标对象的代理对象
  21. */
  22. public Object createProxy(Object target){
  23. //创建一个动态类对象,即增强类对象
  24. Enhancer enhancer = new Enhancer();
  25. //确定需要增强的类,设置为父类
  26. enhancer.setSuperclass(target.getClass());
  27. //确定代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor方法
  28. enhancer.setCallback(this);
  29. //返回创建的代理对象
  30. return enhancer.create();
  31. }
  32. @Override
  33. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  34. before();
  35. Object o1 = methodProxy.invokeSuper(o,objects);
  36. after();
  37. return o1;
  38. }
  39. private void before() {
  40. System.out.println("before......");
  41. }
  42. private void after() {
  43. System.out.println("after......");
  44. }
  45. }
  46. public class App {
  47. public static void main( String[] args ) {
  48. //创建代理对象
  49. Cglib cglib = new Cglib();
  50. //创建目标对象
  51. UserDao userDao = new UserDao();
  52. //获取增强以后的目标对象
  53. UserDao DaoAdvice = (UserDao) cglib.createProxy(userDao);
  54. //测试
  55. DaoAdvice.save();
  56. System.out.println("-------------");
  57. DaoAdvice.delete();
  58. System.out.println("-------------");
  59. DaoAdvice.delete();
  60. System.out.println("-------------");
  61. }
  62. }

3、何时使用JDK还是CGLiB?

1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。 2)如果目标对象实现了接口,可以强制使用CGLIB实现AOP。 3)如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。

4、如何强制使用CGLIB实现AOP?

1)添加CGLIB库(aspectjrt-xxx.jar、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar) 2)在Spring配置文件中加入

5、JDK动态代理和CGLIB字节码生成的区别?

1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类。利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名**实现类**。 2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final,对于final类或方法,是无法继承的。

1、JDK动态代理具体实现原理:
通过实现InvocationHandler接口创建自己的调用处理器;
通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理;
通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;
通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入;
JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力,Spring通过Java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。
2、CGLib动态代理:
利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
3、两者对比:
JDK动态代理是面向接口的。
CGLib动态代理是通过字节码底层继承要代理类来实现,因此如果被代理类被final关键字所修饰,会失败。

作者:最终幻想_5b57
链接:https://www.jianshu.com/p/46d092bb737d
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

设计模式——静态代理

  1. public interface UserService {
  2. //新增用户,为了简化不体现参数
  3. void addUser(); //接口方法不能体现{},即body(类体)
  4. //修改用户,为了简化不体现参数
  5. void editUser();
  6. }
  7. public class UserServiceImpl implements UserService {
  8. @Override
  9. public void addUser() {
  10. System.out.println("新增一个用户");
  11. }
  12. @Override
  13. public void editUser() {
  14. System.out.println("对用户进行修改");
  15. }
  16. }
  17. public class UserServiceProxy implements UserService{
  18. private UserService realSubject;
  19. @Override
  20. public void addUser() {
  21. System.out.println("代理类UserServiceProxy addUser方法的调用前执行...");
  22. realSubject.addUser();
  23. System.out.println("代理类UserServiceProxy addUser方法的调用后执行...");
  24. }
  25. @Override
  26. public void editUser() {
  27. System.out.println("代理类UserServiceProxy editUser方法的调用前执行...");
  28. realSubject.editUser();
  29. System.out.println("代理类UserServiceProxy editUser方法的调用后执行...");
  30. }
  31. public UserServiceProxy() {
  32. }
  33. public UserServiceProxy(UserService realSubject) {
  34. this.realSubject = realSubject;
  35. }
  36. }
  37. public class TestStaticProxy {
  38. @Test
  39. public void test(){
  40. UserService resultSubject = new UserServiceImpl();
  41. UserService proxy = new UserServiceProxy(resultSubject);
  42. proxy.addUser();
  43. proxy.editUser();
  44. }
  45. }

JDK动态代理详解

  1. public interface Hello {
  2. void sayHello();
  3. }
  4. public class HelloImpl implements Hello {
  5. @Override
  6. public void sayHello() {
  7. System.out.println("hello proxy!");
  8. }
  9. }
  10. public class MyInvocationHandler implements InvocationHandler {
  11. private Object target;
  12. public MyInvocationHandler(Object target) {
  13. this.target = target;
  14. }
  15. @Override
  16. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  17. System.out.println("before");
  18. Object result = method.invoke(target, args);
  19. System.out.println("after");
  20. return result;
  21. }
  22. }
  23. public static void main(String[] args) {
  24. System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
  25. Hello hello = new HelloImpl();
  26. MyInvocationHandler handler = new MyInvocationHandler(hello);
  27. Hello o = (Hello)Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), handler);
  28. o.sayHello();
  29. }

生成$Proxy0文件:

  1. public final class $Proxy0 extends Proxy implements Hello {
  2. // 原来equals方法
  3. private static Method m1;
  4. private static Method m3;
  5. private static Method m2;
  6. private static Method m0;
  7. public $Proxy0(InvocationHandler var1) throws {
  8. super(var1);
  9. }
  10. public final boolean equals(Object var1) throws {
  11. try {
  12. // 此处的h是Proxy类的{InvocationHandler h;}成员变量,在newProxy过程中,h即是MyInvocationHandler
  13. // invoke方法调用的实现即是MyInvocationHandler#invoke
  14. return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
  15. } catch (RuntimeException | Error var3) {
  16. throw var3;
  17. } catch (Throwable var4) {
  18. throw new UndeclaredThrowableException(var4);
  19. }
  20. }
  21. public final void sayHello() throws {
  22. try {
  23. super.h.invoke(this, m3, (Object[])null);
  24. } catch (RuntimeException | Error var2) {
  25. throw var2;
  26. } catch (Throwable var3) {
  27. throw new UndeclaredThrowableException(var3);
  28. }
  29. }
  30. public final String toString() throws {
  31. try {
  32. return (String)super.h.invoke(this, m2, (Object[])null);
  33. } catch (RuntimeException | Error var2) {
  34. throw var2;
  35. } catch (Throwable var3) {
  36. throw new UndeclaredThrowableException(var3);
  37. }
  38. }
  39. public final int hashCode() throws {
  40. try {
  41. return (Integer)super.h.invoke(this, m0, (Object[])null);
  42. } catch (RuntimeException | Error var2) {
  43. throw var2;
  44. } catch (Throwable var3) {
  45. throw new UndeclaredThrowableException(var3);
  46. }
  47. }
  48. static {
  49. try {
  50. m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
  51. m3 = Class.forName("proxy.Hello").getMethod("sayHello");
  52. m2 = Class.forName("java.lang.Object").getMethod("toString");
  53. m0 = Class.forName("java.lang.Object").getMethod("hashCode");
  54. } catch (NoSuchMethodException var2) {
  55. throw new NoSuchMethodError(var2.getMessage());
  56. } catch (ClassNotFoundException var3) {
  57. throw new NoClassDefFoundError(var3.getMessage());
  58. }
  59. }
  60. }

参考

【1】Spring中的拦截器与动态代理:https://mp.weixin.qq.com/s?src=11&timestamp=1633947376&ver=3368&signature=wMfzR6eXnLjq9ZCYkThjdIhSPK-grdi4NCkTSXs9cUjq*mvQXhGf9MsJr04y4l2ViAQ2QofzNwc3XL3cXoNveZTtKohLASqBG48yRw-DTOt7NrDFPAHgv9DEiRc3gW&new=1