小牢骚


前言

4.2去参加了暨南大学的区块链应用方向复试,结果……没过。平时我可是背了无数理论、看了不少论文,还拿了机器学习和分布式的证书,到了实践和口语环节瞬间崩盘。于是,决定写点心得,也给自己找找改进方向。


究竟哪儿出问题了?

  1. 理论打怪太久,实战经验挂了

    • 复试前,我天天啃共识算法、安全协议,撸论文撸到眼花,却没动手在本地跑过一遍智能合约。
    • 面试官让我现场演示简单的合约部署,我只会讲概念,配置环境时各种报错,最后连 gas fee 都忘记填。
  2. 英语面试像背单词比赛

    • 全英语环节,我一直在纠结“哪个单词更精准”,结果卡壳太多,句子断断续续根本没逻辑。
    • 被问到 “How does Merkle Tree guarantee data integrity?” 我就像在背单词:hash… branch… verify… 讲到一半就哑火,根本没说出“先构建树,再对比根哈希验证数据”这套流程。

我学到了啥?

  • 理论要快+实践要跟上
    理论是框架,实践才是检验。下次先写个小 demo,再去背算法细节。
  • 回答有套路更安心
    用“先说结论 → 分点细说 → 最后回顾”这种总—分—总结构,一开口就有方向,不怕卡壳。
  • 英语口语先求流畅
    术语要会,但更要练“从 A 到 B”的连贯性。可以准备几套模板句型,比如 “First,… Then,… Finally,…”,随手套用。

接下来打算怎么补课?

我想做到… 我要怎么做 截止时间
实战演练更到位 - 每周搞一个小项目(供应链溯源/简单投票合约)
- 把流程写成文档+录个演示视频
1 个月内完成 2 个
说话更条理 - 学 STAR/总—分—总 模板
- 每天选 3 个技术问题,大声练习并录音回听
2 周内打卡 30 次
英语更顺溜 - 每天用英语复述技术点(限时 2 分钟)
- 找同伴或外教做 Mock Interview
1 个月内完成 20 次
工具环境熟练 - 搭建 Ganache/Hyperledger 本地链
- 练习用 Remix/Truffle 调试合约
2 周内搞定

小结

复试挂了虽然难受,但也算给自己敲了警钟:不要只盯理论,实践 + 流畅表达同等重要。接下来提速实战、理顺思路、练好口语, 虽然不一定有下次
了但是也是一份教训吧

GitTalk 403 问题

GitTalk 403 Problem Solved

hexo icarus 的 blog comment 出现了问题,bing 过后发现是gitalk 的 cors proxy 出了问题导致直接访问会403

也查了一下 Zibri 的 cors worker 架设了一个,完美解决跨域时403的问题。

https://github.com/Zibri/cloudflare-cors-anywhere

steps:

1.Build your own worker in CloudFlare or AnywhereElse.

login and create your own worker.

随便命名

https://workers.cloudflare.com

step1

2.Copy And Paste script to your workers script from Zibri’ s repo.

https://github.com/Zibri/cloudflare-cors-anywhere

index.js

大概看懂这个脚本,用作补充headers的,然后用search的第一个字符串参数作为代理目标网站

可以直接postman GET测试一下:yourWorkerHost+.dev/?https://github.com/login/oauth/access_token

如果有正常返回那完全ok

save and deploy

保存

3.这样这个proxy就做好了,用在你该用的地方

4.记得在真正使用的地方测试是否成功

Comparing ArrayList Vector

首先看这两类都实现List接口,而List接口一共有三个实现类,分别是ArrayList、Vector和LinkedList。List用于存放多个元素,能够维护元素的次序,并且允许元素的重复。3个具体实现类的相关区别如下:

ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。
Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。
LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,他还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。
查看Java源代码,发现当数组的大小不够的时候,需要重新建立数组,然后将元素拷贝到新的数组内,ArrayList和Vector的扩展数组的大小不同。

关于ArrayList和Vector区别如下:

ArrayList在内存不够时默认是扩展50% + 1个,Vector是默认扩展1倍。
Vector提供indexOf(obj, start)接口,ArrayList没有。
Vector属于线程安全级别的,但是大多数情况下不使用Vector,因为线程安全需要更大的系统开销。

arraylist:

1
2
3
4
5
6
7
8
9
10
11
12
13
public boolean add(E e) {      ensureCapacity(size + 1);  // 增加元素,判断是否能够容纳。不能的话就要新建数组     
elementData[size++] = e; return true; }  public void
ensureCapacity(int minCapacity) { modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) { Object oldData[] = elementData; // 此行没看出来用处,不知道开发者出于什么考虑
int newCapacity = (oldCapacity * 3)/2 + 1; // 增加新的数组的大小
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to
size, so this is a win:
elementData =
Arrays.copyOf(elementData,
newCapacity); } }

mybatis 学习笔记

MyBatis

概念:

框架是一个半自动映射的框架。这里所谓的“半自动”是相对于 Hibernate 框架全表映射而言的,MyBatis 框架需要手动匹配提供 POJO、SQL 和映射关系,而 Hibernate 框架只需提供 POJO 和映射关系即可。

比较:

相比Hibernate ,使用 MyBatis 框架手动编写 SQL 要比使用 Hibernate 框架的工作量大,

但 MyBatis 框架可以配置动态 SQL 并优化 SQL、通过配置决定 SQL 的映射规则,以及支持存储过程等。对于一些复杂的和需要优化性能的项目来说,使用 MyBatis框架更加合适。
就目前的情形来说,SSM比SSH更加合适
java目前就业形势。(截止至笔记时刻)
学会啦mybatis hibernate就非常的简单和容易理解啦

mybatis基于JDBC并整合了其功能。
所以可以说完全是替代JDBC的上位应用。
传统JDBC使用内部DriverManager的getConnection() 实现与数据库接通的功能。
通过PreparedStatement pstmt = connection.prepareStatement(sql);放入sql语句,并还需要使用executeQuery()来运行并接到list类型的结果。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/learn_mybatis? characterEncoding=utf-8";
String userName = "root";
String password = "mysql";
Connection connection = DriverManager.getConnection(url, userName, password);
String sql = "select * from user where id = ?";
PreparedStatement pstmt = connection.prepareStatement(sql); pstmt.setString(1, "1");
ResultSet resultSet = pstmt.executeQuery();
List<User> userList = new ArrayList<User> (10);
while (resultSet.next())
{ int id = resultSet.getInt("id");
String name = resultSet.getString("username");
User user = new User();
user.setId(id);
user.setUsername(name);
userList.add(user);
}
System.out.println(userList); }
1
2


conclusion:
1。JDBC内的使用方法会照成硬编码,如果需要改动sql语句和预留statement会需要同时改动结果集。
2.JDBC每次执行都需要调用一次connection来创建链接,多次执行sql会造成资源浪费的情况。

mybatis 配置文件 config.xml:
注释读懂就够了,平时就用这么多配置而已。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

<!--加载外部的properties文件-->
<properties resource="jdbc.properties"></properties>

<!--给实体类的全限定类名给别名-->
<typeAliases>
<!--批量起别名:该包下所有的类的本身的类名:别名还不区分大小写,这里不使用起别名,只是使用package指定其搜索Javabean(不完全等于pojo)的包路径-->
<package name="com.zhengyao.mybatis.pojo"/>
</typeAliases>

<!--environments:运行环境-->
<environments default="development">
<environment id="development">
<!--当前事务交由JDBC进行管理-->
<transactionManager type="JDBC"></transactionManager>
<!--当前使用mybatis提供的连接池-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>

<!--引入映射配置文件-->
<mappers>
<mapper resource="UserMapper.xml"></mapper>
<mapper resource="OrderMapper.xml"></mapper>
</mappers>

</configuration>

这里的jdbc.properties 是额外编写的,指定好写下:

1
2
3
4
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/learn_mybatis?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=lansheng1

配置如上,一般Driver要和数据库版本与maven包的依赖配置版本一致或者兼容。
mappers映射集合内写上各个映射器文件的所在地,在resource内可以直接使用名字调用。

提及一下:一般情况下会有单纯POJO(plain old java object)类来预留与数据库表字段对应的对象与对应pojo映射sql方法的DAO(数据访问对象)接口类

重要部分

映射器文件内部写的是mybatis里的最重要的部分,sql语句的标签和组件。就是会执行的sql语句储存的地方。
使用#{field}可以传入pojo类内对应的field值。
由于mybatis拥有 where、trim foreach等普通编写无法简单做到的方便标签语句,还有
resultMap、association、collection等可以接受多个返回实体的组件来映射到pojo对应类和属性中。

