Java-代理模式

1、什么是代理模式

代理:自己不做,找别人帮你做

代理模式:在一个原有功能的基础上添加新的功能

分类:静态代理和动态代理

2、静态代理

原有方式:就是将核心业务和服务方法都编写在一起

java 复制代码
package com.AE.service;

public class TeamService{
    public void add() {
        try {
            System.out.println("开始事务");
            System.out.println("TeamService---add-----");
            System.out.println("提交事务");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("事务回滚");
        }
    }
}

2.1基于类的静态代理

将服务型代码分离出来,核心业务--保存业务中只有保存功能

java 复制代码
package com.AE.service;

public class TeamService{
    public void add() {
        System.out.println("TeamService---add-----"); // 核心业务
    }
}
java 复制代码
import com.AE;

/**
 * 基于类的静态代理:
 *     要求继承被代理的类
 *     弊端:每次只能代理一个类
 */
public class ProxyTeamService extends TeamService {

    public void add(){
        try {
            System.out.println("开始事务");
            super.add();//核心业务就是由被代理对象完成 ;其他服务功能由代理类完成
            System.out.println("提交事务");
        }catch (Exception e){
            System.out.println("回滚事务");
        }
    }
}

使用:

java 复制代码
public static void main(String[] args) {
		TeamService ser=new ProxyTeamService();
		ser.add();
	}

这种基于类的静态代理最大的问题就是代理类之呢个代理一个类。

2.2、基于接口的静态代理

为核心业务(保存add)创建一个接口,通过接口暴露被代理的方法

要求:代理类和被代理类都实现了同一个接口

java 复制代码
package com.AE;
/**
 * 接口定义核心方法
 */
public interface IService {
    void add();
}
java 复制代码
package  com.AE;
public class TeamService implements IService{
    @Override
    public void add(){
        System.out.println("TeamService---- add----");// 核心业务
    }
}

package com.AE;
public class UserService implements IService{
    @Override
    public void add() {
        System.out.println("UserService---- add-----");
    }
}
java 复制代码
package com.AE;
/**
 * 基于接口的静态代理:
 * 代理类和被代理类实现同一个接口
 */
public class ProxyTranService implements IService {
    private IService service;//被代理的对象
    public ProxyTranService(IService service) {
        this.service = service;
    }
    @Override
    public void add() {
        try {
            System.out.println("开始事务");
           service.add();//核心业务就是由被代理对象完成 ;其他服务功能由代理类完成
            System.out.println("提交事务");
        }catch (Exception e){
            System.out.println("回滚事务");
        }
    }
}
java 复制代码
package com.AE;

public class ProxyLogService implements IService {
    private IService service;//被代理对象
    public ProxyLogService(IService service) {
        this.service = service;
    }
    @Override
    public void add() {
        try {
            System.out.println("开始日志");
            service.add();//核心业务就是由被代理对象完成 ;其他服务功能由代理类完成
            System.out.println("结束日志");
        }catch (Exception e){
            System.out.println("异常日志");
        }
    }
}

测试类:

java 复制代码
public static void main(String[] args) {
        TeamService teamService=new TeamService();//被代理对象
        UserService userService=new UserService();//被代理对象

        ProxyTranService tranService=new ProxyTranService(userService);//事务代理对象--一级代理
        //tranService.add();//代理对象干活

        ProxyLogService logService=new ProxyLogService(tranService);//日志的代理对象--二级代理
        logService.add();
    }

2.3、提取出切面代码,作为AOP接口

就是将业务前后执行的代码,以及异常后执行代码和finally代码提取出来

java 复制代码
package com.AE;

/**
 * 切面:服务代码,切入到核心代码中,切入到哪里,给了四个位置
 */
public interface AOP {
    void before();
    void after();
    void exception();
    void myFinally();
}
java 复制代码
package com.AE;
public class TranAOP implements AOP {
    @Override
    public void before() {
        System.out.println("事务----before");
    }
    @Override
    public void after() {
        System.out.println("事务----after");
    }
    @Override
    public void exception() {
        System.out.println("事务----exception");
    }
    @Override
    public void myFinally() {
        System.out.println("事务----myFinally");
    }
}
java 复制代码
package com.AE;
public class LogAop implements AOP{
    @Override
    public void before() {
        System.out.println("日志----before");
    }
    @Override
    public void after() {
        System.out.println("日志----after");
    }
    @Override
    public void exception() {
        System.out.println("日志----exception");
    }
    @Override
    public void myFinally() {
        System.out.println("日志----myFinally");
    }
}
java 复制代码
package com.AE

