代理模式
1.设计模式原则
- 单一职责原则:一个类或者一个接口只负责唯一项职责,尽量设计出功能单一的接口;
- 依赖倒转原则:针对接口编程,依赖于抽象而不依赖于具体;
- 开放-封闭原则:程序对外扩展开放,对修改关闭;换句话说,当需求发生变化时,我们可以通过添加新模块来满足新需求,而不是通过修改原来的实现代码来满足新需求;
- 迪米特法则:一个对象应该对其他对象保持最少的了解,尽量降低类与类之间的耦合度;
- 里氏代换原则:所有引用基类(父类)的地方必须能透明地使用其子类的对象;
- 接口隔离原则:客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上。
2.代理模式
- 定义:给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用;
- 目的:
(1)通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
(2)通过代理对象对原有的业务增强。
3.静态代理
这种代理方式需要代理对象和目标对象实现一样的接口。
优点:可以在不修改目标对象的前提下扩展目标对象的功能。
缺点:
- 冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
- 不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。
3.1创建接口Hello
package org.example.interfc;
public interface Hello {
public void say();
}
3.2创建目标类HelloWorld
package org.example.stat;
import org.example.interfc.Hello;
public class HelloWorld implements Hello {
@Override
public void say() {
System.out.println("Hello world!!!");
}
}
3.3创建代理类HelloWorldProxy
package org.example.stat;
import org.example.interfc.Hello;
public class HelloWorldProxy implements Hello {
private Hello hello;
public HelloWorldProxy(Hello hello) {
this.hello = hello;
}
@Override
public void say() {
System.out.println("hello前=================");
hello.say();
System.out.println("hello后=================");
}
}
3.4测试类
package org.example;
import org.example.cglib.CglibProxy;
import org.example.cglib.HelloCglib;
import org.example.dynamic.DynamicProxy;
import org.example.interfc.Hello;
import org.example.stat.HelloWorld;
import org.example.stat.HelloWorldProxy;
import org.junit.Test;
public class App
{
//静态代理
@Test
public void test1(){
//创建目标对象
Hello hello = new HelloWorld();
//代理对象
HelloWorldProxy helloWorldProxy = new HelloWorldProxy(hello);
helloWorldProxy.say();
}
//动态代理(JDK)
@Test
public void test2() {
//创建目标对象
Hello hello = new HelloWorld();
//代理对象
DynamicProxy dynamicProxy = new DynamicProxy(hello);
Hello helloProxy = (Hello) dynamicProxy.getProxyInstance();
helloProxy.say();
}
//Cglib代理
@Test
public void test3() {
//创建目标对象
HelloCglib helloCglib = new HelloCglib();
//代理对象
CglibProxy cglibProxy = new CglibProxy(helloCglib);
HelloCglib helloProxy = (HelloCglib) cglibProxy.getInstance();
helloProxy.say();
}
}
3.5查看打印结果
4.动态代理
- 动态代理利用了 JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。
- 动态代理又被称为 JDK 代理或接口代理。静态代理与动态代理的区别主要在:
(1)静态代理在编译时就已经实现,编译完成后代理类是一个实际的 class 文件
(2)动态代理是在运行时动态生成的,即编译完成后没有实际的 class 文件,而是在运行时动态生成类字节码,并加载到 JVM 中
注意:动态代理对象不需要实现接口,但是要求目标对象必须实现接口,否则不能使用动态代理。 - JDK 中生成代理对象主要涉及两个类,第一个类为 java.lang.reflect.Proxy,通过静态方法newProxyInstance 生成代理对象,第二个为 java.lang.reflect.InvocationHandler 接口,通过invoke 方法对业务进行增强
4.1创建代理类DynamicProxy
package org.example.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
//通过Proxy获取动态代理的对象
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
doBefore();
Object object = method.invoke(target, args);
doAfter();
return object;
}
private void doBefore() {
System.out.println("Hello before===============");
}
private void doAfter() {
System.out.println("Hello after=================");
}
}
4.2测试方法
4.3查看打印结果
5.Cglib代理
- 静态代理和动态代理都需要目标对象实现一个接口,对于没有实现接口的目标类,就需要使用Cglib实现代理;
- 原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。
5.1创建目标类HelloCglib
package org.example.cglib;
public class HelloCglib {
public void say() {
System.out.println("Hello world!!!");
}
}
5.2创建代理类CglibProxy
package org.example.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
//目标对象
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
//为目标对象创建一个代理对象
public Object getInstance() {
//使用Enhancer为无接口的类创建代理
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("Hello Cglib!!!");
Object result = methodProxy.invoke(target, objects);
System.out.println("GoodBye Cglib!!!");
return result;
}
}