少女祈祷中...

1.代理模式和静态代理

代理模式

  • Proxy Pattern, 23个经典模式的一种,又称委托模式
  • 为目标对象提供(包装)了一个代理,这个代理可以控制对目标对象的访问
  • 外界不用直接访问目标对象,而是访问代理对象,由代理对象再调用目标对象
  • 代理对象中可以添加监控和审查处理

静态代理

  • 代理对象持有目标对象的句柄
  • 所有调用目标对象的方法,都调用代理对象的方法
  • 代理类和被代理类应该共同实现一个接口,或者是共同继承某个类
  • 对每个方法,需要静态编码(理解简单,但代码繁琐)
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
29
30
31
32
33
34
35
36
37
38
39
40
41
//对外接口
public interface Subject{
public void request();
}

//目标对象
class SubjectImpl implements Subject{
@Override
public void request(){
System.out.println("I am dealing the request.");
}
}

//代理对象
class StaticProxy implements Subject{
//实际目标对象
private Subject subject;

public StaticProxy(Subject subject){
this.subject = subject;
}

@Override
public void request(){
System.out.println("PreProcess");
subject.request();
System.out.println("PostProcess");
}
}

//静态代理模式
public class StaticProxyDemo {
public static void main(String args[]){
//创建实际对象
SubjectImpl subject = new SubjectImpl();

//把实际对象封装到代理对象中
StaticProxy p = new StaticProxy(subject);
p.request();
}
}

2.动态代理

  • 对目标对象的方法每次被调用,进行动态拦截
    sequenceDiagram
    participant 客户端
    participant 代理对象
    participant 代理处理器
    participant 目标对象
    客户端->>代理对象:调用方法
    代理对象->>代理处理器:转发代理处理器
Invoke()方法 代理处理器->>目标对象:判断Method
调用目标对象的方法 目标对象->>代理处理器:返回结果 代理处理器->>代理对象:返回结果 代理对象->>客户端:返回结果

代理处理器

  • 持有目标对象的句柄
  • 实现InvocationHandler接口
    • 实现invoke方法
    • 所有的代理对象方法调用,都会转发到invoke方法来
    • invoke的形参method,就是指代理对象方法的调用
    • 在invoke内部,可以根据method,使用目标对象不同的方法来响应请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ProxyHandler implements InvocationHandler{
private Subject subject;
public ProxyHandler(Subject subject){
this.subject = subject;
}

//此函数在代理对象调用任何一个方法时都会被调用。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//定义预处理的工作,当然你也可以根据 method 的不同进行不同的预处理工作
System.out.println("====before====");
Object result = method.invoke(subject, args);
System.out.println("====after====");
return result;
}
}

代理对象

  • 根据给定的接口,由Proxy类自动生成的对象
  • 类型 com.sun.proxy.$Proxy0,继承自java.lang.reflect.Proxy
  • 通常和目标对象实现同样的接口(可另实现其他的接口)
  • 实现多个接口
    • 接口的排序非常重要
    • 当多个接口里面有方法同名,则默认以第一个接口的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//1.创建目标对象
SubjectImpl realSubject = new SubjectImpl();

//2.创建调用处理器对象
ProxyHandler handler = new ProxyHandler(realSubject);

//3.动态生成代理对象
Subject proxySubject =
(Subject)Proxy.newProxyInstance
(SubjectImpl.class.getClassLoader(),
SubjectImpl.class.getInterfaces(), handler);
//proxySubject真实类型com.sun.proxy.$Proxy0
//proxySubject继承Proxy类,实现Subject接口
//newProxyInstance的第二个参数,就是指定代理对象的接口

//4.客户端通过代理对象调用方法
//本次调用将自动被代理处理器的invoke方法接收
proxySubject.request();

3.AOP编程

  • AOP:Aspect Oriented Programming

面向切面编程 vs 面向对象编程

  • 面向对象:将需求功能划分为不同的、独立,封装良好的类,并让它们
    通过继承和多态实现相同和不同行为。
  • 面向切面:将通用需求功能从众多类中分离出来,使得很多类共享一个
    行为,一旦发生变化,不必修改很多类,而只需要修改这个行为即可
1
2
3
4
5
6
7
8
//aops.xml配置文件示例
<aops>
<aop>
<method>washHand</method>
<type>before</type>
<method>eat</method>
</aop>
</aops>
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//使用动态代理进行简单AOP编程
public static void initXml(){
//省略两类的代码实现

//读取aop配置文件
XmlReader.readXml("aops.xml");
//ResourceListener监听目录变化
ResourceListener.addListener("E:/java/source/PMOOC04-03/");
}

public static void main(String[] args) throws Exception{
Main.initXml();

Person action = new PersonImpl();
ProxyHandler mh = new ProxyHandler(action);
ClassLoader cl = Main.class.getClassLoader();
Class<?> proxyClass = Proxy.getProxyClass(cl, new Class<?>[]{Person.class});
Person proxy = (Person) proxyClass.getConstructor(new Class[]{InvocationHandler.class}).
newInstance(new Object[]{mh});

while(true){
proxy.eat();
try{
Thread.sleep(3000);
}
catch(Exception e){
e.printStackTrace();
}
}
}

//代理处理器
public class ProxyHandler implements InvocationHandler {

static String beforeMethod = "";
static String afterMethod = "";

private Person receiverObject;

public ProxyHandler(Person object){
this.receiverObject = object;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//处理before方法
if(beforeMethod!=null&&beforeMethod.length()>0){
ClassLoader cl = ProxyHandler.class.getClassLoader();
Class<?> c = cl.loadClass(receiverObject.getClass().getName());
Method m=c.getMethod(beforeMethod);
Object obj = c.newInstance();
m.invoke(obj);
}

//处理目标方法
Object result = method.invoke(receiverObject, args);

//处理after方法
if(afterMethod!=null&&afterMethod.length()>0){
method.invoke(receiverObject, args);
ClassLoader cl = ProxyHandler.class.getClassLoader();
Class<?> c = cl.loadClass(receiverObject.getClass().getName());
Method m=c.getMethod(afterMethod);
Object obj = c.newInstance();
m.invoke(obj);
}
return result;
}
}
//

面向切面编程

  • 一个概念/规范,没有限定语言
  • 不是取代OOP编程,而是OOP的补充,和数据库的触发器有点相似
  • 主要内容
    • Aspect :配置文件,包括一些Pointcut和相应的Advice
    • Joint point:在程序中明确定义的点,如方法调用、对类成员访问等
    • Pointcut:一组joint point, 可以通过逻辑关系/通配符/正则等组合起来,定义了相应advice将要发生的地方
    • Advice:定义了在pointcut处要发生的动作,通过before/after/around/来关联
    • weaving:advice代码在具体joint point的关联方式

Java的AOP实现