动态代理访问数据库

test-> 中介[代理, 调用业务代码, 控制事务逻辑] -> 业务逻辑 -> DAO -> 数据库 实现
java动态代理实现访问数据库 dome

代码示例:
数据访问层代码:
dao 层代码

public interface UserDao {
/**
 * 插入数据到数据库
 * @param user
 */
void insertUser(User user);
/**
 * 修改数据
 * @param user
 */
void updateUser(User user);
}

daoImpl 层代码

public class UserDaoImpl implements UserDao {

/**
 * 数据访问层代码,没有事务控制.
 * 出现异常.需要抛出,通知业务代码.
 * 业务代码根据异常,决定提交事务/回滚事务.
 */
  @Override
  public void insertUser(User user) {
    SqlSession session = null;
    try{
        session = MyBatisUtils.openSession();
        session.insert("insertUser", user);
    }catch(Exception e){
        e.printStackTrace();
        throw e;
    }
  }

@Override
public void updateUser(User user) {
    SqlSession session = null;
      try{
        session = MyBatisUtils.openSession();
        session.update("updateUser", user);
      }catch(Exception e){
        e.printStackTrace();
        throw e;
      }
  }
}

封装 Mybatis 工具类替代 JDBC 连接数据库

/**
 * 框架工具代码. 实现创建会话对象的功能.
 * 代码中封装一个会话工厂对象. 提供一个静态访问方法.获取会话对象.
 * 
 * 分析:
 *  会话工厂创建的个数,次数.
 *  工厂创建一个. 创建一次.
 *  
 * java中,什么代码可以保证,一次虚拟机生命周期,运行唯一一次.
 * 
 * @author Administrator
 *
 */
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory;
private static Map currentSessions = new HashMap<>();
/*
 * 用户维护线程资源的一个控制对象.
 * 可以保证,相同的线程中,只有唯一的一个资源对象,与之对应.
 * 功能类似自定义的属性currentSessions
 * 结构和currentSessions几乎一样.
 * ThreadLocal中有属性,Map集合类型的属性.
 * 集合的key是当前线程对象, 集合的value是要绑定的资源对象.
 * 常用方法 :
 * set(T t) -> map.put(Thread.currentThread(), t);
 * get() -> map.get(Thread.currentThread());
 * remove() -> map.remove(Thread.currentThread());
 */
private static ThreadLocal localSessions = new ThreadLocal<>();
static{
    // 创建会话工厂的代码.
    try{
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(
                    Resources.getResourceAsStream("mybatis.cfg.xml")
                );
    }catch(Exception e){
        // 静态代码块出现异常.后续代码还能否执行? 中断JVM
        // 抛出一个初始化代码异常错误.
        throw new ExceptionInInitializerError(e);
    }
}

/*
 * 会话对象创建方法
 * 功能增强. 一个线程,多次访问当前方法,只创建唯一的一个会话对象.
 * 线程有对象. Thread类型的对象就是线程.
 * 如何获取当前线程? Thread.currentThread();
 * 使用当前线程对象作为key,SqlSession对象作为value,创建一个Map集合.管理会话对象.
 */
public static SqlSession openSession(){
    // 1. 获取当前线程中对应的会话对象.
    // SqlSession session = currentSessions.get(Thread.currentThread());
    
    SqlSession session = localSessions.get();
    
    // 2. 判断获取的会话对象是否为null
    if(session == null){
        // 当前线程未创建过会话对象.
        session = sqlSessionFactory.openSession();
        // currentSessions.put(Thread.currentThread(), session);
        localSessions.set(session);
    }
    
    // 3. 返回结果
    return session;
    
    // return sqlSessionFactory.openSession();
}

/**
 * 回收资源方法. 避免关闭会话对象后,在集合中仍然有会话对象的引用. 造成过时对象的应用.
 */
public static void close(){
    SqlSession session = localSessions.get();
    if(session != null){
        session.close();
    }
    
    // 将关闭后的资源从集合中删除
    localSessions.remove();
}

}

实体类

public class User {

// 属性叫field
private Integer id;
private String name;
private String password;
  private int age;
private List address;
  @Override
public String toString() {
    return "User [id=" + id + ", name=" + name + ", password=" + password
            + ", age=" + age + "]";
}
// get/set方法后的字符串成为property  id - property
public Integer getId() {
    return id;
}
public void setId(Integer id) {
    this.id = id;
}
    public String getUsername() {
    return name;
}
public void setUsername(String name) {
    this.name = name;
}
public String getPassword() {
    return password;
  }
  public void setPassword(String password) {
    this.password = password;
}
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
}

}

