JAVA 代理技术是 JAVA 核心技术之一, 也是 JAVA core 中非常重要的一部分, 对于学习 Spring 等 JAVA 生态圈的学习起着非常重要的作用, 比如说 AOP,cglib. 动态代理技术就是产生对象的代理对象的. 举例现实场景就是: 一个明星在出名之前可能没有很多商演找他, 所以商家可以直接联系明星本人进行商谈, 但随着明星知名度越来越高, 商演越来越多, 那么这个时候明星本人就无法处理过多的商谈, 那么这个时候就需要一个角色经纪人 (明星代理人), 而明星只要做商演即可. 说道这里是不是代理的作用就出来了: 拦截直接访问真实业务对象 (被代理对象).
代理分类
根据加载被代理类的时机不同, 将代理分为静态代理和动态代理.
1. 静态代理
如果在编译时就确定被代理的类, 那么就可以直接使用静态代理.
2. 动态代理
如果编译时不能确定, 那么使用类的动态加载机制, 在代码运行期间加载被代理的类这就是动态代理, 比如 RPC 框架和 Spring AOP 机制.
静态代理实现
1.ITask 接口
- /**
- * @ClassName ITask
- * @Description TODO: 描述该接口职责
- * @Author ckmike
- * @Date 18-12-5 下午 5:15
- * @Version 1.0
- * @Copyright ckmike
- **/
- public interface ITask {
- void writelog(String msg);
- }
2.PersonTask 类
- public class PeopleTask implements ITask {
- private String username;
- private String age;
- public PeopleTask() {
- }
- @Override
- public void writelog(String msg) {
- System.out.println("Hello! My name is"+username+",age is"+age);
- }
- public PeopleTask(String username, String age) {
- this.username = username;
- this.age = age;
- }
- public void setUsername(String username) {
- this.username = username;
- }
- public void setAge(String age) {
- this.age = age;
- }
- public String getUsername() {
- return username;
- }
- public String getAge() {
- return age;
- }
- }
3.TaskProxy 代理类
- /**
- * TaskProxy 简要描述
- * <p> TODO: 描述该类职责 </p>
- *
- * @author ckmike
- * @version 1.0
- * @date 18-12-5 下午 5:20
- * @copyright ckmike
- **/
- public class TaskProxy implements ITask {
- private ITask tasker;
- public TaskProxy(ITask tasker) {
- this.tasker = tasker;
- }
- @Override
- public void writelog(String msg) {
- tasker.writelog(msg);
- }
- public void writelog(String msg,String singname){
- tasker.writelog(msg);
- sing(singname);
- }
- private void sing(String singname) {
- System.out.println("唱歌:"+singname);
- }
- }
4. 测试
- TaskProxy taskProxy = new TaskProxy(new PeopleTask("Mike","18"));
- taskProxy.writelog("静态代理测试...","莫妮卡");
这种静态代理的方式, 非常适合做增强老旧复杂代码. 比如说一个复杂的业务代码已经写完了, 经过很久后有一天需要对业务方法进行扩展, 那么这个时候不管出于风险还是出于业务复杂性代码可读性考虑都不适合去动原本已经稳定的代码, 这个时候进行代码增强进行代理就非常合适. 其实这个模型就是 Spring AOP 的雏形, 你看我调用 writelog 方法在执行了增强方法 sing 方法, 是不是有点像 @After?
实现动态代理
JAVA 中是通过 java.lang.reflect.Proxy 中的 newProxyInstance 方法创建一个代理对象.
1.StarAction 接口
- /**
- * @ClassName IStarAction
- * @Description TODO: 描述该接口职责
- * @Author ckmike
- * @Date 18-12-7 下午 5:46
- * @Version 1.0
- * @Copyright ckmike
- **/
- public interface IStarAction {
- // 歌唱
- void sing(String name);
- // 跳舞
- void dance(String name);
- // 拍电影
- void movie(String name);
- }
2.Star 类
- /**
- * Star 简要描述
- * <p> TODO: 描述该类职责 </p>
- *
- * @author ckmike
- * @version 1.0
- * @date 18-12-7 下午 5:51
- * @copyright ckmike
- **/
- public class Star implements IStarAction {
- private String name;
- public Star(String name) {
- this.name = name;
- }
- public String getName() {
- return name;
- }
- @Override
- public void sing(String name) {
- System.out.println(this.name+"演唱歌曲:"+ name);
- }
- @Override
- public void dance(String name) {
- System.out.println(this.name+"表演舞蹈:"+ name);
- }
- @Override
- public void movie(String name) {
- System.out.println(this.name+"参演电影:"+ name);
- }
- }
3.StarProxy 类
- /**
- * StarProxy 简要描述
- * <p> TODO: 描述该类职责 </p>
- *
- * @author ckmike
- * @version 1.0
- * @date 18-12-7 下午 5:58
- * @copyright ckmike
- **/
- public class StarProxy implements InvocationHandler {
- // 代理目标对象
- private Star target;
- public Object bind(Star target){
- this.target = target;
- return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- Object result=null;
- if(method.getName().equals("sing")){
- System.out.println("邀请"+target.getName()+"演唱歌曲, 每首报价 60w!");
- return method.invoke(target,args);
- }else if(method.getName().equals("dance")){
- System.out.println("邀请"+target.getName()+"演唱跳舞, 每场报价 200w!");
- return method.invoke(target,args);
- }else if(method.getName().equals("movie")){
- System.out.println("邀请"+target.getName()+"出演电影, 每部报价 8000w!");
- return method.invoke(target,args);
- }
- return result;
- }
- }
4. 测试用例
- /**
- * Test 简要描述
- * <p> TODO: 描述该类职责 </p>
- *
- * @author ckmike
- * @version 1.0
- * @date 18-12-7 下午 6:15
- * @copyright ckmike
- **/
- public class Test {
- public static void main(String[] args) {
- // 这里特别要注意, 只能转为接口, 因为代理返回的就是接口的代理对象, 无法转为 Star 对象
- IStarAction star = (IStarAction) new StarProxy().bind(new Star("刘德华"));
- star.sing("冰雨");
- star.dance("MoonWalk");
- star.movie("旺角");
- }
- }
总结:
所有的对被代理对象接口的访问都会执行 invoke 方法, 那么也就是通过代理对象拦截了对被代理对象接口的访问, 这个代理对象类似于拦截器. 根据上面的静态和动态代理, 我们发现一个问题, JAVA 的 API 代理要求每个被代理类必须实现某个接口, 这也是 JAVA 面向接口编程的一种表现, 但是如果我们某个类没有实现任何接口, 那么我们还能实现它的代理吗? 答案是肯定的, cglib 就是一个非常成熟流行的代表. 接下来我们就来讲讲 cglib 代理.
cglib 代理
cglib 是针对类来实现代理的, 原理是对指定的业务类生成一个子类, 并覆盖其中业务方法实现代理. 因为采用的是继承, 所以不能对 final 修饰的类进行代理, 也无法代理 final 修饰的成员方法.
- /**
- * CglibProxy 简要描述
- * <p> TODO: 描述该类职责 </p>
- *
- * @author ckmike
- * @version 1.0
- * @date 18-12-5 下午 5:40
- * @copyright ckmike
- **/
- public class CglibProxy implements MethodInterceptor {
- private Object target;
- public Object getInstance(Object target){
- this.target = target;
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(this.target.getClass());
- enhancer.setCallback(this);
- return enhancer.create();
- }
- @Override
- public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
- Object result=null;
- System.out.println("播放电影院广告...");
- result = methodProxy.invokeSuper(o,objects);
- System.out.println("播放电影类似新电影广告...");
- return result;
- }
- }
- public class Movie {
- public void play(String name){
- System.out.println("播放电影:"+name);
- }
- public final void show(String info){
- System.out.println(info);
- }
- }
- public class Test {
- public static void main(String[] args) {
- // cglib 代理
- Movie movie = (Movie) new CglibProxy().getInstance(new Movie());
- movie.play("十面埋伏");
- movie.show("hello, 大家好!");
- }
- }
JAVA 动态代理应用场景
上面我们以及了解了 JAVA 静态代理, JAVA 动态代理以及 cglib 的动态代理. 那么我们通常什么时候会使用代理技术呢? 什么场景上会使用呢?
1. 设计模式中对关闭开放原则, 如果要在原来稳定业务代码中进行扩展或者增强, 对于进入原功能方法进行修改是不应许的, 所以这个时候采用代理技术就可以非常方便的实现这一原则.
2.Spring 中的 AOP 面向切面编程就是使用的动态代理技术实现的.
总结
1. 静态代理, 动态代理都是需要通过接口来实现, 面向接口编程的方式.
2.cglib 可以代理未实现任何接口的类的代理, 但是该类不能是 final 类, 并且 final 方法无法被代理.
3.Spring 中 AOP 技术就是通过动态代理实现的.
来源: http://www.bubuko.com/infodetail-2878811.html