resultMap

是最强大的反射结果集合组件,可以对应多个反射实体,内部可以放置result、association、collection等组件来体现结果集的类型与关系。
实例: 结果集合是一个双实体、某实体结果为集合的集合时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<select id="userToOrders" resultMap="userToOrdersMap">
<!--根据用户id获取用户信息-->
select * from user where id=#{id}
</select>

<resultMap id="userToOrdersMap" type="com.zhengyao.mybatis.pojo.User">
<result column="id" property="id"></result>
<!--构建User的Order多实体查询、实体 orderlist,对比列:id,链接语句:selectOrdersByUser-->
<collection property="orderList" column="id" select="com.zhengyao.mybatis.dao.IOrderDao.selectOrdersByUser">
</collection>

</resultMap>
<--另一个OrderMappers内:-->
<select id="selectOrdersByUser" resultType="com.zhengyao.mybatis.pojo.Order">
select * from orders where uid=#{id}
</select>

resultType 只能传入单一的实体类型, 使用result在resultMap内可以直接提出单一的结果内的字段值使用(property)保留/定义至结果集
而其他类型会在比较双列的同时把比较列(column)只保留一列,即原字段会被剔除。

其他内容我觉得导师总结的比我自己领悟的还多,这里就cp了。

Mybatis学习笔记

JDBC问题分析

传统JDBC代码;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) throws ClassNotFoundException, SQLException {

Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/learn_mybatis?characterEncoding=utf-8";
String userName = "root";
String password = "mysql";
Connection connection = DriverManager.getConnection(url, userName, password);
String sql = "select * from user where id = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, "1");
ResultSet resultSet = pstmt.executeQuery();
List<User> userList = new ArrayList<User> (10);
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("username");
User user = new User();
user.setId(id);
user.setUsername(name);
userList.add(user);
}
System.out.println(userList);
}

使用JDBC存在的问题分析:

  • 数据库连接创建,释放频繁造成系统资源浪费,影响系统性能,连接数据库存在重复代码,导致重复编码
  • sql语句在代码中存在硬编码,导致代码不易维护,实际应用中sql变化的可能性较大,sql改动需要改动java代码
  • 使用PreparedStatement也存在硬编码的问题,导致代码不易维护
  • 对结果集的解析也存在硬编码的问题,sql变化也会导致解析代码变化,系统不容易维护,如果每次返回成pojo对象解析比较方便

JDBC问题解决方案

解决方案:

  • 使用连接池进行连接数据库,并把连接数据库的硬编码配置写入配置文件
  • sql语句也使用配置文件进行配置
  • 使用反射或者内省自动对结果集进行返回

自定义框架设计

使用端:

  • 提供核心配置sqlMapConfig.xml文件,配置数据源信息以及引入sql配置文件
  • 提供所有的Sql配置文件 mapper.xml文件

框架端:

image-20200426112403919

Mybatis框架的基础介绍

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

Mybatis官网

configuration(配置)

properties属性

mybatis的配置文件可以引入外部的properties文件来进行赋值

1
2
3
4
5
6
7
<properties resource = ""></properties>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>

类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:

1
2
3
4
5
6
7
8
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

1
2
3
<typeAliases>
<package name="domain.blog"/>
</typeAliases>

每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子:

1
2
3
4
@Alias("author")
public class Author {
...
}

而Mybatis也默认内置了一些别名,用于用户便利操作

别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

环境配置(environments)

image-20200426142603077

其中事务管理器分两种:

  • JDBC:使用JDBC的提交和回滚设置,依赖于从数据源得到的连接来管理事务作用域

  • MANGAGED:这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为

    如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

数据源类型:

  • UNPOOLED: 这个数据源的实现会每次请求时打开和关闭连接
  • POOLED:这个数据源的实现会每次请求时打开和关闭连接
  • JNDI:这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用

映射器(mappers)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>

这些配置会告诉 MyBatis 去哪里找映射文件

动态Sql

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

