【Spring】Spring AOP实现原理
Spring AOP实现原理
在之前的一文中介绍过Spring AOP的功能使用,但是没有深究AOP的实现原理,今天正好看到几篇好文,于是就自己整理了一下AOP实现的几种方式,同时把代理模式相关知识也稍微整理一下。
代理模式
代理模式的UML类图如下:
可以看到还是很简单的,代理类实现了被代理类的接口,同时与被代理类是组合关系。下面看一下代理模式的实现。
静态代理
接口类:
interface Person {
void speak();
}
真实实体类:
class Actor implements Person {
private String content;
public Actor(String content) {
this.content = content;
}
@Override
public void speak() {
System.out.println(this.content);
}
}
代理类:
class Agent implements Person {
private Actor actor;
private String before;
private String after;
public Agent(Actor actor, String before, String after) {
this.actor = actor;
this.before = before;
this.after = after;
}
@Override
public void speak() {
//before speak
System.out.println("Before actor speak, Agent say: " + before);
//real speak
this.actor.speak();
//after speak
System.out.println("After actor speak, Agent say: " + after);
}
}
测试方法:
public class StaticProxy {
public static void main(String[] args) {
Actor actor = new Actor("I am a famous actor!");
Agent agent = new Agent(actor, "Hello I am an agent.", "That's all!");
agent.speak();
}
}
结果:
动态代理
在讲JDK的动态代理方法之前,不妨先想想如果让你来实现一个可以任意类的任意方法的代理类,该怎么实现?有个很naive的做法,通过反射获得Class和Method,再调用该方法,并且实现一些代理的方法。我尝试了一下,很快就发现问题所在了。于是乎,还是使用JDK的动态代理接口吧。
JDK自带方法
首先介绍一下最核心的一个接口和一个方法:
首先是java.lang.reflect包里的InvocationHandler接口:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
我们对于被代理的类的操作都会由该接口中的invoke方法实现,其中的参数的含义分别是:
- proxy:被代理的类的实例
- method:调用被代理的类的方法
- args:该方法需要的参数
使用方法首先是需要实现该接口,并且我们可以在invoke方法中调用被代理类的方法并获得返回值,自然也可以在调用该方法的前后去做一些额外的事情,从而实现动态代理,下面的例子会详细写到。
另外一个很重要的静态方法是java.lang.reflect包中的Proxy类的newProxyInstance方法:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
其中的参数含义如下:
- loader:被代理的类的类加载器
- interfaces:被代理类的接口数组
- invocationHandler:就是刚刚介绍的调用处理器类的对象实例
该方法会返回一个被修改过的类的实例,从而可以自由的调用该实例的方法。下面是一个实际例子。
Fruit接口:
public interface Fruit {
public void show();
}
Apple实现Fruit接口:
public class Apple implements Fruit{
@Override
public void show() {
System.out.println("<<<<show method is invoked");
}
}
代理类Agent.java:
public class DynamicAgent {
//实现InvocationHandler接口,并且可以初始化被代理类的对象
static class MyHandler implements InvocationHandler {
private Object proxy;
public MyHandler(Object proxy) {
this.proxy = proxy;
}
//自定义invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(">>>>before invoking");
//真正调用方法的地方
Object ret = method.invoke(this.proxy, args);
System.out.println(">>>>after invoking");
return ret;
}
}
//返回一个被修改过的对象
public static Object agent(Class interfaceClazz, Object proxy) {
return Proxy.newProxyInstance(interfaceClazz.getClassLoader(), new Class[]{interfaceClazz},
new MyHandler(proxy));
}
}
测试类:
public class ReflectTest {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
//注意一定要返回接口,不能返回实现类否则会报错
Fruit fruit = (Fruit) DynamicAgent.agent(Fruit.class, new Apple());
fruit.show();
}
}
结果:
可以看到对于不同的实现类来说,可以用同一个动态代理类来进行代理,实现了“一次编写到处代理”的效果。但是这种方法有个缺点,就是被代理的类一定要是实现了某个接口的,这很大程度限制了本方法的使用场景。下面还有另外一个使用了CGlib增强库的方法。
CGLIB库的方法
CGlib是一个字节码增强库,为AOP等提供了底层支持。下面看看它是怎么实现动态代理的。
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGlibAgent implements MethodInterceptor {
private Object proxy;
public Object getInstance(Object proxy) {
this.proxy = proxy;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.proxy.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
//回调方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println(">>>>before invoking");
//真正调用
Object ret = methodProxy.invokeSuper(o, objects);
System.out.println(">>>>after invoking");
return ret;
}
public static void main(String[] args) {
CGlibAgent cGlibAgent = new CGlibAgent();
Apple apple = (Apple) cGlibAgent.getInstance(new Apple());
apple.show();
}
}
结果:
可以看到结果和JDK动态代理是一样的,但是可以直接对实现类进行操作而非接口,这样会有很大的便利。
参考文献
- 《Java编程思想》第14章
- java动态代理(JDK和cglib)
【Spring】Spring AOP实现原理的更多相关文章
- 深入浅析Spring的AOP实现原理
转载来源:https://www.jb51.net/article/81788.htm AOP(Aspect-OrientedProgramming,面向切面编程),可以说是OOP(Object-Or ...
- Spring IOC AOP的原理 如果让你自己设计IOC,AOP如何处理(百度)
百度的面试官问,如果让你自己设计一个IOC,和AOP,如何设计, 我把IOC的过程答出来了,但是明显不对, (1) IOC 利用了反射,自己有个id,classtype,hashmap,所有的功能都在 ...
- 【Spring】AOP实现原理(一):AOP基础知识
AOP相关概念 在学习AOP实现原理之前,先了解下AOP相关基础知识. AOP面向切面编程,它可以通过预编译方式或者基于动态代理对我们编写的代码进行拦截(也叫增强处理),在方法执行前后可以做一些操作, ...
- Spring核心技术AOP实现原理
关于Spring的AOP也是Spring的非常重要的一项技术.大致上可以这样说,面向切面编程,它的出现说明可以在不修改代码的情况下实现对功能的增强.而增强就是给一个方法增加一些功能.AOP主要思想就是 ...
- 新秀学习SSH(十四)——Spring集装箱AOP其原理——动态代理
之前写了一篇文章IOC该博客--<Spring容器IOC解析及简单实现>,今天再来聊聊AOP.大家都知道Spring的两大特性是IOC和AOP. IOC负责将对象动态的注入到容器,从而达到 ...
- 理解Spring:AOP的原理及手动实现
引入 到目前为止,我们已经完成了简易的IOC和DI的功能,虽然相比如Spring来说肯定是非常简陋的,但是毕竟我们是为了理解原理的,也没必要一定要做一个和Spring一样的东西.到了现在并不能让我们松 ...
- 【Spring】AOP实现原理(三):创建代理
AbstractAutoProxyCreator 在AbstractAutoProxyCreator的wrapIfNecessary方法中,调用getAdvicesAndAdvisorsForBean ...
- 【Spring】AOP实现原理(二):Advisor获取
@EnableAspectJAutoProxy @EnableAspectJAutoProxy注解可以用来开启AOP,那么就从@EnableAspectJAutoProxy入手学习一下Spring A ...
- Spring之AOP实现原理
- spring ioc aop 原理
spring ioc aop 原理 spring ioc aop 的原理 spring的IoC容器是spring的核心,spring AOP是spring框架的重要组成部分. 在传统的程序设计中,当调 ...
随机推荐
- hdu 5719(Arrange)(冷静分析)
A数组显示从0到i的最小值B数组显示从0到i的最大值由此可得:A数组是单调不增的(怎么也会不使得最小值变大)B数组是单调不减的.设premin和premax为i位以前的最小值和最大值.可以得出以下几点 ...
- ABP入门系列(3)——领域层创建实体
这一节我们主要和领域层打交道.首先我们要对ABP的体系结构以及从模板创建的解决方案进行一一对应.网上有代码生成器去简化我们这一步的任务,但是不建议初学者去使用. 一.首先来看看ABP体系结构 领域层就 ...
- 不用asp.net MVC,用WebForm照样可以实现MVC(请看最后一句话)
在<避开WebForm天坑,拥抱ASP.Net MVC吧>这篇博客中我讲到了ASP.net WebForm由于一些先天的“诱导犯罪”的缺陷,现在用ASP.net MVC的公司越来越多.但是 ...
- android知识杂记(一)
记录项目中用的零碎知识点,用以备忘. android:screenOrientation:portrait 限制横屏 activity启动状态 singleTop 只执行一次,通常用在欢迎页面 sin ...
- 谈谈final的作用
前言 一直想写写这个话题.代表公司也面试过一些求职者,每次面试我必问的两个问题之一就是“请你谈一谈对于final关键字的理解”.这是一个简单的小问题,但是不要小看它,通过对这个问题的回答以及一些简单的 ...
- 【异常处理_iis】无法启动IIS Express\iisexpress.exe
正调试着程序,突然不能调试了.重启了也没用,还是报错:无法启动程序 C:\Program Files(X86)\IIS Express\iisexpress.exe. 和之前无法启动IIS Expre ...
- C# WPF 让你的窗口始终钉在桌面上
IntPtr hWnd = new WindowInteropHelper(Application.Current.MainWindow).Handle; IntPtr hWndProgMan = F ...
- FB引擎系列-之CloudSand
CloudSand,欲打破之前的集中版本制作的模式, http://code.taobao.org/p/cloudsand包含服务器端代码(php)和客户端代码(unity) EasyDown的时 ...
- [我给Unity官方视频教程做中文字幕]beginner Graphics – Lessons系列之网格Meshes
[我给Unity官方视频教程做中文字幕]beginner Graphics – Lessons系列之网格Meshes 本篇分享一下第5个已完工的视频,即<beginner Graphics – ...
- 《OOC》笔记(0)——为何要看这本书
<OOC>笔记(0)——为何要看这本书 <OOC>全名是<Object-oriented Programming with ANSI-C>,作者Axel-Tobia ...