代理是一种设计模式,提供了对目标对象另外的访问方式,即通过代理对象访问目标对象。可以不修改目标对象,对目标对象功能进行拓展。在我们学习Spring的时候就会发现,AOP(面向切面编程)的底层就是代理。
代理的实现可以分为静态代理和动态代理。动态代理又根据实现的方式分为:基于JDK接口的动态实现和基于Cglib类的动态实现
举例:假如说你要租房子,那么有两种方式,第一种方式:可以直接找房主,和房主进行协商;第二种方式:直接找中介,那么这里的中介就相当于是房主的一个代理。
代理的目的是为了使被代理类的功能更加纯粹,不用考虑其他的问题,其他的一些操作都可以在代理类中完成。
在javaweb中就用一个典型的例子,我们可以将service层看做是dao层的代理,dao层需要专注于纯粹的数据库交互。
代理的好处
所谓的静态代理,就是对于一个实体类。我们需要为每一个实体类创建一个代理类。
静态代理对于有一定java基础的人而言,还是很简单的。
接口
public interface PersonDao {
// 增加方法
void addPerson();
}
目标类【接口的实现类】
public class PersonDaoImpl implements PersonDao {
@Override
public void addPerson() {
System.out.println("增加成功");
}
}
代理工厂【代理类】 实现和目标类相同的接口
声明接口的实现类
public class PersonDaoProxy implements PersonDao {
private PersonDao personDao;
public PersonDaoProxy() {
}
public PersonDaoProxy(PersonDao personDao) {
this.personDao=personDao;
}
@Override
public void addPerson() {
System.out.println("代理类调用PersonDao接口的实现类");
personDao.addPerson();
}
}
测试:过调用代理类中的方法 来实现最目标类方法的增强
public class PersonTest {
private PersonDao personDao = new PersonDaoImpl();
@Test
public void test(){
// 通过调用代理类中的方法 来实现最目标类方法的增强
PersonDaoProxy personDaoProxy = new PersonDaoProxy(personDao);
personDaoProxy.addPerson();
}
}
使用jdk的反射机制,创建对象的能力,创建的是代理类的对象。而不用你创建类文件。不用写java文件。
**动态:**在程序执行时,调用JDK提供的方法才能创建代理类的对象。
jdk动态代理,必须有接口,目标类必须实现接口,没有接口时,需要使用cylib动态代理。
可以在不改变原来目标方法功能的前提下,可以在代理中增强自己的功能代码。
程序开发中的意思。
实例:
比如:你所在的项目中,有一个功能是其他人(公司的其他部门,其他小组的人)写好的,你可以使用。
有一个功能,需要使用 学生类的实例化对象的一个print()方法。你发现这个功能,现在还缺点,不能完全满足我项目的需要,我需要在gn.print()执行前,需要增加判断权限,得自己在写判断逻辑的代码。
用动态代理实现print()调用时,可以在代理工厂类中的invoke()方法里调用print()之前编写判断权限代码,而不用去改变原来学生类.class文件。
理解:
代理模式是设计模式的其中一种**,也是Java相关框架中的重要应用。我也是初学者, 个人见解, 不喜勿喷, 简单的说就是需要进行功能增强的代理类和原本真实对象的被代理类会实现同样的接口,代理类的字节码文件是在jvm运行的时候动态生成该类(下面进行的介绍),但是代理类会多去继承一个Java中的Proxy 类,代理类负责为代理类(也就是生成真是对象的java类)预处理信息、增强信息、过滤信息最终把已经增强的转发给代理类。然而,回想之后,代理类又是谁生成的呢? 因此,还需要一个类去动态的生成代理类,这个类在编写的时候还需要用到一个Java中的invocationhandler类,这个类是用于增强被代理类中的方法,也就是谁继承了invocationhandler,谁就要去实现该接口对需要增强的类的方法(该接口中的invoke方法), 并且通过调用代理类生成器的生成代理类方法**,就会去调用该实现类的invoke方法, 这是个人自己的理解,所说的动态生成器类就是在这个生成动态代理类的Java类中,不能有别的自己编写的Java类的引用(可以在该类中看是否有import导入自己编写的类),万事俱备, 只欠东风, 那就是搞一个测试, 去看下按照自己的理解是否可行。
使用Proxy.newProxyInstance() 要求被代理类最少实现一个接口
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
三个参数:
一个类的对象,用于加载代理对象的字节码,(写死)写的是被代理对象的类加载器
通过targetObject.getClass().getClassLoader()来对要生成的代理对象进行类的加载,通常用的就是目标类的类加载器
一个Interface对象的数组,就是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了, 通俗的讲就是被代理类所实现的接口,而代理类也需要这个接口,否则它怎么知道你要用到哪个方法呢
一个InvocationHandler对象,就是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上,也就是需要怎么实现对被代理对象的加强。
InvocationHandler接口:
作用:内部写增强代码
内部的invoke()方法:
invoke()方法(调用):目标类中的接口方法都会经过该方法
此方法内部写对方法的增强部分
注意使用匿名内部类需要调用外部成员变量,外部成员变量要设置为final
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
}
接口
public interface PersonDao {
// 添加
void addPerson();
}
目标类【实现了接口的类】
public class PersonDaoImpl implements PersonDao {
@Override
public void addPerson() {
System.out.println("添加成功!");
}
}
动态代理类工厂【根据目标类 直接生成 代理类对象的类】
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 代理类【通过目标类来生成代理类对象】
public class ProxyFactory implements InvocationHandler {
// 声明目标类对象
private Object object;
// 创建 一个 可以根据目标类生成代理类的对象 的方法
public Object getProxyInstance(Object targetObject){
this.object = targetObject;
// java.lang.reflect.Proxy.newProxyInstance()函数的作用是:返回某个对象的代理对象
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(proxy.getClass().toString()+"---------------");
System.out.println("前置通知----增强代码段-----");
Object result = method.invoke(object, args); // 被调用的目标方法
System.out.println("后置通知----增强代码段-----");
return result;
}
}
测试:
@Test
public void test(){
// 创建目标类
PersonDaoImpl personDaoImpl = new PersonDaoImpl();
// 创建动态代理类工厂
ProxyFactory proxyFactory = new ProxyFactory();
// 调用代理工厂中的方法 根据目标类生成 代理类对象
PersonDao proxyInstance = (PersonDao) proxyFactory.getProxyInstance(personDaoImpl);
// 用代理类对象直接调用目标类的方法
proxyInstance.addPerson();
}