常用的动态sql有如下几种:

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach
    `

    if 类型的动态sql

1
2
3
4
5
6
7
8
9
10
11
<select id="findByCondition" parameterType="user" resultType="user">
select * from User
<where>
<if test="id!=0">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
</where>
</select>

choose、when、otherwise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>

trim、where、set

使用where标签可以动态的舍去第一个and

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

1
2
3
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>

foreach

对集合进行遍历

其中item就是循环的每个参数,index就是索引值,collection就是集合的参数,open就是传入循环开始的参数,close就是循环结束的参数,separator就是使用对应标识符来切割

1
2
3
4
5
6
7
8
9
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>

抽取通用sql

1
2
3
4
5
6
7
8
9
10
11
12
抽取通用的sql出来
<sql id="Base_Column_List">
id, corp_id, org_id, bill_code, borrow_begin_date, borrow_end_date,
reason, department, borrower, contact, apply_date, applyer_id,
status, return_date, comments, create_time,busi_type,borrow_type,k2_id,k2_form_url,k2_status, ts
</sql>
使用include进行引用通用的sql
<select id="queryRecBorrowList" resultMap="BaseResultMap" parameterType="hashmap">
select
<include refid="Base_Column_List" />
from rec_borrow
</select>

作业

完成订单对用户的一对一查询、用户对订单的多对一查询

1、用户与订单是一对多的关系,订单与用户是一对一的关系.通过订单查询出关联的用户

2、用户与订单是一对多的关系,订单与用户是一对一的关系.通过订单查询出关联的用户

声明一个用户订单实体

1
2
3
4
5
6
7
8
public class Order {

private Integer id;
private String orderTime;
private Double total;
// 表明该订单属于哪个用户
private User user;
}

一个用户实体

1
2
3
4
5
6
public class User implements Serializable {
private Integer id;
private String username;
//表示用户关联的订单
private List<Order> orderList = new ArrayList<>();
}

resultMap是Mybatis最强大的元素,它可以将查询到的复杂数据(比如查询到几个表中数据)映射到一个结果集当中。

resultMap包含的元素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--column不做限制,可以为任意表的字段,而property须为type 定义的pojo属性-->
<resultMap id="唯一的标识" type="映射的pojo对象">
<id column="表的主键字段,或者可以为查询语句中的别名字段" jdbcType="字段类型" property="映射pojo对象的主键属性" />
<result column="表的一个字段(可以为任意表的一个字段)" jdbcType="字段类型" property="映射到pojo对象的一个属性(须为type定义的pojo对象中的一个属性)"/>
<association property="pojo的一个对象属性" javaType="pojo关联的pojo对象">
<id column="关联pojo对象对应表的主键字段" jdbcType="字段类型" property="关联pojo对象的主席属性"/>
<result column="任意表的字段" jdbcType="字段类型" property="关联pojo对象的属性"/>
</association>
<!-- 集合中的property须为oftype定义的pojo对象的属性-->
<collection property="pojo的集合属性" ofType="集合中的pojo对象">
<id column="集合中pojo对象对应的表的主键字段" jdbcType="字段类型" property="集合中pojo对象的主键属性" />
<result column="可以为任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" />
</collection>
</resultMap>

如果collection标签是使用嵌套查询,格式如下:

1
2
<collection column="传递给嵌套查询语句的字段参数" property="pojo对象中集合属性" ofType="集合属性中的pojo对象" select="嵌套的查询语句" > 
</collection>

注意:标签中的column:要传递给select查询语句的参数,如果传递多个参数,格式为column= ” {参数名1=表字段1,参数名2=表字段2} ;

以下以实例介绍resultMap的用法:

一、简单需求:一个商品的结果映射;

1、创建商品pojo对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public class TShopSku  {
/**
* 主键ID
*/
private Long id;

/**
* 商品名
*/
private String skuName;

/**
* 分类ID
*/
private Long categoryId;


/**
* 主键ID
* @return ID
*/
public Long getId() {
return id;
}

/**
* 主键ID,
* @param id
*/
public void setId(Long id) {
this.id = id;
}

/**
* 商品名
* @return SKU_NAME 商品名
*/
public String getSkuName() {
return skuName;
}

/**
* 商品名
* @param skuName 商品名
*/
public void setSkuName(String skuName) {
this.skuName = skuName == null ? null : skuName.trim();
}

/**
* 分类ID
* @return CATEGORY_ID 分类ID
*/
public Long getCategoryId() {
return categoryId;
}

/**
* 分类ID
* @param categoryId 分类ID
*/
public void setCategoryId(Long categoryId) {
this.categoryId = categoryId;
}

对应的resultMap:

1
2
3
4
5
<resultMap id="BaseResultMap" type="com.meikai.shop.entity.TShopSku">
<id column="ID" jdbcType="BIGINT" property="id" />
<result column="SKU_NAME" jdbcType="VARCHAR" property="skuName" />
<result column="CATEGORY_ID" jdbcType="BIGINT" property="categoryId" />
</resultMap>

二、商品pojo类添加属性集合:

一个商品会有一些属性,现在需要将查询出的商品属性添加到商品对象中,首先需要在原商品pojo类的基础上中添加属性的集合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 属性集合
*/
private List<TShopAttribute> attributes;

/**
* 获得属性集合
*/
public List<TShopAttribute> getAttributes() {
return attributes;
}

/**
* 设置属性集合
* @param attributes
*/
public void setAttributes(List<TShopAttribute> attributes) {
this.attributes = attributes;
}

将Collection标签添加到resultMap中,这里有两种方式:

1、嵌套结果:

对应的resultMap:

1
2
3
4
5
6
7
8
9
<resultMap id="BasePlusResultMap" type="com.meikai.shop.entity.TShopSku">
<id column="ID" jdbcType="BIGINT" property="id" />
<result column="SKU_NAME" jdbcType="VARCHAR" property="skuName" />
<result column="CATEGORY_ID" jdbcType="BIGINT" property="categoryId" />
<collection property="attributes" ofType="com.meikai.shop.entity.TShopAttribute" >
<id column="AttributeID" jdbcType="BIGINT" property="id" />
<result column="attribute_NAME" jdbcType="VARCHAR" property="attributeName" />
</collection>
</resultMap>

查询语句:

1
2
3
4
5
<select id="getById"  resultMap="basePlusResultMap">
select s.ID,s.SKU_NAME,s.CATEGORY_ID,a.ID,a.ATTRIBUTE_NAME
from t_shop_sku s,t_shop_attribute a
where s.ID =a.SKU_ID and s.ID = #{id,jdbcType =BIGINT};
</select>

2、关联的嵌套查询(在collection中添加select属性):

商品结果集映射resultMap:

1
2
3
4
5
6
7
<resultMap id="BasePlusResultMap" type="com.meikai.shop.entity.TShopSku">
<id column="ID" jdbcType="BIGINT" property="id" />
<result column="SKU_NAME" jdbcType="VARCHAR" property="skuName" />
<result column="CATEGORY_ID" jdbcType="BIGINT" property="categoryId" />
<collection column="{skuId=ID}" property="attributes" ofType="com.meikai.shop.entity.TShopAttribute" select="getAttribute" >
</collection>
</resultMap>

复制代码
collection的select会执行下面的查询属性语句:

1
2
3
4
5
<select id="getAttribute"  resultMap="AttributeResultMap">
select a.ID,s.ATTRIBUTE_NAME
from t_shop_attribute a
where a.ID = #{skuId,jdbcType =BIGINT};
</select>

属性结果集映射:

1
2
3
4
<resultMap id="AttributeResultMap" type="com.meikai.shop.entity.TShopAttribute">
<id column="ID" jdbcType="BIGINT" property="id" />
<result column="ATTRIBUTE_NAME" jdbcType="VARCHAR" property="attributeName" />
</resultMap>

BasePlusResultMap包含了属性查询语句的Collection

所以通过下面的查询商品语句就可获得商品以及其包含的属性集合:

<select id="getById"  resultMap="BasePlusResultMap">
    select s.ID,s.SKU_NAME,s.CATEGORY_ID
    from t_shop_sku s
    where  s.ID = #{id,jdbcType =BIGINT};
</select>

代理 、动态代理 笔记

代理 、动态代理

代理概念:

中介,即为单或多个服务类对象提供一种服务以控制其他类对该对象的访问。

在某些情况下,一个对象不适合或不能直接引用另一个对象,代理对象可以在请求类和目标类之间起到中介的作用。
代理模式:
代理模式是一种设计模式

jdk: reflect

代理的作用:

1。功能增强
在执行目标类的功能的同时可以进行
2。访问控制
控制访问类是否能访问目标类。

代理:
接口: SellingFigure.interface
接口实现类:厂商 .java implement SellingFigure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270


//静态代理类:
package StaticProxy;

public class bilibiliProxy implements SellFigure {
private SellFigure s;

public bilibiliProxy(SellFigure s) {
this.s = s;
}

public int SellingFigure(String buyerName) {
int oprice=s.SellingFigure(buyerName);
double sprice= oprice+(oprice*0.25);
System.out.println("您通过bilibili购买:");
return (int) sprice;

}
}

//接口类(销售行为)
package StaticProxy;

public interface SellFigure {
public int SellingFigure(String proxyname);
}


//接口实现类(厂家)
package StaticProxy;

public class SellingImpl implements SellFigure {
public SellingImpl() {
}

public int SellingFigure(String proxyname) {
System.out.println("此为目标实现类的目标方法。 static way");
int price;
if(proxyname.equals("bilibili"))
price=980;
else if(proxyname.equals("acfun"))
price=680;
else
price=900;
return price;
}
}

//staticReflect.properties

Typo=bilibili
methodName=SellingFigure
implName=SellingImpl
name=SellFigure


//测试类:


package StaticProxy;

import org.junit.Test;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

//not proxy using
public class otakuTest {
@Test
//不使用代理直接获取。
public void test1(){
SellFigure s = new SellingImpl();
System.out.println("死宅1号买入价格:"+ s.SellingFigure("myself") );

}
@Test
//使用反射与配置文件调用bilibiliProxy的方法代理
public void test2() throws Exception{
//1. read properties by classloader and properties.class
ClassLoader clz=otakuTest.class.getClassLoader();
InputStream clzs = clz.getSystemResourceAsStream("staticReflect.properties");
Properties pro = new Properties();
pro.load(clzs);
//2. get property in properties file.
String typo = pro.getProperty("Typo");
String methodname = pro.getProperty("methodName");
String classname ="staticProxy."+typo+"Proxy";
String implename = "staticProxy."+pro.getProperty("implName");
//3. load the .class into RAM for a class object
Class clazz = Class.forName(classname);
Class implclazz = Class.forName(implename);
//4. create the instance and get the method
Object obj = clazz.getConstructor(SellFigure.class).newInstance(implclazz.newInstance());
Method met = clazz.getDeclaredMethod(methodname,String.class);
//5. setAccessible to true for the hidden private method and fields.
met.setAccessible(true);
//6. using the method.class 's method invoke to execute the method as a object file.
Object a = met.invoke(obj,typo);

System.out.println(a);


//
// SellFigure s = new bilibiliProxy(new SellingImpl());
// System.out.println("死宅2号买入价格:"+ s.SellingFigure("bilibili") );
}
@Test
//静态代理 使用acfun代理购买。
public void test3(){
SellFigure s = new acfunProxy(new SellingImpl());
System.out.println("死宅3号买入价格:"+ s.SellingFigure("acfun") );
}



}



//动态代理:
1.jdk
//代理辅助类
package Dynamicproxy.jdks;


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DbilibiliProxy implements InvocationHandler {


public DbilibiliProxy(Object target) {
this.target = target;
}

private Object target;

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("通过辅助代理bilibili");
//代理执行方法
Object res = method.invoke(target,args);
//功能增强
if(res!=null){
Integer price=(Integer) res;
price=(int)(price*1.25);
res=price;
}
return res;

}
}
//实现类与接口同静态。

//客户测试类
package Dynamicproxy.jdks;

import java.io.InputStream;
import java.util.Properties;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

//using dynamic proxy
public class otaku {
public static void main(String[] args) throws Exception {
//1. read properties by classloader and properties.class
ClassLoader clz = otaku.class.getClassLoader();
InputStream clzs = clz.getSystemResourceAsStream("staticReflect.properties");
Properties pro = new Properties();
pro.load(clzs);
//2. get property in properties file.
String typo = pro.getProperty("Typo");
String interfaceName = pro.getProperty("name");
String proxyName = "dynamicproxy.jdks." + "D" + typo + "Proxy";
interfaceName = "dynamicproxy.jdks." + interfaceName;
String implename = "dynamicproxy.jdks." + pro.getProperty("implName");
InvocationHandler proxyhandler = (InvocationHandler) Class.forName(proxyName).getConstructor(Object.class).newInstance(Class.forName(implename).newInstance());
SellFigure dynamicWay = (SellFigure) Proxy.newProxyInstance(Class.forName(interfaceName).getClassLoader(), Class.forName(implename).getInterfaces(), proxyhandler);
System.out.println(dynamicWay.SellingFigure(typo));
}
}


2.cglib代理

//代理辅助类:

package Dynamicproxy.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

//代理创建器
public class DynamicCglibProxy implements MethodInterceptor {

private Enhancer enhancer = new Enhancer();
private Object target;

public DynamicCglibProxy(Object obj) {
target = obj;
}
public Object getProxy(){
//设置需要代理的父类
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
//动态创建子类实例
return enhancer.create();
}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("通过动态代理实现");
Object result = method.invoke(target, objects);
System.out.println("由cglib代理出售了!");
int t = (int) ((Integer)result *1.25);
result=t;
return result;
}
}



//被代理类
package Dynamicproxy.cglib;


public class Selling {
int price;
//
// public int SellingFigureTobilibili(String proxyname) {
// price=980;
// return price;
// }

public int SellingFigure(String proxyname) {
System.out.println("此为目标实现类的目标方法。dynamic way ");

if(proxyname.equals("bilibili"))
price=980;
else if(proxyname.equals("acfun"))
price=680;
else
price=900;
return price;
}

// public int SellingFigureToacfun(String proxyname) {
// price=680;
// return price;
// }

}

//测试类

package Dynamicproxy.cglib;

//客户端类
public class otaku {
public static void main(String[] args) {
Selling Sellfigure = new Selling();
DynamicCglibProxy dynamicCglibProxy = new DynamicCglibProxy(Sellfigure);
Selling sellProxy = (Selling) dynamicCglibProxy.getProxy();
System.out.println(sellProxy.SellingFigure("bilibili"));;
}
}

动态代理分两类
jdk自带的动态代理reflect Proxy
和cglib第三方类,分别的实现原理是反射、ASM(字节码编译)。
反射速率会差上一大截。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//测试结果
//创建代理的速度
Create JDK Proxy: 13 ms
Create CGLIB Proxy: 217 ms //较慢
Create JAVAASSIST Proxy: 99 ms
Create JAVAASSIST Bytecode Proxy: 168 ms //较慢
Create ASM Proxy: 3 ms //最快

================
Run JDK Proxy: 2224 ms, 634,022 t/s //很慢,jdk生成的字节码考虑了太多的条件,所以字节码非常多,大多都是用不到的
Run CGLIB Proxy: 1123 ms, 1,255,623 t/s //较慢,也是因为字节码稍微多了点
Run JAVAASSIST Proxy: 3212 ms, 438,999 t/s //非常慢,完全不建议使用javassist的代理类来实现动态代理
Run JAVAASSIST Bytecode Proxy: 206 ms, 6,844,977 t/s //和asm差不多的执行速度,因为他们都是只生成简单纯粹的执行字节码,非常少(所以直接用javassist生成代理类的方式最值得推荐[从速度上讲])
Run ASM Bytecode Proxy: 209 ms, 6,746,724 t/s //asm什么都是最快的,毕竟是只和最底层打交道

java反射 笔记

基础笔记1: 08/26 2020

java基础 反射

plus:反射我这里是根据b站的狂神和黑马的课程学习。

Java反射机制是在运行状态中,
对于任意一个类,都能知道这个类的所以属性和方法;
对于任何一个对象,都能够调用它的任何一个方法和属性;
这样动态获取新的以及动态调用对象方法的功能就叫做反射。

特性

image
包含内容:通过class类对象来set和get内部的方法,虽然使用reflect会使隐秘性和安全性降低,但调用到改写都更为灵活,同时也是动态代理的基石。

java反射有专名包 java.lang.reflect 但是内部方法主要是用在动态代理
java 反射主要使用的是class类的: method constructor fields interface 等内容来达到反射出想要结果的效果。

获取类对象(内存中的class)的四种方法:

§1 字节码–>class对象:

Class.forName(“完整包路径的类名”)

§2 class.java 某类

可以直接调用属性即可
xxx.class

§3 处于runtime时 instance 获得

instanceName.getClass();

§4 某些特殊的系统类

such as Integer

use Integer.Type

获得后的类使用方法:

1.Field(定义的类对象内的属性)

classname.getFields()
get all(but only public announced) in a list
classname.getField(“fieldname”)
get specific one (but only public announced,not public may return null) in a list
classname.getDeclareFields()
get all(include private announced) in a list
classname.getDeclareField(“fieldname”)
get specific(include private announced) in a list

fields可以这样用,用getset来设置某实例内field对应方法的值:
下面的方法和构造器也是同样的getDeclarexxx()

for field in fields:
field.get(instance)
field.set(instance,values)

2.Constructor 构造器

Constructor is a method with special mechanism.
So it standalone to get set
大部分时间上来说,constructor是拿来创建有重写构造方法的类实例的。不会像普通方法一样invoke

classname.getConstructor(someType.class)
:如果不填参数对应类型的类,会默认调用空参数构造方法。
填哪种参类就对应哪个构造方法。

填String.class 就代表着传入参数是string的构造方法。

constructor.newInstance(args*)
:对获得的构造器传入参数后创建对应实例。
如果不传参,那和使用classobj.newInstance()没有区别

3.method 方法类 常用

classname.getMethod(“methodname”)
获取方法可以直接使用类对象来直接获取。

执行函数
method.invoke(instance,args)
想执行该函数必须拥有实例,
args
是函数所需的参数,也可以不存在

instance一般通过newInstance创建

注意:获取私有统统需要xxx.setAccessible(true)

##附加 ClassLoader

ClassLoader在动态代理和这里都会用到,用来读取类/输入流进入内存以供使用。
Class.getClassLoder() 获得指定类的类加载器

classloader 一般使用方法
classloader.getSystemResource()
classloader.getSystemResourceAsStream(“filename which in Resources “);
然后使用properties 类的load 来读取流,用getproperty就可以获取对应的值了。

Spring MVC 概念笔记

Spring MVC:

Spring mvc 和servlet的模式流程:

1.用户发请求
2.Servlet接受请求数据,调用对应的业务逻辑方法
3.业务处理完毕,返回更新后丶数据给servlet
4.servlet转向JSP,由JSP来渲染页面
5.响应给前端更新后的页面

mvc概念图
注:
Spring MVC 框架并不关心使用的视图技术,唯一需要的只是对应的映射值。

职责分析:

Controller:
(Servlet:调用层)
1.取得表单数据
2.调用业务逻辑
3.转向定向页面
Model:
(service and DAO 层)
1.业务逻辑
2.保存数据的状态
View:
前端页面层,mvc教程里默认是jsp。
java:越大的项目相比其他语言越好维护

补充概念:

概念:

VO(View Object): 视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。

DTO(Data Transfer Object): 数据传输对象,这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。

DO(Domain Object): 领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。

PO(Persistent Object): 持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。

模型:

对象在三层架构应用中的位置





  用户发出请求(可能是填写表单),表单的数据在展示层被匹配为VO。


  展示层把VO转换为服务层对应方法所要求的DTO,传送给服务层。


  服务层首先根据DTO的数据构造(或重建)一个DO,调用DO的业务方法完成具体业务。


  服务层把DO转换为持久层对应的PO(可以使用ORM工具,也可以不用),调用持久层的持久化方法,把PO传递给它,完成持久化操作。


  对于一个逆向操作,如读取数据,也是用类似的方式转换和传递,略。

具体配置:
1)创建 Web 应用并引入 JAR 包

在 MyEclipse 中创建一个名为 springMVCDemo01 的 Web 应用,在 springMVCDemo01 的 lib 目录中添加 Spring MVC 程序所需要的 JAR 包,包括 Spring 的 4 个核心 JAR 包、commons-logging 的 JAR 包以及两个与 Web 相关的 JAR 包(spring-web-3.2.13.RELEASE.jar 和 spring-webmvc-3.2.13. RELEASE.jar)。

另外,在 Spring MVC 应用中使用注解时不要忘记添加 spring-aop-3.2.13.RELEASE.jar 包,添加后的 JAR 包如图 1 所示。

添加后的JAR包
图 1 添加后的 JAR 包
2)在 web.xml 文件中部署 DispatcherServlet

在开发 Spring MVC 应用时需要在 web.xml 中部署 DispatcherServlet,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>springMVC</display-name>
<!-- 部署 DispatcherServlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 表示容器再启动时立即加载servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- 处理所有URL -->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

上述 DispatcherServlet 的 servlet 对象 springmvc 初始化时将在应用程序的 WEB-INF 目录下查找一个配置文件,该配置文件的命名规则是“servletName-servlet.xml”,例如 springmvc-servlet.xml。

另外,也可以将 Spring MVC 配置文件存放在应用程序目录中的任何地方,但需要使用 servlet 的 init-param 元素加载配置文件。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 部署 DispatcherServlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 表示容器再启动时立即加载servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

3)创建 Web 应用首页

在 springMVCDemo01 应用的 WebContent 目录下有个应用首页 index.jsp。index.jsp 的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
未注册的用户,请<a href="${pageContext.request.contextPath }/register"> 注册</a>
<br/>
已注册的用户,去<a href="${pageContext.request.contextPath }/login"> 登录</a>
</body>
</html>

4)创建 Controller 类

在 src 目录下创建 controller 包,并在该包中创建 RegisterController 和 LoginController 两个传统风格的控制器类(实现了 Controller 接口),分别处理首页中“注册”和“登录”超链接的请求。

RegisterController 的具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class LoginController implements Controller {
public ModelAndView handleRequest(HttpServletRequest arg0,
HttpServletResponse arg1) throws Exception {
return new ModelAndView("/WEB-INF/jsp/register.jsp");
}
}
LoginController 的具体代码如下:
package controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class RegisterController implements Controller {
public ModelAndView handleRequest(HttpServletRequest arg0,
HttpServletResponse arg1) throws Exception {
return new ModelAndView("/WEB-INF/jsp/login.jsp");
}
}

5)创建 Spring MVC 配置文件并配置 Controller 映射信息

传统风格的控制器定义之后,需要在 Spring MVC 配置文件中部署它们(学习基于注解的控制器后不再需要部署控制器)。在 WEB-INF 目录下创建名为 springmvc-servlet.xml 的配置文件,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- LoginController控制器类,映射到"/login" -->
<bean name="/login" class="controller.LoginController"/>
<!-- LoginController控制器类,映射到"/register" -->
<bean name="/register" class="controller.RegisterController"/>
</beans>

6)应用的其他页面

RegisterController 控制器处理成功后跳转到 /WEB-INF/jsp 下的 register.jsp 视图,LoginController 控制器处理成功后跳转到 /WEB-INF/jsp 下的 login.jsp 视图,因此在应用的 /WEB-INF/jsp 目录下应有 register.jsp 和 login.jsp 页面,这两个 JSP 页面的代码在此省略。
只需要有对应的值和name可以调出就可以

Spring IOC 笔记

spring原理学习笔记-1 8/27 2020

IOC:Inverse Of Control

控制反转

读作 “反转控制”,更好理解,不是什么技术,而是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理。
正控:若要使用某个对象,需要自己去负责对象的创建
反控:若要使用某个对象,只需要从 Spring 容器中获取需要使用的对象,不关心对象的创建过程,也就是把创建对象的控制权反转给了Spring框架

好莱坞法则: Don’t call me ,I’ll call you

简而言之:
spring 的IOC就是它负责了对象的创建的时机,你只需要在配置或者注解中将创建对象时的内容,其创建和调用都由spring 托管。
spring内由IOCcontainer容器实现。

ApplicationContext 是 BeanFactory 的子接口之一,换句话说:BeanFactory 是 Spring IoC 容器所定义的最底层接口,而 ApplicationContext 是其最高级接口之一,并对 BeanFactory 功能做了许多的扩展,所以在绝大部分的工作场景下,都会使用 ApplicationContext 作为 Spring IoC 容器。

【getBean】 对应了多个方法来获取配置给 Spring IoC 容器的 Bean。
① 按照类型拿 bean:
bean = (Bean) factory.getBean(Bean.class);
注意: 要求在 Spring 中只配置了一个这种类型的实例,否则报错。(如果有多个那 Spring 就懵了,不知道该获取哪一个)
② 按照 bean 的名字拿 bean:
bean = (Bean) factory.getBean(“beanName”);
注意: 这种方法不太安全,IDE 不会检查其安全性(关联性)
③ 按照名字和类型拿 bean:(推荐)
bean = (Bean) factory.getBean(“beanName”, Bean.class);
【isSingleton】 用于判断是否单例,如果判断为真,其意思是该 Bean 在容器中是作为一个唯一单例存在的。而【isPrototype】则相反,如果判断为真,意思是当你从容器中获取 Bean,容器就为你生成一个新的实例。
注意: 在默认情况下,【isSingleton】为 ture,而【isPrototype】为 false
关于 type 的匹配,这是一个按 Java 类型匹配的方式
【getAliases】方法是获取别名的方法
这就是 Spring IoC 最底层的设计,所有关于 Spring IoC 的容器将会遵守它所定义的方法。

ApplicationContext

根据 ApplicationContext 的类继承关系图,可以看到 ApplicationContext 接口扩展了许许多多的接口,因此它的功能十分强大,所以在实际应用中常常会使用到的是 ApplicationContext 接口,因为 BeanFactory 的方法和功能较少,而 ApplicationContext 的方法和功能较多。

通过上一篇 IoC 的例子,我们来认识一个 ApplicationContext 的子类——ClassPathXmlApplicationContext。

先在这里提一下三种注入方式:
1.constructor
2.Set
3.API
先在【src】目录下创建一个 【bean.xml】 文件:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 通过 xml 方式装配 bean -->
<bean name="source" class="pojo.Source">
<property name="fruit" value="橙子"/>
<property name="sugar" value="多糖"/>
<property name="size" value="超大杯"/>
</bean>
</beans>

这里定义了一个 bean ,property会用id对应字段set方法注入。value 和 ref的区别在于:property的value直接注入写下的值。ref寻找spring容器中生成的对应对象来注入, 这样 Spring IoC 容器在初始化的时候就能找到它们,然后使用 ClassPathXmlApplicationContext 容器就可以将其初始化:

1
2
3
4
5
6
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Source source = (Source) context.getBean("source", Source.class);

System.out.println(source.getFruit());
System.out.println(source.getSugar());
System.out.println(source.getSize());

这样就会使用 Application 的实现类 ClassPathXmlApplicationContext 去初始化 Spring IoC 容器,然后开发者就可以通过 IoC 容器来获取资源了啦!

关于 Spring Bean 的装配以及一些细节,会在下一篇文章中讲到
ApplicationContext 常见实现类:

1.ClassPathXmlApplicationContext:
读取classpath中的资源

1
2
3
4
5
6
7
8
9
10
11
12
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
2:FileSystemXmlApplicationContext:-
//读取指定路径的资源

ApplicationContext ac = new FileSystemXmlApplicationContext("c:/applicationContext.xml");
//3.XmlWebApplicationContext:
//需要在Web的环境下才可以运行

XmlWebApplicationContext ac = new XmlWebApplicationContext(); // 这时并没有初始化容器
ac.setServletContext(servletContext); // 需要指定ServletContext对象
ac.setConfigLocation("/WEB-INF/applicationContext.xml"); // 指定配置文件路径,开头的斜线表示Web应用的根目录
ac.refresh(); // 初始化容器

BeanFactory 和 ApplicationContext 的区别:

BeanFactory: 是Spring中最底层的接口,只提供了最简单的IoC功能,负责配置,创建和管理bean。
在应用中,一般不使用 BeanFactory,而推荐使用ApplicationContext(应用上下文),原因如下。
ApplicationContext:
1.继承了 BeanFactory,拥有了基本的 IoC 功能;
2.除此之外,ApplicationContext 还提供了以下功能:
① 支持国际化;
② 支持消息机制;
③ 支持统一的资源加载;
④ 支持AOP功能;
Spring IoC 的容器的初始化和依赖注入

虽然 Spring IoC 容器的生成十分的复杂,但是大体了解一下 Spring IoC 初始化的过程还是必要的。这对于理解 Spring 的一系列行为是很有帮助的。

注意: Bean 的定义和初始化在 Spring IoC 容器是两大步骤,它是先定义,然后初始化和依赖注入的。

Bean 的定义分为 3 步:
1.Resource 定位
Spring IoC 容器先根据开发者的配置,进行资源的定位,在 Spring 的开发中,通过 XML 或者注解都是十分常见的方式,定位的内容是由开发者提供的。
2.BeanDefinition 的载入
这个时候只是将 Resource 定位到的信息,保存到 Bean 定义(BeanDefinition)中,此时并不会创建 Bean 的实例
3.BeanDefinition 的注册
这个过程就是将 BeanDefinition 的信息发布到 Spring IoC 容器中
注意:此时仍然没有对应的 Bean 的实例。
做完了以上 3 步,Bean 就在 Spring IoC 容器中被定义了,而没有被初始化,更没有完成依赖注入,也就是没有注入其配置的资源给 Bean,那么它还不能完全使用。

对于初始化和依赖注入,Spring Bean 还有一个配置选项——【lazy-init】,其含义就是是否初始化 Spring Bean。在没有任何配置的情况下,它的默认值为 default,实际值为 false,也就是 Spring IoC 默认会自动初始化 Bean。如果将其设置为 true,那么只有当我们使用 Spring IoC 容器的 getBean 方法获取它时,它才会进行 Bean 的初始化,完成依赖注入。

IoC 是如何实现的

最后我们说说IoC是如何实现的。想象一下如果我们自己来实现这个依赖注入的功能,我们怎么来做? :

读取标注或者配置文件,看看JuiceMaker依赖的是哪个Source,拿到类名
使用反射的API,基于类名实例化对应的对象实例
将对象实例,通过构造函数或者 setter,传递给 JuiceMaker
我们发现其实自己来实现也不是很难,Spring实际也就是这么做的。这么看的话其实IoC就是一个工厂模式的升级版!当然要做一个成熟的IoC框架,还是非常多细致的工作要做,Spring不仅提供了一个已经成为业界标准的Java IoC框架,还提供了更多强大的功能,所以大家就别去造轮子啦!
首先:第一个问题,参与者都有谁? 1)对象
2)IOC/DI容器
3)某个对象的外部资源
第二问题:依赖,谁依赖谁?为什么需要依赖? 依赖嘛,很好理解的,对象依赖于IOC/DI容器,至于为什么要依赖呢?对象需要IOC/DI容器来提供对象需要的外部资源。
第三个问题:注入,谁注入谁?又注入了什么呢? 显而易见是IOC/DI容器注入对象,注入了what呢?肯定注入的是某个需要的东西那就是注入对象所需要的资源,肯定不会注入无关紧要的内容,你说呢?
第四个问题:控制反转,谁控制谁?控制什么?为什么叫反转呢?存在正转吗? 控制反转,控制什么?肯定是IOC/DI容器控制对象,主要是控制对象实例的创建,反转是相对于正向而言的,那么什么算是正向的呢?考虑一下常规情况下的应用程序,如果要在A里面使用C,你会怎么做呢?当然是直接去创建C的对象,也就是说,是在A类中主动去获取所需要的外部资源C,这种情况被称为正向的。那么什么是反向呢?就是A类不再主动去获取C,而是被动等待,等待IoC/DI的容器获取一个C的实例,然后反向的注入到A类中。
第五个问题:控制反转和依赖注入式同一个概念吗? 依赖注入和控制反转是对同一件事情的不同描述,从某个方面讲,就是它们描述的角度不同。依赖注入是从应用程序的角度在描述,可以把依赖注入描述完整点:应用程序依赖容器创建并注入它所需要的外部资源;而控制反转是从容器的角度在描述,描述完整点:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。

4种注入方式
了解了这些基本的概念,弄明白她们之间的联系和区别,能够帮助我们更好的理解,接着小编来重点介绍一下依赖注入,在spring ioc中有四种依赖注入,分别是:

a.接口注入

b.setter方法注入

c.构造方法注入

d.注解方式注入
接着小编对这三种注入方式一一进行讲解,通过demo的讲解,希望能够帮助小伙伴们更好的理解,不足之处还请多多指教。

接口注入

public class ClassA {
private InterfaceB clzB;
public void doSomething() {
Ojbect obj = Class.forName(Config.BImplementation).newInstance();
clzB = (InterfaceB)obj;
clzB.doIt();
}
……
解释一下上述的代码部分,ClassA依赖于InterfaceB的实现,我们如何获得InterfaceB的实现实例呢?传统的方法是在代码中创建 InterfaceB实现类的实例,并将赋予clzB.这样一来,ClassA在编译期即依赖于InterfaceB的实现。为了将调用者与实现者在编译期分离,于是有了上面的代码。我们根据预先在配置文件中设定的实现类的类名(Config.BImplementation),动态加载实现类,并通过InterfaceB强制转型后为ClassA所用,这就是接口注入的一个最原始的雏形。

setter方法注入

setter注入模式在实际开发中有非常广泛的应用,setter方法更加直观,我们来看一下spring的配置文件:


<!-- 使用spring管理对象的创建,还有对象的依赖关系 -->    
<bean id="userDao4Mysql" class="com.tgb.spring.dao.UserDao4MysqlImpl"/>    

<bean id="userDao4Oracle" class="com.tgb.spring.dao.UserDao4OracleImpl"/>    

<bean id="userManager" class="com.tgb.spring.manager.UserManagerImpl">    
      <!-- (1)userManager使用了userDao,Ioc是自动创建相应的UserDao实现,都是由容器管理-->    
      <!-- (2)在UserManager中提供构造函数,让spring将UserDao实现注入(DI)过来 -->    
      <!-- (3)让spring管理我们对象的创建和依赖关系,必须将依赖关系配置到spring的核心配置文件中 -->    

    <property name="userDao" ref="userDao4Oracle"></property>    
</bean>    

接着我们来看一下,setter表示依赖关系的写法

import com.tgb.spring.dao.UserDao;

public class UserManagerImpl implements UserManager{

private UserDao userDao;    

//使用设值方式赋值    
public void setUserDao(UserDao userDao) {    
    this.userDao = userDao;    
}    

@Override    
public void addUser(String userName, String password) {    

    userDao.addUser(userName, password);    
}    

}
构造器注入

构造器注入,即通过构造函数完成依赖关系的设定。我们看一下spring的配置文件:


    <!-- 使用spring管理对象的创建,还有对象的依赖关系 -->    
    <bean id="userDao4Mysql" class="com.tgb.spring.dao.UserDao4MysqlImpl"/>    

    <bean id="userDao4Oracle" class="com.tgb.spring.dao.UserDao4OracleImpl"/>    

    <bean id="userManager" class="com.tgb.spring.manager.UserManagerImpl">    
        <!-- (1)userManager使用了userDao,Ioc是自动创建相应的UserDao实现,都是由容器管理-->    
        <!-- (2)在UserManager中提供构造函数,让spring将UserDao实现注入(DI)过来 -->    
        <!-- (3)让spring管理我们对象的创建和依赖关系,必须将依赖关系配置到spring的核心配置文件中 -->    

        <constructor-arg ref="userDao4Oracle"/>    
    </bean>    

</beans>    

我们再来看一下,构造器表示依赖关系的写法,代码如下所示:

import com.tgb.spring.dao.UserDao;

public class UserManagerImpl implements UserManager{    

    private UserDao userDao;    

    //使用构造方式赋值    
    public UserManagerImpl(UserDao userDao) {    
        this.userDao = userDao;    
    }    

    @Override    
    public void addUser(String userName, String password) {    

        userDao.addUser(userName, password);    
    }    
}    

使用字段(Filed)注入(用注解方式)

在Spring中,注入依赖对象可以采用手工装配或自动装配,在实际应用开发中建议使用手工装配,因为自动装配会产生许多未知情况,开发人员无法预见最终的装配结果。

手工装配依赖对象又分为两种方式:

一种是在XML文件中,通过在bean节点下配置;如上面讲到的使用属性的setter方法注入依赖对象和使用构造器方法注入依赖对象都是这种方式。

另一种就是在java代码中使用注解的方式进行装配,在代码中加入@Resource或者@Autowired、

Autowired是自动注入,自动从spring的上下文找到合适的bean来注入
Resource用来指定名称注入
Qualifier和Autowired配合使用,指定bean的名称,如
@Autowired
@Qualifier(“userDAO”)
private UserDAO userDAO;
怎样使用注解的方式来为某个bena注入依赖对象呢?

首先,我们需要在Spring容器的配置文件applicationContext.Xml文件中配置以下信息,该信心是一个Spring配置文件的模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>  

<beans

xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:p="http://www.springframework.org/schema/p"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-2.5.xsd

">

</beans>

注意:只有配置了红色部分的代码才可以引入注解的命名空间,否则报错。

以上的配置隐式的注册了多个对注释进行解析的处理器:AutowiredAnnotationBeanPostProcessor、

CommonAnnotationBeanPostProcessor、

PersistenceAnnotationBeanPostProcessor等。

其次,在配置文件中打开context:annotation-config节点,告诉Spring容器可以用注解的方式注入依赖对象;其在配置文件中的代码如下:

1
2
3
4
5
6
7
8
9
<beans>  

……

<context:annotation-config></context:annotation-config>

……

</beans>

第三,在配置文件中配置bean对象,如下:

1
2
3
<bean id="userDao" class="com.springtest.dao.impl.UserDAOImpl"></bean>  

<bean id="userBiz" class="com.springtest.biz.impl.UserBizImpl"></bean>

第四,在需要依赖注入的BIZ类中,声明一个依赖对象,不用生成该依赖对象的setter方法,并且为该对象添加注解:

1
2
3
4
5
6
7
public class UserBizImpl implements UserBiz {  
@Resource(name="userDao")
private UserDAO userDao = null;
public void addUser() {
this.userDao.addUser();
}
}

其中,在Java代码中可以使用@Autowired或@Resource注解方式进行Spring的依赖注入。两者的区别是:@Autowired默认按类型装配,@Resource默认按名称装配,当找不到与名称匹配的bean时,才会按类型装配。

比如:我们用@Autowired为上面的代码UserDAO接口的实例对象进行注解,它会到Spring容器中去寻找与UserDAO对象相匹配的类型,如果找到该类型则将该类型注入到userdao字段中;

如果用@Resource进行依赖注入,它先会根据指定的name属性去Spring容器中寻找与该名称匹配的类型,例如:@Resource(name=”userDao”),如果没有找到该名称,则会按照类型去寻找,找到之后,会对字段userDao进行注入。

通常我们使用@Resource。

使用注解注入依赖对象不用再在代码中写依赖对象的setter方法或者该类的构造方法,并且不用再配置文件中配置大量的依赖对象,使代码更加简洁,清晰,易于维护。

在Spring IOC编程的实际开发中推荐使用注解的方式进行依赖注入。

依赖注入—自动装配:略(不推荐)

总结:

接口注入:

接口注入模式因为具备侵入性,它要求组件必须与特定的接口相关联,因此并不被看好,实际使用有限。

Setter 注入:

对于习惯了传统 javabean 开发的程序员,通过 setter 方法设定依赖关系更加直观。如果依赖关系较为复杂,那么构造子注入模式的构造函数也会相当庞大,而此时设值注入模式则更为简洁。如果用到了第三方类库,可能要求我们的组件提供一个默认的构造函数,此时构造子注入模式也不适用。

构造器注入:

在构造期间完成一个完整的、合法的对象。所有依赖关系在构造函数中集中呈现。依赖关系在构造时由容器一次性设定,组件被创建之后一直处于相对“不变”的稳定状态。只有组件的创建者关心其内部依赖关系,对调用者而言,该依赖关系处于“黑盒”之中。

注解注入:

使用注解注入依赖对象不用再在代码中写依赖对象的setter方法或者该类的构造方法,并且不用再配置文件中配置大量的依赖对象,使代码更加简洁,清晰,易于维护。

在Spring IOC编程的实际开发中推荐使用注解的方式进行依赖注入。
使用纯注解方式配置Spring容器并获取bean的过程

  纯注解方法的话,那就舍弃xml配置文件。  

创建配置类

  如果我们使用纯注解方式实现IoC,那么需要创建一个配置类来代替上面这个xml配置文件,然后就可以不用创建这个xml配置文件了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package cn.woshilan.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import cn.woshilan.pojo.Person;

@Configuration
public class MyConfiguration {

@Bean
public Person person() {
return new Person();
}

}

  解释一下上面这个MyConfiguration类:

  1、创建的配置类,类名随意,但是需要使用@Configuration注解,标识其为配置类,这个类的功能相当于之前的xml配置文件。

  2、public Person person() 这个方法,使用了@Bean注解,这个方法的功能等同于xml中的标签,方法名对应标签中的id,方法返回值类型对应中的class。在方法体中完成对象的实例化即可。

  如果设置bean的id,即不使用方法名作为bean的id,可以在@Bean中直接设置:@Bean(“newId”)

测试代码

  注意,在测试的时候,因为没有使用xml配置文件的方式,所以不是使用ClassPathXmlApplicationContext,而是使用AnnotationConfigApplicationContext类,需要接受一个参数,参数就是我们的配置类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package cn.woshilan.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import cn.woshilan.config.MyConfiguration;
import cn.woshilan.pojo.Person;

public class TestIOC {
public static void main(String[] args) {
// 这里使用的是AnnotationConfigApplicationContext
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);

// 此时的getBean(),第一个参数是MyConfiguration中的某个方法名,因为配置类中的方法名对应<bean>中的id
Person bean = context.getBean("person", Person.class);

System.out.println(bean);
}
}

  上面就实现了使用注解方式来完成让Spring管理bean。

配置注解扫描package
  前面我们创建了MyConiguration配置类来代替xml配置文件,然后在类中可以定义很多的function来创建对象,每一个function其实是对应一个xml中的一个标签的。

  这就意味着,创建每一个bean都需要去Myconfiguration配置类中创建一个function,这样其实并不方便,所以可以使用包扫描的方式,包扫描的方式就是在MyConfiguration类上使用@ComponentScan注解指定要扫描哪些package,如果那些package中的class上面有@Component、@Service、@Repository、@Controller这几个注解中的一个,那么spring就会为其创建bean,bean的id默认是类名首字母小写,但也支持自定义。

  注意,上面列举了4个注解,@Component、@Service、@Repository、@Controller的功能一样,都是标识让Spring管理这些类创建的bean,但是他们的语义是有区别的,所以用法也不相同:

  @Component注解,用来普通的javabean上;最纯粹的

  @Service注解,用在Service层,一般是service.impl包下的类上面;

  @Repository注解,用在数据访问层,也就是DAO层上

  @Controller注解是用在spring mvc中的控制器上面。

创建包含注解的Person类

  创建Person类,包名为cn.woshilan.pojo。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package cn.woshilan.pojo;

import org.springframework.stereotype.Component;

@Component // 使用这个注解,bean的id默认是类名首字母小写
// @Component("person111") 可以手动设置bean的id
public class Person {

private int id;
private String name;
private String addr;

// 隐藏了有参和无参构造方法、setter、getter、toString
}

使用注解配置扫描package

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package cn.woshilan.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan({"cn.woshilan.pojo", "cn.woshilan.demo"}) // 指定扫描哪些package
public class MyConfiguration {

}
```  
  

