MOOC 个人学习笔记

# 1. 代理模式和静态代理

# 代理模式

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

# 静态代理

  • 代理对象持有目标对象的句柄
  • 所有调用目标对象的方法,都调用代理对象的方法
  • 代理类和被代理类应该共同实现一个接口,或者是共同继承某个类
  • 对每个方法,需要静态编码 (理解简单,但代码繁琐)
// 对外接口
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. 动态代理

  • 对目标对象的方法每次被调用,进行动态拦截
Error: Navigating frame was detached

# 代理处理器

  • 持有目标对象的句柄
  • 实现 InvocationHandler 接口
    • 实现 invoke 方法
    • 所有的代理对象方法调用,都会转发到 invoke 方法来
    • invoke 的形参 method,就是指代理对象方法的调用
    • 在 invoke 内部,可以根据 method,使用目标对象不同的方法来响应请求
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. 创建目标对象
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 面向对象编程

  • 面向对象:将需求功能划分为不同的、独立,封装良好的类,并让它们
    通过继承和多态实现相同和不同行为。
  • 面向切面:将通用需求功能从众多类中分离出来,使得很多类共享一个
    行为,一旦发生变化,不必修改很多类,而只需要修改这个行为即可
//aops.xml配置文件示例
<aops>
    <aop>
        <method>washHand</method>
        <type>before</type>
        <method>eat</method>
    </aop>
</aops>
// 使用动态代理进行简单 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 实现