目录
第四章 代理
4.1 什么是代理
概念:代理就好比是买卖关系之间的中介,充当着中间人的角色,转达买卖双方的诉求,比如,买家说:你的商品不好,不值这个价钱,降低一点,或者多给一点。卖家说:我的商品好极了,没有任何瑕疵,错过了就没有,自然不肯降价。这时中间人(代理)就出来说话了,中间人对买家说,你把钱给我,把你的要求说出来,我给你办的妥妥的。然后在对卖家说,把你的商品给我,保证你的商品卖的稳稳地。就这样,问题就解决了。
结论
:
如果两者之间存在这直接关系,当需求改变时,容易引起另一个的不同意,从而无法满足需求,此时,中间人(代理)来缓和关系,并满足他们的需求。 把这个结论运用到代码当中,当需要对代码进行功能增强时,发现原来的代码不支持在原有的基础上进行添加。或者说,添加了会造成原有的代码结构混乱。所以,为了解决这个问题,提出了代理概念。
4.2 代理有几种模式
代理分为静态代理和动态代理。
静态代理
: 表示它的状态在编码时就已经决定了,在虚拟机编码阶段,就把它增强的代码编码到代理对象上。
动态代理
: 它的状态在程序运行时动态的生成代理类,通过代理类去实现功能的增强,它的字节码文件由程序运行时通过反射机制生成。
4.3 静态代理的实现
4.3.1 基于接口实现静态代理
创建一个新的maven项目
定义一个接口UserDao ,有增删改查方法。
public interface UserDao {
void save();
void delete();
void update();
void find();
}
定义一个实现类实现UserDao接口,并实现所有方法
class UserDaoImpl implements UserDao{
@Override
public void save() {
System.out.println("UserDaoImpl saving...");
}
@Override
public void delete() {
System.out.println("UserDaoImpl deleting...");
}
@Override
public void update() {
System.out.println("UserDaoImpl updating...");
}
@Override
public void find() {
System.out.println("UserDaoImpl finding...");
}
}
定义一个代理类对象,用于代理UserDao的实现类
package com.lyf.spring5.proxy;
public class StaticProxy {
//定义接口属性,通过这个接口属性去调用代理对象的值
private UserDao userDao;
//通过有参构造接口赋值
public StaticProxy(UserDao userDao){
this.userDao = userDao;
}
/**
* 增强的功能,添加记录日志功能
*/
public void log(){
System.out.println("记录日志");
}
/**
* 增强的检查权限功能
*/
public void check(){
System.out.println("检查权限");
}
/**
* 代理的方法实现功能增强
*/
public void proxy(){
//this 表示当前对象调用自己的方法,可以省略不写
this.check();// 表示删除前对权限进行校验,查看是否有权限执行删除方法
userDao.delete();
this.log();//表示删除方法执行完后就执行记录日志的方法
check();
userDao.find();
log();
check();
userDao.save();
log();
check();
userDao.update();
log();
}
}
定义一个主方法,测试静态代理
public class StaticMain {
public static void main(String[] args) {
// 创建一个实现了UserDao接口的UserDaoImpl实现类
UserDao userDao = new UserDaoImpl();
//创建代理类对象,并赋值给代理类
StaticProxy staticProxy = new StaticProxy(userDao);
//调用代理类方法
staticProxy.proxy();
}
}
运行结果
4.3.2 基于继承实现静态代理
编写一个类实现UserDaoImpl类。
package com.lyf.spring5.proxy;
public class ExtendUserDaoImpl extends UserDaoImpl {
public static void main(String[] args) {
ExtendUserDaoImpl proxy = new ExtendUserDaoImpl();
proxy.proxy();
}
/**
* 代理方法
*/
public void proxy(){
log();
super.update();
check();
log();
super.delete();
check();
log();
super.find();
check();
log();
super.save();
check();
}
/**
* 增强方法 记录日志
*/
public void log(){
System.out.println("记录日志");
}
/**
* 增强方法 检查权限
*/
public void check(){
System.out.println("检查权限");
}
}
运行结果
总结:
静态代理通过硬编码的方式实现,如果需求不断的变更,会造成代码的冗余,代码结构混乱不清晰,造成难维护的局面。
4.4 动态代理的实现
概述
:动态代理有两种实现方式,一种是基于JDK动态代理实现,另一种是基于CGLIB实现。
JDK代理
:底层采用反射机制,通过java.lang.reflect.Proxy类来实现,调用Proxy类的newINstance方法创建代理对象。spring框架默认采用JDK动态代理。
CGLIB代理
:开源的高性能的代码生成框架。生成的代理对象是基于目标对象的子类。
区别:
JDK代理 是基于一个接口实现的,被代理的目标对象必须要有一个接口父类;CGLIB代理是基于继承实现的,生成的对象是继承该目标对象。
4.4.1 JDK动态代理代码实现
使用UserDao接口与实现类UserDaoImpl代理案例
因为实习InvocationHandler 类,所以在创建代理对象的参数当中直接传递了this
package com.lyf.spring5.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxy implements InvocationHandler {
//被代理的对象,这里是接口,主函数传过来的实现是该接口的实现类,该接口的实现类才是被代理的对象。
private UserDao userDao;
/**
* 有参构造方法,给代理对象的属性userDao赋值
* @param userDao
*/
public DynamicProxy(UserDao userDao){
this.userDao =userDao;
}
/**
* 获取代理对象,通过反射类Proxy的静态方法newInstance创建代理对象
* newInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)创建代理对象的方法参数介绍
*ClassLoader loader: 通过类加载器来定义代理类
*Class<?>[] interfaces: 代理类实现的接口列表
*InvocationHandler h):调度方法调用的处理函数
* @return 返回一个代理对象
*/
public UserDao proxy(){
UserDao result = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(), this);
return result;
}
/**
* 该方法是实现代码的动态代理核心
* @param proxy 表示代理类
* @param method 代理类调用被代理类的方法
* @param args 被代理类的方法参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
check();//调用增强的方法
//被代理的对象,参数,返回方法执行的结果
Object result = method.invoke(userDao, args);
log();//调用增强的方法
return result;//返回方法的返回值
}
/**
* 增强的功能,添加记录日志功能
*/
public void log(){
System.out.println("记录日志");
}
/**
* 增强的检查权限功能
*/
public void check(){
System.out.println("检查权限");
}
public static void main(String[] args) {
// new 一个被代理对象
UserDao userDao = new UserDaoImpl();
//调用代DynamicProxy生成代理对象
UserDao result = new DynamicProxy(userDao).proxy();
//返回的代理对象调用被代理对象的方法
result.update();
String save = result.save();
System.out.println(save);
result.find();
result.delete();
}
}
反射类对象创建代理对象的方法参数
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
运行结果
简写形式
public class DynamicMain {
public UserDao proxy(UserDao userDao){
UserDao userDao1= (UserDao) Proxy.newProxyInstance(UserDaoImpl.class.getClassLoader(),
UserDaoImpl.class.getInterfaces(),
(proxy, method, args) -> {
System.out.println("检查权限");
Object result = method.invoke(userDao, args);
System.out.println("记录日志");
return result;
});
return userDao1;
}
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
UserDao proxy = new DynamicMain().proxy(userDao);
proxy.find();
proxy.update();
}
}
4.4.2 CGLIB动态代理实现
引入CGLIB代理依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
CGLIb 实现了MethodInterceptor 接口,所以在Hnhancer.create()传人的this对象。
package com.lyf.spring5.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLIb implements MethodInterceptor {
//被代理的目标对象
private UserDaoImpl userDao;
/**
* 有参构造,给被代理对象赋值
* @param userDao
*/
public CGLIb(UserDaoImpl userDao){
this.userDao = userDao;
}
/**
* 调用Enhancer类的create静态方法创建被代理对象的代理类。
* 传人create方法中的参数,一个是被代理对象类型,一个是回调函数,该会函调函数被当前类继承,所以,直接传人当前对象即可。
* @return
*/
public UserDaoImpl createCGLIB(){
return (UserDaoImpl) Enhancer.create(UserDaoImpl.class, this);
}
/**
* 重写的MethodInterceptor
* @param o 代理类对象本身
* @param method 代理类执行的方法
* @param objects 方法执行的参数
* @param methodProxy 方法代理
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
/**
* 第一种实现方式
* 使用反射进行代理
*/
// System.out.println("权限检查");
// Object result = method.invoke(userDao, objects);
// System.out.println("记录日志");
// return result;
/**
* 第二种实现方式
* methodProxy.invoke使用 目标对象进行代理
*/
// System.out.println("权限检查");
// Object result = methodProxy.invoke(userDao, objects);
// System.out.println("记录日志");
// return result;
//
/**
* 第三种实现方式
* methodProxy.invokeSuper使用 目标对象进行代理
*/
System.out.println("权限检查");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("记录日志");
return result;
}
public static void main(String[] args) {
//创建被代理类对象
UserDaoImpl userDao = new UserDaoImpl();
//传人被代理对象
CGLIb cglIb = new CGLIb(userDao);
//创建代理对象
UserDaoImpl result = cglIb.createCGLIB();
//代理对象调用目标对象方法
result.update();
}
}
运行结果
简写形式
public class CGLIbProxy {
public Object enhancer(Object obj){
return Enhancer.create(UserDaoImpl.class, (MethodInterceptor) (o, method, args, methodProxy) -> {
System.out.println("权限检查");
Object result = methodProxy.invoke(obj, args);
System.out.println("日志检查");
return result;
});
}
public static void main(String[] args) {
UserDaoImpl userDao = new UserDaoImpl();
UserDaoImpl result = (UserDaoImpl) new CGLIbProxy().enhancer(userDao);
result.find();
result.update();
result.delete();
}
}