文章目录
代理模式
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的开发步骤
-
加载驱动
-
建立连接通道
-
建立PrepareStatement
-
输送SQL命令道数据库中执行,并带回运行结果
-
销毁连接通道,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);
}
}