参考:https://blog.csdn.net/qq_46158352/article/details/130616198
一.静态代理:(不通用)
一个代理类只代理一个对象(因为静态代理类写的时候就是实现被代理类对象的接口,不同接口就要写不同的代理类)
被代理对象一定要实现接口
public class StaticProxyExample {
static class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("添加功能");
}
@Override
public void delete() {
System.out.println("删除功能");
}
}
//静态代理类
static class StaticProxy implements UserDao {
//要代理的对象
UserDaoImpl userDaoImpl;
public StaticProxy(UserDaoImpl userDaoImpl) {
this.userDaoImpl = userDaoImpl;
}
public void add() {
System.out.println("before");
userDaoImpl.add();
System.out.println("after");
}
public void delete() {
System.out.println("before");
userDaoImpl.delete();
System.out.println("after");
}
}
public static void main(String[] args) {
//相当于是通过静态代理类去调用被代理类的方法(一个静态代理类只能代理1个类)
UserDao staticProxyUserDao = new StaticProxy(new UserDaoImpl());
staticProxyUserDao.add();
staticProxyUserDao.delete();
}
}
before
添加功能
after
before
删除功能
after
二.Jdk动态代理:(通用,实现接口的类)
1.JDK动态代理不会像静态代理那样一个代理类只代理一个对象;而是形成一个模板,只要传入要被代理的对象,即可代理(代理类实现接口的方法,是反射去获取接口,然后去实现)
2.被代理对象一定要实现接口(因为Proxy类的构造方法入参:被代理对象的类加载器、被代理对象实现的接口、实现了InvocationHandler的类对象)
Proxy.newProxyInstance()方法创建了代理对象,并将方法调用委托给了JdkProxy。
//Jdk动态代理
public class JdkDynamicProxyExample {
//Jdk动态代理:要求被代理类要实现接口的形式
//因为Jdk动态代理,生成代理时需要传入被代理类的实现的所有接口:newProxyInstance(ud2.getClass().getClassLoader(), ud2.getClass().getInterfaces(), handler)
static class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("添加功能");
}
@Override
public void delete() {
System.out.println("删除功能");
}
}
static class JdkProxy implements InvocationHandler {//调用处理器
private Object target;
public JdkProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
//Method对象的invoke方法入参:目标对象,和目标对象方法的入参
Object result = method.invoke(target, args);
System.out.println("after");
return result;
}
}
public static void main(String[] args) throws Exception {
相当于是通过Jdk动态代理类去调用被代理类的方法(一个jdk动态代理类可以代理多个类)
//Proxy.newProxyInstance():内部用反射,去构建一个代理类,拥有和传入接口一样的方法,方法内部调用的JdkProxy的invoke()方法。invoke()方法拓展了功能
//Spring AOP就是这样的,相当于Jdk动态代理类代理了切面类和目标类,在invoke()方法里面调目标类的方法前和后先调切面类的方法
UserDaoImpl userDaoImpl2 = new UserDaoImpl();
JdkProxy jdkProxy = new JdkProxy(userDaoImpl2);
//Proxy类的构造器newProxyInstance()的入参为:userDaoImpl2的类加载器、userDaoImpl2实现的接口和实现了InvocationHandler的类JdkProxy
UserDao proxyUserDao = (UserDao) Proxy.newProxyInstance(userDaoImpl2.getClass().getClassLoader(), userDaoImpl2.getClass().getInterfaces(), jdkProxy);
proxyUserDao.add();
proxyUserDao.delete();
}
}
before
添加功能
after
before
删除功能
after
三.Cglib动态代理:(通用,普通类都可)
1.CGLIB动态代理不要求被代理的类实现接口
2.实现原理是在运行时动态生成被代理类的子类:并重写
Enhancer类创建了代理对象,并将方法调用委托给了CglibProxy
public class CglibDynamicProxyExample {
//Cglib动态代理,不要求被代理类要实现接口的形式
//因为Cglib,生成代理时,将目标类设置为父类的形式生成的代理
static class UserDaoImpl {
public void add() {
System.out.println("添加功能");
}
public void delete() {
System.out.println("删除功能");
}
}
static class CglibProxy implements MethodInterceptor {//方法拦截器
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before");
Object result = proxy.invokeSuper(obj, args);
System.out.println("after");
return result;
}
}
public static void main(String[] args) {
UserDaoImpl userDaoImpl = new UserDaoImpl();
Enhancer enhancer = new Enhancer(); //翻译 Enhancer:加强器
// 此处将目标类设置为父类,生成该类的子类来实现动态代理,所以如果此时将Hello类声明为final,则会报IllegalArgumentException;但不要求实现接口
enhancer.setSuperclass(UserDaoImpl.class);
enhancer.setCallback(new CglibProxy());
UserDaoImpl userDaoImplProxy = (UserDaoImpl) enhancer.create();
//相当于是通过Cglib动态代理类去调用被代理类的方法(一个Cglib动态代理类可以代理多个类)
userDaoImplProxy.add();
userDaoImplProxy.delete();
}
}
before
添加功能
after
before
删除功能
after
四.两种代理当目标类中有final和static和内部类的情况
1.Jdk动态代理 :只会代理接口中的非static方法,方法如果调用了其他方法,其他方法不会被代理
(1):目标类实现的接口中不可能有final修饰的方法,所以不能代理
public interface testInterface {
//报错,Modifier 'final' not allowed here,接口中不允许定义final的方法
//public final set();
}
(2):目标类实现的接口中有static修饰的方法,因为static修饰的方法不能重写,所以不能代理
从Java 8开始,接口当中允许定义静态方法。格式:
public static 返回值类型方法名称(参数列表){
方法体
}
(3)目标类中的内部类:Jdk动态代理是根据目标类的接口来生成代理类的,所以代理不了
2.Cglib动态代理
(1):目标类中有final修饰的方法,因为final修饰的方法可以被继承,但是不能重写,所以不能代理
(2):目标类中有static修饰的方法,因为static修饰的方法可以被继承,但是不能重写,所以不能代理
(3):目标类中的内部类:
代理目标对象不能是内部类(因为内部类的创建依赖外部类),如果是内部类,cglib代理内部会获取到一个有参构造函数(参数是外部类对象,如果实在需要代理一个内部类,可以通过传递构造参数实现)