进行测试
```java
package cn.woshilan.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import cn.woshilan.config.MyConfiguration;
import cn.woshilan.pojo.Person;

public class TestIOC {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);

Person bean = context.getBean("person", Person.class);

System.out.println(bean);
}
}

使用xml配置注解扫描package

  如果要使用配置文件来指定扫描注解的package,需要使用context这个xmlns,下面这个xml配置文件实现了上面MyConfiguration的功能:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<!-- 需要使用context这个xmlns,然后使用<context:component-scan>指定需要扫描哪些package -->
<context:component-scan base-package="cn.woshilan.pojo, cn.woshilan.demo"></context:component-scan>
</beans>

使用注解实现注入
  前面介绍了使用注解来让Spring容器来管理bean对象,创建bean的时候,默认都是调用无参构造方法。如果我们为对象的属性赋值,那么就需要进行注入了。

对普通数据类型的属性注入

  这里的普通数据类型是指几种基本数据类型、以及String,对他们赋值,可以直接使用@Value注解实现。比如下面对Person类的各个属性进行赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package cn.woshilan.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Person {

@Value("999")
private int id;

@Value("Jenny")
private String name;

@Value("America")
private String addr;

// 隐藏了有参和无参构造方法、setter、getter、toString
}

  上面的@Value注解接收一个字符串类型的值,Spring会根据属性的类型,自动将字符串类型的值转换为属性类型的数据。

  另外,@Value不仅支持直接指定属性值,使用@Value(“${key}”)可以读取外部properties配置文件中的配置项key的值,如果要读取外部properties配置文件的值,需要借助xml配置Spring,指定外部properties配置文件。

  作为测试,在classpath(src)下创建一个config.properties,添加一项内容:

1
name=hello world
  修改Spring配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<!-- 指定外部配置文件的路径 -->
<context:property-placeholder location="classpath:config.properties" />

<!-- 需要使用context这个xmlns,然后使用<context:component-scan>指定需要扫描哪些package -->
<context:component-scan base-package="cn.woshilan.pojo, cn.woshilan.demo"></context:component-scan>
</beans>

  为Person类中name属性指定值为外部配置文件的name

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package cn.woshilan.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Person {

@Value("999")
private int id;

@Value("${name}") // 使用外部配置文件中name的值
private String name;

@Value("America")
private String addr;

// 隐藏了有参和无参构造方法、setter、getter、toString
}

  测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package cn.woshilan.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.woshilan.pojo.Person;

public class TestIOC {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");


Person bean = context.getBean("person", Person.class);

System.out.println(bean);
// Person [id=999, name=hello world, addr=America]
}
}
```  

