时间:2022年5月21日
上午
首先查询了@Component系列注解,很有印象,这几个注解我只用过@Controller…… 参考文章被我放到spring收藏里了。
感觉差不多了之后就尝试写lambda,没想到上来踩了个大坑:lambda不能赋值给泛型方法。
目前正尝试理解lambda不能与泛型方法联动的理由。
- @Component系列注解
- @Component
因为没有语义,常用来将非controller、service、dao包下的类交给spring,比如pojo、entity。
- @Controller
无需多说,老熟人了。
- @Service
这个可能要放到实现类(impl)上,反正我xml里配置的是实现类。 ________________________________ 通过@Autowired注解实现类的mapper属性时,该属性可能会划波浪线,那是因为sprig找不到mapper的实现类。此时,在applictionContext.xml中将它们配置成<bean>,其mapper属性也是找不到对象的(赋值一定要用ref而不是value)。 实际上,mapper确实没有实现类,它的接口对应的是mapper.xml。 在applicationContext.xml中配置org.mybatis.spring.mapper.MapperScannerConfigurer的<bean>可以让spring找到这些xml文件。
- @Repository
注解在dao包的类上。 ________________________________ 配置了org.mybatis.spring.mapper.MapperScannerConfigurer的<bean>后,似乎就用不到这个注解了。
- @Mapper
这是mybatis的注解,用来告诉mybatis被它注解的类应该有个mapper.xml。 ________________________________ 由于是mybatis的注解而不是spring的,这个注解并不能让spring将被它注解的类配置到applicationContext.xml中成为<bean>,也就是说,spring不能找到 仅 被@Mapper注解的类。
总之,学到这里就对spring+mybatis有个模板了:
mybatis-config.xml 一、配置<settings>,开启日志、二级缓存等。
applicationContext.xml 二、用<context:property-placeholder>载入database.properties。 三、配置datasource的<bean> 四、配置SqlSessionFactoryBean的<bean>,传入datasource和configLocation(mybatis-condif.xml) 五、配置MapperScannerConfigurer的<bean>,传入SqlSessionFactoryBean和放置mapper的包。
applicationContext.xml 六、开<context:component-scan>,传入被扫描的包(多个包则开多个) 七、controller与service包中使用相关注解。
顺便把spring+springmvc也写在这里:
applicationContext.xml 一、开<mvc:annotation-driven>,和<context:annotation-config>不是一个东西。 二、开<mvc:default-servlet-handler>,用于阻止spring的DispatcherServlet拦截前端对css、js、img等静态资源的请求。 三、配置org.springframework.web.servlet.view.InternalResourceViewResolver的<bean>,spring的视图解析器(话说这东西必须得配置吗?)
web.xml 四、配置org.springframework.web.servlet.DispatcherServlet,传入applicationContext.xml。 五、配置org.springframework.web.filter.CharacterEncodingFilter,传入字符集名(是spring用于转换前后端字符集的内置filter)。
啊,浑身清爽 ~ ~
springboot+各种东西,这点还不太熟悉。
欠缺的部分:事务、spring中开启mapperscan后是否还要把mapper.xml配置给mybatis、
下午
看见 官方文档(英文)说lambda不支持泛型方法后,我就立马放弃了。不过好歹也算是为它烦恼了一个小时的,不留点记录怎么能行呢?
官方文档上记载了lambda不支持泛型方法,知道这件事是从别的文章上,不过文章有些短,就直接把官方文档搬过来了。
文档上说lambda不适应拥有类型参数(type parameter)的方法,那么什么是类型参数呢?
<E> E get(E e);
这个 E 就是类型参数,“<E>” 是在定义 E 这个类型参数。
已经很明显了,泛型就是通过类型参数(type parameter)来定义的,lambda不适应携带类型参数的方法,就是在直言无法支持泛型方法。
为什么呢?我的理解是这样的:
在泛型方法中定义的类型参数(E)只能在这个泛型方法中使用,脱离了这个泛型方法就再也没办法获取这个类型参数了。 而lambda本身就是为了在方法之外 “定义” 方法,既然已经身置泛型方法之外了,那就无论如何都不可能使用 E 这个类型了,就算再定义一个 E,此 E 非彼 E 也是不能用的。
干嘛非得使用泛型方法里的 E 呢? 你想啊,lambda是在干什么?是在重写函数式接口的唯一抽象方法。 那重写方法有什么条件?参数列表完全一致,返回值一致,方法名一致。 参数列表完全一致,这就要求你lambda必须用我泛型方法里定义的这个 E,而不是你自己定义的什么 T、V、K。哪怕你定义了一个 E,那也不是我泛型方法里的 E,和我的参数列表不一致,就不算重写我的方法。
lambda:你还想要我怎样???毁灭吧!
不过在 玩耍 debug 的过程中倒是发现了另外一件有趣的事情,双冒号表达式是可以支持泛型方法的……
不想了不想了,pass。
接下来是双冒号表达式 “MyClass::instance_method” 用法的使用条件:
一、抽象方法的参数必须比实例方法(instance_method)的参数多一个 二、多出来的这个参数必须是class类型的 三、多出来的这个参数必须在参数列表的首位(最左侧)。
接下来是我已经期待了两天的时刻:把lambda常见的应用场景收集起来!
话虽如此,但也就找到两条,其中一条算不算常用还很难说…( _ _)ノ|
-
lambda的不常用场景
new Thread(Runnable),创建一个线程,定义线程执行的代码。 JButten_instance.addActionListener(ActionListener),给jbutton上监控,定义监控的执行代码。
-
java.util.stream.Stream
这里有一篇 已经把stream解释得很清晰的文章,就是有点长,好在可以根据目录直接定位到stream那一段。 这里有另一篇 也很详细地讲解了stream的文章(简书),就是有点多,好在也有目录(doge)
三个特征: 一、若不进行终端操作,所有代码都 不执行。 二、用了终端操作之后,这个流就 不能用了(可以保存成集合或数组,从而反复使用,缺点是每次使用都得再封装成流)。 三、流不是集合,对流进行操作不是在对集合进行操作,无论如何都不会改变集合中的元素。
基本数据类型流:IntStream、LongStream、DoubleStream >>> toArray(),返回(拆封为)基本数据类型数组 >>> boxed(),转换为 包装类对象流。 >>> 内置sum()、average()、max()、min() >>> Random_instance.ints(size, origin, bound),返回一个 IntStream 实例,其中size为元素数目,origin为最小值,bound为最大值,且左闭右开。(同样地,可以使用doubles(),longs())。 >>> IntStream.range(start, end),返回一个IntStream实例,其中start为最小值,end为最大值,步长默认为1,左闭右开(DoubleStream没有这个静态方法。rangeClosed()可以包含右边界) ________________________________ 基本数据类型流的collect()无法通过collectors.toList()将它们封装成集合(没有这种重载),因为基本数据类型本来就没办法直接放到集合里。
串行流(sequential())、并行流(parallel())、无序流(unordered()) 这方面有点模糊,可能是我还没有看到描述得很清晰的文档,目前对它们的了解仅限于:一个线程、多个线程、破坏有序集合的顺序。 至于list.stream()究竟到手那种流……可能 是串行流? ________________________________ 对无序流有点在意,因为 javadoc 说对于[2,4,6]形成的集合,无序流模式下计算map(a->a*2)可能给出[12,4,8]这种结果,所以给它排序是很重要的,除非根本不在意它的顺序(这种场景还是蛮多的)。
-
创建(包装为)stream
list.stream(),从集合实例中获取流对象(以下简称 “流”) list.parallelStream(),从集合中获取并行流(另外两种流通过流对象转换)
Arrays.stream(T[]),通过 T 数组构建一个流(有入参为int[]、long[]、double[]的重载方法,分别获取IntStream、LongStream、DoubleStream)
Stream.of(T…),用一串元素构成流(类似Arrays.asList())
Stream.Iterate(T, UnaryOperator<T>),迭代计算一个初始值,以每次得到的结果作为元素形成流(需要用 limit() 约束其迭代次数,否则它就是一个无限流,可能会被其他流操作截断为有限流) Stream.generate(supplier),根据被定义的供给者(supplier)获取数个元素,形成流(同上,以 limit() 约束)
-
流操作(中间操作)
limit(long),仅保留流中前n个元素(如果是无序流,因为流是无序的,所以不知道究竟保留了哪些元素) filter(predicate),筛选元素(保留使predicate.test()返回true的)。 distinct(),去重。 skip(long),跳过前n个元素。 ________________________________ 这该死的 sql 既视感(笑死
map(Function),按照Function的 “定义” 计算每一个元素,其结果映射为新的流(能用来获取对象属性,简直神迹) mapToXXX(ToXXXFunction),映射为 基本数据流(XXX为:Int、Long、Double)。 flatMap(Function),将每个元素转换为流,最后合并成一个大流(同样,有int、long、double适用的方法)。 ________________________________ 不能和下面的 reduce()放到一起还蛮可惜的,这回是hadoop既视感(笑死,我大数据还没入门
sorted(),按照默认规则排序 sorted(Comparator),传入一个比较器(函数式接口)
-
终端操作(用了就会把流 消耗掉 的操作)
reduce(T, BinaryOperator),根据二元算子(BinaryOperator)的 “定义” 进行迭代计算,得到一个 T 类型的返回值(参数 T 参与第一次迭代)。 reduce(BinaryOperator),流中第一个元素与第二个元素进行第一次迭代,直至得到最终结果。 ________________________________ 迭代:每次计算的结果作为下一次计算的参数之一。 此方法不会影响流中元素,仅返回计算结果。
XXXMatch(predicate),对每个元素进行测试(XXX:all,要求全部为true;any:任意一个为true;none,不能有true) findXXX(),获取一个元素(XXX:First,流中第一个;Any,任意一个)
max(Comparator),传入比较器,返回最大值 min(Comparator),传入比较器,返回最小值
count(),返回元素总数(类似数组的 length,集合的 size()) foreach(Consumer),对流中每个元素做些什么 ,但是禁止色色,另一件神器。 collect(Collector),返回(拆封成)一个集合(一般Collectors的方法获取实参,Collectors 不是 Collector的实现类) toArray(IntFunction<A[]>),返回(拆封成)一个数组(传入 Integer[]::new 这种形式即可,有亿点复杂,换成 lambda 应该是这样的 a->new Integer[a],a 代表流的大小
-
Collectors(很多方法没放到这里)
toList(),让collect()返回List集合。 toSet(),让collect()返回Set集合。
我是真没想到一写就写了一下午,关键是文章还没看完……
此处 并没有 把Stream的所有方法枚举 把别人文章上的方法都搬过来了倒是真的。
后记
有种整个下午都写跑题了的感觉,不过学了stream发现这东西是真好用。
遗留:
- 收集日常可以用到lambda的方法(怨气……
- 了解spring事务相关的部分(怨气*2……
|