在某些情况下,一个对象不适合或者不能直接引用另一个对象,可以定义一个类作为调用另外一个类的中介, 类似于生活中的律师,票贩子 这就是代理类
或者需要对目标对象的方法进行一些增强,但是不能直接修改目标类,因为这违反了开闭原则
概念:
抽象角色(抽象主题):真实角色和代理角色的共同实现的接口
真实角色(真实主题): 代理角色所代理的真实角色,是我们最终要引用的对象
代理角色(代理主题):代理角色内部含有对真实角色的引用。代理角色可以在执行真实角色操作时,附加其他的操作;相当于对真实角色进行封装。
分类:
静态代理:在程序运行前代理和被代理对象在代理之前是确定的。
静态代理有两种实现方式:
继承: 代理类 继承父类 重写父类的方法
聚和:代理类和目标类必须实现同一个接口 代理类有目标类的对象属性,重写接口的方法时 可以通过目标类对象属性调用目标类方法
-----------------------------------------------------------------------------------------------
动态代理:在程序运行前事先并不知道真实的角色。
CGLIB动态代理:不需要实现接口,因其使用的是继承
JDK动态代理: 真实角色必须实现接口
---------------------------------------
接下来演示静态代理
新建一个接口
public interface TrainStationDao { //火车站接口 void Buytickets();//购票方法 }
建这个接口的实现类
public class TrainStationImpl implements TrainStationDao { public void Buytickets() { System.out.println("购票中............"); System.out.println("购票成功"); } }
这样就实现了火车站购票方法 但是如果我想拓展成为在火车票代售点购买火车票呢
新建一个代理类
public class TrainStationImplProxy extends TrainStationImpl { //使用继承的方式 实现静态代理 @Override public void Buytickets() { System.out.println("火车票代售点"); super.Buytickets(); } }
这个类继承了TrainStationImpl类 并且重写了父类中的Buytickets()方法 也就是购票方法 新增了火车票代售点功能 然后在调用父类的方法
测试代码
public static void main(String[] args) { TrainStationDao trainStation=new TrainStationImplProxy(); //因为代理类继承了TrainStationImpl类 间接也就是实现了TrainStationDao接口,这是向上转型 是安全的
trainStation.Buytickets(); }
输出结果
火车票代售点
购票中............
购票成功
接下来用聚合的方式实现静态代理
新建一个接口
public interface AirportDao {//飞机场接口 void Buytickets();//购票方法 }
新建一个实现类
public class AirportDaoImpl implements AirportDao { public void Buytickets() { System.out.println("购票中........."); System.out.println("购票成功"); } }
这里也想改成在飞机票代售点购票
新建一个代理类
public class AirportDaoImplProxy implements AirportDao { //使用聚合的方式 实现静态代理 private AirportDao airportDao; //目标类的对象属性
public void Buytickets() { //也实现了和目标类相同的接口 重写目标类也有的方法 System.out.println("飞机票代售点"); airportDao.Buytickets(); //通过目标类对象属性调用目标类中相同的方法 } public AirportDaoImplProxy(AirportDao airportDao) { //有参构造方法 将传入的目标对象,设置为类中目标对象属性的值 super(); this.airportDao = airportDao; } }
测试代码
public class Test { public static void main(String[] args) { AirportDao airportDao=new AirportDaoImplProxy(new AirportDaoImpl()); airportDao.Buytickets(); } }
输出结果
飞机票代售点
购票中.........
购票成功
总结起来就是
继承就是代理类去继承目标类 并重写目标类想要调用的方法,在重写方法内部 就可以调用父类的方法 并且也可以新增新的功能
聚合就是代理类实现目标类实现的接口 然后定义一个属性 为目标类对象,通过有参构造方法给这个属性赋值,重写接口定义的方法,通过目标对象属性就能调用目标类的方法
但是静态代理有很大的弊端 比如我有一个类想要拓展 就要新建一个代理类 不方便 会有很多类 所以一般使用动态代理
动态代理
定义一个动态代理类
public class TrainStationImplJDKProxy implements InvocationHandler { private Object target;//目标类的对象 public Object newInstance(Object targetClassObject){//创建代理对象方法 this.target=targetClassObject;//将传入的目标类对象,赋值给代理类的属性目标对象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); //通过Proxy的静态方法newProxyInstance(参数1:目标类的类加载器,参数2:目标类实现的接口Class对象数组,参数3:这个代理类自身); } //执行目标类的方法 并且可以拓展一些新的功能 参数1:代理类对象,参数2:要执行的方法对象,参数3:方法执行所需要的参数 public Object invoke(Object proxy, Method method, Object[] args)throws Throwable { System.out.println("火车票代售点");//这是我拓展的新的功能 return method.invoke(target, args); //使用方法对象的invoke()方法 传入目标对象 和参数 相当于调用目标类的对应方法,并将原有的方法返回值 返回 } }
测试代码
public class Test2 { public static void main(String[] args) { TrainStationDao trainStationDao=(TrainStationDao)new TrainStationImplJDKProxy().newInstance(new TrainStationImpl()); trainStationDao.Buytickets(); } }
输出结果还是与之前静态代理的结果一样 我就不写了
这是使用jdk的动态代理,
代理类要实现InvocationHandler接口 重写invoke()方法 然后通过Proxy类的静态方法newProxyInstance()方法返回一个代理类的实例
是基于接口的 也就是目标类必须实现了接口 如果没有实现 那么jdk动态代理无法生成
可以使用Cglib来生成代理类 这个是基于类来生成 也就是继承目标类的方式 接下来演示
public class AirportDaoImplCglibProxy implements MethodInterceptor { private Object target;//目标类的对象 public Object newInstance(Object targetClassObject){//创建代理对象方法 this.target=targetClassObject;//将传入的目标类对象,赋值给代理类的属性目标对象 Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(target.getClass());//设置代理类的父类 ,也就是目标类 enhancer.setCallback(this);//设置回调 return enhancer.create();//创建代理对象 并返回 } public Object intercept(Object arg0, Method arg1, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("飞机票代售点");//这是我拓展的新的功能 return methodProxy.invoke(target, args); //使用方法代理对象的invoke()方法 传入目标对象 和参数 相当于调用目标类的对应方法,并将原有的方法返回值 返回 } }
Cglib和JDK实现还是很相似的
实现MethodInterceptor接口 重写intercept()方法 然后使用Enhancer类的方法,设置代理类的父类 设置回调 和创建代理对象
测试代码如下
public class Test2 { public static void main(String[] args) { AirportDao airportDao=(AirportDao)new AirportDaoImplCglibProxy().newInstance(new AirportDaoImpl()); airportDao.Buytickets(); } }
输出结果也和静态代理的结果一样
如果目标类实现了接口 优先使用jdk动态代理生成代理类,没有实现接口在使用Cglib生成代理类