对引用数据类型的属性进行注入——构造方法和setter注入

  对引用数据类型的属性进行注入时,有两种方式:构造方法注入和setter注入,如果使用xml配置文件形式,分别是用<constructor-args>和<property>标签。

  而如果是使用注解的话,那么就有两个注解可以实现这个功能:@Autowired@Resource,他们都能实现依赖注入,但是他们也有区别:

  @Resource是Java内置的注解,不能写在方法上,只能写在属性字段上。

  @Autowired是spring提供的注解,即可以写在属性字段上,也可以写在构造方法上,还可以写在setter上。
```java
package cn.woshilan.pojo;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Company {

@Resource // @Resource只能写在属性之前
private Person person1;

@Autowired // @Autowired可以写在属性前
private Person person;

public Company() {
super();
}

@Autowired // @Autowired也可以写在构造方法前,会自动根据所需参数类型进行注入
public Company(Person person1, Person person) {
super();
this.person1 = person1;
this.person = person;
}

@Autowired // @Autowired还可以写在setter上
public void setPerson(Person person) {
this.person = person;
}

public void setPerson1(Person person1) {
this.person1 = person1;
}

@Override
public String toString() {
return "Company [person1=" + person1 + ", person=" + person + "]";
}
}

  上面使用@Autowired和@Resource来进行注入的时候,是自动注入的。会根据byName方式,查看spring容器中是否有一个bean的id,该和属性名称相同的,如果有的话,就将这个bean注入到属性中。如果没有的话,就根据byType进行注入,即根据容器中的bean的type进行匹配,这个时候,如果有多个type相同的bean,就会报错,需要使用@Qualifier(“bean_id”)明确指定注入哪一个bean。

集合数据类型的注入

  对于集合类型,不建议使用注解方式注入,建议使用xml配置文件方式。

IoC的相关设置
  除了对bean的注入,还有其他的bean的设置也很重要,比如懒加载、作用域、初始化和销毁时调用的方法。

懒加载

  懒加载在xml配置中是设置bean的lazy-init属性值,true为设置为懒加载,false为设置为spring容器创建时创建bean,默认不是懒加载。

  可以使用@Lazy注解来设置懒加载,可以在配置类中创建对象的方法上使用,还可以在class的上面设置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

package cn.woshilan.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

import cn.woshilan.pojo.Person;

@Configuration
public class MyConfiguration {

@Bean
@Lazy
public Person person111() {
return new Person(8888, "xyz", "beijing");
}
}

还可以在class上设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package cn.woshilan.pojo;

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

@Component
@Lazy
public class Person {
private int id;
private String name;
private String addr;

// 隐藏了有参和无参构造方法、setter、getter、toString
}
```  



