距离上次写博客已经过去了一星期,这一个星期学到的知识我都完全没有做笔记,全靠一颗快要生锈的脑子,真感叹我的脑子什么时候怎么这么好了。哈哈哈哈!!
上次我们说到了SpringMVC,而这个星期我有学习了当下火爆的另外两大框架:Spring和Mybatis,并把它们串联整合到了一起,这也要感谢SpringBoot的功劳啊,嗯,感觉脑容量已经趋近于饱和了,废话不多说,我要开始释放(装逼)了。
一、Spring
1.概念
它想把全球最好的技术组合到一起,为企业提供高质量的企业级的应用程序框架,减轻开发者开发的难度,减少重复的代码。为了让spring来实现对象的管辖,让不同技术之间能简单的互相配合。它创新的形成了一套新的理论体系,可谓前无古人后无来者。其中最核心的是:IoC控制反转、DI依赖注入、Bean工厂、SpringAOP面向切面编程、事务控制。经典的三大框架最终形成SSM(Spring,SpringMVC,Mybatis)。
Spring 框架是一个分层架构,由7个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,如下图所示:(摘自csdn:cgblpx)
我主要学习了它的两大核心IOC和DI还有AOP面向切面
2. IOC
2.1概念
首先说明IOC(Inversion of Control),即“控制反转”,不是什么技术,而是一种设计思想。简单来说就是将对象Object的创建的权力及对象的生命周期的管理过程交由Spring框架来处理,从此在开发过程中不在需要关注对象的创建和生命周期的管理,而是在需要的时候由Spring框架提供,这个由Spring框架管理对象创建和生命周期的机制称之为控制反转。
2.2 IOC注解
在Spring维护的Map集合里进行IOC标识一般有两种方式:
2.2.1方式一
第一种是:Spring提供了四个注解,常用来标识IOC的三种分别是
- @Service 用于注册服务层的Bean
- @Controller 用于注册控制层的Bean
- @Component 用于注册所有的Bean
Spring里万物皆是Bean,在里面维护了一个Map集合,K就是被标记的类名,V就是返回的一个对象或者IOCnew的对象,用@Comtroller的标记的类的K默认是类名,而这个注解可以在后面加入参数自定义K的名字,如:@Component(“a”),这时K的名称就是a。
2.2.2方式二
第二种是:@Configuration + @Bean 的组合
@Configuration在类上标记,配置类的意思,而Bean一般在方法上标记,方法名是K,返回的对象是V
2.2.3区别
那么问题来了,为什么有两种方式呢,它们的区别又是什么呢?
通过注解的方式更加的简单,但是默认使用无参构造创建的对象,适用封装类或通用类上等
组合注解的方式表达的更加的具体,可以自定义返回的对象,适用于有具体业务时使用
2.2.4测试
首先需要在核心配置文件里加入包扫描,这样可以对有注解的进行IOC,并传入参数,一般是启动类所在的同级包或包下的包,例:
<context:component-scan base-package="cn.tedu.ioc"></context:component-scan>
在测试类例通过spring.getBean方法通过Bean的名子进行对象的调用和通过对象调用里面的方法
User u = (User) spring.getBean("user");
System.out.println(u);
User2 u2=(User2) spring.getBean("自定义名称");
System.out.println(u2);
u2.get();
结果如下:
这时我们可以看到,通过getBean确实可以的打印出对象并使用对象里的方法
3.DI
3.1概念
在创建对象的过程中Spring可以依据对象的关系,自动把其它对象注入(无需创建对象,直接拿着使用)进来,这个过程称之为DI(Dependency Injection)依赖注入。相对于IoC而言,依赖注入(DI)更加准确地描述了IoC的设计理念。所谓依赖注入,即组件之间的依赖关系由容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。
3.2手动DI---体会过程
理解了DI的思想后,我们模仿这种思想来实现一下DI
首先来创建两个测试类:
package cn.tedu.di;
public class Dept {
String name="java开发";
@Override
public String toString() {
return "Dept{" +
"name='" + name + '\'' +
'}';
}
}
package cn.tedu.di;
public class Emp {
String name="jack";
private Dept d;
public Dept getD(){
return d;
}
public void setD(Dept d){
this.d=d;
}
@Override
public String toString() {
return "Emp{" +
"name='" + name + '\'' +
", d=" + d +
'}';
}
}
如图我们在Emp里封装了Dept类型的d,并添加GetSet方法,加入ToString方法便于查看
创建测试类进行测试:
分别打印对象得到结果:
这里的d为NULL
进行依赖注入并再次打印
e.setD(d);
依赖注入成功,但是这样需要 new两个对象并把一个对象作为参数传入另一个对象中,这样不仅麻烦,而且大大增加的耦合度,不便于后期进行维护修改等
3.3DI注解
一般有两种方式进行DI注入:
3.3.1方式一
需要谁注入就创建它的成员变量,在成员变量上加@Autowired注解即可,这样是默认的类型注入,通过Map集合中的V.class获取类型并注入
3.3.2方式二
根据名称注入 @Qualifier(名称)+ @Autwired 二者必须都在
名称在SpringMap容器里的k进行查找
@Autowired
@Qualifier("user")
private User user;
3.3.3区别
一个通过类型注入
一个通过K值注入
3.3.4注意
Spring容器一般都是单实现,K值不能重复,否则报错
DI的前提是IOC,且只能一次IOC(不能在类上添加IOC注释后再去添加Bean返回自定义的对象,否则报错)
4.IOC与DI
在平时的java应用开发中,我们要实现某一个功能或者说是完成某个业务逻辑时可能需要多个对象来协作完成,在没有使用Spring的时候,每个对象在需要使用他的合作对象时,自己均要使用像new object() 这样的语法来将合作对象创建出来,这个合作对象是由自己主动创建出来的,创建合作对象的主动权在自己手上,自己需要哪个合作对象,就主动去创建,创建合作对象的主动权和创建时机是由自己把控的,而这样就会使得对象间的耦合度高了,A对象需要使用合作对象B来共同完成一件事,A要使用B,那么A就对B产生了依赖,也就是A和B之间存在一种耦合关系,并且是紧密耦合在一起,而使用了Spring之后就不一样了,创建合作对象B的工作是由Spring来做的,Spring创建好B对象,然后存储到一个容器里面,当A对象需要使用B对象时,Spring就从存放对象的那个容器里面取出A要使用的那个B对象,然后交给A对象使用,至于Spring是如何创建那个对象,以及什么时候创建好对象的,A对象不需要关心这些细节问题(你是什么时候生的,怎么生出来的我可不关心,能帮我干活就行),A得到Spring给我们的对象之后,两个人一起协作完成要完成的工作即可。
所以控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。
DI(依赖注入)其实就是IOC的另外一种说法,DI是由Martin Fowler 在2004年初的一篇论文中首次提出的。他总结:控制的什么被反转了?就是:获得依赖对象的方式反转了。
IoC是设计思想,IoC有三个核心:BeanFactory、反射、DI。BeanFactory利用反射实现对象的创建,DI实现对象关系管理
5.AOP
5.1概念
AOP-面向切面编程,我个人理解:java是OOP编程,它导致了大量重复的代码,且无法复用,而AOP为了弥补这一点将通用需求功能从不相关的类当中分离出来,能够使得很多类共享一个行为,一旦发生变化,不必修改很多类,而只需要修改这个行为即可,通用代码写一遍即可,使得代码有更好的可读性和易于维护性,与OOP有很好的互补关系
需要导入jar包才能使用
AOP有三要素:切面、切点、通知
5.2切面
可以泛指为一个写共通代码的类,切面包含切点和通知,在类上用@Aspect来标记这是一个切面类
5.3通知
来指定具体做什么事情。如方法执行前做什么,方法执行后做什么,抛出异常做什么,从而实现对象行为(方法)的增强,总的来说就是一个自定义方法。通知又分为五种:
- 前置通知before
- 后置通知after
- 环绕通知around
- 返回后通知afterReturning
- 异常通知afterThrowing
这五种注解分别在方法上添加,根据不同场景用不同的注解
5.4切点
配置切点表达式(expression)来指定在哪些类的哪些方法上织入(ware)横切逻辑;被切的地方叫连接点(JoinPoint),简单来说就是写的通知要在哪些类上应用。
在方法上添加@Pointcut注解,并在括号里传入切点表达式表示要在哪里应用通知
5.5AOP示例
package cn.tedu.Service;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Service;
@Service//ioc
@Aspect//Aop的类,切面
public class Aopaspect {
//切面由切点和通知构成
// 切点:(具体要用通知的类或方法)
@Pointcut("execution( * cn.tedu.Service..*.*(..))")//切点表达式*是通配符,..表示0-n个,一般直接吧切点表达式写在Around里
public void point(){
}
// 通知:(一个方法,自定义功能)
@Around("point()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
long l = System.currentTimeMillis();//计时开始
//执行业务方法 joinPoint连接点
Object o = joinPoint.proceed();
l=System.currentTimeMillis()-l;//计时结束
System.out.println("执行时间"+l);
return o;
}
}
二、Mybatis
1.概念
之前我们已经学习过了JDBC,有没有感觉到写那些配置文件非常的繁琐呢?
Mybatis是由apache提供的一个针对持久层开源框架,对JDBC访问数据库的过程进行了简化和封装使用mybatis可以只关注SQL语句本身,而不需要关注(JDBC中的)注册驱动、获取连接、获取传输器、释放资源等过程。
mybatis可以将要执行的SQL语句使用xml文件的方式或者注解方式配置起来,在执行时,将Java对象中携带的参数值和SQL骨架进行映射,生成最终要执行的SQL,将执行的结果处理后再返回。
2.优势
- JDBC连接访问数据库有大量重复的代码,而mybatis可以极大的简化JDBC代码
- JDBC没有自带连接池,而mybatis自带的有连接池
- JDBC中是将SQL语句、连接参数写死在程序中,而mybatis是将SQL语句以及连接参数都写在配置文件中。
- JDBC执行查询后得到的ResultSet我们需要手动处理,而mybatis执行查询后得到的结果会处理完后,将处理后的结果返回。
- 能够更好的完成ORM(对象关系映射)
- Mybatis有高级缓存机制,第一次执行会查询返回,第二次查询同样的数值会调用缓存直接返回
3.具体实现
实现流程图(摘自csdn:cgblpx)
3.1首先需要在pom.xml里添加JDBC和Mybatis的jar包
<!--mybatis依赖包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!--jdbc依赖包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
3.2创建实体类,类型和数据库中的字段类型对应,名字最好相同
package cn.tedu.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Dept {
private Integer id;
private String dname;
private String loc;
}
3.3编写mybatis-config.xml(核心配置文件)告知要连接数据库的基本信息
<?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>
<environments default="test">
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai" />
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--引入Dept映射文件-->
<mappers>
<mapper resource="DeptMapper.xml"></mapper>
</mappers>
</configuration>
注:
1.在映射文件里查询时需要添加resultType属性,参数是实体类的全路径,写的时候很麻烦还容易写错,我们可以在配置文件里起别名,如下
<!--配置别名-->
<typeAliases>
<typeAlias type="cn.tedu.pojo.Dept" alias="Dept"></typeAlias>
</typeAliases>
2.如果我们的数据库是列名是xxx_xxx,而实体类的属性名用驼峰命名,名字不同实体类和数据库对应不上会执行sql出错,我们可以在配置文件里添加开启驼峰规则。
<!--开启驼峰规则-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
3.4在持久层创建接口用来存放方法
package cn.tedu.Dao;
import cn.tedu.pojo.Dept;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public interface DeptMapper {
void save(Dept dept);
void delete(Integer id);
void up(String dname,Integer id);
List<Dept> getAll();
}
注:在接口里写方法是要注意业务的要求,注意返回值与参数是否正确
3.5编写DeptMapper.xml(映射文件)并在核心配置文件里引入,这个文件是写sql语句
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.tedu.Dao.DeptMapper">
<!--新增部门记录-->
<insert id="save">
insert into dept values (#{id},#{dname},#{loc})
</insert>
<!--删除记录-->
<delete id="delete">
delete from dept where id=#{id}
</delete>
<!--修改记录-->
<update id="up">
update dept set loc=#{dname} where id=#{id}
</update>
<!--查询记录-->
<select id="getAll" resultType="Dept">
select * from dept;
</select>
</mapper>
注:
1.namespace要写接口的全路径,一个接口负责一张表的sql
2.对于常用sql语句我们可以提取sql片段,提高sql的复用率,在sql里用到的地方直接洗sql片段的id即可
<!--动态sql,提取sql片段,提高sql的复用率-->
<sql id="cols">
id,dname,loc
</sql>
3.前面提到的数据库字段名和实体类属性名不同但很规范的情况,我们除了要在核心配置文件里加入开启驼峰规则外,还需要在映射文件里加入以下语句,如果不规范就要在resultMap里进行一一对应,所以我们更应该进行代码的规范化。然后把查询的resultType属性改成resultMap,参数传入resultMap的id值
<!--字段名和类里属性名不同,用resultMap修改后一一映射
autoMapping="true"自动匹配驼峰规则
-->
<resultMap id="a" type="User" autoMapping="true">
<!--
<result column="user_name" property="userName"></result>
<result column="user_addr" property="userAddr"></result>
<result column="user_age" property="userAge"></result>
-->
</resultMap>
4.动态sql替代符#和$的作用相同,都可以获取参数的值,它们的区别是#自动拼接引号,有效防止sql注入的风险,而$就是普通的拼接,不安全,sql语句还可能出错
5.在xml文件里写<或>等会被当作xml文本标签,sql语句会出错,这时就需要用替代符来替代
6.更多的动态sql请参考https://blog.csdn.net/u012932876/article/details/117713982
3.6在测试类里进行测试
package cn.tedu.test;
import cn.tedu.Dao.DeptMapper;
import cn.tedu.pojo.Dept;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
public class Test1 {
@Test
public void get() throws IOException {
//读取配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//创建会话工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//开启会话工厂,,准备执行sql
SqlSession session = factory.openSession(true);
//调用getMapper传入接口class对象
DeptMapper mapper = session.getMapper(DeptMapper.class);
Dept dept1 = new Dept(666,"阎静辉","山西");
mapper.save(dept1);
System.out.println("插入成功");
mapper.delete(666);
System.out.println("删除成功");
mapper.up("阎静辉", 666);
session.commit();
System.out.println("修改成功");
List<Dept> all = mapper.getAll();
for (Object dept : all) {
System.out.println(dept);
}
System.out.println("查询成功");
}
}
注:开启会话工厂时默认参数为空,此时事务需要手动关闭提交,参数为true则自动进行事务的开启关闭。
到这里我的分享就结束了,IOC和DI更加理解的深刻了一点,sql也更加的熟练,后面学习SpringBoot框架Mybatis还可以更加的简单,下一篇分享我刚出炉的一个SSM小项目。
满怀希望就会所向披靡!!!