spring AOP 代理(静态与动态+使用cglib实现)
一、没有代理模式
缺点:
1、工作量特别大,如果项目中有多个类,多个方法,则要修改多次。
2、违背了设计原则:开闭原则(OCP),对扩展开放,对修改关闭,而为了增加功能把每个方法都修改了,也不便于维护。
3、违背了设计原则:单一职责(SRP),每个方法除了要完成自己本身的功能,还要计算耗时、延时;每一个方法引起它变化的原因就有多种。
4、违背了设计原则:依赖倒转(DIP),抽象不应该依赖细节,两者都应该依赖抽象。而在Test类中,Test与Math都是细节。
假设需实现一个计算的类Math、完成加、减、乘、除功能,如下所示:
Math类:
public class Math {
//加
public int add(int n1,int n2){
int result=n1+n2;
System.out.println(n1+"+"+n2+"="+result);
return result;
}
//减
public int sub(int n1,int n2){
int result=n1-n2;
System.out.println(n1+"-"+n2+"="+result);
return result;
}
//乘
public int mut(int n1,int n2){
int result=n1*n2;
System.out.println(n1+"X"+n2+"="+result);
return result;
}
//除
public int div(int n1,int n2){
int result=n1/n2;
System.out.println(n1+"/"+n2+"="+result);
return result;
}
}
现在需求发生了变化,要求项目中所有的类在执行方法时输出执行耗时。最直接的办法是修改源代码,如下所示:
public class Math {
//加
public int add(int n1,int n2){
//开始时间
long start = getTime();
delay();
int result=n1+n2;
System.out.println(n1+"+"+n2+"="+result);
System.out.println("共用时:" + (getTime() - start));
return result;
}
//减
public int sub(int n1,int n2){
//开始时间
long start = getTime();
delay();
int result=n1*n2;
System.out.println(n1+"-"+n2+"="+result);
System.out.println("共用时:" + (getTime() - start));
return result;
}
//乘
public int mut(int n1,int n2){
//开始时间
long start = getTime();
delay();
int result=n1*n2;
System.out.println(n1+"X"+n2+"="+result);
System.out.println("共用时:" + (getTime() - start));
return result;
}
//除
public int div(int n1,int n2){
//开始时间
long start = getTime();
delay();
int result=n1/n2;
System.out.println(n1+"/"+n2+"="+result);
System.out.println("共用时:" + (getTime() - start));
return result;
}
public static long getTime() {
return System.currentTimeMillis();
}
public static void delay(){
try {
int n = (int) new Random().nextInt(500);
Thread.sleep(n);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试运行:
Math math = new Math();
int n=100;
int m=5;
math.add(n,m);
math.sub(n,m);
math.mut(n,m);
math.div(n,m);

二、静态代理
1、定义抽象主题接口。
IMath:
public interface IMath {
int add(int n1,int n2);
int sub(int n1,int n2);
int mut(int n1,int n2);
int div(int n1,int n2);
}

2、实现接口:
MathImpl
public class MathImpl implements IMath{
//加
public int add(int n1,int n2){
int result=n1+n2;
System.out.println(n1+"+"+n2+"="+result);
return result;
}
//减
public int sub(int n1,int n2){
int result=n1-n2;
System.out.println(n1+"-"+n2+"="+result);
return result;
}
//乘
public int mut(int n1,int n2){
int result=n1*n2;
System.out.println(n1+"X"+n2+"="+result);
return result;
}
//除
public int div(int n1,int n2){
int result=n1/n2;
System.out.println(n1+"/"+n2+"="+result);
return result;
}
}
3、代理类
MathProxy
public class MathProxy implements IMath {
IMath math = new MathImpl();
@Override
public int add(int n1, int n2) {
//开始时间
long start = getTime();
delay();
int result = math.add(n1, n2);
System.out.println("共用时:" + (getTime() - start));
return result;
}
@Override
public int sub(int n1, int n2) {
//开始时间
long start = getTime();
delay();
int result = math.sub(n1, n2);
System.out.println("共用时:" + (getTime() - start));
return result;
}
@Override
public int mut(int n1, int n2) {
//开始时间
long start = getTime();
delay();
int result = math.mut(n1, n2);
System.out.println("共用时:" + (getTime() - start));
return result;
}
@Override
public int div(int n1, int n2) {
//开始时间
long start = getTime();
delay();
int result = math.div(n1, n2);
System.out.println("共用时:" + (getTime() - start));
return result;
}
public static long getTime() {
return System.currentTimeMillis();
}
public static void delay(){
try {
int n = (int) new Random().nextInt(500);
Thread.sleep(n);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试运行:
IMath math = new MathProxy();
int n1=100,n2=5;
math.add(n1, n2);
math.sub(n1, n2);
math.mut(n1, n2);
math.div(n1, n2);

通过静态代理,是否完全解决了上述的4个问题:
已解决:
1、解决了“开闭原则(OCP)”的问题,因为并没有修改Math类,而扩展出了MathProxy类。
2、解决了“依赖倒转(DIP)”的问题,通过引入接口。
3、解决了“单一职责(SRP)”的问题,Math类不再需要去计算耗时与延时操作,但从某些方面讲MathProxy还是存在该问题。
未解决:
4、如果项目中有多个类,则需要编写多个代理类,工作量大,不好修改,不好维护,不能应对变化。
如果要解决上面的问题,可以使用动态代理。
三、动态代理
1、接口不变:

2、实现类接口不变

3、修改代理类,实现接口InvocationHandler
DynamicProxy
package com.wbg.springAOP.springdynamic; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Random;
/**
* 动态代理类
*/
public class DynamicProxy implements InvocationHandler {
IMath math = new MathImpl();
//目标代理对象
Object targetObject;
public DynamicProxy(Object target){
this.targetObject=target;
}
public DynamicProxy(){ }
/**
* 获得被代理后的对象
* @param object 被代理的对象
* @return 代理后的对象
*/
public Object getProxyObject(Object object){
this.targetObject=object;
return Proxy.newProxyInstance(
targetObject.getClass().getClassLoader(), //类加载器
targetObject.getClass().getInterfaces(), //获得被代理对象的所有接口
this); //InvocationHandler对象
//loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来生成代理对象进行加载
//interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
//h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上,间接通过invoke来执行
} /**
* 获取时间
* @return
*/
public static long getTime() {
return System.currentTimeMillis();
} /**
* 延迟
*/
public static void delay(){
try {
int n = (int) new Random().nextInt(500);
Thread.sleep(n);
} catch (InterruptedException e) {
e.printStackTrace();
}
} /**
* 当用户调用对象中的每个方法时都通过下面的方法执行,方法必须在接口
* proxy 被代理后的对象
* method 将要被执行的方法信息(反射)
* args 执行方法时需要的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start=getTime();
delay();
Object result=method.invoke(targetObject,args);
System.out.println("共用时:" + (getTime() - start));
return result;
}
}
4、测试:
方式一:
DynamicProxy dynamicProx = new DynamicProxy(new MathImpl());
IMath math = (IMath) Proxy.newProxyInstance(Test.class.getClassLoader(),new Class[]{IMath.class},dynamicProx);
int n1=100,n2=5;
math.add(n1, n2);
math.sub(n1, n2);
math.mut(n1, n2);
math.div(n1, n2);
方式二:
//实例化一个MathProxy代理对象
//通过getProxyObject方法获得被代理后的对象
IMath math=(IMath)new DynamicProxy().getProxyObject(new MathImpl());
int n1=100,n2=5;
math.add(n1, n2);
math.sub(n1, n2);
math.mut(n1, n2);
math.div(n1, n2);
运行结果

JDK内置的Proxy动态代理可以在运行时动态生成字节码,而没必要针对每个类编写代理类。中间主要使用到了一个接口InvocationHandler与Proxy.newProxyInstance静态方法,参数说明如下:
使用内置的Proxy实现动态代理有一个问题:被代理的类必须实现接口,未实现接口则没办法完成动态代理。
如果项目中有些类没有实现接口,则不应该为了实现动态代理而刻意去抽出一些没有实例意义的接口,通过cglib可以解决该问题。
四、动态代理,使用cglib实现
CGLIB(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口,通俗说cglib可以在运行时动态生成字节码。
1、引用cglib,通过maven
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.9</version>
</dependency>
2、实现DynamicProxyCgilb类 接口MethodInterceptor
package com.wbg.springAOP.springdynamic; import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method;
import java.util.Random; /*
* 动态代理类
* 实现了一个方法拦截器接口
*/
public class DynamicProxyCgilb implements MethodInterceptor { //被代理对象
Object targetObject;
//动态生成一个新的类,使用父类的无参构造方法创建一个指定了特定回调的代理实例
public Object getProxyObject(Object object){
this.targetObject = object;
//增强器,动态代码生成器
Enhancer enhancer=new Enhancer();
//回调方法
enhancer.setCallback(this);
//设置生成类的父类类型
enhancer.setSuperclass(targetObject.getClass());
//动态生成字节码并返回代理对象
return enhancer.create();
}
// 拦截方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long start=getTime();
delay();
Object result=methodProxy.invoke(targetObject,objects);
System.out.println("共用时:" + (getTime() - start));
return result;
} /**
* 获取时间
* @return
*/
public static long getTime() {
return System.currentTimeMillis();
} /**
* 延迟
*/
public static void delay(){
try {
int n = (int) new Random().nextInt(500);
Thread.sleep(n);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

3、测试
MathImpl math = (MathImpl) new DynamicProxyCgilb().getProxyObject(new MathImpl());
int n1=100,n2=5;
math.add(n1, n2);
math.sub(n1, n2);
math.mut(n1, n2);
math.div(n1, n2);

使用cglib可以实现动态代理,即使被代理的类没有实现接口,但被代理的类必须不是final类。demo:https://github.com/weibanggang/springaopstaticanddynamic.git
spring AOP 代理(静态与动态+使用cglib实现)的更多相关文章
- spring aop,静态及动态代理例子
@Aspect@Componentpublic class AopText { @Pointcut("execution(public * com.llf.service.*Service. ...
- jdk动态代理与cglib代理、spring aop代理实现原理
原创声明:本博客来源与本人另一博客[http://blog.csdn.net/liaohaojian/article/details/63683317]原创作品,绝非他处摘取 代理(proxy)的定义 ...
- jdk动态代理与cglib代理、spring aop代理实现原理解析
原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...
- 何为代理?jdk动态代理与cglib代理、spring Aop代理原理浅析
原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...
- jdk动态代理与cglib代理、spring Aop代理原理-代理使用浅析
原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...
- Spring框架系列(11) - Spring AOP实现原理详解之Cglib代理实现
我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理.@pdai Spring框架系列 ...
- Spring AOP代理时 ClassCastException: $Proxy0 cannot be cast to (类型转换错误)
Spring AOP代理时 ClassCastException: $Proxy0 cannot be cast to (类型转换错误) 问题: 今天在用AfterReturningAdvice时,a ...
- java中代理,静态代理,动态代理以及spring aop代理方式,实现原理统一汇总
若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的. 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类. ...
- Spring AOP的作用,动态代理模式
AOP即面向切面编程.AOP是基于代理模式的. 代理模式: 当我们需要修改一个类,在类中加入代码时,为了不破坏这个类的封装性.可以使用代理模式,建立一个代理类. 比如:修改需求,在调用UserCont ...
- Spring AOP 代理类,BeanNameAutoProxyCreator cglib
BeanNameAutoProxyCreator支持拦截接口和类,但不支持已经被jdk代理过的类$Proxy8.使用cglib才能代理,如下 <!-- 通过bean的名字来匹配选择要代理的bea ...
随机推荐
- Aspose.Cells 对excel的使用总结
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...
- ubuntu基本配置学习(1)
[转载]源自:http://www.haogongju.net/art/2048612 附加另外一篇文章:http://www.cnblogs.com/vincent-hv/archive/2013/ ...
- Java温故而知新(4)类String字符串
字符串是由字符组成,在Java中,字符串是对象,是描述字符的基本数据结构.String类可以用来保存一个字符串,本类是最终类,不允许继承: 1.String对象的创建 初始化 由于String对象特别 ...
- asp.net core 错误定位 & vs2017 远程调试部署在centos上的asp.net core程序
前言 程序运行中会出现各种BUG. 排除BUG有三种方式. 一.访问页面直接报错误信息 出于安全,服务器是关闭这个功能的.在centos上可以用 命令设置环境变量来解决: export A ...
- MySQL--主从数据库同步原理
主从数据库的复制原理:摘自MySQL官网 1. 异步 * 主在执行sql之后,记录二进制bin-log文件. * 同时从连接主服务器,并从主获取binlog,存于本地relay-log,并从上次记 ...
- 【阿里云产品公测】ACE下上传文件永久存储实践
本帖主要内容: ;$,=VB:' 在阿里云的ACE下,我是如何实现让上传的文件永久保存的? ,%"!8T 本文以PHP为例,具体知识点如下: WD# 96V 第一,扩展服务“存储 ...
- 第三方库 jsoncpp 读写json
一.摘要 JSON 的全称为:JavaScript Object Notation,顾名思义,JSON 是用于标记 Javascript 对象的,JSON 官方的解释为:JSON 是一种轻量级的数据传 ...
- python 数据结构应用
- Mysql存储Emoji表情[为何utf8不能存储以及如何使Mysql能够存储Emoji表情]
https://segmentfault.com/a/1190000008533577
- PHP | 别家网站都有的登录功能,你的网站也可以有!
如果说一个网站是一个独立的王国,那登录功能就相当于这个[王国]的大门.进出往来的人必须要通过这道[门]才能进出这个[王国],这样才能有效的达到对人流量和用户的有效监管,也可以进一步了解每个用户的喜好, ...