有不够细致的地方或者想讨论的可以在评论区留言
生活中常见的示例
明星的经纪人,第三方无法直接与某明星接触,只能联系经纪人,经纪人可以代为某明星进行一些事情的处理
Java中代理常分以下三种
-
静态代理: 核心理解->持有target,编码在target的方法前后进行处理
-
动态代理:核心理解->代理对象proxy和被代理对象target要实现共同的接口
-
Cglib代理:核心理解->为被代理对象target生成子类,子类即为proxy
静态代理
创建一个测试IUserDao
public interface IUserDao {
public void save();
}
创建一个测试UserDao
public class UserDao implements IUserDao {
public void save(){
System.out.println("测试保存数据=======");
}
}
创建静态代理对象
public class StaticProxyUserDao implements IUserDao {
//持有被代理对象
private UserDao userDao;
public StaticProxyUserDao(UserDao userDao){
this.userDao = userDao;
}
@Override
public void save() {
System.out.println("目标方法执行前的处理。。。。。。。。。。");
//执行被代理对象的方法
userDao.save();
System.out.println("目标方法执行后的处理。。。。。。。。。。。");
}
}
总结:
静态代理,target和proxy都需要实现共同的接口,且proxy需要持有target,可以以构造器的方式。
动态代理
动态代理为何代理对象proxy和被代理对象target要实现共同的接口?
先从源码分析中查看JDK对动态代理的支持类java.lang.reflect.Proxy, 该类下有newProxyInstance()方法来创建代理对象,注意该方法中的第二个入参Class<?>[] interfaces 表示被代理类target的接口类型,因此需要target要实现某个接口
@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);
}
}
示例
在上一步静态代理中已经创建了IUserDao和UserDao,可以复用
在次创建JDK动态代理
public class JdkProxyFactory {
private Object target;
//构造方法注入target
public JdkProxyFactory(Object target){
this.target = target;
}
/**
* 创建代理对象
* @return
*/
public Object getProxyInstance(){
//使用Proxy的newProxyInstance方法
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),//target的类加载器
target.getClass().getInterfaces(),//target的接口类型
new InvocationHandler() { //InvocationHandler 很好理解,即在代理对象proxy中对target方法的额外处理
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("目标方法执行前的处理。。。。。。。。。。。。。。");
//执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("目标方法执行后的处理。。。。。。。。。。");
return returnValue;
}
});
}
}
JDK动态代理测试
public class JdkProxyTest {
public static void main(String[] args) {
IUserDao target = new UserDao();
System.out.println("userDao.getClass() = " + target.getClass());
IUserDao proxy = (IUserDao) new JdkProxyFactory(target).getProxyInstance();
System.out.println("proxy.getClass() = " + proxy.getClass());
//调用代理对象的方法
proxy.save();
}
}
结果
userDao.getClass() = class com.coolcoding.boot.proxy.UserDao
proxy.getClass() = class com.sun.proxy.$Proxy0
目标方法执行前的处理。。。。。。。。。。。。。。
测试保存数据=======
目标方法执行后的处理。。。。。。。。。。
Cglib代理
核心理解:为被代理对象target生成子类,底层使用字节码处理框架ASM,有兴趣可以自己深入研究ASM
创建代理对象
/**
* 首先要实现org.springframework.cglib.proxy.MethodInterceptor
*
*
* Cglib代理方式总结:
* 1.目标类不能为final,否则抛异常报错
* 2.目标类的方法如果为final, static, 则子类无法重写,则不会被代理对象拦截。
*
*
* Create by Administrator on 2020/5/14 10:57
*/
public class CglibProxyFactory implements MethodInterceptor {
private Object target;
//通过构造器注入target
public CglibProxyFactory(Object target){
this.target = target;
}
@Override
public Object intercept(Object o,
Method method,
Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("目标方法执行前的处理。。。。。。。。。。。。。。");
Object returnValue = method.invoke(target, args);
System.out.println("目标方法执行后的处理。。。。。。。。。。");
return returnValue;
}
/**
* 创建cglib代理对象
* @return
*/
public Object getProxyInstance(){
//使用org.springframework.cglib.proxy.Enhancer
Enhancer enhancer = new Enhancer();
//设置父类类型 -> 对应核心理解中的为target创建子类
enhancer.setSuperclass(target.getClass());
//设置回调方法,则调用时会触发该类中的intercept方法
enhancer.setCallback(this);
//创建代理对象 即子类
return enhancer.create();
}
}
Cglib测试
UserDao2 与UserDao一致,唯一区别是UserDao2没有实现接口,因为Cglib不需要target实现接口
public class CglibTest {
public static void main(String[] args) {
//生成一个目标对象
UserDao2 target = new UserDao2();
System.out.println("userDao.getClass() = " + target.getClass());
//创建代理对象
UserDao2 proxy = (UserDao2) new CglibProxyFactory(target).getProxyInstance();
System.out.println("proxy.getClass() = " + proxy.getClass());
//执行代理对象的方法
proxy.save();
}
}
Cglib结果
userDao.getClass() = class com.coolcoding.boot.proxy.UserDao2
proxy.getClass() = class com.coolcoding.boot.proxy.UserDao2$$EnhancerByCGLIB$$74c2897e
目标方法执行前的处理。。。。。。。。。。。。。。
保存数据....cglib....
目标方法执行后的处理。。。。。。。。。。
Cglib总结:
-
Cglib中target不能为final类型,因为final类型的类无法为其创建子类
-
target中的方法不能为final或者static,虽然可以正常运行,但是由于方法无法被子类重写,而不会被代理对象拦截。