前言

Spring AOP 就是通过代理模式来实现切面编程的。代理模式用来为其他对象提供一种代理,以控制对这个对象的访问。

代理对象在客户端和目标对象之间起到中介的作用。通过控制对这个对象的访问,可以做一些自己想做的事。比如在AOP中,方法调用前打印请求方的信息,结束时记录用时,便于后续分析。还可以在代理中进行权限校验,将职责进行清晰,一个类负责一件事,也易于分别进行测试。低耦合和对代码进行改造,拓展性好,而不用对目标对象进行改造。

本文会从静态代理模式到动态代理再到SpringAOP来介绍。

1. 静态代理

静态代理代码实现:

// 抽象主题
public interface IBus {
Object create();
}
// 具体主题
public class Bus implements IBus {
@Override
public Bus create() {
System.out.println("give you a bus");
return this;
}
}
// 代理主题角色
public class BusProxy implements IBus {
private String userName;
private Bus bus; public BusProxy(Bus bus, String userName) {
this.userName = userName;
this.bus = bus;
} @Override
public Object create() {
if (userName.equals("god")) {
return bus.create();
}
throw new IllegalStateException("you are not a god");
} }
// 静态工厂 隐藏具体主题
public class Factory { public static BusProxy getBus(String userName) {
return new BusProxy(new Bus(), userName);
} }

UML

代理模式中是没有Factory 部分的,是本人加上的。只是为了对于用户隐藏具体对象。静态代理的缺点就是不能复用,每次代理都需要写一个代理类。

2. 动态代理

动态代理和静态代理有什么不一样?动态代理可以复用,静态代理不行。静态代理不同的类都需要写一套。动态代理可以理解为动态生成的。

2.1 JDK 动态代理

通过实现InvocationHandler接口

// 实现 InvocationHandler
public class JDKProxy implements InvocationHandler { // 具体主题
private Object target; // 构造器传入 具体主题
public JDKProxy(Object object) {
this.target = object;
} // 调用者需使用bind() 本质上通过 Proxy.newProxyInstance 获取代理主题
public Object bind() {
Class clazz = target.getClass();
return Proxy.newProxyInstance(
clazz.getClassLoader(),
clazz.getInterfaces(),
this);
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeMethod();
// 反射执行
Object object = method.invoke(target, args);
afterMethod();
return object;
} private void beforeMethod() {
System.out.println("before method");
} private void afterMethod() {
System.out.println("after method");
}
}
// 测试类使用动态代理
public class Test {
public static void main(String[] args) {
IBus bus = (IBus) new JDKProxy(new Bus()).bind();
bus.create();
}
}

动态生成的代理类

源码中Proxy.newProxyInstance中式通过 静态方法ProxyGenrator.genrateClass 动态生成代理类的字节码。

方法中为代理的类"动态地"继承了Proxy 类。(所有生成的动态代理类都是Proxy类的子类)Java不支持多继承,因此JDK动态代理只能代理实现了接口的类,不能代理已经继承了别的类的类。

其中 JDKProxy 定义的是代理行为 而非代理类,而代理类是在运行时通过反射动态的创建得出来的。

2.2 Cglib 动态代理

通过代理类继承目标类的方式来实现实现代理类,无法代理final修饰的方法

CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。

代码

<!-- maven cglib 依赖 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
public class CGLibProxy implements MethodInterceptor {

    @Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
beforeMethod();
// 方法调用
Object result = methodProxy.invokeSuper(o, objects);
afterMethod();
return result;
} private void beforeMethod() {
} private void afterMethod() {
} }
public class CGLibTest {
public static void main(String[] args) {
// 创建Enhancer对象,类似于JDK动态代理的Proxy类
Enhancer enhancer = new Enhancer();
// 设置目标类的字节码文件
enhancer.setSuperclass(Bus.class);
// 设置回调函数
enhancer.setCallback(new CGLibProxy());
// 这里的creat方法就是正式创建代理类
IBus bus = (IBus) enhancer.create();
// 调用代理类的 create 方法
bus.create();
}
}

Spring 的 AOP

Spring AOP 面向切面编程。就像代码案例中的beforeMethod 和 afterMethod 一样。在 method中进行统一拦截和验证,日志记录,这样关注点进行了分离,职责分离。通用化的代码放在切面上,不再和原来的业务代码耦合。

Spring AOP代码使用

@Aspect
@Component
public class ControllerAspect {
private static final Logger log = LoggerFactory.getLogger(ControllerAspect.class); // where 切入点
@Pointcut("execution(public * org.cs.backend.pattern.structure.proxy.spring..*.*(..))")
public void webLog() {
} // when 什么时候切入
@Before("webLog()")
public void before(JoinPoint joinPoint) {
// what 作什么
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = attributes.getRequest();
log.info("request remote ip: [{}]", request.getRemoteAddr());
} }

AOP 重点名词:

Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。

Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。

Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。

Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。(共有五种:before,after (无论是否发生异常,都进行执行的通知),around ,afterRunning,afterThrowing)

Target(目标对象):织入 Advice 的目标对象.。

Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程。

  • Advice 的种类:

    • 前置通知 Before
    • 后置通知 AfterReturning
    • 异常通知 AfterThrowing
    • 最终通知 After
    • 环绕通知 Around

