说起Spring的AOP(Aspect-Oriented Programming)面向切面编程大家都很熟悉(Spring不是这次博文的重点),但是我先提出几个问题,看看同学们是否了解,如果了解的话可以不用继续往下读:

  1. Spring的AOP的实现方式有哪些?

  2. 为什么使用代理机制?

  3. 它们是怎么实现的?

  4. 它们的区别是什么?

  下面进入正题,Spring采用代理的方式实现AOP,具体采用了JDK的动态代理和CGLib实现。使用动态代理和CGLib的目的是在现有类的基础上增加一些功能。简单地将就是有一个Proxy类,实现了原始类的方法,并且在原始类的基础上增加了新的功能。那么这么做可以实现很多功能:

  1. 在方法前后进行日志处理。

  2. 进行额外的校验,比如参数的验证功能等。

  3. 实现一些懒加载,也就是实例化的时候如果不去调用真正的方法的时候,这个类的属性就不会存在(Hibernate有这样类似的功能)。

  下面咱们用简单的代码实现它是如何进行代理的,首先采用的是JDK的动态代理实现:

  定义一个接口:

package com.hqs.proxy;

/**
* 操作系统光接口
* @author hqs
*
*/
public interface OpSystem {
public void work();
}

  定义一个实现类:

package com.hqs.proxy;

/**
* Mac 的实现
* @author hqs
*
*/
public class Mac implements OpSystem { public void work() {
System.out.println("Mac is running");
} }

  关键位置来了,我们通过实现JDK自带的反射机制的包的InvocationHandler来进行反射处理,实现它之后需要实现里边的invoke方法,这个invoke方法里边的参数分别为:代理类实例,用于调用method的;method参数是实际执行的方法;args所传输的参数数组。

package com.hqs.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class OpHandler implements InvocationHandler { private final OpSystem ops; public OpHandler(OpSystem ops) {
this.ops = ops;
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before system running");
method.invoke(ops, args);
System.out.println("After system running");
return null;
} public static void main(String[] args) {
Mac mac = new Mac();
OpHandler oph = new OpHandler(mac);
OpSystem os = (OpSystem)Proxy.newProxyInstance(oph.getClass().getClassLoader(),
mac.getClass().getInterfaces(), oph); os.work();
System.out.println(os.getClass());
} } 输出:
Before system running
Mac is running
After system running
class com.sun.proxy.$Proxy0

  然后看到里边的main方法中,代理类实例化对象的方法Proxy.newProxyInstance,这个是JDK的反射方法去实例化代理类,其中有三个参数分别是,去实例化代理类的class loader;所代理的类的所有接口Class数组;hander处理类,用于做拦截使用的类。最后我输出了一下os.getClass(),大家可以看到的是代理类的实例,而不是真正代理类的实例,这么做的好处就是很方便的复用这个代理类,比如你可以重复调用它而不用去重新实例化新类,再一点就是你可以针对不同的方法进行拦截,比如你可以method.getName()去判断调用的方法名字是什么从而更细粒度的拦截方法。咱们继续看用CGLib的实现:  

package com.hqs.proxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; /**
* CGLib Interceptor用于方法拦截
* @author hqs
*
*/
public class CGLibInterceptor implements MethodInterceptor { private final Mac mac; public CGLibInterceptor(Mac mac) {
this.mac = mac;
} @Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("Before system running");
method.invoke(mac, args);
System.out.println("After system running");
return null;
} public static void main(String[] args) {
Mac mac = new Mac(); //实例而非接口
MethodInterceptor handler = new CGLibInterceptor(mac);
Mac m = (Mac)Enhancer.create(mac.getClass(), handler);
m.work();
System.out.println(m.getClass()); } } 输出:
Before system running
Mac is running
After system running
class com.hqs.proxy.Mac$$EnhancerByCGLIB$$1f2c9d4a

  首先需要引入cglib包,然后才能使用他的MethodInterptor,它也采用method.invoke实现对代理类的调用。它的代理类创建采用Enhancer的create方法,其中传入了需要创建的类的class,以及Callback对象,因为MethodInterceptor继承了Callback对象。用于指向方法前后进行调用的类。  

public interface MethodInterceptor
extends Callback

  这是这两个类的基本实现,那么它们的区别是什么呢?

  1. JDK的动态代理只能针对接口和其实现类,如果没有实现类只有接口也是可以代理的,这里就不在举例了。为什么JDK的动态代理只针对接口代理,因为这个是JDK的定义。
  2. 如果不针对接口实现动态代理那就用到了CGLib了,也就是可以针对具体的类进行代理,大家可以参考我的代码。

  这些是它们的根本区别,但是Spring推荐使用JDK的动态代理,面向接口去编程。使用CGLib去做代理的时候需要注意,它生成的代理类存放在JVM的Perm space里边,那么是不是生成的代理对象就不进行回收了?其实不是的,不经常回收但是还是回收的,当类被加载,加载类的classLoader什么时候变得对垃圾回收可用的时候才进行回收。也就是你自己创建所有类移除classLoader之后,那么这个classLoader就会被回收,一般非常精通CGLib的话可以进行这块内容深入开发,因为它可以做出amzing的事情如果你熟悉的话。

  如果有不对的地方欢迎拍砖~

  

  

