9月-10月踩坑记录(2019)

前言

很早就想整理自己的踩坑记录发上来,每次把自己踩过的坑发给自己的小号,想着有一天能整理一下。毕竟这些经验自己也是一步一个坑踏过来的。

第一个坑:关于MyBatis参数类型为String的问题

  • 问题描述

    当时使用MyBatis框架写了一个查询数据库的功能,入参是用户名 username(string)。

    public User queryUserByUsername(String username);
    
    <select id="queryUserByUsername" parameterType="String"resultType="com.coorperation.entity.User">
    SELECT
    user_id,user_name,password,user_email,user_phone_number,real_name,profile_img,user_type,user_status,salt
    		FROM
    		tb_user
    		<where>
    			<if test="username!=null and username!=''">
    				user_name = #{username}
    			</if>
    		</where>
    </select>
    

    然后抛了这个异常:

    org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'username' in 'class java.lang.String'
    

    其实翻译一下也知道,它的意思是说String中没有username这个属性,但是MyBatis的确是用#{}来获取入参的,这种方法要怎么解决呢。

  • 解决方案一

    因为MyBatis要求如果为参数为String的话,不管接口方法的形参是什么,在Mapper.xml中引用需要改变为_parameter才能使识别。

    <select id="queryUserByUsername" parameterType="String" resultType="com.coorperation.entity.User">
    SELECT
    user_id,user_name,password,user_email,user_phone_number,real_name,profile_img,user_type,user_status,salt
    		FROM
    		tb_user
    		<where>
    			<if test="username!=null and username!=''">
    				user_name = #{_username}<!--解决方法-->
    			</if>
    		</where>
    </select>
    
  • 解决方案二

    在接口参数中加@param

    public void queryUserByUsername(@Param("username")String username);
    

    然后在xml中正常使用#{username}即可正常运行。