作用域

  作用域在xml中是设置scope属性,同样的,在注解中,提供了@Scope与之对应。和@Lazy使用相同,可以在配置类和javabean上使用。

```java
@Component
@Scope("singleton")
public class Person {

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package cn.woshilan.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

import cn.woshilan.pojo.Person;

@Configuration
@ComponentScan("cn.woshilan.pojo")
public class MyConfiguration {

@Bean
@Scope("prototype")
public Person person() {
return new Person();
}
}

初始化和销毁回调程序

  如果是xml配置,可以使用init-method和destroy-method来设置初始话bean和销毁bean的时候调用。

  如果使用注解,可以实现两个接口,并重写两个方法即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package cn.woshilan.pojo;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

@Component
public class Person implements InitializingBean, DisposableBean{

@Override
public void afterPropertiesSet() throws Exception {
System.out.println("初始化时被调用");
}

@Override
public void destroy() throws Exception {
System.out.println("被销毁时调用");
}
}

  如果不实现InitializingBean, DisposableBean接口,可以使用下面这种方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package cn.woshilan.pojo;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.stereotype.Component;

@Component
public class Person{

@PostConstruct
public void onInit() {
System.out.println("初始化时被调用");
}

@PreDestroy
public void onDestroy() {
System.out.println("被销毁时调用");
}

}

  还可以在配置类中进行设置,首先删除Person中的注解,然后修改配置类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package cn.woshilan.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import cn.woshilan.pojo.Person;

@Configuration
@ComponentScan("cn.woshilan.pojo")
public class MyConfiguration {

@Bean(initMethod="onInit", destroyMethod="onDestroy")
public Person person() {
return new Person();
}
}

图像识别学习日志——目标检测正负样本理解

在做目标检测任务时对其中的正负样本不太清楚,看了很多资料,发现这篇博客解释的比较清晰,下面图片也出自上述博客。

​ 以人脸识别为例,如果你的任务是识别教室中的人脸,那么负样本的选取应该是教室中的窗户、椅子、墙、人的身体、衣服颜色等等,而不是天空、月亮这些对本任务没有帮助的场景。当然,如果不确定应用环境,那么应该选取尽可能有不同光照不同环境的自然图像作为负样本。

​ 个人理解的正样本就是任务所要检测的目标物,比如在人脸识别中不同种族年龄的人脸、不同表情的人脸、戴不同装饰的人脸等等情况下的人脸;而负样本则是目标物所处的不同背景(注意:此背景不包含人脸),比如人脸会出现在不同环境中,街道、室内总之所有能想到的环境中都有可能出现人脸哈哈,负样本就是这些不包含人脸的图片,如果需要很多负样本,则可以把这些不包含人脸的图片裁剪成所需图片大小。

​ 参考faster以及SSD两种检测框架中对于正负样本的选取准则,首先,检测问题中的正负样本并非人工标注的那些框框,而是程序中(网络)生成出来的ROI,也就是faster rcnn中的anchor boxes以及SSD中在不同分辨率的feature map中的默认框,这些框中的一部分被选为正样本,一部分被选为负样本,另外一部分被当作背景或者不参与运算。不同的框架有不同的策略,大致都是根据IOU的值,选取个阈值范围进行判定,在训练的过程中还需要注意均衡正负样本之间的比例。在fast的框架中,也是需要多SS算法生成的框框与GT框进行IOU的判断,进而选取正负样本,总之,正负样本都是针对于程序生成的框框而言,而非GT数据。

一个小技巧:在训练检测网络时,若已经训练出一个较好的检测器,在用它进行测试时,还会有一些误检,这时可以把误检的图像加入负样本中retrain检测网络,迭代次数越多则训练模型越好

英文原版

C and C++ 路线

C++语言

机制多,粒度细 “造轮子” 啥都能干,但 都 只 能 从 底 层 做

服务端开发较多

java

企业化的后台、客户端开发语言
第一:《C Primer Plus》(第5版)中文版 买新版
第二:《C++Primer》中文版

第三:《C++ Primer Plus》 比第二更适合零基础
第四:《Java编程思想》
第五:《Java核心技术》卷1卷2
第六:《Python核心编程》
第七:《Python编程从入门到实战》
第八:《Go语言实战》 or《Go In Action》
第九:《算法导论》 不适合初学者
第十:《算法》第四版 红砖
第十一:《TCP/IP详解》
第十二:《计算机网络 自顶向下》
第十三:《私房菜》 工具书
第十四:《Unix环境高级编程》
第十五:《Spring实战》
第十六:《Spring Boot实战》
第十七:《Spring技术内幕》 很难
第十八:《MySQL必知必会》 涵盖 《SQL必知必会》的内容
第十九:《高性能MySQL》
第二十:《重构 改善既有代码的设计》 需要一些经验,拔高内容

学习语言:高强度

学习编程的基础四大件:比语言本身重要

数据结构和算法

学完之后要刷leetcode(算法小红砖 剑指offer)
字符串 链表 二叉树 堆栈 队列 哈希 查找 排序 动态 规划

计算机网络

tcp/ip 协议栈(tcp/ip详解)

操作系统

进程和线程 并发 和锁 内存分布调度等等 (深入理解操作系统)

设计模式

单例 工厂 代理 策略模式 模版方法(大话设计模式)

实践

Linux操作系统掌握 shell编程 脚本等等
编译/调试工具 Linux平台上 gcc 以及makefile(跟我一起写makefile)要会写 gdb调试工具

Linux系统编程 !
多线程编程 网络编程(unix环境高级编程)(Linux高性能服务器编程)(posix多线程程序设计)