AOP 将业务代码和通用代码分离之后,如何让通用代码在相应的代码上起作用的?(AOP 的三种织入方式)

  • 编译时织入:需要特殊的Java编译器 如AspectJ
  • 类加载的时候织入:需要特殊的Java编译器,如AspectJ 和ApspectWerkz
  • 运行时织入: Spring采用的方式,通过动态代理的方式,实现简单

Spring在使用动态代理时如何选择的?

  • 目标类是接口时,Spring就会用JDK的动态代理,否则默认使用Spring使用Cglib代理。

JDK 和 CGLib比较

JDK动态代理生成代理对象速度比cglib快;cglib生成的代理对象比JDK动态代理生成的代理对象执行效率高。

JDK以接口形式接收代理实例,而不是代理类。CGLib以类或接口形式接收代理实例

总结

代理模式就是在原有的对象上,在不影响原有的对象下进行增强。这样可以功职责分明,分别测试。和适配器模式不同的是,适配器模式和原有对象实现的接口是不同的,而代理模式是相同的。

动态代理中 JDK的方式需要目标对象未继承其他类的。而CGLib要求目标对象未被final修饰。

JDK动态代理用Java内部的反射机制,CGLib以继承的方式通过ASM创建字节码。

参考博客

从代理模式 到 SpringAOP的更多相关文章

  1. springAOP之代理模式

    springAOP指的是在spring中的AOP,什么是AOP,相对于java中的面向对象(oop),在面向对象中一些公共的行为,像日志记录,权限验证等如果都使用面向对象来做,会在每个业务方法中都写上 ...

  2. 带你入门代理模式/SpringAop的运行机制

    SpringAop 是spring框架中最重要的一项功能之一,同时也是企业级开发记录事物日志等不可或缺的一部分,如果说你的系统需要记录用户访问接口的操作,那SpringAop是很完美的了,当然,拦截器 ...

  3. Java静态代理与动态代理模式的实现

    前言:    在现实生活中,考虑以下的场景:小王打算要去租房,他相中了一个房子,准备去找房东洽谈相关事宜.但是房东他很忙,平时上班没时间,总找不到时间去找他,他也没办法.后来,房东想了一个办法,他找到 ...

  4. Spring增强代理模式

    1. 依赖注入;(掌握) 2. XML自动注入;(掌握) 3. 全注解配置;(掌握) 4. 代理模式;(掌握,难点) 依赖注入 构造参数注入 constructor-arg:构造器注入: index: ...

  5. Java设计模式之代理模式(静态代理和JDK、CGLib动态代理)以及应用场景

    我做了个例子 ,需要可以下载源码:代理模式 1.前言: Spring 的AOP 面向切面编程,是通过动态代理实现的, 由两部分组成:(a) 如果有接口的话 通过 JDK 接口级别的代理 (b) 如果没 ...

  6. java 代理模式-静态代理与动态代理

    最近在研究SpringAOP,当然要学习AOP就要知道这么健硕.强大的功能的背后究竟隐藏着怎样不可告人的“秘密”?? 接下来就是查阅了许多资料详细的研究了一下Java的代理模式,感觉还是非常非常重要的 ...

  7. java设计模式6——代理模式

    java设计模式6--代理模式 1.代理模式介绍: 1.1.为什么要学习代理模式?因为这就是Spring Aop的底层!(SpringAop 和 SpringMvc) 1.2.代理模式的分类: 静态代 ...

  8. 大型Java进阶专题(六)设计模式之代理模式

    代理模式 前言 又开始我的专题了,又停滞了一段时间了,加油继续吧.都知道 SpringAOP 是用代理模式实现,到底是怎么实现的?我们来一探究竟,并且自己仿真手写还原部分细节. 代理模式的应用 在生活 ...

  9. 12.java设计模式之代理模式

    基本介绍: 代理模式(Proxy)为一个对象提供一个替身,以控制对这个对象的访问.即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能,想在 ...

随机推荐

  1. HTTP笔记2--简单的HTTP 协议

    HTTP概念 HTTP用于客户端和服务器之间的通信 客户端:请求访问文本或图像等资源的一端 服务器端而提供资源响应的一端 通过请求和响应的交换达成通信 HTTP 协议规定,请求从客户端发出,最后服务器 ...

  2. JVM进阶篇

      class Person { private String name = "Jack"; private int age; private final double salar ...

  3. linux下新建用户

    新建用户的两种方式: 一步步创建 useradd -m user1 #-m 是建立家目录 passwd user1 #设置密码 usermod -a -G root user1 #加入管理员 chsh ...

  4. Install pyaudio on Ubuntu

    pip install python3-pyaudio sudo apt-get install portaudio19-dev python-all-dev pip install pyaudio

  5. javascript questions & code review

    javascript questions & code review refs https://github.com/learning-js-by-reading-source-codes/j ...

  6. docker-compose All In One

    docker-compose All In One docker-compose 多容器应用 $ docker-compose -h Define and run multi-container ap ...

  7. Promise nested then execute order All In One

    Promise nested then execute order All In One Promise nested then nested Promise not return new Promi ...

  8. React Transforming Elements All In One

    React Transforming Elements All In One https://reactjs.org/docs/react-api.html#transforming-elements ...

  9. CSS & SASS & SCSS & less

    CSS & SASS & SCSS & less less vs scss https://github.com/vecerek/less2sass/wiki/Less-vs. ...

  10. node mailer & email bot

    node mailer & email bot email https://nodemailer.com/about/ https://github.com/nodemailer/nodema ...