10. Spring AOP + Mybatis

Spring AOP 通知

  1. 前置通知

    在目标方法前切入追加功能。采用@Before,方法定义格式如下:

    @Before("切入点表达式")
    public void xxx(){...}
    
  2. 后置通知

    在目标方法后切入追加功能,如果目标方法抛异常不会切入功能。采用@AfterReturning

    @AfterReturning("切入点表达式")
    public void xxx(Object retval){...}
    
  3. 最终通知

    在目标方法后切入追加功能,目标方法有无异常都会切入功能。采用@After

    @After("切入点表达式")
    public void xxx(){...}
    
  4. 异常通知

    在目标方法抛出异常后切入追加功能。采用@AfterThrowing

    @AfterThrowing(...)
    public void xxx(Exception e){}
    
  5. 环绕通知

    在目标方法前和后切入追加功能。采用@Around

    public Object xxx(ProccedingJoinPoint pjp){
        //前面切入
        Object obj = pjp.proceed();//调用目标方法
        //后面切入
        return obj;
    }
    

案例1:采用AOP实现异常处理

需求:当服务调用中发生异常,将异常记录到日志文件中。

  1. 切面

    将异常信息记录到日志文件FileWriter、PrintWriter或log4j

  2. 切入点

    给所有controller方法within(cn.xdl.controller..*)

  3. 通知

    发生异常后,异常通知 @AfterThrowing

  4. 实现过程

    • 编写切面组件,追加异常通知方法

      @Component
      @Aspect
      public class ExceptionBean {
      
          @AfterThrowing(throwing="ex",pointcut="within(cn.xdl.controller..*)")
          public void handle(Exception ex){
              try {
                  FileWriter fw = new FileWriter("E:\\spring_07_error.log", true);
                  PrintWriter out = new PrintWriter(fw);
                  out.println("-----------发生了异常------------");
                  out.println("-异常类型:"+ex);
                  out.println("-发生时间:"+new Date());
                  out.println("-异常详情:");
                  StackTraceElement[] stes = ex.getStackTrace();
                  for(StackTraceElement s : stes){
                      if(s.toString().contains("cn.xdl")){
                          out.println(s.toString());
                      }
                  }
                  out.flush();
                  out.close();
                  fw.close();
              } catch (IOException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              }
          }
      
      }
      
    • 追加@Component、@Aspect、@AfterThrowing标记

案例2:AOP实现原理(动态代理模式)

原理:Spring容器配置AOP,容器返回的BookContorller对象实际是一个动态代理技术生成子类对象(CGLIB技术)。动态代理类将BookController对象所有方法重写,在重写时加入BookController功能和切入新功能(切面组件)。

扩展:CGLIB技术使用方法(了解)

  • 根据现有组件创建代理对象

    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(bean.getClass());//将bean类型做父类
    enhancer.setCallback(this);//注册回调函数
    return enhancer.create();
    
  • 每次调用代理对象方法,都会采用回调机制调用原组件方法,可以在回调方法中做扩展操作

    public class CglibProxyFactoryBean implements MethodInterceptor{
    
        public Object intercept(
            Object obj, Method method, Object[] args, MethodProxy arg3) throws Throwable {
        //切入目标方法前面执行的逻辑
        System.out.println("回首2017");
        Object retval = method.invoke(target, args);//调用目标组件对象的method方法
        //切入目标方法后面执行的逻辑
        System.out.println("展望2018");
        return retval;
        }
    
    }
    

Mybatis框架

简介

MyBatis前身是iBatis.MyBatis本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

