logo头像

一路过来,不过游牧自己。。

SSM之Spring(五)


这一节主要讲讲代理这一概念,代理在Sping中是一种非常重要的思想,学好代理是学好Spring的重要一环!

一、什么是代理?

代理就是起到一个代为处理的作用,相当于一个中介!举个例子:
我们这有三个角色,一个房东,一个租客,一个中介(类似于链家,赶集那种)。房东有房子要出租,一般会挂到中介平台上,中介会收取一定的费用,而租户就直接上中介去找房子,这样选择会更多!那么这个中介的作用就是我们说的代理!

二、代理详解

在Spring中,代理分成两种,一种是静态代理,另外一种就是动态代理,下面我们用代码来聊聊这两个东西

1、静态代理

A)角色分析:

抽象角色:一般使用接口或者抽象类来实现
真实角色:被代理的角色
代理角色:代理真实角色,之后一般会做一些附属的操作
客户:使用代理角色来进行一些操作

B)UML画图示意:

代理示意图

C)代码实现:

首先创建一个Rent接口:

1
2
3
public interface Rent {
public void rent();
}

然后创建真实角色,这个角色实现了Rent接口:

1
2
3
4
5
public class Host implements Rent{
public void rent(){
System.out.println("房屋出租");
}
}

然后创建一个代理类,代理类也会实现Rent接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Proxy implements Rent {
private Host host;
public Proxy(Host host) {
super();
this.host = host;
}
public Host getHost() {
return host;
}
public void setHost(Host host) {
this.host = host;
}
public void rent(){
SeeHouse();
host.rent();
fare();
}

private void SeeHouse(){
System.out.println("带房客看房");
}
private void fare() {
System.out.println("收取中介费");

}
}

最后我们去实现client,他是我们实际操作的接口:

1
2
3
4
5
6
7
public class Client {
public static void main(String[] args) {
Host host=new Host();
Proxy proxy=new Proxy(host);
proxy.rent();
}
}

我们解释下,类proxy和host类都实现了Rent接口,但是Proxy代理类中实现的是Host中的方法,Host是一个完全纯粹的类,他只有一个功能,就是提供租房,而Proxy不同,他提供了host的住房,还有一些其他功能,例如带客户看房,收取中介费等,所以功能更加繁杂!
所以输出的结果就是:

1
2
3
带房客看房
房屋出租
收取中介费

D)使用静态代理的好处和缺点:

a)使得真实角色处理的业务更加纯粹,不再去关注一些公共的事情
b)公共的业务由代理完成,实现了业务的分工
c)公共业务发生拓展时变得更加集中和方便

缺点:如果类多了,那么会有多个代理类,工作量变大,开发效率变低了!

2、动态代理

动态代理就是解决静态代理的缺点,也整合了他的所有优点
(1)其角色组成和静态代理也是一样的
(2)动态代理的代理类是动态生成的
(3)分为两类:基于接口的动态代理(jdk动态代理),基于类的动态代理(cglib)
现在javassist来生成动态代理。Javassist是一个开源的分析、编辑和创建Java字节码的类库。是jboss下面的一个子项目,
(4)Jdk动态代理——Proxy和invocationHandler接口

InvocationHandler

InvocationHandler是通过一个代理实例零调用处理程序实现的接口。
每个代理实例都有一个相关的调用处理程序。当一个方法是在一个代理实例调用,调用的方法进行编码并派遣其调用处理程序的invoke方法。
里面有一个方法:

1
invoke(Object proxy, Method method, Object[] args)

在代理实例上处理方法调用,并返回结果。
参数解释:
proxy -代理实例,调用的方法
method-对接口方法的调用相应的 method代理实例的 方法实例。声明类的 方法对象将接口的方法声明,这可能是一个超接口代理接口,代理类继承的方法。
args -包含在方法上通过代理实例调用的实参的值对象的数组,或 null如果接口方法不需要参数。原始类型的实参是包裹在适当的原始包装类的实例,如 java.lang.Integer或 java.lang.Boolean。
返回结果
在代理实例上的方法调用返回的值。如果声明的返回类型的接口方法是比较原始的类型,则该方法返回的值必须是相应的原始包装类的一个实例;否则,它必须是一个类型分配给声明的返回类型。如果此方法返回的值是 null和接口方法的返回类型是原始的,然后 NullPointerException将由法对代理实例调用抛出。如果该方法返回的值是不兼容的接口的方法声明的返回类型如上所述,一个 ClassCastException将由法对代理实例调用抛出。

Proxy

Proxy创建动态代理类的实例提供了静态方法,也是所有动态代理类的父类的方法创建。
我们来看一个方法:
newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)
返回指定的接口,将方法调用指定的调用处理程序的代理类的一个实例。 注意他的第三个参数就是InvocationHandler!

代码实现:

下面从代码的角度来看这个动态代理可能会更简单:
首先还是要有个接口:

1
2
3
public interface Rent {
public void rent();
}

对于房东来说:

1
2
3
4
5
public class Host implements Rent{
public void rent(){
System.out.println("房屋出租");
}
}

然后我们写的代理类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class ProxyInovationHandler implements InvocationHandler{
private Object target;//目标对象--真实对象,这里注意对象是Object
public void setTarget(Object target) {
this.target = target;
}
/**
* 生成代理类
* */
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
/**
* proxy是代理类
* method 代理类的调用处理程序的方法对象
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("args="+args[0]);
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
public void log(String methodName){
System.out.println("执行"+methodName+"方法");
}
}

然后定义一个User类接口:

1
2
3
4
5
6
7

public interface UserService {
public void add();
public void update();
public void delete();
public void search();
}

他的实现接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class UserServiceImpl implements UserService {

@Override
public void add() {
System.out.println("增加用户");
}

@Override
public void update() {
System.out.println("修改用户");
}

@Override
public void delete() {
System.out.println("删除用户");
}

@Override
public void search() {
System.out.println("查询用户");
}

}

最后的客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Client {
public static void main(String[] args) {
// UserService userService = new UserServiceImpl();
// ProxyInovationHandler pih = new ProxyInovationHandler();
// pih.setTarget(userService);
// UserService proxy = (UserService)pih.getProxy();
// proxy.delete();

ProxyInovationHandler ph = new ProxyInovationHandler();
ph.setTarget(new ArrayList());
List list = (List)ph.getProxy();
list.add(1);
}
}

其实动态代理的本质就是:通过proxy生成一个代理实例,通过代理实例调用方法时,会触发InvocationHandler定义的invoke方法,会将代理对象,方法参数传给args,所以一般会在invoke中加入公共业务!

学习之路漫漫长,多走路,多思考!
——————YoungerFary

微信打赏

赞赏是不耍流氓的鼓励