概述
- 代理模式:属于结构型的模式。指对象本身不做实际的操作,而是通过其他对象(代理对象)来实现得到自己想要的结果(增强处理)
- 意义:目标对象只需要关注自己的实现细节,通过代理对象来实现功能的增强,可以扩展目标对象的功能。
静态代理:
静态代理:所谓静态就是在程序运行前已经存在代理类了,代理类有程序员创建或者工具生成,代理类和被代理类在程序运行前的关系已经确定。
案例:
/**
* 定义一个学生行为的接口
* @author user
*
*/
public interface SturentAction {
//买书的行为
public void buyBook();
}
//被代理对象学生
public class Sturent implements SturentAction{
@Override
public void buyBook() {
System.out.println("学生:"+"学生买到了书");
}
}
//学生的代理类 可看成书店
public class ProxySturent implements SturentAction{
private Sturent sturent;
public ProxySturent(Sturent Sturent) {
this.sturent=Sturent;
}
@Override
public void buyBook() {
System.out.println("书店:"+"书店到了学生想要的书");
sturent.buyBook();
}
}
//测试类
public class SturentTest {
public static void main(String[] args) {
Sturent sturent=new Sturent();
ProxySturent proxySturent=new ProxySturent(sturent);
proxySturent.buyBook();
}
}
结果:书店代理学生找到了自己要的书
书店:书店到了学生想要的书
学生:学生买到了书
静态代理的问题:
- 不利于代码的扩展,如在接口中添加一个方法,它的实现类都要添加这个方法,否则报错。
- 如果对被代理对象增加其他的增强操作,可能要创建很多代理类
动态代理:
动态代理:动态代理类是有在程序运行期间有JVM根据反射等机制动态生成的,代理类和被代理类的关系是在程序运行时确定的。在不改变原有的功能代码的前提下,能够动态的实现方法的增强。
可以在不改变方法源码的情况下,实现对方法功能的增强,提高代码复用性。
简化了编程工作,提高开发效率,同时提高软件系统的可扩展性
可以为被代理对象的所有方法做代理
非常灵活 支持任意接口类型的实现类对象做代理,也可以直接接本身做代理
动态代理分为:jdk动态代理和Cglib动态动态代理
jdk动态代理:
jdk动态代理:是使用反射包中类和接口实现动态代理
实例:
1.创建接口:
2.创建实现类(需要代理的类)
3.创建增强类
4.创建代理类(方法拦截器),这要实现InvocationHandler接口,重写invoke方法,这里可以对方法进行增强。
- InvocationHandler接口,用来做方法拦截
- invoke方法中的的参数介绍
。Proxy:代理实例,可以通过newProxyInstance创建代理实例
。Method:执行的目标方法,反射通过调用invoke方法执行
。args:参数数组
public class TransactionHandler implements InvocationHandler {
//增强类对象
private DaoTransaction transaction;
//需要代理的目标对象
private Object obj;
//构造方法
public TransactionHandler(DaoTransaction transaction,Object o) {
this.transaction=transaction;
this.obj=o;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret=null;
//判断当前方法是否是save方法,是才做事务操作
if ("save".equals(method.getName())) {
transaction.before();//增强操作
ret=method.invoke(obj, args);
transaction.after();
}else {
//不做增强处理
}
return ret;
}
}
测试
public class TransactionTest {
public static void main(String[] args) {
//增强类
DaoTransaction transaction=new DaoTransaction();
//目标类,要实现接口
IStudentService studentService=new IStudentServiceImple();
//方法拦截器 代理类,要实现InvocationHandler接口,重写invoke方法,在方法中实现增强处理
TransactionHandler transactionHandler=new TransactionHandler(transaction, studentService);
//创建代理类对象(代理实例)
IStudentService serviceProxy=(IStudentService)Proxy.newProxyInstance(studentService.getClass().getClassLoader(), studentService.getClass().getInterfaces(), transactionHandler);
//调用方法
serviceProxy.save();
}
}
运行结果:
开启事务操作
保存学生信息
关闭事务
这就完成了一个简单的动态代理
JDK动态代理这一步最最重要:
//创建代理类对象(代理实例)
IStudentService serviceProxy=(IStudentService)Proxy.newProxyInstance(studentService.getClass().getClassLoader(), studentService.getClass().getInterfaces(), transactionHandler);
生成动态代理的对象
newProxyInstance方法源码:
- ClassLoader类加载器:直接通过需要代理的类获取
- Class[]:目标类实现的所有接口
- InvocationHandler :方法拦拦截处理器,可以在里面实现方法的增强处理。
newProxyInstance方法要传入,类加载器,接口,和InvocationHandler
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
JDK动态代理原理:
通过生成代理类对象字节码文件学习:
自己没有生成。。只能看别人的啦。
生成的动态代理大概这样:
$Proxy1就是生成的动态代理对象,它继承了Proxy类和实现了目标接口IStudentService。
super就是Proxy,.h就是InvocationHandler接口,.invoke方法调用的就是我们设置的代理类(方法拦截处理器)TransactionHandler 中的invoke方法。
Proxy类源码:
方法原理执行的过程:
JDK动态代理主要就是通过生成代理实例,这个代理实例继承了一个代理类(Proxy类)并且实现了目标接口,在代理实例中通过反射获取目标接口中的方法。在代理实例生成的方法中通过调用设置的代理类(实现了InvocationHandler接口的类)中的invoke方法来实现对目标方法的增强处理。
注意:jdk动态代理生成的代理实例是通过实现目标接口,通过反射获取目标方法的,因此jdk动态代理的目标类必须要实现接口。
CGLIB动态代理:
为什么会有CGLIB动态代理:jdk动态代理有一个前提,是需要的代理的类必须要实现接口,如果没有实现接口,只能通过CGLIB来实现,其实CGLIIB是对JDK动态代理的一个补充。
CGLIB创建代理类(方法拦截器),这要实现MethodInterceptor接口,重写intercept方法,这里可以对方法进行增强。
//cglib代理类 方法拦截器
public class CglibInterceptor implements MethodInterceptor {
//增强类对象
private DaoTransaction transaction;
//构造方法
public CglibInterceptor(DaoTransaction transaction) {
this.transaction=transaction;
}
/**
* 在此方法中做目标方法的增强处理
*/
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//调用增强处理方法
transaction.before();
//调用目标类的目标方法
Object ret = proxy.invokeSuper(obj, args);
transaction.after();
//调用增强处理方法
return ret;
}
}
测试:
public class CglibInterceptorTest {
public static void main(String[] args) {
//得到方法拦截器
CglibInterceptor cglibInterceptor=new CglibInterceptor(new DaoTransaction());
//使用CGLIB框架生成目标类的子类(代理类)实现增强
Enhancer enhancer=new Enhancer();
//设置父类字节码
enhancer.setSuperclass(IStudentServiceImple.class);
//设置拦截器
enhancer.setCallback(cglibInterceptor);
//创建代理实例
IStudentService servcie=(IStudentService)enhancer.create();
servcie.save();
}
}
结果:
开启事务操作
保存学生信息
关闭事务
CGLIB原理:
生成的代理实例字节码;
生成的代理实例继承了目标方法。方法save是重写了目标类中的save方法。在方法中调用了设置的方法拦截器(实现MethodInterceptor接口的类)中的intercept方法。在方法拦截器中的intercept方法中做了对目标方法的增强处理
方法执行关系:
CGLIB生成的代理实例是通过继承目标类的方式生成的一个目标类的子类,在代理实例的方法中会去调用传入的方法拦截器(实现MethodInterceptor接口的类)中的intercept方法,在intercept方法中做了对目标类的增强处理。
注意:
CGLIB动态代理:它的目标类和目标方法不能final修饰,因为CGLIB动态代理主要通过继承的方式获取目标类的方法,final修饰的类和方法不能不继承。
动态代理总结:
JDK动态代理:
1.首先要自定义方法拦截,通过实现InvocationHandler接口,在invoke方法中实现对目标方法的增强处理。
2.JDK动态代理生成的代理实例, 主要通过实现目标接口的方式,并通过反射获取接口中的方法,在代理实例中的方法会调用方法拦截(实现InvocationHandler接口的类)中的invoke方法,以此达到对方法的增强
3.JDK代理的目标类必须实现接口
CGLIB动态代理:
1.自定义方法拦截,通过实现MethodInterceptor接口,在intercept方法中事项对目标方法的增强处理
2.CGLIB动态代理生成的代理实例,是通过继承目标类重写目标类中的方法,在从写的方法中会调用传入的MethodInterceptor中的intercept方法,已达到对目标方法的增强。
3.CGLIB动态代理的目标类和方法不能被final修饰,因为final修饰的类和方法不能被继承