浅谈Spring的AOP实现-代理机制的更多相关文章

  1. 浅谈Spring的AOP实现-动态代理

    说起Spring的AOP(Aspect-Oriented Programming)面向切面编程大家都很熟悉(Spring不是这次博文的重点),但是我先提出几个问题,看看同学们是否了解,如果了解的话可以 ...

  2. 浅谈Spring中JDK动态代理与CGLIB动态代理

    前言Spring是Java程序员基本不可能绕开的一个框架,它的核心思想是IOC(控制反转)和AOP(面向切面编程).在Spring中这两个核心思想都是基于设计模式实现的,IOC思想的实现基于工厂模式, ...

  3. 浅谈spring中AOP以及spring中AOP的注解方式

    AOP(Aspect Oriented Programming):AOP的专业术语是"面向切面编程" 什么是面向切面编程,我的理解就是:在不修改源代码的情况下增强功能.好了,下面在 ...

  4. spring学习三:Spring的Aop、代理

    ref:https://mp.weixin.qq.com/s/J77asUvw8FcnF-6YlX6AAw AOP相关术语:    Joinpoint(连接点):类里面可以被增强的方法,这些方法称为连 ...

  5. 浅谈Spring中的Quartz配置

    浅谈Spring中的Quartz配置 2009-06-26 14:04 樊凯 博客园 字号:T | T Quartz是一个强大的企业级任务调度框架,Spring中继承并简化了Quartz,下面就看看在 ...

  6. 浅谈Spring的两种配置容器

    浅谈Spring的两种配置容器 原文:https://www.jb51.net/article/126295.htm 更新时间:2017年10月20日 08:44:41   作者:黄小鱼ZZZ     ...

  7. Spring的AOP与代理

    spring 支持两种注入方式: setter/constructor 支持多种配置方式: xml/java5注解/java类配置 支持两种事务管理: 声明性/编程性 实际上上述方式只有一个就能保证系 ...

  8. 1.1浅谈Spring(一个叫春的框架)

    如今各种Spring框架甚嚣尘上,但是终归还是属于spring的东西.所以在这里,个人谈一谈对spring的认识,笔者觉得掌握spring原理以及spring所涉及到的设计模式对我们具有极大的帮助.我 ...

  9. 浅谈Spring MVC知识

    关于MVC框架,我相信大家都不陌生,都会说也就是模型-视图-控制器这三层的框架结构,如果你参加面试的时候考官会问:“MVC框架是什么?你说一说.”其实我们都知道这个问题还需要问的,只要你是一个开发人员 ...

随机推荐

  1. SQL Server 事务与隔离级别实例讲解

    上班途中,你在一处ATM机前停了下来.正当你在敲入密码的时候,你的一位家人也正在镇上的另一处TAM机上输入密码.你打算从某个还有500元余额的账户上转出400元,而你的家人想从同一账户取走300元.倘 ...

  2. 函数和常用模块【day05】:装饰器前奏(一)

    本节内容 定义 原则 实现装饰器的储备知识 函数及变量 高阶函数 一.定义 1.装饰器:本质是函数. 2.功能:用来装饰其他函数,顾名思义就是,为其他的函数添加附件功能的. 二.原则 不能修改被装饰函 ...

  3. 工具类 | window批处理杀死指定端口进程

    window批处理杀死指定端口进程,注意保存时使用ansi格式,运行输入端口即可 @echo off setlocal enabledelayedexpansion set /p port=请输入端口 ...

  4. bzoj千题计划282:bzoj4517: [Sdoi2016]排列计数

    http://www.lydsy.com/JudgeOnline/problem.php?id=4517 组合数+错排公式 #include<cstdio> #include<ios ...

  5. 关于An internal error occurred during: "Launching MVC on Tomcat 6.x". java.lang.NullPointerException异常处理

    一大早上来启动打开myeclipse就报一个这样的错误An internal error occurred during: "Launching MVC on Tomcat  6.x&quo ...

  6. 按esc键 关闭模态框

    <!-- help start --> <div class="modal fade" id="myModal" tabindex=" ...

  7. 关闭ios弹出框:“would like to use your current location”

    图一: 图二: 使用cordova生成ios项目,首次打开获取用户定位时会弹出两次对话框,关闭图二中对话框方法: document.addEventListener("deviceready ...

  8. Dream_Spark-----Spark 定制版:005~贯通Spark Streaming流计算框架的运行源码

    Spark 定制版:005~贯通Spark Streaming流计算框架的运行源码   本讲内容: a. 在线动态计算分类最热门商品案例回顾与演示 b. 基于案例贯通Spark Streaming的运 ...

  9. linux笔记_day12_shell编程

    1.shell中如何进行算术运算 A=1 B=2 1)let 算术运算表达式 let C=$A+$B 2)$[算术运算表达式] C=$[$A+$B] 3)$(($A+$B)) 4) expr 算术表达 ...

  10. 【ARTS】01_04_左耳听风-20181203~1209

    ARTS: Algrothm: leetcode算法题目 Review: 阅读并且点评一篇英文技术文章 Tip/Techni: 学习一个技术技巧 Share: 分享一篇有观点和思考的技术文章 Algo ...