一、前言
- 代理是一个抽象的概念,简单理解就可以理解为在一个java类上去给它增加一些新的功能,但是却不用动原来的代码,在Java中分为静态代理和动态代理,然而动态代理又分为 jdk 动态代理和 cglib 动态代理
二、静态代理
2.1 要求
- 静态代理通常用于对原有业务逻辑的扩充。比如持有二方包的某个类,并调用了其中的某些方法。然后出于某种原因,比如记录日志、打印方法执行时间,但是又不好将这些逻辑写入二方包的方法里。所以可以创建一个代理类实现和二方方法相同的方法,通过让代理类持有真实对象,然后在原代码中调用代理类方法,来达到添加我们需要业务逻辑的目的。
- 这其实也就是代理模式的一种实现,通过对真实对象的封装,来实现扩展性。一个典型的代理模式通常有三个角色,这里称之为代理三要素。且在静态代理中,代理类在程序运行前就已经定义好.java源文件,其与目标类的关系在程序运行前就已经确立。在程序运行前代理类已经编译为.class文件。
- Jdk的静态代理要求,目标对象和代理对象都要实现相同的接口。然后提供给客户端使用。
2.2 静态代理实现
2.2.1 基于类实现静态代理
- 编写核心业务(被代理类)类
public class Person {
// 核心业务方法
public void eat(){
System.out.println("人要吃饭的");
}
}
- 编写代理类(代理类需要继承被代理类)
public class ProxyPerson extends Person{
@Override
public void eat() {
System.out.println("前置增强");
super.eat();
System.out.println("后置增强");
}
}
- 编写测试类
public class OClassMain {
public static void main(String[] args) {
ProxyPerson person=new ProxyPerson();
person.eat();
//==>
/*
前置增强
人要吃饭的
后置增强
*/
}
}
- 缺点: 一个核心业务类至少要有一个代理类来对其进行补充,这样要建很多代理类,效率低
2.2.2 基于接口实现静态代理
- 编写一个接口(与它相关的核心业务类都要实现本接口)
public interface IPerson {
void eat();
}
- 核心业务类(并实现接口)
- 私有一个接口对象IPerson,这个是我们后面要被代理的对象;
- 写一个一参构造方法,当外面要创建本ProxyPerson 代理对象时,需要传递一个实现了IPerson接口的核心业务类才能创建代理对象;
- 本ProxyPerson 代理对象也要实现IService接口;
- 本类重写的核心方法由外部传进来的被代理对象提供,其他业务由本代理提供。
public class Person implements IPerson{
@Override
public void eat() {
System.out.println("人是要吃饭的");
}
}
- 编写代理类
public class ProxyPerson implements IPerson{
// 要被代理的对象
private Person person;
public ProxyPerson(Person person){
this.person=person;
}
@Override
public void eat() {
System.out.println("前置增强");
person.eat(); //核心业务是由被代理对象person做,其他由代理类完成
System.out.println("后置增强");
}
}
- 编写测试类
public class OInterfaceMain {
public static void main(String[] args) {
// 创建要被代理的对象
Person person=new Person();
// 代理
ProxyPerson proxyPerson=new ProxyPerson(person);
// 执行生成的代理对象
proxyPerson.eat();
//==>
/*
前置增强
人是要吃饭的
后置增强
*/
}
}
优点: 可以实现多个核心业务类重复使用一个代理类;
缺点: 代理类需要实际编写代理业务代码,后面如果要改,会很不方便。
三、动态代理
概念
- 对代理模式而言,一般来说,具体主题类与其代理类是一一对应的,这也是静态代理的特点。但是,也存在这样的情况:有N个主题类,但是代理类中的“预处理、后处理”都是相同的,仅仅是调用主题不同。那么,若采用静态代理,那么必然需要手动创建N个代理类,这显然让人相当不爽。动态代理则可以简单地为各个主题类分别生成代理类,共享“预处理,后处理”功能,这样可以大大减小程序规模,这也是动态代理的一大亮点。
- 在动态代理中,代理类是在运行时期生成的。因此,相比静态代理,动态代理可以很方便地对委托类的相关方法进行统一增强处理,如添加方法调用次数、添加日志功能等等。本文分别对JDK动态代理和cglib动态代理进行讲解。
3.1 JDK动态代理
JDK动态代理机制的相关类/接口
java.lang.reflect.Proxy
:该类用于动态生成代理类,只需传入目标接口、目标接口的类加载器以及InvocationHandler便可为目标接口生成代理类及代理对象。
// 方法 1: 该方法用于获取指定代理对象所关联的InvocationHandler
static InvocationHandler getInvocationHandler(Object proxy)
// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
// 方法 3:该方法用于判断指定类是否是一个动态代理类
static boolean isProxyClass(Class cl)
// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
JDK动态代理使用步骤
- 创建被代理的接口和类
/**
* 被代理对象接口
*/
public interface ActionService {
String sayHello(String name);
}
/**
* 要被代理的对象
*/
public class ActionServiceImpl implements ActionService {
@Override
public String sayHello(String name) {
return "hello "+name;
}
}
/**
* 代理测试
*/
public class ApplicationMain {
public static void main(String[] args) {
// 创建要被代理的对象
ActionService action=new ActionServiceImpl();
// 使用jdk动态代理
ActionService actionService=(ActionService)Proxy.newProxyInstance(ActionServiceImpl.class.getClassLoader(), ActionServiceImpl.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置增强");
method.invoke(action,args);
System.out.println("后置增强");
return null;
}
});
// 执行代理生成的对象,里面对action对象进行了加强
actionService.sayHello("寻梦");
}
}
- 实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理
public class MyInvocationHandler implements InvocationHandler {
private ActionService action;
public MyInvocationHandler(ActionService actionService){
this.action=actionService;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置增强");
method.invoke(action,args);
System.out.println("后置增强");
return null;
}
}
- 调用Proxy的静态方法,创建代理类并生成相应的代理对象;
- 使用代理
public class ApplicationMain {
public static void main(String[] args) {
// 使用jdk动态代理
ActionService actionService=(ActionService)Proxy.newProxyInstance(ActionServiceImpl.class.getClassLoader(),
ActionServiceImpl.class.getInterfaces(),new MyInvocationHandler(new ActionServiceImpl()));
// 执行代理生成的对象,里面对action对象进行了加强;
actionService.sayHello("寻梦");
//==>
/*
前置增强
hello 寻梦
后置增强
*/
}
}
3.2 cglib动态代理
概念
- JDK的动态代理机制只能代理实现了接口的类。而不能实现接口的类就不能使用JDK的动态代理,CGLIB是针对类来实现代理的,它的原理是对指定目标类生成一个子类,并覆盖其中的方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
cglib动态代理使用步骤
- 导入相关依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
- 定义拦截器类实现
MethodInterceptor
接口
public class MyMethodInterceptor implements MethodInterceptor {
// 准备一个目标对象
private Object objectTaget;
public MyMethodInterceptor(Object objectTaget){
this.objectTaget=objectTaget;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//增强行为
System.out.println("方法执行前的增强行为");
//调用目标类中的方法
Object Objectss= methodProxy.invoke(objectTaget,objects);
//增强行为
System.out.println("方法执行后的增强行为");
return null;
}
//获取代理对象的方法
public Object getProxy(){
//可以通过Enhancer对象中的create()方法可以去生成一个类,用于生成代理对象
Enhancer enhancer=new Enhancer();
//设置父类(将目标类作为代理类的父类)
enhancer.setSuperclass(objectTaget.getClass());
//设置拦截器(回调对象为本身对象)
enhancer.setCallback(this);
//生成一个代理类对象并返回给调用着
return enhancer.create();
}
}
- 创建测试类
public class CglibMain {
public static void main(String[] args) {
// 创建目标对象
CglibTestItem cglibTestItem=new CglibTestItem();
// 对目标对象进行代理加强
MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor(cglibTestItem);
// 获取代理对象
CglibTestItem proxyObj = (CglibTestItem) myMethodInterceptor.getProxy();
// 使用代理对象执行方法
proxyObj.say();
//==>
/*
方法执行前的增强行为
你好这是cglib代理
方法执行后的增强行为
*/
}
}