前言
代理模式在软件开发中经常用到,它是指为对象提供一种的代理,以控制对这个对象的访问。代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。代理模式主要有两种:静态代理和动态代理,接下来,我们就用实际例子来解释
静态代理
静态代理实现很简单,就是客户端不直接使用目标对象方法,而是调用代理对象方法,而代理对象方法里面调用目标对象,同时在其前后做相应的增强
显示生活中,大学生毕业往往面临找工作,找工作的方式有很多种,比如托亲戚帮忙找,下面我们就用这样一种场景来做案例来解释静态代理
我们首先定义一个接口
Person:
public interface Person {
public void findJob();
}
然后定义学生类
Student :
public class Student implements Person{
@Override
public void findJob() {
System.out.println("钱多活少离家近");
}
}
最后是亲戚类
Qinqi:
public class Qinqi implements Person {
private Student student;
public Qinqi(Student student) {
this.student = student;
}
@Override
public void findJob() {
System.out.println("亲戚开始帮忙寻找");
this.student.findJob();
System.out.println("找到了,签订劳务合同");
}
}
可以看到亲戚类在构造中注入student,然后在findJob方法中调用student的findJob方法,同时在前后做了增强
测试
public class Test {
public static void main(String[] args) {
Qinqi qinqi = new Qinqi(new Student());
qinqi.findJob();
}
}
静态代理很简单,但是这里有一个问题,就是静态代理只能对特定的类进行代理,不具有普遍性,所以就出现了动态代理
动态代理
动态代理和静态代理的思路进本一致,只不过其应用的功能更加强大,下面我们还是用找工作来解释,比如社会上有很多帮忙找工作的猎头,可以帮助各种需要找工作的和需要工作人员的人提供帮助。
动态代理主要有两种实现方式:JDK实现方式和CGlib实现方式
JDK实现方式
定义代理类
JobProxy :
public class JobProxy implements InvocationHandler {
private Object target;
public Object getInstance(Object target) throws Exception {
this.target=target;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object obj = method.invoke(this.target,args);
after();
return obj;
}
private void before() {
System.out.println("我是中介,开始连线");
}
private void after() {
System.out.println("如果合适的话,就签合同");
}
}
这个类中,首先实现InvocationHandler 接口,然后在getInstance方法中将需要代理的对象进行注入等一系列操作,最后在invoke中对目标对象方法进行增强。
定义接口
jdk实现方式需要目标对象实现相应的接口,因为代理类的getInstance方法返回的是相应接口的类型
Employee:
public interface Employee {
public void findJob() ;
}
Employer :
public interface Employer {
public void findWorker();
}
我们定义了两个接口分别是雇佣者和被雇佣者
实现接口
Student :
public class Student implements Employee{
@Override
public void findJob() {
System.out.println("员工要求:钱多活少离家近");
}
}
Boss :
public class Boss implements Employer{
@Override
public void findWorker() {
System.out.println("老板要求:听话勤快技术好");
}
}
我们的Student 类和Boss 类分别实现了这两种接口
测试
Test :
public class Test {
public static void main(String[] args) {
try{
Employee student = (Employee)new JobProxy().getInstance(new Student());
student.findJob();
Employer boss = (Employer)new JobProxy().getInstance(new Boss());
boss.findWorker();
} catch (Exception e) {
e.printStackTrace();
}
}
}
通过测试我们可以看到,中介类对象可以对雇佣者和被雇佣者两种对象进行代理,这就是JDK方式的动态代理
CGlib方式的动态代理
我们还是上面找工作为例
引入CGlib
我们在pom.xml里引入CGlib相关依赖
pom.xml:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
新建代理类
CglibJobProxy :
public class CglibJobProxy implements MethodInterceptor {
public Object getInstance(Class<?> clazz) throws Exception {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o,objects);
after();
return obj;
}
private void before() {
System.out.println("我是中介,开始连线");
}
private void after() {
System.out.println("如果合适的话,就签合同");
}
}
我们代理类实现MethodInterceptor 接口,然后在getInstance中引入目标对象的class进行注入,最后在intercept方法中对相应方法进行加强
新建目标类
我们新建Student和Boss类,此时,这两个类就不需要实现什么接口了
Boss:
public class Boss{
public void findWorker() {
System.out.println("老板要求:听话勤快技术好");
}
}
Student :
public class Student {
public void findJob() {
System.out.println("员工要求:钱多活少离家近");
}
}
测试
Test :
public class Test {
public static void main(String[] args) {
try{
Student student = (Student) new CglibJobProxy().getInstance(Student.class);
student.findJob();
Boss boss = (Boss)new CglibJobProxy().getInstance(Boss.class);
boss.findWorker();
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试中,我们可以看到,使用Cglib代理类对象的getInstance输入参数是目标对象的class,然后返回的类型也是目标对象的类型,这是Cglib与JDK代理使用方式最大的不同