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 ...
随机推荐
- 四、spring之DI
Bean依赖容器,那容器如何注入Bean的依赖资源,Spring IOC容器注入依赖资源主要有以下两种基本实现方式: setert注入:通过setter方法进行注入依赖:参考代码HelloTest2 ...
- 二、NAT(地址转换模式)
刚刚我们说到,如果你的网络ip资源紧缺,但是你又希望你的虚拟机能够联网,这时候NAT模式是最好的选择.NAT模式借助虚拟NAT设备和虚拟DHCP服务器,使得虚拟机可以联网.其网络结构如下图所示: NA ...
- php FastCGI 进程意外退出 错误解决方法
服务器windows2008,php5.5 / php5.6,需要安装VC++运行库. 到Microsoft网站下载“Microsoft Visual C++ 2012 Redistributable ...
- ccf-201609-2 火车购票
问题描述 请实现一个铁路购票系统的简单座位分配算法,来处理一节车厢的座位分配. 假设一节车厢有20排.每一排5个座位.为方便起见,我们用1到100来给所有的座位编号,第一排是1到5号,第二排是6到10 ...
- csharp:Learn how to post JSON string to generic Handler using jQuery in ASP.Net C#.
/// <summary> ///參考: http://james.newtonking.com/json/help/index.html# /// 塗聚文(Geovin Du) 2014 ...
- 怎样修改织梦网站的favicon图标
现在很多的网站浏览器栏上都有favicon图标,比如百度,大家用织梦做好网站后,可能发现自己的网站favicon图标默认的不好看,如何修改织梦网站的favicon导航图标呢,很多人肯定有过困惑,小编遇 ...
- 基于Vue的WebApp项目开发(二)
利用webpack解析和打包.vue组件页面 相关知识: vue项目中的每个页面其实都是一个.vue的文件,这种文件,Vue称之为组件页面,必须借助于webpack的vue-loader才能运行,所以 ...
- 任务十:Flexbox 布局练习
任务目的 学习如何flex进行布局,学习如何根据屏幕宽度调整布局策略. 任务描述 需要实现的效果如效果图(点击打开)所示,调整浏览器宽度查看响应式效果,红色的文字是说明,不需要写在 HTML 中. 任 ...
- klee错误汇报二:KLEE的optimize选项的一个困惑
问题已经提交github:https://github.com/klee/klee/issues/650 在一个对命令行参数进行建模的符号执行过程中,添加optimize选项与不添加optimize选 ...
- Data Flow ->> OLE DB Destination ->> Fast Load
OLE DB Destination组件提供了fast load选项,用bulk模式load数据而不是row-to-row的模式.这样性能上好.但是需要注意一点就是,一旦用了fast load,err ...