美文网首页
动态代理访问数据库

动态代理访问数据库

作者: flyHunter雄 | 来源:发表于2017-04-21 18:56 被阅读0次

    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<Thread, SqlSession> 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<SqlSession> 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);
    }
    

    相关文章

      网友评论

          本文标题:动态代理访问数据库

          本文链接:https://www.haomeiwen.com/subject/whedzttx.html