静态代理
参考
https://blog.csdn.net/justloveyou_/article/details/74203025
https://blog.csdn.net/briblue/article/details/73928350
根据代理类的产生方式和时机分为静态代理和动态代理两种。代理类不仅可以有效的将具体的实现与调用方进行解耦,通过面向接口进行编码完全将具体的实现隐藏在内部,而且还可以在符合开闭原则的前提下,对目标类进行进一步的增强。典型地,Spring AOP 是对JDK动态代理的经典应用。
例一
/*
* 接口的应用:代理模式
*
*/
public class NetWorkTest {
public static void main(String[] args) {
Server server = new Server();
// server.browse();
ProxyServer proxyServer = new ProxyServer(server);
// 外面是代理类执行的方法,其实是对真正工作的方法的增强
proxyServer.browse();
}
}
//接口有这个行为或者功能
interface NetWork {
public void browse();
}
// 被代理类 真正要去工作的这个类
class Server implements NetWork {
@Override
public void browse() {
System.out.println("真实的服务器访问网络");
}
}
// 代理类 代理了真实工作的类 对真正工作的方法进行了增强
class ProxyServer implements NetWork {
private NetWork work;
// 这里的参数work是接口 其实传Server也行 多态的体现
public ProxyServer(NetWork work) {
this.work = work;
}
public void check() {
System.out.println("联网之前的检查工作");
}
@Override
public void browse() {
check();
work.browse();
}
}
例二
package com.atguigu.java1;
public class StaticProxyTest {
public static void main(String[] args) {
Proxy s = new Proxy(new RealStar());
s.confer();
s.signContract();
s.bookTicket();
s.sing();
s.collectMoney();
}
}
//接口定义所拥有的能力
interface Star {
void confer();// 面谈
void signContract();// 签合同
void bookTicket();// 订票
void sing();// 唱歌
void collectMoney();// 收钱
}
// 被代理类
class RealStar implements Star {
public void confer() {
}
public void signContract() {
}
public void bookTicket() {
}
public void sing() {
System.out.println("明星:歌唱~~~");
}
public void collectMoney() {
}
}
// 代理类
class Proxy implements Star {
private Star real;
public Proxy(Star real) {
this.real = real;
}
public void confer() {
System.out.println("经纪人面谈");
}
public void signContract() {
System.out.println("经纪人签合同");
}
public void bookTicket() {
System.out.println("经纪人订票");
}
public void sing() {
real.sing();
}
public void collectMoney() {
System.out.println("经纪人收钱");
}
}
小结
朋友圈卖面膜的举例,按理说,顾客可以直接从厂家购买产品,但是现实生活中,很少有这样的销售模式。一般都是厂家委托给代理商进行销售,顾客跟代理商打交道,而不直接与产品实际生产者进行关联。
所以,代理就有一种中间人的味道。
需要注意的有下面几点:
- 用户只关心接口功能,而不在乎谁提供了功能。上图中接口是 Subject。
- 接口真正实现者是上图的 RealSubject,但是它不与用户直接接触,而是通过代理。
- 代理就是上图中的 Proxy,由于它实现了 Subject 接口,所以它能够直接与用户接触。
- 用户调用 Proxy 的时候,Proxy 内部调用了 RealSubject。所以,Proxy 是中介者,它可以增强 RealSubject 操作。
例三
这里再加上看电影的例子
https://blog.csdn.net/briblue/article/details/73928350
package com.atguigu.java1;
public class MovieProxy {
public static void main(String[] args) {
RealMovie realmovie = new RealMovie();
Movie movie = new Cinema(realmovie);
movie.play();
}
}
// 首先得有一个接口,通用的接口是代理模式实现的基础。这个接口我们命名为 Movie,代表电影播放的能力。
interface Movie {
void play();
}
// 然后,我们要有一个真正的实现这个 Movie 接口的类,和一个只是实现接口的代理类。
class RealMovie implements Movie {
@Override
public void play() {
System.out.println("您正在观看电影 《肖申克的救赎》");
}
}
// 它实现了 Movie 接口, Cinema 就是Proxy 代理对象,
// 它有一个 play() 方法。
// 不过调用 play() 方法时,它进行了一些相关利益的处理,那就是广告。现在,我们编写测试代码。
class Cinema implements Movie {
RealMovie movie;
public Cinema(RealMovie movie) {
super();
this.movie = movie;
}
@Override
public void play() {
guanggao(true);
movie.play();
guanggao(false);
}
public void guanggao(boolean isStart) {
if (isStart) {
System.out.println("电影马上开始了,爆米花、可乐、口香糖9.8折,快来买啊!");
} else {
System.out.println("电影马上结束了,爆米花、可乐、口香糖9.8折,买回家吃吧!");
}
}
}
静态代理总结
现在可以看到,代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。代理类增强了被代理类的方法。代理类在执行之前或者之后做了其他的操作。
动态代理
在第一节我们已经提到,动态代理可以在程序运行期间根据需要动态的创建代理类及其实例来完成具体的功能,下面我们结合具体实例来介绍JDK动态代理。
看代码注释加理解
package com.atguigu.java1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 同样地,首先得有一个接口,通用的接口是代理模式实现的基础。
interface Subject {
public void doSomething();
}
// 被代理角色(目标类)
// 然后,我们要有一个真正的实现这个 Subject 接口的类,以便代理。
class RealSubject implements Subject {
public void doSomething() {
System.out.println("call doSomething()");
}
}
// 代理角色(代理类)与客户端
// 在动态代理中,代理类及其实例是程序自动生成的,因此我们不需要手动去创建代理类。
// 在Java的动态代理机制中,InvocationHandler(Interface)接口和Proxy(Class)类是实现我们动态代理所必须用到的。
// 事实上,Proxy通过使用InvocationHandler对象生成具体的代理代理对象,下面我们看一下对InvocationHandler接口的实现:
class ProxyHandler implements InvocationHandler {
private Object proxied; // 被代理对象
public ProxyHandler(Object proxied) {
this.proxied = proxied;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在转调具体目标对象之前,可以执行一些功能处理
System.out.println("前置增强处理: yoyoyo...");
// 转调具体目标对象的方法(三要素:实例对象 + 实例方法 + 实例方法的参数)
Object obj = method.invoke(proxied, args);
// 在转调具体目标对象之后,可以执行一些功能处理
System.out.println("后置增强处理:hahaha...");
return obj;
}
}
// 在实现了InvocationHandler接口后,我们就可以创建代理对象了。
// 在Java的动态代理机制中,我们使用Proxy类的静态方法newProxyInstance创建代理对象,如下
public class DynamicProxy {
public static void main(String args[]) {
// 真实对象real 这都是多态的体现 类似于父类的引用指向子类的对象
Subject real = new RealSubject();
// 生成real的代理对象
Subject proxySubject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[] { Subject.class }, new ProxyHandler(real));
proxySubject.doSomething();
System.out.println("代理对象的类型 : " + proxySubject.getClass().getName());
System.out.println("代理对象所在类的父类型 : " + proxySubject.getClass().getGenericSuperclass());
}
}
到此为止,我们给出了完整的基于JDK动态代理机制的代理模式的实现。我们从上面的实例中可以看到,代理对象proxySubject的类型为”com.sun.proxy.$Proxy0”,这恰好印证了proxySubject对象是一个代理对象。除此之外,我们还发现代理对象proxySubject所对应的类继承自java.lang.reflect.Proxy类,这也正是JDK动态代理机制无法实现对class的动态代理的原因:Java只允许单继承。
JDK中InvocationHandler接口与Proxy类
(1). InvocationHandler接口
InvocationHandler 是一个接口,官方文档解释说:每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
InvocationHandler中的invoke() 方法决定了怎么样处理代理传递过来的方法调用。
(2). Proxy类
JDK通过 Proxy 的静态方法 newProxyInstance 动态地创建代理,该方法在Java中的声明如下:
/**
* @description
* @author rico
* @created 2017年7月3日 下午3:16:49
* @param loader 类加载器
* @param interfaces 目标类所实现的接口
* @param h InvocationHandler 实例
* @return
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
事实上,Proxy 动态产生的代理对象调用目标方法时,代理对象会调用 InvocationHandler 实现类,所以 InvocationHandler 是实际执行者。
Spring AOP 与 动态代理
AOP 专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在 Java EE 应用中,常常通过 AOP 来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等,AOP 已经成为一种非常常用的解决方案。
AOP机制是 Spring 所提供的核心功能之一,其既是Java动态代理机制的经典应用,也是动态AOP实现的代表。Spring AOP默认使用Java动态代理来创建AOP代理,具体通过以下几个步骤来完成:
Spring IOC 容器创建Bean(目标类对象);
Bean创建完成后,Bean后处理器(BeanPostProcessor)根据具体的切面逻辑及Bean本身使用Java动态代理技术生成代理对象;
应用程序使用上述生成的代理对象替代原对象来完成业务逻辑,从而达到增强处理的目的。