public class ProxyAOPService implements IService {
    private IService service;//被代理对象
    private AOP aop;//要加入切面
    public ProxyAOPService(IService service, AOP aop) {
        this.service = service;
        this.aop = aop;
    }
    @Override
    public void add() {
        try {
            aop.before();
            service.add();//被代理对象干活
            aop.after();
        }catch (Exception e){
            aop.exception();
        }finally {
            aop.myFinally();
        }
    }
}

测试类:

java 复制代码
@Test
    public void test02(){
        IService teamService=new TeamService();//被代理对象--核心内容
        AOP logAop=new LogAop();//切面-服务内容
        AOP tranAop=new TranAOP();
        IService service=new ProxyAOPService(teamService,logAop); //代理对象--一级代理
        IService service2=new ProxyAOPService(service,tranAop);//代理对象--二级代理
        service2.add();
    }

2.4、静态代理总结

1、在不修改目标对象的功能前提下,对目标对象功能扩展

2、缺点:因为代理对象,需要与目标对象实现一样的接口。所以会有很多代理类,类太多。一旦接口增加方法,目标对象与代理对象都要维护。

3、动态代理

静态代理:要求代理类一定存在

动态代理:程序运行的时候,根据要被代理的对象动态生成代理类,省去代理类的编写。

类型:1、基于JDK的动态代理

2、基于CGLIB的动态代理

有接口就用JDK的动态代理,没有就用CGLIB的动态代理

3.1、基于JDK的动态代理

3.1.1、直接编写

java 复制代码
/**
 * newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
 *        ClassLoader :类加载器,因为动态代理类,借助别人的类加载器。一般使用被代理对象的类加载器。
 *        Class<?>[] interfaces:接口类对象的集合,针对接口的代理,针对哪个接口做代理,一般使用的就是被代理对象的接口。
 *        InvocationHandler:句柄,回调函数,编写代理的规则代码
 * 
 * public Object invoke(Object arg0, Method arg1, Object[] arg2)
 *        Object arg0:代理对象
 *        Method arg1:被代理的方法
 *        Object[] arg2:被代理方法被执行的时候的参数的数组
 */
public static void main1(String[] args) {
        TeamService teamService = new TeamService();// 被代理对象
        // JDK提供的
        IService proxyService = (IService) Proxy.newProxyInstance(
                teamService.getClass().getClassLoader(),
                teamService.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        try {
                            System.out.println("开始事务");
                            Object invoke = method.invoke(teamService, args); // 核心业务
                            System.out.println("提交事务");
                            return invoke;
                        } catch (Exception e) {
                            e.printStackTrace();
                            System.out.println("回滚事务");
                        }  finally {
                            System.out.println("finally---");
                        }
                        return null;
                    }
                }
        );
        proxyService.add();
        System.out.println(teamService.getClass());
        System.out.println(proxyService.getClass());
    }

3.1.2、结构化设计

方式一

提前写好InvocationHandler,并且使用切面来简化。

java 复制代码
public class ProxyHandler implements InvocationHandler {
    private IService service;// 被代理对象、目标对象
    private AOP aop;

    public ProxyHandler(IService service, AOP aop) {
        this.service = service;
        this.aop = aop;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            aop.before();
            Object invoke = method.invoke(service, args); // 核心业务
            aop.after();
            return invoke;
        } catch (Exception e) {
            e.printStackTrace();
            aop.exception();
            throw e;
        }  finally {
            aop.myFinally();
        }
    }
}

然后只需要将被代理类和切面类传进去即可,然后通过动态返回的代理类进行核心业务的执行。

java 复制代码
public static void main2(String[] args) {
        // 目标对象
        IService teamService = new TeamService();
        // 准备切面
        AOP aop = new TranAop();
        IService service = (IService) Proxy.newProxyInstance(
                teamService.getClass().getClassLoader(),
                teamService.getClass().getInterfaces(),
                new ProxyHandler(teamService, aop)
        );
        service.add();
        System.out.println(service.getClass());
    }

方式二

再次简化代码的编写,因为可以直接获取动态返回的代理类,因为参数都可以通过被代理类和切面类得到。

java 复制代码
public class ProxyFactory {
    private IService service;
    private AOP aop;

