代理模式
- Proxy,为对象提供一个替身,以控制对这个
对象的访问(被控制对象)。通过代理对象访问目标对象,在目标对象的实现的基础上,增强额外的功能操作,扩展目标对象的功能
- 远程对象、创建开销大的对象、需要安全控制的对象
- 代理形式,
静态代理、动态代理(JDK代理、接口代理)、Cglib代理(可以在内存中动态创建对象,不需要实现接口,属于动态代理的范畴)

静态代理
- 需要定义接口或父类,被代理对象与代理对象
一起实现相同的接口或者继承相同的父类
- 如果有很多对象有相似的方法,可以将共同的方法提出来,通过代理对象完成,
安全性/合法性验证
- 优点,再不修改目标对象的功能前提下,通过代理对象对目标对象功能进行拓展
- 缺点,实现同一个接口,一旦增加方法,代理对象和目标对象都需要维护

- ITeacherDao
public interface ITeacherDao {
void teach();
}
public class TeacherDao implements ITeacherDao{
@Override
public void teach() {
System.out.println("TeacherDao teach");
}
}
public class TeacherDaoProxy implements ITeacherDao{
private ITeacherDao iTeacherDao;
public TeacherDaoProxy(ITeacherDao iTeacherDao) {
this.iTeacherDao = iTeacherDao;
}
@Override
public void teach() {
iTeacherDao.teach();
System.out.println("TeacherDaoProxy teach");
}
}
动态代理
- 代理对象不需要实现接口,但是目标对象要实现接口
- 利用JDK的API,动态得在内存中构建代理对象

public static Object newProxyInstance(
ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("dynamic proxy...");
Object returnVal = method.invoke(target, args);
System.out.println("end");
return returnVal;
}
});
}
}
ProxyFactory pf = new ProxyFactory(new TeacherDao())
ITeacherDao proxy = (ITeacherDao) pf.getProxyInstance()
proxy.teach()
proxy.hello("zhangsan")
- 通过反射的方式获取其方法名,进行调用

invoke方法

- 最终调用
NativeMethodAccessorImpl的方法,调用目标对象的方法

- 代理对象中包含
target目标对象

cglib代理
- 针对于
没有实现任何接口的类进行代理,底层ASM
- 通过继承的形式,子类代理,是一个代码生成包,被应用到
AOP框架中,SpringAOP
- 目标对象需要实现接口,JDK代理
- 目标对象不需要实现接口,Cglib代理
- 底层通过
字节码处理框架ASM转换字节码并生成新的类
- 代理的目标类不能是
final修饰;目标对象的方法如果是final/static,就不会被拦截,不会执行额外的业务方法

- 需要引包
- ProxyFactory
public class ProxyFactory implements MethodInterceptor {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInterface() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib proxy");
Object returnVal = method.invoke(target, objects);
return returnVal;
}
}

- 同样包含
target

- 过程
- 代理类调用
teacher.teach();,调用代理类中的teach,并最终调用intercept
- 当调用
method.invoke(target, objects);,通过反射进行调用
- 当调用
methodProxy.invokeSuper(o, objects);,通过代理类FastClass,通过索引,最终调用代理类的FastClass中的super.teacher(),调用父类方法
- 当调用
methodProxy.invoke(o, objects);,通过代理类FastClass,调用被代理类FastClass的teacher()
动态代理与cglib
- cglib不能处理
final修饰的类,这种类无法继承
- cglib需要强制无参构造
- 代理对象需要继承
Proxy,而Java是单继承的,只能通过接口去扩展
- 底层通过反射,创建一个暂时的
$Proxy0类,并生成对应的class文件,加载该class文件,生成代理对象,通过InvocationHandler,去执行方法
- 采用创建一个继承实现类的子类,子类包含父类的实例对象,通过这个实例对象进行方法调用
FastClass机制,会导致整个过程生成三个类。过程中,对类的方法建立索引,调用方法时,根据方法的签名来计算索引,通过索引调用对应的方法
- 生成的代理类、为生成的代理类中的每个方法建立了索引的类、为被代理类的所有方法建立了索引的类
methodProxy调用invokeSuper,由代理类,最终调用代理类中的拦截器方法,在调用父类方法;调用invoke,如果由代理类调用,那么进入被代理类的FastClass调用invoke,再次由代理类调用该方法,导致循环,造成栈溢出,应该由被代理类调用,调用被代理类的FastClass的invoke,调用其被代理类的本方法
小结
- 防火墙代理,内网通过代理穿透防火墙,实现对公网的访问
- 缓存代理,请求图片资源,先到缓存代理取,取不到,再到公网或数据库中取
- 远程代理,远程对象的本地代表,将远程对象当作本地对象调用,通过网络与远程对象沟通
- 同步代理,多线程编程中,完成多线程间同步工作
模板方法模式
- 案例,制作豆浆,制作流程一样,选材不同,口味不同
- Template Method Pattern,行为型模式,在一个抽象类中公开定义了执行它的方法的模板,子类按需要重写方法,但是调用要以抽象类中定义的方式进行
- 定义了一个操作的算法骨架,将一些步骤延迟到子类进行,使子类不改变算法接口,可以重定义该算法的某些特定的步骤

- 案例类图

- SoyaMilk,算法骨架
public abstract class SoyaMilk {
final void make() {
select();
addIngredient();
soak();
beat();
}
private void select() {
System.out.println("select fresh soya bean.");
}
abstract void addIngredient();
private void soak() {
System.out.println("soak...");
}
private void beat() {
System.out.println("beat...");
}
}
public class PeanutSoyaMilk extends SoyaMilk{
@Override
void addIngredient() {
System.out.println("PeanutSoyaMilk");
}
}
钩子方法
- 默认不做任何事,可以是一种条件,子类按需覆盖
- 案例中,需要制作纯豆浆,不添加任何配料
- 通过
customerWantIngredient判断
final void make() {
select();
if (customerWantIngredient()) {
addIngredient();
}
soak();
beat();
}
- customerWantIngredient,钩子方法
boolean customerWantIngredient() {
return true;
}
- PureSoyaMilk,重写钩子方法
customerWantIngredient
public class PureSoyaMilk extends SoyaMilk{
@Override
void addIngredient() {
}
@Override
boolean customerWantIngredient() {
return false;
}
}
Spring源码
- IOC初始化时运用到了模板方法模式
ConfigurableApplicationContext

refresh方法

- obtainFreshBeanFactory方法,调用两个抽象方法
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
this.refreshBeanFactory();
return this.getBeanFactory();
}
- refreshBeanFactory

- getBeanFactory

- 钩子方法,默认空实现

postProcessBeanFactory空实现

onRefresh空实现

小结
- 算法只存在父类中,容易修改,只需要修改父类的模板方法,实现最大代码复用
- 既统一了算法,也具有很大的灵活性,保证算法的结构不变,由子类提供部分步骤的实现
- 每个不同的实现,都需要一个子类实现,导致类的个数增加
- 模板方法用
final修饰,防止子类重写
- 适用于当完成某个过程,该过程要执行一系列步骤,这些步骤顺序基本相同,仅在个别步骤的实现不同,可以用模板方法