转载

【代理设计模式】看这篇文章就懂了

【代理设计模式】看这篇文章就懂了 在前面 :设计模式源于生活,而又高于生活!

什么是代理模式

为其他对象提供一种代理以控制对这个对象的访问。

为什么使用代理模式

中介隔离: 在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到 中介的作用 ,其特征是 代理类和委托类实现相同的接口

开闭原则,增加功能: 代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类 增加额外的功能 来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类 预处理消息 过滤消息 把消息转发给委托类 ,以及事后对 返回结果的处理 等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

代理模式实现原理

代理模式主要包含三个角色,即 抽象主题角色 (Subject)、 委托类角色( 被代理角色,Proxied)以及 代理类角色 (Proxy)

【代理设计模式】看这篇文章就懂了

抽象主题角色(Subject): 可以是接口,也可以是抽象类

委托类角色(proxied): 真实主题角色,业务逻辑的具体执行者

代理类角色(Proxy): 内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理

代理模式应用场景

SpringAop、日志收集、权限控制、过滤器、RPC远程调用

代理模式创建的方式

静态代理 和 动态代理

静态代理

静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。

所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

一句话,自己手写代理类就是静态代理。

基于接口实现方式

//主题(Subject)

public interface OrderService {

void order () ;

}

//实现接口

public class OrderServiceImpl implements OrderService {

public void order () {

System.out.println( "用户下单操作.." );

}

}

//代理类

