面向接口编程
面向接口编程
我们之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程
-
根本原因 : 解耦 , 可拓展 , 提高复用 , 分层开发中 , 上层不用管具体的实现 , 大家都遵守共同的标准 , 使得开发变得容易 , 规范性更好
-
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;
-
而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。
关于接口的理解
-
接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
-
接口的本身反映了系统设计人员对系统的抽象理解。
-
接口应有两类:
- 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
- 第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);
- 一个体有可能有多个抽象面。抽象体与抽象面是有区别的。
三个面向区别
-
面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法 .
-
面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现 .
-
接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题.更多的体现就是对系统整体的架构
利用注解开发
-
UserMapper接口、
@Select("select * from user") List<User> getUsers();
-
核心配置文件中绑定接口
<!--绑定接口--> <mappers> <mapper class="com.study.dao.UserMapper"/> </mappers>
-
测试
@Test public void getUsers(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.getUsers(); for (User user : userList) { System.out.println(user); } sqlSession.close(); }
本质:反射机制实现
底层:动态代理
本质上利用了jvm的动态代理机制
注解增删改
改造MybatisUtils工具类的getSession( ) 方法,重载实现。
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession(true); //事务自动提交
}
public static SqlSession getSession(boolean flag){
return sqlSessionFactory.openSession(flag);
}
接口
package com.study.dao;
import com.study.pojo.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.Map;
public interface UserMapper {
@Select("select * from user")
List<User> getUsers();
//方法存在多个参数,所有的参数前面都要加上@Param("id")注
@Select("select * from user where id=#{id}")
User getUserByID(@Param("id")int id);
@Update("update user set name=#{name},pwd=#{password} where id=#{id}")
int updateUser(User user);
@Insert("insert into user(id,name,pwd) values(#{id},#{name},#{password})")
int addUser(User user);
@Delete("delete from user where id=#{id}")
int deleteUser(@Param("uid")int id);
}
在配置文件绑定Mapper接口
<mappers>
<mapper class="com.study.dao.UserMapper"/>
</mappers>
测试代码同上
@Param
注解
-
当在UserMapper中使用如下
@param
时@Delete("delete from user where id=#{id}") int deleteUser(@Param("uid")int id);
测试时报错:
`org.apache.ibatis.exceptions.PersistenceException:
Error updating database. Cause: org.apache.ibatis.binding.BindingException: Parameter ‘id’ not found. Available parameters are [uid, param1]`
报错了,它说“id”没有找到,可用参数为“uid” -
将SQL语句中的#{id}改为#{uid},也就是注解中的名字,正常运行:
@Delete("delete from user where id=#{uid}") int deleteUser(@Param("uid")int id);
单个基本类型的形参时不使用@Param也能执行成功。
那么其他更加复杂的情况呢,比如说,多个基本类型?引用类型?
下面我们就来深入探讨一下@Param()究竟该怎么用。
-
UserMapper接口
@Select("select * from user where id=#{id} and name=#{username}") User getUserByID2(int id, String username);
-
测试
@Test public void getUserById2(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserByID2(2, "张三"); System.out.println(user); sqlSession.close(); }
报错:
org.apache.ibatis.exceptions.PersistenceException: ###Error querying database.Cause: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [arg1, arg0, param1, param2]
添加@Param后:
@Select("select * from user where id=#{id} and name=#{username}") User getUserByID2(@Param("id")int id, @Param("username")String username);
-
测试正常运行。
以上都是举得直接在接口中注解SQL语句的例子,在Mapper.xml文件中@Param()同样使用,只需要注意的是,使用@Param()注解,parameterType属性就不用再设置了。
三、总结
简单总结一下:
-
sql的#{}中的参数就是@Param(“”)中设置的参数,而不是方法的形参
-
传入单个基本类型或String类型的时候,使用不使用@Param()注解都可以。
-
如果参数是 JavaBean , 则不能使用@Param。
-
需要传入多个参数时,有三种方法:JavaBean、@Param()注解、Map。个人认为优先使用JavaBean,当需要传入的数据较多但又不能完全将JavaBean利用起来的时候使用@Param()注解或Map。
-
单个基本类型,SQL对应的就是形参的名字;使用JavaBean,SQL中对应的就是JavaBean在结果集映射的属性(没有显性映射就是默认的属性);使用@Param(),SQL中对应的就是@Param()注解中的名字;使用Map,SQL中对应的就是Map的键名。
-
使用@Param()注解的时候,Mapper.xml文件无需再设置parameterType属性。
#{}和${}区别
{}中的参数即为Mapper接口中对应方法的形参
#{}
1. #{} 的作用主要是替换**预编译**语句(PrepareStatement)中的占位符? 【推荐使用】
2. 底层是preparedStatement
3. 预编译
4. 能防止sql注入,自动添加了‘ ’ 引号
例如:select * from user where username = #{name} //输入的参数lisa,就会自动加上引号,变成:select * from user where username = ‘lisa’
${}
1. 不防注入,就是**字符串拼接**
2. 只能${}的情况:order by、like 语句只能用${}了,用#{}会多个' '导致sql语句失效;此外动态拼接sql也要用${}
3. #{} 这种取值是编译好SQL语句再取值
${} 这种是取值以后再去编译SQL语句
详细讲解:
1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:
order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by "111",
如果传入的值是id,则解析成的sql为order by "id".
2. $将传入的数据直接显示生成在sql中。如:order by $user_id$,
如果传入的值是111,那么解析成sql时的值为order by user_id,
如果传入的值是id,则解析成的sql为order by id.
3. #方式能够很大程度防止sql注入。
4.$方式无法防止Sql注入。
5.$方式一般用于传入数据库对象,例如传入表名.
6.一般能用#的就别用$.
MyBatis排序时使用order by 动态参数时需要注意,用$而不是#
字符串替换
默认情况下,使用#{}格式的语法会导致MyBatis创建预处理语句属性并以它为背景设置
安全的值(比如?)。这样做很安全,很迅速也是首选做法
有时你只是想直接在sql语句中插入一个不改变的
字符串。比如,像ORDER BY,你可以这样来使用:ORDER BY ${columnName}
这里MyBatis不会修改或转义字符串。重要:接受从用户输出的内容并提供给语句中
不变的字符串,这样做是不安全的。这会导致潜在的sql注入攻击,因此你不应该
允许用户输入这些字段,或者通常自行转义并检查。
使用注解和配置文件协同开发,才是MyBatis的最佳实践!
Lombok
Lombok项目是一个java库,它可以自动插入到编辑器和构建工具中,增强java的性能。不需要再写getter、setter或equals方法,只要有一个注解,你的类就有一个功能齐全的构建器、自动记录变量等等。
Lombok是一个在Java开发过程中用注解的方式,简化了 JavaBean(实体类) 的编写,避免了冗余和样板式代码而出现的插件,让编写的类更加简洁。
1. 使用步骤
- 在IDEA中安装lombok插件:File —> Settings —> Plugins —> Browse repositories —> 搜索lombok
- 在pom.xml中导入lombok的maven依赖(包)
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
<scope>provided</scope>
</dependency>
- 在实体类上加注解即可
2.lombok常用注解
- @Data : 自动生成set/get方法,toString方法,equals方法,hashCode方法,不带参数的构造方法
- @NoArgsConstructor/@RequiredArgsConstructor/@AllArgsConstructor
自动生成无参/有参构造方法 - @Setter/@Getter : 自动生成set和get方法
- @ToString : 自动生成toString方法
- @EqualsAndHashcode : 从对象的字段中生成hashCode和equals方法
3.以下为不常用注解:
@NonNull : 用在成员方法或者构造方法的参数前面,会自动产生一个关于此参数的非空检查,如果参数为空,则抛出一个空指针异常
@CleanUp : 自动资源管理:不用再在finally中添加资源的close方法
@Value : 用于注解final类
@Builder : 产生复杂的构建器api类
@SneakyThrows : 异常处理(谨慎使用)
@Synchronized : 同步方法安全的转化
@Getter(lazy=true) :
@Log: 支持各种logger对象,使用时用对应的注解,如:@Log4j
4. 优缺点
优点:
-
能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、 toString等方法,提高了一定的开发效率
-
让代码变得简洁,不用过多的去关注相应的方法
-
属性做修改时,也简化了维护为这些属性所生成的getter/setter方法等
缺点:
-
不支持多种参数构造器的重载
-
虽然省去了手动创建getter/seter方法的麻烦,但大大降低了源代码的可读性和完整性,降低了阅读源代码的舒适度
总结
Lombok虽然有很多优点,但Lombok更类似于一种IDE插件,项目也需要依赖相应的jar包。Lombok依赖jar包是因为编译时要用它的注解,为什么说它又类似插件?因为在使用时,eclipse或Intelli] DEA都需要安装相应的插件,在编译器编译时通过操作AST(抽象语法树)改变字节码生成,变向的就是说它在改变java语法。
它不像spring的依赖注入或者mybatis的ORM一样是运行时的特性,而是编译时的特性,这里我个人最感觉不爽的地方就是对插件的依赖!因为Lombok只是省去了一些人丁生成代码的麻烦,但IDE都有快捷键来协助生成etter/setter等方法,也非常方便
知乎上有位大神发表过对Lombok的一些看法:这是一种低级趣味的插件,不建议使用。JAva发展到今天,各种插件层出不穷,如何甄别各种插件的优劣?能从架构上优化你的设计的,能提高应用程序性能的,实现高度封装可扩展的..., 像1mbok这种,像这种插件,已经不仅仅是插件了,改变了你如何编写源码,事实上,少去了的代码你写上去又如何?如果JAVA家族到外充斥这样的东西,那只不过是一垃披着金属颜色的屎,迟早会被其它的语言取代。
虽然话糙但理确实不糙,试想一个项目有非常多类似Lombok这样的插件,个人觉得真的会极大的降低阅读源代码的舒适度,虽然非常不建议在属性的getter/seter写一些业务代码,但在多年项目的实战中,有时通过给getter/setter加一点点业务代码,能极大的简化某些业务场景的代码。所谓取舍,也许就是这时的舍弃一定的规范,取得极大的方便。
我现在非常坚信一条理念,任何编程语言或插件,都仅仅只是工具而已,即使工具再强大也在于用的人,就如同小米加步枪照样能前飞机大炮的道理一样。结合具体业务场景和项目实际情况,无需一味追求高大上的技术,适合的才是王道。
Lombok有它的得天独厚的优点,也有它避之不及的缺点,熟知其优缺点,在实战中灵活运用才是王道
那只不过是一垃披着金属颜色的屎,迟早会被其它的语言取代。
虽然话糙但理确实不糙,试想一个项目有非常多类似Lombok这样的插件,个人觉得真的会极大的降低阅读源代码的舒适度,虽然非常不建议在属性的getter/seter写一些业务代码,但在多年项目的实战中,有时通过给getter/setter加一点点业务代码,能极大的简化某些业务场景的代码。所谓取舍,也许就是这时的舍弃一定的规范,取得极大的方便。
我现在非常坚信一条理念,任何编程语言或插件,都仅仅只是工具而已,即使工具再强大也在于用的人,就如同小米加步枪照样能前飞机大炮的道理一样。结合具体业务场景和项目实际情况,无需一味追求高大上的技术,适合的才是王道。
Lombok有它的得天独厚的优点,也有它避之不及的缺点,熟知其优缺点,在实战中灵活运用才是王道