Mybatis框架可以简化数据库访问操作,对以前JDBC技术做了封装操作。主要封装功能如下:

  • 封装了获取连接、创建statement、执行sql、释放连接整个过程
  • 封装了SQL参数映射过程(sql使用#{属性名})
  • 封装了查询结果映射成实体对象过程(名称对应)
  • 封装了Mapper映射器接口,可以动态生成Dao对象
  • 封装了很多辅助功能,比如缓存、延迟加载
  • 封装了多表查询关联映射功能

框架结构

案例3:使用MyBatis对USER表操作

  1. 搭建mybatis环境

    • 引入mybatis.jar和ojdbc.jar
    • 引入src/sqlmap-config.xml

    sqlmap-config.xml代码如下:

      
    
    
        
        
            
        
    
        
            
                
                
                    
                    
                    
                    
                
            
        
        
        
            
        
     
    
  2. 编写实体类User.java

    public class User implements Serializable{
    
         private Integer id;
         private String login_name;
         private String nick_name;
         private String real_name;
         private Integer grade_id;
         private String password;
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public String getLogin_name() {
            return login_name;
        }
        public void setLogin_name(String login_name) {
            this.login_name = login_name;
        }
        public String getNick_name() {
            return nick_name;
        }
        public void setNick_name(String nick_name) {
            this.nick_name = nick_name;
        }
        public String getReal_name() {
            return real_name;
        }
        public void setReal_name(String real_name) {
            this.real_name = real_name;
        }
        public Integer getGrade_id() {
            return grade_id;
        }
        public void setGrade_id(Integer grade_id) {
            this.grade_id = grade_id;
        }
        public String getPassword() {
            return password;
        }
        public void setPassword(String password) {
            this.password = password;
        }
    
    
    }
    
  3. 编写SQL定义文件UserMapper.xml

      
    
    
    
        
    
        
    
        
            delete from xdl_user where id=#{id}
        
    
        
            insert into xdl_user 
            (ID,LOGIN_NAME,NICK_NAME,REAL_NAME,GRADE_ID,PASSWORD)
            values 
            (#{id},#{login_name},#{nick_name},#{real_name},#{grade_id},#{password})
        
    
        
            update xdl_user set nick_name=#{nick} where id=#{id}
        
    
    
    
  4. 获取SqlSession对象

    public class MyBatisUtil {
    
        public static SqlSession openSession() throws IOException{
            //SqlSessionFactoryBuilder
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //SqlSessionFactory
            //加载了主配置文件中的连接参数和sql定义文件信息
            Reader reader = Resources.getResourceAsReader("sqlmap-config.xml");
            SqlSessionFactory factory = builder.build(reader);
            //SqlSession
            SqlSession sqlSession = factory.openSession();
            return sqlSession;
        }
    
    }
    

    使用sqlSession调用sql语句

    public class TestUser {
    
        @Test
        public void test1() throws IOException{
            SqlSession sqlSession = MyBatisUtil.openSession();
            List list = sqlSession.selectList("user.findAll");
            for(User user:list){
                System.out.println(user.getId()+" "
                    +user.getLogin_name()+" "+user.getNick_name());
            }
            sqlSession.close();
        }
    
        @Test
        public void test2() throws IOException{
            SqlSession sqlSession = MyBatisUtil.openSession();
            User user = new User();
            user.setId(1);
            user.setLogin_name("tom");
            user.setNick_name("tom");
            user.setPassword("123");
            user.setReal_name("同名");
            user.setGrade_id(1);
            sqlSession.insert("user.save",user);
            sqlSession.commit();
            sqlSession.close();
        }
    
        @Test
        public void test3() throws IOException{
            SqlSession sqlSession = MyBatisUtil.openSession();
            int rows = sqlSession.delete("user.delete",1);
            System.out.println("删除记录数:"+rows);
            sqlSession.commit();
            sqlSession.close();
        }
    
        @Test
        public void test4() throws IOException{
            SqlSession sqlSession = MyBatisUtil.openSession();
            User user = sqlSession.selectOne("user.findById",37);
            if(user != null){
                System.out.println(user.getId()+" "+user.getLogin_name());
            }else{
                System.out.println("未找到记录");
            }
            sqlSession.close();
        }
    
        @Test
        public void test5() throws IOException{
            SqlSession sqlSession = MyBatisUtil.openSession();
            Map map = new HashMap();
            map.put("id", 37);//#{id}
            map.put("nick", "SPRING");//#{nick}
            sqlSession.update("user.updateNickname",map);
            sqlSession.commit();
            sqlSession.close();
        }
    }
    

案例4:MyBatis使用技巧

  1. 定义类型别名

    在sqlmap-config.xml中定义< typeAliases>

    
        
    
    

    这样在定义SQL时,parameterType或resultType属性可以指定为user,简化类型定义.

  2. 无效列类型:111

    原因是:SQL参数使用#{}时,遇到null值。

    解决方法:#{key,jdbcType=xxx}

    
    insert into xdl_user 
    (ID,LOGIN_NAME,NICK_NAME,REAL_NAME,GRADE_ID,PASSWORD)
    values 
    (#{id},
    #{login_name,jdbcType=VARCHAR},
    #{nick_name,jdbcType=VARCHAR},
    #{real_name,jdbcType=VARCHAR},
    #{grade_id,jdbcType=NUMERIC},
    #{password,jdbcType=VARCHAR})
    

    参考下表:

  3. Mapper映射器

    Mapper映射器是一个接口规范,按规则定义接口后,MyBatis可以自动生成接口的实现对象。(原理:动态代理技术JDK Proxy)

    规则如下:

    • 接口中定义方法,方法可以映射执行SQL操作
    • 接口方法名与SQL定义的id保持一致
    • 接口方法参数类型与SQL定义的parameterType保持一致
    • 接口方法返回类型,多行查询用List;单行查询用resultType类型;增删改用int或void
    • SQL定义的namespace与接口包名.类型名一致

    例如SQL定义

    
    

    对应的接口方法

    public User findById(int id);
    

你可能感兴趣的:(spring+boot)