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 实现
- AspectJ(Eclipse), https://www.eclipse.org/aspectj/
- Spring AOP,https://spring.io/projects/spring-framework