代理模式以及mybatis中的简单实现

代理模式

1.1 代理模式的作用

​ 将开发中的主要业务和次要业务进行松耦合的组装.让开发人员只关心功能里面的主要业务,不再关心次要业务.降低了对现有代码维护的难度.

1.2 代理模式的本质

​ 监控状态.监控行为特征

<input type="button" onclick="处理函数">

1.3 代理模式的分类

​ 1. 静态代理

​ 2. 动态代理(JDK动态代理,cglib动态代理)

​ 案例: 饭前便后要洗手

​ 主要业务: 吃饭,上厕所

​ 次要业务: 洗手

1.4 JDK动态代理模式实现(基于接口的动态代理)

​ (1) 接口角色:定义需要监听的行为(只要吃饭就监控,但是吃饭的行为细节可以不一样)

package com.wq.proxy;

public interface BaseService {

    public String eat();
    public String ec();

}

​ (2) 接口的实现类(需要知道去监听的具体的某个人)

package com.wq.proxy.impl;

import com.wq.proxy.BaseService;

/**
 * @ClassName ServiceImpl
 * @Description 实现类
 * @Author wq
 * @Date 2019/1/31 16:26
 * @Version 1.0.0
 */
public class ServiceImpl implements BaseService {

    /**
     * 只关心主要业务
     */
    public String eat() {
        System.out.println("吃饭");
        return "eat";
    }

    /**
     * 只关心主要业务
     */
    public String ec() {
        System.out.println("上厕所");
        return "ec";
    }

}

​ (3) 通知类: 对次要业务进行具体的实现; 通知JVM,当前被拦截的主要的业务方法和被声明的次要方法应该如何绑定.

package com.wq.proxy.advice;

import com.wq.proxy.BaseService;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @ClassName Invocation
 * @Description 通知:告诉主业务在做主业务之前需要怎么做
 * @Author wq
 * @Date 2019/1/31 16:30
 * @Version 1.0.0
 */
public class Invocation implements InvocationHandler {

    /**
     * 监控的行为抽象
     */
    private BaseService baseService;

    /**
     * 创建通知的时候需要指定监控谁
     * @param obj
     */
    public Invocation (BaseService obj){
        this.baseService = obj;
    }


    /**
     * 在被监控的行为将要执行的时候,拦截住这个方法.
     * 被监控方法的信息会被作为参数传递进来
     *      与主业务的方法绑定执行
     * @param proxy  代理对象(执行主业务方法对象的代理对象)
     * @param method 被拦截的方法
     * @param args   运行时所有的实参封装在数组中
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //确认当前呗拦截的方法
        String methodName = method.getName();
        //根据被拦截的行为的不同决定如何将次要业务和主要业务绑定执行
        Object invoke = null;
        if ("eat".equals(methodName)){
            //先洗手后吃饭
            wash();
            invoke = method.invoke(this.baseService, args);
            invoke = "eat";
        }

        if ("ec".equals(methodName)){
            //先上厕所后洗手
            invoke = method.invoke(this.baseService, args);
            wash();
            invoke = "ec";
        }
        return invoke;
    }

    /**
     * 次要业务
     */
    public void wash(){
        System.out.println("洗手");
    }

}

​ (4) 监控对象(代理对象):创建是由JVM提供:被监控的是谁,监控的方法

package com.wq.proxy.factory;

import com.wq.proxy.BaseService;
import com.wq.proxy.advice.Invocation;
import com.wq.proxy.impl.ServiceImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * @ClassName ProxyFactory
 * @Description 代理对象创建工厂
 * @Author wq
 * @Date 2019/1/31 16:52
 * @Version 1.0.0
 */
public class ProxyFactory {

    /**
     * 代理对象的类型应该由被监控的类型决定
     * type:被监控对象的类型
     */
    public static BaseService newInstatnce(Class type) throws IllegalAccessException, InstantiationException {
        //创建一个被监控的实例对象
        BaseService o = (BaseService) type.newInstance();
        InvocationHandler invocation = new Invocation(o);
        //向JVM申请负责监控BaseService的代理对象
        //loader:被监控对象类文件真实地址
        BaseService proxyInstance = (BaseService) 					Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), invocation);
        return proxyInstance;
    }
}

1.5 Mybatis中的代理模式探究

1.1 JDBC的开发步骤

  1. 加载驱动

  2. 建立连接通道

  3. 建立PrepareStatement

  4. 输送SQL命令道数据库中执行,并带回运行结果

  5. 销毁连接通道,PreparedStatement