public class OrderServiceProxy implements OrderService {

/**

* 代理对象

private OrderService proxiedOrderService;

public OrderServiceProxy ( OrderService orderService ) {

this .proxiedOrderService=orderService;

}

public void order () {

System. out .println( "日志收集开始.." );

proxiedOrderService.order();

System. out .println( "日志收集结束.." );

}

}

//测试

public class ClientTest {

public static void main (String[] args) {

OrderService orderService = new OrderServiceProxy( new OrderServiceImpl());

orderService.order();

}

}

接口继承方式实现

//继承接口实现类

public class OrderServiceProxy extends OrderServiceImpl {

public void order () {

System.out.println( "日志收集开始.." );

super .order();

System.out.println( "日志收集结束.." );

}

}

//测试

public class ClientTest {

public static void main (String[] args) {

OrderService orderService = new OrderServiceProxy();

orderService.order();

}

}

动态代理

动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。

动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成 。

JDK动态代理

JDK动态代理的一般步骤如下:

1.创建被代理的接口和类;

2.实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理;

3.调用Proxy的静态方法,创建代理类并生成相应的代理对象;

//主题()Subject

public interface OrderService {

void order () ;

}

//实现接口

public class OrderServiceImpl implements OrderService {

public void order () {

System.out.println( "修改数据库订单操作.." );

}

}

//代理类

public class JdkInvocationHandler implements InvocationHandler {

/**

* 目标代理对象

public Object target;

public JdkInvocationHandler (Object target) {

this .target = target;

}

public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {

System.out.println( ">>>日志收集开始>>>>" );

// 执行代理对象方法

Object reuslt = method.invoke(target, args);

System.out.println( ">>>日志收集结束>>>>" );

return reuslt;

}

/**

* 获取代理对象接口

*

* @param <T>

* @return

public <T> T getProxy () {

return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this );

}

}

JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler( new OrderServiceImpl());

OrderService proxy = jdkInvocationHandler.getProxy();

proxy.order();

原理分析

1. 获取代理的生成的 class 文件

System .getProperties () .put (" sun .misc.ProxyGenerator.saveGeneratedFiles ", " true ");

【代理设计模式】看这篇文章就懂了

2.使用反编译工具该Proxy0.class

【代理设计模式】看这篇文章就懂了

注意:继承了 Proxy 类,实现了代理的接口,由于java不能多继承,

这里已经继承了 Proxy 类了,不能再继承其他的类,

所以JDK的动态代理不支持对实现类的代理,

只支持接口的代理。

CGLIB动态代理

Cglib是一个 强大的,高性能,高质量 的代码生成类库。它可以在运行期扩展JAVA类与实现JAVA接口。其底层实现是通过ASM字节码处理框架来 转换字节码并生成新的类 。大部分功能实际上是ASM所提供的,Cglib只是封装了ASM,简化了ASM操作,实现了运行期生成新的class。

CGLIB原理

运行时动态的生成一个被代理类的子类(通过ASM字节码处理框架实现),子类重写了被代理类中所有非final的方法。在子类中采用 方法拦截 的技术拦截所有父类方法的调用,顺势植入横切逻辑。

Cglib优缺点

优点:JDK动态代理要求被代理的类必须实现接口,当需要代理的类没有实现接口时Cglib代理是一个很好的选择。另一个优点是 Cglib动态代理比使用java反射的JDK动态代理要快

缺点:对于被代理类中的final方法,无法进行代理,因为子类中 无法重写final函数

CGLIB代理实现

实现MethodInterceptor接口的intercept方法后,所有生成的代理方法都调用这个方法。

intercept方法的具体参数有

obj 目标类的实例

1 .method 目标方法实例(通过反射获取的目标方法实例)

2 .args 目标方法的参数

3

.proxy

代理类的实例

该方法的返回值就是目标方法的返回值。

public class OrderServiceImpl {

public void order () {

System. out .println( "用户下单操作.." );

}

}

public class CglibMethodInterceptor implements MethodInterceptor {

public Object intercept (Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

System.out.println( "<<<<<日志收集开始...>>>>>>>" );

Object reuslt = proxy.invokeSuper(obj, args);

System.out.println( "<<<<<日志收集结束...>>>>>>>" );

return reuslt;

}

}

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D://code" );

CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();

Enhancer enhancer = new Enhancer();

// 设置代理类的付类

enhancer.setSuperclass(OrderServiceImpl . class ) ;

// 设置回调对象

enhancer.setCallback(cglibMethodInterceptor);

// 创建代理对象

OrderServiceImpl orderServiceImpl = (OrderServiceImpl) enhancer.create();

orderServiceImpl.order();

Maven依赖

< dependencies >

< dependency >

< groupId > cglib </ groupId >

< artifactId > cglib </ artifactId >

< version > 3.2.12 </ version >

</ dependency >

</ dependencies >

结果

>>>>cglib日志收集开始....

执行订单业务逻辑代码

>>>>cglib日志收集结束....

静态代理与动态代理区别

静态代理需要自己写代理类,而动态代理不需要写代理类。

JDK动态代理与CGLIB实现区别

JDK动态代理底层实现:

JDK的动态代理使用Java的反射技术生成动态代理类,只能代理实现了接口的类, 没有实现接口的类不能实现动态代理。

CGLIB动态代理底层实现:

运行时动态的生成一个被代理类的子类(通过ASM字节码处理框架实现),子类重写了被代理类中所有非final的方法,在子类中采用方法拦截的技术拦截所有父类方法的调用,不需要被代理类对象实现接口,从而CGLIB动态代理效率比Jdk动态代理反射技术效率要高。

案例:使用AOP拦截Controller所有请求日志

@Aspect

@Component

@Slf4j

public class AopLogAspect {

// 申明一个切点 里面是 execution表达式

@Pointcut( "execution(* com.xuyu.controller.*.*(..))" )

private void serviceAspect () {

}

// 请求method前打印内容

@Before(value = "serviceAspect()" )

public void methodBefore (JoinPoint joinPoint) {

ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder

.getRequestAttributes();

HttpServletRequest request = requestAttributes.getRequest();

// 打印请求内容

log .info( "===============请求内容===============" );

log .info( "请求地址:" + request.getRequestURL().toString());

log .info( "请求方式:" + request.getMethod());

log .info( "请求类方法:" + joinPoint.getSignature());

log .info( "请求类方法参数:" + Arrays.toString(joinPoint.getArgs()));

log .info( "===============请求内容===============" );

}

// 在方法执行完结后打印返回内容

@AfterReturning(returning = "o" , pointcut = "serviceAspect()" )

public void methodAfterReturing (Object o) {

log .info( "--------------返回内容----------------" );

log .info( "Response内容:" + o.toString());

log .info( "--------------返回内容----------------" );

}

}

Maven依赖信息

< parent >

< groupId > org.springframework.boot </ groupId >

< artifactId > spring-boot-starter-parent </ artifactId >

< version > 2.0.1.RELEASE </ version >

</ parent >

< dependencies >

< dependency >

< groupId > cglib </ groupId >

< artifactId > cglib </ artifactId >

< version > 3.2.12 </ version >

</ dependency >

<!-- sprinboot web -->

< dependency >

< groupId > org.springframework.boot </ groupId >

< artifactId > spring-boot-starter-web </ artifactId >

</ dependency >

< dependency >

< groupId > org.projectlombok </ groupId >

< artifactId > lombok </ artifactId >

< version > 1.16.10 </ version >

</ dependency >

< dependency >

< groupId > commons-lang </ groupId >

< artifactId > commons-lang </ artifactId >

< version > 2.6 </ version >

</ dependency >

< dependency >

< groupId > org.springframework.boot </ groupId >

< artifactId > spring-boot-starter-aop </ artifactId >

</ dependency >

</ dependencies >

结果

===============请求内容===============

: 请求地址:http://127.0.0.1:8080/getUser

: 请求方式:GET

: 请求类方法:String com.xuyu.service.controller.IndexController.getUser(String,Integer)

: 请求类方法参数:[xuyu, 2]

: ===============请求内容===============

: >>>userName:{},xuyu

: --------------返回内容----------------

: Response内容:success_getUser

: --------------返回内容----------------

【代理设计模式】看这篇文章就懂了

基本数据类型使用姿势不对导致的线上"死循环"问题排查

百度网盘"不限速"工具 Pandownload开发者被抓

分布式中采用Logback的MDC机制与AOP切面结合串联日志

三个方案解决Elasticsearch深度翻页问题

彻底搞清分库分表(垂直分库,垂直分表,水平分库,水平分表)

小白也能学会的RAID磁盘冗余阵列教程

【代理设计模式】看这篇文章就懂了

欢迎分享转发,有帮助的话点个“在看”

原文  http://mp.weixin.qq.com/s?__biz=MzU2NDc4MjE2Ng==&mid=2247484499&idx=1&sn=72a79605fe46805072097d9822987387
正文到此结束
Loading...