    public ProxyFactory(IService service, AOP aop) {
        this.service = service;
        this.aop = aop;
    }
    /**
     * 获取动态代理对象的实例
     */
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(
                service.getClass().getClassLoader(),
                service.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        try {
                            aop.before();
                            Object invoke = method.invoke(service, args); // 核心业务
                            aop.after();
                            return invoke;
                        } catch (Exception e) {
                            e.printStackTrace();
                            aop.exception();
                            throw e;
                        }  finally {
                            aop.myFinally();
                        }
                    }
                }
        );
    }
}

然后通过返回的代理类进行核心业务的执行,然后假如有多个服务代码进行,则可以通过二次代理进行使用。

java 复制代码
    public static void main(String[] args) {
        // 目标对象
        IService teamService = new TeamService();
        // 准备切面
        AOP aop = new TranAop();
        AOP logAop = new LogAop();
        // 获取代理对象
        IService service = (IService) new ProxyFactory(teamService, aop).getProxyInstance();
        IService service1 = (IService) new ProxyFactory(service, logAop).getProxyInstance();

        service1.add();
    }

3.2、基于CGLIB的动态代理

Cglib代理,也叫做子类代理。在内存中构建一个子类对象从而实现对目标对象功能的扩展。

  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现。
  • CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception。

  • CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉

3.2.1、代码编写

java 复制代码
public class NBAService {

    public int add(String name,int id){
        System.out.println("NBAService---- add----");
        return id;
    }
}
java 复制代码
public static void main(String[] args) {
        //目标对象:没有接口
        NBAService nbaService=new NBAService();
        //创建代理对象:选择cglib动态代理
        NBAService proxyService= (NBAService) Enhancer.create(nbaService.getClass(),
                new MethodInterceptor() {//回调函数编写代理规则
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                try {
                    System.out.println("开始事务");
                    Object invoke = methodProxy.invokeSuper(o, objects);//核心
                    System.out.println("提交事务");
                    return invoke;
                }catch (Exception e){
                    System.out.println("事务回滚");
                    throw e;
                }finally {
                    System.out.println("finally------------");
                }
            }
        });
        //代理对象干活
       int res=proxyService.add("AE",12);
        System.out.println(res);
    }

3.2.2、结构化设计

java 复制代码
public class CglibProxyFactory {

    public Object getProxyInstance(NBAService nb, AOP aop) {
        // cglib代理
        return Enhancer.create(nb.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                try {
                    aop.before();
                    Object o1 = methodProxy.invokeSuper(o, objects);
                    aop.after();
                    return o1;
                } catch (Exception e) {
                    aop.exception();
                    e.printStackTrace();
                } finally {
                    aop.myFinally();
                }
                return null;
            }
        });
    }
}
java 复制代码
    public static void main(String[] args) {
        NBAService nb = new NBAService();// 没有接口的目标对象
        AOP aop = new TranAop();// 创建切面
        // 创建代理对象:选择CGLIB动态代理
        NBAService proxyInstance = (NBAService) new CglibProxyFactory().getProxyInstance(nb, aop);
        int ae = proxyInstance.add("AE", 12);
        System.out.println(ae);
        System.out.println(proxyInstance.getClass());
    }
相关推荐
巭犇1 分钟前
c语言中define使用方法
c语言·开发语言
qing_04060319 分钟前
C++——string的了解和使用
开发语言·c++·string
lj90772264420 分钟前
Dockerfile部署xxljob
java·docker
多则惑少则明30 分钟前
idea 编辑器常用插件集合
java·编辑器·intellij-idea
The Straggling Crow36 分钟前
go 战略
开发语言·后端·golang
BLUcoding41 分钟前
RabbitMQ08_保证消息可靠性
java·rabbitmq
MogulNemenis41 分钟前
力扣415周赛
java·数据结构·算法·leetcode
ai安歌42 分钟前
【JavaWeb】利用IDEA2024+tomcat10配置web6.0版本搭建JavaWeb开发项目
java·开发语言·后端·tomcat·web·intellij idea
2407-2 shw1 小时前
weblogic CVE-2018-2894 靶场攻略
java·运维·服务器·安全·weblogic
尘浮生1 小时前
Java项目实战II基于Java+Spring Boot+MySQL的作业管理系统设计与实现(源码+数据库+文档)
java·开发语言·数据库·spring boot·后端·mysql·spring