1.2 JDBC中的主要业务与次要业务解析

​ 次要业务:

​ 加载驱动

​ 建立连接通道

​ 建立PreparedStatement

​ 销毁连接通道和PreparedStatement

​ 主要业务:

​ 输送SQL命令到数据库执行并但会运行结果

1.3 简单实现

​ 接口代码

package com.wq.proxy2.service;

import java.sql.SQLException;

public interface SqlSession {

    int save(String sql) throws SQLException;
}

​ 实现类

package com.wq.proxy2.service.impl;

import com.wq.proxy2.service.SqlSession;

import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * @ClassName DeptMapper
 * @Description 接口实现类
 * @Author wq
 * @Date 2019/1/31 18:05
 * @Version 1.0.0
 */
public class DeptMapper implements SqlSession {
    private PreparedStatement ps;
    public int save(String sql) throws SQLException {
        return ps.executeUpdate(sql);
    }
}

​ 通知

package com.wq.proxy2.service.proxy;

import com.wq.proxy2.service.SqlSession;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * @ClassName DeptMapperProxy
 * @Description TODO
 * @Author wq
 * @Date 2019/1/31 18:07
 * @Version 1.0.0
 */
public class DeptMapperProxy  implements InvocationHandler {

    private SqlSession sqlSession;

    private PreparedStatement preparedStatement;
    private Connection connection;

    /**
     * 代理对象构造器
     * @param sqlSession   指定需要代理的接口
     */
    public DeptMapperProxy(SqlSession sqlSession){
        this.sqlSession = sqlSession;
    }

    /**
     * 次要业务1:
     *     加载驱动.    建立连接通道.   创建preapredStatement
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    public void init() throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.jdbc.Driver");
        connection = DriverManager.getConnection("jdbc:mysql://119.23.220.195/kkb", "root", "root");
        preparedStatement = connection.prepareStatement("");
    }

    /**
     * 次要业务2
     *      关闭连接和preparedStatement
     * @throws SQLException
     */
    public void destroy() throws SQLException {
        if (this.preparedStatement != null){
            this.preparedStatement.close();
        }
        if (this.connection != null){
            this.connection.close();
        }
    }

    /**
     * 代理之后的主业务+次要业务逻辑
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        init();
        //反射给实现类的preparedStatement赋值
        Field ps = sqlSession.getClass().getDeclaredField("ps");
        ps.setAccessible(true);
        ps.set(sqlSession, this.preparedStatement);
        //执行目标方法
        Object invoke = method.invoke(sqlSession, args);
        destroy();
        return invoke;
    }
}

​ 代理对象生成工厂

package com.wq.proxy2.service.factory;

import com.wq.proxy2.service.SqlSession;
import com.wq.proxy2.service.proxy.DeptMapperProxy;

import java.lang.reflect.Proxy;

/**
 * @ClassName ProxyFactory
 * @Description 代理对象Bean工厂
 * @Author wq
 * @Date 2019/2/1 9:01
 * @Version 1.0.0
 */
public class ProxyFactory {

    /**
     * 代理对象工厂方法
     *
     * @param type
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static SqlSession getInstance(Class type) throws IllegalAccessException, InstantiationException {


        //创建呗监控的实例对象
        SqlSession sqlSession = (SqlSession) type.newInstance();

        //创建通知对象
        DeptMapperProxy deptMapperProxy = new DeptMapperProxy(sqlSession);

        //注册JDK代理
        SqlSession sqlSession1 = (SqlSession) Proxy.newProxyInstance(sqlSession.getClass().getClassLoader(), sqlSession.getClass().getInterfaces(), deptMapperProxy);

        return sqlSession1;
    }
}

​ 测试代码

package com.wq.proxy2.service.main;

import com.wq.proxy2.service.SqlSession;
import com.wq.proxy2.service.factory.ProxyFactory;
import com.wq.proxy2.service.impl.DeptMapper;

import java.sql.SQLException;

/**
 * @ClassName TestMain
 * @Description TODO
 * @Author wq
 * @Date 2019/2/1 9:13
 * @Version 1.0.0
 */
public class TestMain {

    public static void main(String[] args) throws InstantiationException, IllegalAccessException, SQLException {
        String sql = "insert into dept(dname,loc,flag)values('shiye','cq',1)";
        SqlSession instance = ProxyFactory.getInstance(DeptMapper.class);
        int i = instance.save(sql);
        System.out.println("i="+i);
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值