业务层代码 service

public interface UserService {
/**
 * 注册用户
 */
void register(User user);
    /**
 * 修改用户
 */
void modify(User user);
}

业务层的实现类 service.impl

/**
 * 业务层中,需要控制事务逻辑.
 * 业务层需要访问数据库的时候, 调用DAO代码实现数据访问.
 * @author Administrator
 *
 */
public class UserServiceImpl implements UserService {

private UserDao userDao ;

/**
 * 注册用户, 数据新增. 调用DAO实现数据的新增.
 * 如果DAO代码正常执行结束,提交事务
 * 如果DAO代码抛出异常,回滚事务.
 */
@Override
public void register(User user) {
    System.out.println("UserServiceImpl.register运行");
    this.userDao.insertUser(user);
}

@Override
public void modify(User user) {
    this.userDao.updateUser(user);
}
}

动态代理类

/**
 * 事务控制器. 用于处理事务控制逻辑的.
 * 反射调用具体的业务代码.
   * 
 * @author Administrator
 *
 */
public class TransactionHandler implements InvocationHandler {

private Object target;

/**
 * 方法是执行具体代码的方法.
 * @param proxy - 代理对象. 就是代理业务代码的对象
 * @param method - 运行的方法. 这个方法是真正的业务方法. 如: UserServiceImpl.login();
 * @param args - 真正的业务方法,执行的时候,需要的参数.
 * @return 真正的业务方法的返回值.
 */
@Override
public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
    // 定义返回结果
    Object returnValue = null;
    SqlSession session = null;
    // 执行具体的业务方法.
    // 方法运行需要依赖真正的业务实现类对象.
    try{
        // 增加新的逻辑. 如: 事务控制
        session = MyBatisUtils.openSession();
        System.out.println("当前代理的方法是 : " + method.getName());
        returnValue = method.invoke(target, args);
        session.commit();
    }catch(Exception e){
        e.printStackTrace();
        if(session != null)
            session.rollback();
    }finally{
        MyBatisUtils.close();
    }
    
    return returnValue;
  }

  public Object getTarget() {
    return target;
  }

  public void setTarget(Object target) {
    this.target = target;
  }
}

创建代理对象的工厂类

/**
 * 用于创建代理对象的工厂.
 * 功能是,动态的创建代理对象.
 * 相当于使用反射技术,创建一个匿名内部类, 提供UserService接    口提供的所有方法.
 * 
 * @author Administrator
 *
 */
public class TransactionProxy {
// 目标对象. 要代理的真正的对象.
private Object target;
private InvocationHandler h;

/**
 * 获取代理实例.
 * 这个对象,是反射生成的对象.
 * 对象通过Proxy创建.
 * @return
 */
public Object getProxyInstance(){
    
    ClassLoader loader = this.getClass().getClassLoader();
    Class[] interfaces = this.target.getClass().getInterfaces();
    
    /*
     * @param loader - 类加载器. 用来反射生成对象的工具.
     * @param interfaces - 生成的代理对象,需要实现的接口.
     * @param h - 代理对象,在提供服务的时候,具体逻辑是什么. 之前定义的InvocationHandler
     */
    Object proxy = Proxy.newProxyInstance(loader, interfaces, h);
    
    return proxy;
    
}

public InvocationHandler getH() {
    return h;
}

public void setH(InvocationHandler h) {
    this.h = h;
}

public Object getTarget() {
    return target;
}

public void setTarget(Object target) {
    this.target = target;
}

}

Until测试

 @Test
public void testDynamicProxy(){
    //
    UserService userService = new UserServiceImpl();
    ((UserServiceImpl)userService).setUserDao(new UserDaoImpl());
    //创建代理工厂
    TransactionProxy proxyFactory = new TransactionProxy();
    //创建代理
    TransactionHandler h = new TransactionHandler();
    h.setTarget(userService);
    proxyFactory.setH(h);
    proxyFactory.setTarget(userService);
    UserService service = (UserService) proxy;
    //创建用户对象
    User user = new User();
    user.setAge(25);
    user.setUsername("张三");
    user.setPassword("250");
    //实现注册
    service.register(user);
}

你可能感兴趣的:(动态代理访问数据库)