代理
代理的简单概念就是生成一个代理对象,来对目标对象的访问进行管理。代理类和委托类(目标类)实现相同的接口。可通过代理对目标对象的方法进行增强。
一、代理的分类
静态代理:
一个目标对象只能有一个代理对象,是一对一的关系。在程序运行前编写代码(编译,生成.class文件)的时候,就已经将接口、代理类、目标类写死在代码中。
动态代理:
一个代理对象可以有多个目标对象,是一对多的关系。在程序运行的时候生成代理。
二、代码实现
废话不多说,干讲不容易理解,用代码实例来品一品。
1.静态代理:
(1)代码demo
接口:
public interface Homework {
void handOut();
}
目标类(委托类):
public class Student implements Homework {
private String name;
public Student(String name){
this.name=name;
}
@Override
public void handOut(){
System.out.println(name+"交作业");
}
}
代理类:
public class StudentProxy implements Homework{
private Student student;
public StudentProxy(Student student){
this.student=student;
}
@Override
public void handOut() {
//增强
System.out.println("检查过了");
student.handOut();
}
}
测试:
public class Test {
public static void main(String[] args) {
Student student=new Student("小明");
StudentProxy studentProxy=new StudentProxy(student);
studentProxy.handOut();
}
}
控制台输出:
(2)总结:
静态代理在程序写的时候已经将代理类,代理与委托类之间的关系写死了。编译后,我们可以在.class文件中找到我们的代理类。
2.动态代理
动态代理有两种代理策略或者说动态代理有两种实现的方式。
- JDK动态代理
- CGLib动态代理
(1)jdk动态代理
a.代码demo
接口
public interface Homework01 {
void handOut();
}
目标类:
public class Student01 implements Homework01 {
@Override
public void handOut() {
System.out.println("交作业");
}
InvocationHandler的实现类:
public class InvocationHandlerImpl implements InvocationHandler {
private Object object;
public InvocationHandlerImpl(Object obj){
this.object=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强
System.out.println("检查作业");
Object result=method.invoke(object,args);
return result;
}
}
测试:
public class Test {
public static void main(String[] args) {
Student01 student=new Student01();
InvocationHandlerImpl ih=new InvocationHandlerImpl(student);
Homework01 proxy= (Homework01) Proxy.newProxyInstance(
student.getClass().getClassLoader(),
student.getClass().getInterfaces(),
ih);
proxy.handOut();
}
}
控制台输出:
b.总结
- jdk实现动态代理,可以看出动态代理并没有写死,在运行的时候我们才进行创建代理类。
- jdk动态代理代理的是接口而不是类。因此要求目标对象必须要实现接口。
- InvocationHandler是其核心,里面有一个invoke方法。当我们执行代理对象的方法时(也就是执行proxy.handOut();)会被自动替换成执行invoke方法。在invoke方法中通过反射来执行目标对象的对应方法。
c.注意点
因为jdk动态代理代理的是接口所以在进行代理对象的类型转换时要用接口转换,而不是转换为目标类,否则报错。
Homework01 proxy= (Homework01) Proxy.newProxyInstance(
student.getClass().getClassLoader(),
student.getClass().getInterfaces(),
ih);
报错:Exception in thread “main” java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.wan.test.jdkProxy.Student01
(2)cglib动态代理
cglib实现动态代理必须得先带入相应的依赖,所以本次demo我在springboot项目上演示。
a.代码demo
引入依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
目标类:
public class Student {
public void handOut(){
System.out.println("上交作业");
}
}
代理类创建工厂:
public class ProxyFactory implements MethodInterceptor {
private Object object;
public ProxyFactory(Object object){
this.object=object;
}
public Object getCglibProxy(){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(object.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
/***
* @author wan
* @param o: 代理对象
* @param method: 目标类方法
* @param objects: 方法参数
* @param methodProxy:方法的代理
* @return: java.lang.Object
**/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//增强
System.out.println("检查作业");
//代理子类调用父类目标类的方法
Object o1=methodProxy.invokeSuper(o,objects);
//直接通过反射调用目标类的方法
Object o2=method.invoke(object,objects);
return o1;
}
}
测试:
public class Test {
public static void main(String[] args) {
ProxyFactory proxyFactory=new ProxyFactory(new Student());
Student student=(Student)proxyFactory.getCglibProxy();
student.handOut();
}
}
控制台输出:
b.总结
- cglib动态代理代理的是类而不是接口。因此目标对象不一定要实现接口。其原理就是将目标类当成父类,生成其子类代理类,可以重写非final方法,进行调用。
- cglib的核心是MethodInterceptor。当方法被调用后(student.handOut();),会被拦截执行intercept方法。可以通过methodProxy()目标类方法的代理,调用父类目标类的方法;也可以通过method直接调用目标类的方法method.invoke(object,objects)。