第二个坑:JQuery中为动态生成的按钮绑定点击事件

  • 问题描述

    在JQuery中为一个动态渲染生成的按钮绑定监听时间,如果直接用 button.click(function{//逻辑});是没有办法绑定成功的。

  • 解决方案

    在JQuery中如果需要动态渲染按钮,然后给这个按钮直接绑定click事件是无法生效的,必须使用父容器来为这个按钮委托指派点击事件。假设按钮的id为button,按钮父容器的id为parent,代码如下:

    button.click(function(){
        //逻辑
    });
    button.bind("click",function(){
        //逻辑
    });
    /*用以上两种方法绑定点击事件是无效的*/
    /*必须得使用父容器委托绑定*/
    $(parent).on('click','#button',function(){
       	//逻辑
    })
    

第三个坑:使用getResourceAsStream获取配置文件

  • 问题描述

    使用getResourceAsStream获取配置文件,默认从项目目录开始,如果要是传如同c:\xxx\xxx这样的绝对路径,是没有办法读到的。

  • 解决方案

    我们通常会使用getClassAsStream获取properties配置文件。代码如下:

    public class test{
        public static void main(String[] args){
            Properties pro = new Properties();
            //这里只能传相对路径,而不能传绝对路径
            InputStream in = test.class.getClassLoader().getResourceAsStream("config/xxx.properties");
        }
    }
    

    如果一定要用绝对路径,要用FileInputStream来读。

第四个坑:Shiro自定义拦截器无限重定向(集成SpringBoot)

  • 问题描述

    这是一个Shiro框架集成Spring Boot产生的问题,我们在使用Shiro框架时,通常会自己实现一个拦截器,来基于url控制权限的访问,那么假设我在配置文件中配置了filterChainDefinitionMap.put("/login", "anon");,这段配置就表示了我访问login页面是不需要权限的,然后我再自己实现一个过滤器,过滤器的内部逻辑为,如果检测到这个用户没有登录,那么跳转到登陆界面,这个拦截器的名字就叫url,和anon区分开,然后就会发生一个神奇的现象:当我访问/login的时候,出现无限重定向。我们希望的结果是,/login走anon拦截器,但是实际结果为,/login走了我们自定义的url过滤器,而过滤器内部实现是如果用户没有登录,那么跳转到/login进行登录,这就造成了跳转到/login,检测到没登录,又跳转到/login一直循环往复下去。

  • 解决方案一

    经过排查得知,这个过滤器本来是应该交给Shiro进行管理,但是Spring Boot会默认托管过滤器。

    看看官方定义:

    • SpringBoot文档:任何Servlet或Filter bean都将自动注册到servlet容器中。
    • 要禁用特定Filter或Servlet bean的注册,请为其创建注册bean并将其标记为禁用。

    由于这个定义,我们在访问/login这个页面的时候,首先会访问Shiro的anon过滤器,然后程序并不会在这里停下来,会继续访问我们Spring Boot中管理的我们的自定义过滤器url,于是就会造成循环重定向的问题。

    解决方案就是,关闭SpringBoot注册该过滤器

    public FilterRegistrationBean registration(MyFilter filter) {  
        FilterRegistrationBean registration = new FilterRegistrationBean(filter); 
        registration.setEnabled(false); 
        return registration;
    }
    
  • 解决方案二

    配置ShiroFilter的自定义过滤器时直接new而不使用 @Bean方式配置。

    /*开启注释会产生循环重定向问题
    	 * 症结大概存在于SpringBoot和Shiro会为该拦截器都加载到自己的容器中
    	 * 导致有些页面先走anno拦截器再走该自定义拦截器
    	 * 而该拦截器内部逻辑是未登录自动跳转到登陆界面
    	 * 于是每次在访问login页面时都会产生循环重定向问题
    	 * 
    	 * 解决方案:配置时直接new而不使用 @Bean方式配置。*/
    	//@Bean(name="urlPathMatchingFilter")
    public URLPathMatchingFilter URLPathMatchingFilter() {
    	URLPathMatchingFilter urlPathMatchingFilter = new URLPathMatchingFilter();
    	return urlPathMatchingFilter;
    }
    

    ShiroFilter:

    filters.put("url", URLPathMatchingFilter());

    在配置过滤器时,原先是采用@Bean的方式自动注入到ShiroFilter中,但是现在我们直接通过new的方式手动注入,避开Spring的依赖注入,同样可以达到正确的效果。

第五个坑:Spring Boot Bean加载顺序导致依赖注入为null(二级缓存)

  • 问题描述

    在使用Redis做MyBatis二级缓存时,想把缓存交给Spring托管,然后自动注入RedisUtil来完成Redis的操作,但是我发现在RedisUtil正常的状况下,发现怎么注入都是null。

    经过日志排查,发现cache总是在RedisUtil生成bean之前就已经被生成了。(加@DependOn也无效,很迷,如果有知道为什么的大佬希望可以告诉我)

  • 解决方案

    自己写一个SpringUtil工具类,当Cache使用到RedisUtil时,使用getBean的方式获取RedisUtil对象,相当于配置了一个懒加载。

    @Component
    @Lazy(false)
    public class SpringUtil implements ApplicationContextAware {
     
        private static ApplicationContext applicationContext;
     
        //获取applicationContext
        public static ApplicationContext getApplicationContext() {
            return applicationContext;
        }
     
        //通过name获取 Bean.
        public static Object getBean(String name) {
            return getApplicationContext().getBean(name);
        }
     
        //通过class获取Bean.
        public static <T> T getBean(Class<T> clazz) {
            return getApplicationContext().getBean(clazz);
        }
     
        //通过name,以及Clazz返回指定的Bean
        public static <T> T getBean(String name, Class<T> clazz) {
            return getApplicationContext().getBean(name, clazz);
        }
     
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            if (SpringUtil.applicationContext == null) {
                SpringUtil.applicationContext = applicationContext;
            }
        }
     
        private SpringUtil() {
        }
    }
    

    Cache中使用:

    private synchronized RedisUtil getRedisUtil() {
    	if(redisUtil==null) {
    		redisUtil = SpringUtil.getBean(RedisUtil.class);
    	}
    	return redisUtil;
    }
    
    public void putObject(Object key, Object value) {
    	RedisUtil redisUtil = getRedisUtil();
    	redisUtil.set(serializeUtil.serialize(key), serializeUtil.serialize(value));
    }
    

第六个坑:Ajax异步导致页面显示异常

  • 问题描述

    使用Ajax获取后端数据渲染页面时,发现一个异常状况,当页面打开的时候一切显示正常,但过了一秒之后页面的所有数据都消失了。

    页面的显示条件是通过日期查询数据库中的数据,当时间为null时,默认取数据库中保存时间最晚的数据。

    所以经过排查发现,JQuery中存在两个取数据的ajax,第一个ajax会获取后端回传的时间数据,并再发送请求获取数据,在页面初始化时被显式调用,但是由于ajax是异步的,所以页面上的所有ajax都会一起去向服务器发起请求,所以这种情况下就是第二个ajax首先向服务器发送了获取数据的请求,但是此时第一个ajax还没有正常返回时间,导致第二个请求的时间是null,所以服务器返回给他一个数据库中最新的数据,但是第一个请求此时获取到了时间,发送回服务器,服务器中并没有那个时间的数据,所以返回空表,导致前台数据一闪而过。

  • 解决方案

    将第一个ajax的async属性设置为false,让他同步执行,这样由于他是显式调用的,所以一定是先执行的,而且在它执行完毕之前,在它后面的ajax无法执行。

第七个坑:MyBatis关于大于号小于号无法识别的问题

  • 问题描述

    在MyBatis中如果在查询条件里写了xxxx>xxxx或者xxxx<xxxx诸如此类的消息,需要对其进行特殊处理。

  • 解决方案

    使用<![CDATA[ sql语句 ]]>中的<![CDATA[ ]]>在mybatis中,保证sql语句不被改变。

结语

很早就想整理这个,一直在写技术文章导致这个拖了很长时间,有些异常可能没什么印象了,描述的也不是很清楚,就不往上写了。写上的这些是我自己比较有印象的一些坑,希望可以帮助到大家,也避免自己再次踩坑。

已标记关键词 清除标记
相关推荐
简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级、中级、高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情、执着,对IT的憧憬、向往!此时此景,笔者只专注Android、Iphone等移动平台开发,看着这些源码心中有万分感慨,写此文章纪念那时那景! Java 源码包 Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除,从账户中取出amt,如果amt>账户余额抛出异常,一个实体Bean可以表示不同的数据实例,我们应该通过主键来判断删除哪个数据实例…… ejbCreate函数用于初始化一个EJB实例 5个目标文件,演示Address EJB的实现 ,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制转型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口函数得到远程接口的引用,用远程接口的引用访问EJB。 EJB中JNDI的使用源码例子 1个目标文件,JNDI的使用例子,有源代码,可以下载参考,JNDI的使用,初始化Context,它是连接JNDI树的起始点,查找你要的对象,打印找到的对象,关闭Context…… ftp文件传输 2个目标文件,FTP的目标是:(1)提高文件的共享性(计算机程序和/或数据),(2)鼓励间接地(通过程序)使用远程计算机,(3)保护用户因主机之间的文件存储系统导致的变化,(4)为了可靠和高效地传输,虽然用户可以在终端上直接地使用它,但是它的主要作用是供程序使用的。本规范尝试满足大型主机、微型主机、个人工作站、和TACs 的不同需求。例如,容易实现协议的设计。 Java EJB中有、无状态SessionBean的两个例子 两个例子,无状态SessionBean可会话Bean必须实现SessionBean,获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,计算利息等;在有状态SessionBean中,用累加器,以对话状态存储起来,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除…… Java Socket 聊天通信演示代码 2个目标文件,一个服务器,一个客户端。 Java Telnet客户端实例源码 一个目标文件,演示Socket的使用。 Java 组播组中发送和接受数据实例 3个目标文件。 Java读写文本文件的示例代码 1个目标文件。 java俄罗斯方块 一个目标文件。 Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密   Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。   设定字符串为“张三,你好,我是李四”   产生张三的密钥对(keyPairZhang)   张三生成公钥(publicKeyZhang)并发送给李四,这里发送的是公钥的数组字节   通过网络或磁盘等方式,把公钥编码传送给李四,李四接收到张三编码后的公钥,将其解码,李四用张三的公钥加密信息,并发送给李四,张三用自己的私钥解密从李四处收到的信息…… Java利用DES私钥对称加密代码实例 同上 java聊天室 2个目标文件,简单。 java模拟掷骰子2个 1个目标文件,输出演示。 java凭图游戏 一个目标文件,简单。 java求一个整数的因子 如题。 Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,密钥   Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥,通常应对私钥加密后再保存、如何从文件中得到公钥编码的字节数组、如何从字节数组解码公钥。 Java数据压缩与传输实例 1个目标文件 摘要:Java源码,文件操作,数据压缩,文件传输   Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页