最近才想起来动态代理模式的学习,惭愧惭愧,Spring中的AOP就是基于代理模式的,之前却一直没有去学习,十分后悔,今天来学习总结一下自己的心得和体会,如果有不对的地方,虚心学习,一起进步。
代理模式分为静态代理和动态代理,我们慢慢来。
静态代理
先说一下实际的情况:公司领导需要开会和对员工进行评价,领导类有两个方法:开会和根据员工名进行评价。但实际上领导有秘书,有的时候开会前需要秘书去准备材料,当秘书材料准备好了就开会。
通过上面这个小案例,我们再来看静态代理:就是代理类(秘书)和被代理类(领导)实现相同的接口,这样当我们调用代理类的方法时和被代理类调用方法相同,其核心就是代理类里面定义一个私有的被代理类对象属性。然后我们通过代码来具体看看:
- 首先定义一个接口,原来规范功能,这里就是开会和给员工评价,接口IWork代码如下:
package com.ctvit.service;
/**
* 该接口定义领导的工作内容,开会和员工评价
*/
public interface IWork {
/**
* 开会功能
*/
void meeting();
/**
* 领导对员工进行评价
* @param name 传入员工姓名领导进行评价等级
* @return 返回等级,数字越大评价越好
*/
int evaluate(String name);
}
- 第二不:我们定义一个领导类来实现该接口:
package com.ctvit.service;
import java.util.Random;
/**
* 领导类,实现IWork接口,实现自己的职责
*/
public class Leader implements IWork{
@Override
public void meeting() {
System.out.println("领导进行公司开会了,十分辛苦");
}
@Override
public int evaluate(String name) {
Random random = new Random();
int i = random.nextInt(10);
System.out.println("领导思考了很久,对"+name+"做出了评价:"+i);
return i;
}
}
- 第三步:书写秘书类,领导类让秘书去准备资料:
package com.ctvit.service;
/**
* 领导秘书,有时候领导很忙,就让秘书去做一些工作
*/
public class Secretary implements IWork{
private Leader leader;
public Secretary(Leader leader){
this.leader=leader;
}
@Override
public void meeting() {
System.out.println("小秘书收到领导通知,开始准备开会材料...");
leader.meeting();
}
@Override
public int evaluate(String name) {
return leader.evaluate(name);
}
}
- 最后查看运行效果:
- 总结:可以看到,这个代理模式类似套娃,就是我们都实现统一的接口,然后我们让代理类定义一个私有被代理类属性,通过构造方法传入一个被代理类对象然后赋值给这个私有属性,然后来调用方法,这样就保证了被代理类的方法正常执行,但代理类可以在这些方法执行前执行自己的方法,这点就和AOP思想吻合。
动态代理
JDK动态代理:
- 我们接着上面的静态代理案例继续写,我们首先定义一个类WorkInvocationHandler来实现InvocationHandler接口的invoke方法,该类代码如下:
package com.ctvit.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class WorkInvocationHandler implements InvocationHandler {
private Object object;
public WorkInvocationHandler(Object object){
this.object=object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//打印一下object类和proxy的类对象信息
System.out.println("object:"+object.getClass().getSimpleName());
System.out.println("proxy:"+proxy.getClass().getSimpleName());
//判断当前方法是否是meeting开会方法,如果是就代理,让秘书去准备材料
if ("meeting".equals(method.getName())){
System.out.println("代理小秘书先准备会材料...");
return method.invoke(object,args);
}else if("evaluate".equals(method.getName())){//判断是否是评价方法
System.out.println("老板小秘书在准备员工资料给领导看");
if (args[0] instanceof String){
if ("sun".equals(args[0])){//判断传入的参数是否是sun这个员工,如果是则执行下面的代码
System.out.println("sun因为犯过错,评分为30分");
return 30;
}
}
return method.invoke(object,args);
}
return null;
}
}
- 从上面的代码我们看出来,和静态代理不同的是,我们实现了一个官方提供的接口:InvocationHandler 并且实现了该接口的invoke方法,然后我们可以对所有的方法进行代理。这样我们就可以重写静态代理代码中的测试部分了,代码如下:
public class Test {
public static void main(String[] args) {
/* Leader leader = new Leader();
Secretary secretary = new Secretary(leader);
secretary.meeting();
secretary.evaluate("程序逸");*/
Leader leader = new Leader();
IWork proxy=(IWork) Proxy.newProxyInstance(Leader.class.getClassLoader(),new Class[]{IWork.class},new WorkInvocationHandler(leader));
proxy.meeting();
proxy.evaluate("程序逸");
proxy.evaluate("sun");
}
}
- 运行结构如下:
- 我们还可以通过在invoke方法返回代理对象proxy来实现连续调用,我们来看一下这个案例:
package com.ctvit.service2;
public interface IWork {
IWork iwork(String method);
}
package com.ctvit.jdk2;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class WorkInvocationHandler implements InvocationHandler {
private Object object;
public WorkInvocationHandler(Object object){
this.object=object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("iwork".equals(method.getName())){
System.out.println("work----"+args[0]);
return proxy;
}
return null;
}
}
package com.ctvit.service2;
import com.ctvit.jdk2.WorkInvocationHandler;
import java.lang.reflect.Proxy;
public class TestDemo {
public static void main(String[] args) {
IWork iWork=(IWork) Proxy.newProxyInstance(IWork.class.getClassLoader(),new Class[]{IWork.class},new WorkInvocationHandler(new IWork() {
@Override
public IWork iwork(String method) {
return null;
}
}));
iWork.iwork("AAA").iwork("BBB").iwork("CCC");
}
}
CGLIB动态代理
- 如果使用Maven项目,首先导入CGlib的依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
- 我们使用的案例是第一个静态代理的案例,可以继续接着写。使用CGlib代理模式依旧是需要创建一个实现类来实现MethodInterceptor接口,代码如下:
package com.ctvit.jdk2;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class LeaderMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if ("meeting".equals(method.getName())) {
System.out.println("代理先准备会议材料...");
return methodProxy.invokeSuper(o, objects);
} else if ("evaluate".equals(method.getName())) {
if(objects[0] instanceof String) {
if ("sun".equals(objects[0])) {
System.out.println("sun 犯过错误,所以考评分数较低...");
return 70;
}
}
return methodProxy.invokeSuper(o, objects);
}
return null;
}
}
- 然后我们就可以写测试类:
package com.ctvit.service2;
import com.ctvit.jdk2.LeaderMethodInterceptor;
import com.ctvit.service.Leader;
import net.sf.cglib.proxy.Enhancer;
public class TestDemo2 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();//通过CGLIB动态代理获取代理对象过程
enhancer.setSuperclass(Leader.class);//设置enhancer对象的父类
enhancer.setCallback(new LeaderMethodInterceptor());//设置enhancer的回调对象
Leader leader=(Leader)enhancer.create();
leader.meeting();
leader.evaluate("程序逸");
leader.evaluate("sun");
}
}
- 运行结果:
- 有关CGlib动态代理我们需要注意实现的MethodInterceptor接口的方法的四个参数:
参数名 | 作用 |
---|---|
Object o | 表示需要增强的对象 |
Method method | 表示需要拦截的方法 |
Object[] objects | 表示方法的参数列表 |
MethodProxy methodProxy | 触发父类的方法对象 |