编程不只是一门技术,更是一门艺术!
结构型
代理模式
客户端并不直接调用实际的对象,而是通过调用代理对象,来间接的调用实际的对象,同时也可以通过代理对象来控制对实际对象的访问。代理模式也可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。
- 静态代理:实质是自己手写代理类,也就是在程序运行前就已经存在的编译好的代理类。
- 动态代理:在运行时根据需要动态生成类字节码,并加载到JVM中。
静态代理
创建一个接口,然后创建被代理类并实现该接口。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。
缺点:
- 代理类需要实现接口的全部抽象方法,一旦目标接口增加方法目标类和代理类均需要修改。
- 每一个目标类均需要一个代理类,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。
public interface UserDao {
void save();
}
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("正在保存用户...");
}
}
public class TransactionHandler implements UserDao {
// 目标代理对象
private UserDao target;
public TransactionHandler(UserDao target) {
this.target = target; // 构造代理对象时传入目标对象
}
@Override
public void save() {
//调用目标方法前的处理
System.out.println("开启事务控制...");
//调用目标对象的方法
target.save();
//调用目标方法后的处理
System.out.println("关闭事务控制...");
}
}
JDK动态代理
JDK动态代理中,代理类在运行时利用反射机制动态创建,不针对某一个接口,不针对某一个特定的被代理类,达到了代理类的复用。
public interface IHello {
void sayHello();
}
public class HelloImpl implements IHello {
@Override
public void sayHello() {
System.out.println("Hello world!");
}
}
public class MyInvocationHandler implements InvocationHandler {
/** 目标对象 */
private Object target;
public MyInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------插入前置通知代码-------------");
// 执行相应的目标方法
Object rs = method.invoke(target,args);
System.out.println("------插入后置处理代码-------------");
return rs;
}
}
public class Test {
public static void main(String[] args) throws Exception, InvocationTargetException {
// 动态创建代理类
IHello iHello = (IHello) Proxy.newProxyInstance(
IHello.class.getClassLoader(), // 加载接口的类加载器
new Class[]{IHello.class}, // 目标类的所有接口,目的是代理接口中的方法
new MyInvocationHandler(new HelloImpl())); // 自定义的InvocationHandler
iHello.sayHello(); // 通过代理类调用目标方法
}
}
CGLIB动态代理
JDK代理要求被代理的类必须实现接口,有很强的局限性,但是CGLIB动态代理则没有此类强制性要求。CGLIB会让生成的代理类继承被代理类(所以final方法不会被代理,final类不会被代理),代理类中会为委托方法生成两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super
调用委托方法,另一个是代理类独有的方法。在CGLIB中,方法的调用并不是通过反射来完成的,而是通过FastClass机制直接对方法进行调用,FastClass机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法,提高效率。
public class HelloService {
public HelloService() {
System.out.println("HelloService构造");
}
/**
* 该方法不能被子类覆盖,Cglib是无法代理final修饰的方法的
*/
final public String sayOthers(String name) {
System.out.println("HelloService:sayOthers>>"+name);
return null;
}
public void sayHello() {
System.out.println("HelloService:sayHello");
}
}
public class MyMethodInterceptor implements MethodInterceptor{
/**
* sub:cglib生成的代理对象
* method:被代理对象方法
* objects:方法入参
* methodProxy: 代理方法
*/
@Override
public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("======插入前置通知======");
Object object = methodProxy.invokeSuper(sub, objects);
System.out.println("======插入后者通知======");
return object;
}
}
public class Client {
public static void main(String[] args) {
// 通过CGLIB动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
// 设置enhancer对象的父类
enhancer.setSuperclass(HelloService.class);
// 设置enhancer的回调对象
enhancer.setCallback(new MyMethodInterceptor());
// 创建代理对象
HelloService proxy= (HelloService)enhancer.create();
// 通过代理对象调用目标方法
proxy.sayHello();
}
}