Spring 是什么?
Spring 是一个实现解耦的开源框架,其核心是个轻量级容器,核心技术是IoC / DI 和 AOP ,可以看作是一个用于存储 Java 对象的容器,它实现了对类的容器管理,也可以说它可以根据配置的 XML 文件来帮助创建容器,基于此核心容器所建立的应用程序,可以达到程序组件的松散解耦,从而简化整个应用程序的维护与升级。
Spring 框架核心由以下七个模块组成:
哪些可以放进 Spring 容器?
Dao 类、Service 类、Controller 类、工具类。
哪些不可以?
实体类、Servlet、filter、listener。
创建一个 web 工程或 Java 工程,导入 Spring 的核心包。
编写一个普通的 Java 类(JavaBean):
package com.hello;
public class Hello
{
public void sayHello(String name)
{
System.out.println("Hello"+name);
}
}
创建 Spring 配置文件 applicationContext.xml,将 JavaBean 由 Spring 容器来管理:
<beans>
<bean id="hello" class="com.hello">beans>
beans>
使用 Spring 容器配置的 Bean:
public class Test
{
public static void main(String[] args)
{//读取 Spring 配置文件,创建一个 Bean 工厂
BeanFactory factory=new ClassPathXmlApplicationContext("applicationContext.xml");
//读取 Spring 容器一个称为 hello 的bean,Spring 容器自动创建对象实例
Hello h=(Hello)factory.getBean("hello");
h.sayHello("Evie");
}
}
创建一个接口:
package com.dao;
public interface UserDao
{
public void Save(String name,String password);
}
创建一个实现类,将用户信息保存到 MySQL 数据库:
package com.dao;
public class UserDaoMysqlImpl implements UserDao
{
public void save(String name,String password)
{
System.out.println("UserDaoMysqlImpl");
}
}
创建一个实现类,将用户信息保存到 Oracle 数据库:
package com.dao;
public class UserDaoOracleImpl implements UserDao
{
public void save(String name,String password)
{
System.out.println("UserDaoOracleImpl");
}
}
创建一个管理类,将接口对象作为其属性:
package com.manager;
import com.dao;
public class UserManager
{
private UserDao dao;
public void save(String name,String password)
{
dao.save(name,passeord);
}
public UserDao getDao()
{
return dao;
}
public void setDao(UserDao dao)
{
this.dao=dao;
}
}
在 Spring 配置文件 applicationContext.xml ,将 JavaBean 由 Spring 容器来管理:
<beans>
<bean id="mysqlimpl" class="com.dao.UserDaoMysqlImpl">beans>
<bean id="oracleimpl" class="com.dao.UserDaoOracleImpl">beans>
<bean id="manager" class="com.manager.UserManager">
<property name="dao" ref="oracleimpl">property>
bean>
beans>
编写测试类:
public class Test
{
public static void main(String[] args)
{
//读取 Spring 配置文件,创建一个 Bean 工厂
BeanFactory factory=new ClassPathXmlApplicationContext("applicationContext.xml");
//读取 Spring 容器一个称为 hello 的bean,Spring 容器自动创建对象实例
UserManager manager=(Hello)factory.getBean("manager");
manager.save("Evie",123);
//由于配置文件中配置的是注入 oracleimpl ,所以该数据信息保存到 Oracle 数据库中
}
}
通过上面的例子,我们可以看到使用 Spring 容器将不同层级的对象放入容器中,每次使用的时候调用容器中的对象,就不用每次都创建对象,达到节约内存空间的目的。简单来讲**,Spring 容器就是存储 JavaBean 对象**的容器。
IOC ,Inversion of Control,不创建对象,但是描述创建对象的方式,在工程中使用该 Bean 时,由 Spring 容器创建 Bean 的实例,而在代码中不直接与对象和服务连接。简单来说,就是把对象的创建和对象之间的调用过程,全都交给 Spring 容器来管理。
底层:XML 解析、工厂模式、反射机制。IOC 思想基于 IOC 容器,其底层就是对象工厂。
Spring 容器就是 IOC 容器。从以下三个部分回答:
容器概念:IOC 本身就是一个容器,由它来创建 Bean 的实例。本质上就是一个 map(key,value),里面存放各种对象(XML 文件里配置的 bean 节点、@Repository、@service、@controller、@component)。在项目启动时,会读取配置文件里的 bean 节点,,扫描以上注解,根据类的全限定名,底层运用反射机制创建对象实例,放进 map 。
此时,map 里(IOC 容器里)就有各种对象了,现在为止对象仅仅是存储在该容器中,没有被使用。
之后,我们在后续代码中要使用容器里的对象时,再通过 DI 注入(XML 文件里 bena 节点内的 ref 属性、@Autowired、@Resource 等注解)。把属性注入对象里,给对象属性赋值。
控制反转:在没有引入 IOC 之前,对象 A 依赖对象 B,那么对象 A 在初始化或者运行到某一个点的时候,自己必须主动去创建对象 B 或者使用已经创建的对象 B,控制权在对象 A 手中。
引入 IOC 容器之后,获得依赖对象的过程被反转了,控制被反转后(主动变被动)之后,获得依赖对象的过程就由自身管理变成了由 IOC 容器自动注入。对象 A 和 B 就失去了联系,对象 A 和对象 B 都依赖 IOC 容器。当对象 A 运行到需要对象 B 的时候, IOC 容器就会主动创建一个对象 B,注入到对象 A 需要的地方。
控制反转就体现在这里,对象 A 获得依赖对象 B 的过程从主动行为变成了被动行为,控制权颠倒了。全部对象的控制权全部上交给 IOC 容器,相当于是一个“粘合剂”,它把系统中的所有对象粘合在一起发挥作用。
依赖注入:依赖注入是实现 IOC 的方法,由 IOC 容器在运行期间,动态地将某种依赖关系注入到对象之中。
简单来说,就是依靠容器完成对象的创建、赋值、管理等工作,实现业务层对象之间的解耦。
IOC 依靠 DI 实现控制反转:Spring 容器底层使用反射机制创建对象
Aspect-Oriented Programming,面向切面编程,可以看成是 OOP 的补充和完善。OOP 引入封装、继承、多态等概念来建立一种对象层次结构,模拟公共行为的一个集合。但是当我们需要为分散的对象引入公共的行为时,OOP 显得无能为力。
也就是说,OOP 允许定义从上到下的关系,但是不适合定义从左到右的关系。例如日志功能,日志代码往往水平地散布在所有对象层次,并且与对象的核心功能无关。这种散布在各处的无关的代码被称为横切代码,在 OOP 中它导致了大量的代码重复,不利用各个模块的重用。
AOP 技术利用一种“横切”的技术,底层是动态代理,降低模块间的耦合,减少系统的重复代码,有利于未来的维护和扩展。
AOP 是动态代理的规范化,便于开发人员以统一方式使用。
AOP 将软件系统分为两个部分:
创建一个静态代理模式,就实现了一个最简单的 AOP。
@Repository、@Service、@Controller,它们分别对应存储层 Bean,业务层 Bean,和展示层 Bean。
@Repository、@Service、@Controller 和 @Component 将类标识为 Bean。
@Repository:只能标注在 DAO 类上,因为该注解的作用不只是将类识别为 Bean,同时还能将所标注的类中抛出的数据访问异常封装为 Spring 的数据访问异常类型。Spring本身提供了一个丰富的并且是与具体的数据访问技术无关的数据访问异常结构,用于封装不同的持久层框架抛出的异常,使得异常独立于底层的框架。
@Service:通常作用在业务层,但是目前该功能与 @Component 相同。
@Controller:通常作用在控制层,但是目前该功能与 @Component 相同。
@Component:一个泛化的概念,仅仅表示一个组件 (Bean) ,可以作用在任何层次。
@Controller
@Controller(“Bean的名称”)
定义控制层Bean,如Action
@ResponseBody
作用是将 Java 对象转为 JSON 格式数据。将 controller 的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到 response 对象的 body 区,通常用来返回 JSON 数据或者是 XML 数据。
在使用此注解之后不会再走视图处理器,而是直接将数据写入到输入流中,他的效果等同于通过 response 对象输出指定格式的数据。
@ResponseBody是作用在方法上的,表示该方法的返回结果直接写入 HTTP response body 中,一般在异步获取数据时使用(也就是AJAX)。
在使用 @RequestMapping 后,返回值通常解析为跳转路径,但是加上 @ResponseBody 后返回结果不会被解析为跳转路径,而是直接写入 HTTP response body 中。,比如异步获取 json 数据,加上 @ResponseBody 后会直接返回 JSON 数据。
@Service
@Service(“Bean的名称”)
定义业务层Bean
@Repository
@Repository(“Bean的名称”)
定义DAO层Bean
@Component
定义Bean, 不好归类时使用.
@Autowired (Srping提供的)
默认按类型 Type 匹配,自动装配( Srping 提供的),可以写在成员属性或写在 setter 方法上,一定要找到匹配的 Bean,否则抛异常。 默认值就是 true 。
@Autowired
@Qualifier(“bean的名字”)
按名称装配 Bean ,与 @Autowired 组合使用,解决按类型匹配找到多个Bean 问题。
@Resource JSR-250提供的
默认按名称 Name 装配,当找不到名称匹配的 bean 再按类型 Type 装配.
可以写在成员属性上或 sette r方法上
可以通过 @Resource(name=“beanName”) 指定被注入的bean的名称, 要是未指定name属性, 默认使用成员属性的变量名,一般不用写name属性.
@Resource(name=“beanName”)指定了name属性,按名称注入但没找到bean, 就不会再按类型装配了.
@Autowired 根据类型注入,
@Resource 默认根据名字注入,其次按照类型搜索
@Autowired @Qualifie(“userService”) 两个结合起来可以根据名字和类型注入
MVC(Model—View—Controller)模式是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)、控制器(Controller)。
MVC 模式的目的是实现一种动态的程序设计,简化后续对程序的修改与扩展,并且使程序的部分内容能够重复利用。
MVC 的价值:
出现的先后顺序:
Struts –> WebWork –> Struts2 –> Spring MVC
Spring MVC的优势:
注意:实现 Spring MVC 要实现 Controller 接口
Spring MVC 流程图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FpTwpROO-1647328177275)(https://s2.loli.net/2022/02/14/cn6TxfjrMCdHk8D.jpg)]
HandlerMapping 与 HandlerAdaptor
处理器映射器与处理器适配器是 Spring MVC 容器中的对象,分别需要实现接口 HandlerMapping 与 HandlerAdaptor 。
利用 eclipse 开发工具,新建一个 Dynamic Web Project ,将所有依赖的 jar 包放到 WEB-INF/lib 目录下;
Spring MVC 的加载:在 web.xml 增加如下配置
<servlet>
<servlet-name>helloworldservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>helloworldservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
创建一个类,实现 Controller 接口
创建 Spring 的配置文件 .xml ,放到 WEB-INF 目录下,文件内容直接 copy 下面的内容
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
beans>
配置 HandlerMapping 和 HandlerAdapter:在上一步添加的 .xml 文件中添加如下内容
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
配置已经写好的 Controller:继续在 .xml 文件中添加如下内容
<bean name="/自定义名称" class="这里写创建的实现Controller接口的类的路径"/>
创建一个 View,用于展示数据:在 WEBContent 目录下新建一个 .jsp 文件
配置 ViewResolver:在创建的文件 .xml 中添加下面的内容
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
bean>
运行项目,验证是否正确:访问如下路径
http://localhost:8080/这里是项目名/这里写上面第6步中的自定义名称
注意:上面的例子中没有 Module 参与。
以上方法就相当于是接口回调,我们写一个类,就要在 .xml 文件中配置一个 Bean ,效率不高!
所以一般情况下,我们使用反射机制动态回调,系统自己加载所需要的类。一个类就对应一个 Controller ,注意实现 Controller 接口的类名要根据业务来取。
此时,一个类中可以存在多个方法信息,一个方法信息对应一个 URL,实现多个不同的 URL 访问。
Web 开发模式
Spring MVC 创建对象(Controller 控制器对象),并将其存放到 Spring MVC 容器中。
注意区别:Spring MVC 容器是管理控制器对象的,而 Spring 容器是管理 service 和 dao 等对象,是业务层容器。
控制器对象接受请求并显示处理结果,控制器对象可以当作一个 servlet 使用,但它不是 servlet ,Spring MVC 赋予控制器对象相较于 Servlet 的额外功能。
Web 开发底层是 Servlet ,而 Spring MVC 容器中存在 Servlet (DispatherServlet中央调度器)。
DispatherServlet 是一个 Servlet,继承 HttpServlet,它属于前端控制器;
DispatherServlet 对象读取 Spring MVC 的配置文件(XXX-servlet.xml ),创建文件中的 Controller 对象(SpringMVC 容器对象),接受用户请求并分派给 Controller 对象中的某一对应方法。
Spring MVC 执行流程
用户发出请求;
Tomcat(Web 服务器,Java 的 Web 组件)读取 web.xml 配置文件,加载控制器 DispatcherServlet 。并读取文件中的
注意:Tomcat 启动,创建 Spring MVC 的过程(底层源码):
通过
DispatcherServlet 对象执行 init() 方法;
WebApplicationContext ctx=new ClassPathXmlApplicationContext(locations);//locations就是要读取的 SpringMVC 配置文件(XXX-servlet.xml)
getServletContext().setAttribute(key,ctx);//SpringMVC 容器对象放到 ServletContext 中
处理请求:调用 DispatcherServlet 的 doDispatch() 方法:
DispatcherServlet.doDispatch(request,response)
doDispatch() 方法是 DispatcherServlet 的核心方法,所有请求都在这个方法中完成。
DispatcherServlet 根据 Spring MVC 的配置文件(XXX-servlet.xml),得知请求所对应的控制器类(Controller 类)中的某一方法。
注意:在 Spring MVC 的配置文件中,
但是,此时被注解的 bean ( Java 类或方法)并没有被加入到 Spring 容器中,此时需要用:
扫描并注册,表示扫描指定的包,将注解 @controller 的类注册为 Spring 容器中的 bean,那么此时我们在该 XXX-servlet.xml 文件中就不需要定义 bean 了,实现 xml 文件的零配置(VS 上面的创建了一个 SpringMVC 使用的接口回调技术,更为简化了)。
总结: 使用反射机制动态回调更好!也就是说,我们在 XXX-servlet.xml 文件中要写上:
DispatcherServlet 收到存在用户请求对应的处理方法这个信号后,就将该请求转发给控制器类中的这个方法处理。
Spring MVC 框架执行完方法后,对 ModelAndView 进行处理,并转发到结果页面(.jsp)。
请求映射 @RequestMapping:
用于绑定请求地址和业务方法。
注解式开发:扫描器自动扫描匹配,创建对象
@Controller
注解位置:控制器类(controller 类)上方,表示创建此类的对象,且对象放在 Spring MVC 容器中。
作用:创建控制器对象(处理器对象),接受请求并处理请求。
@RequestMapping
注解位置1:处理器方法上方,@RequestMapping(value) ,value 就是请求中的 uri 地址(唯一),以 / 开头 。
注解位置2:类上方,value 可以不写
作用:绑定请求和处理器方法,把指定的请求交给对应的方法处理。
@ResponseBody
注解位置:处理器方法上方
作用:将 JSON 字符串通过 HttpServletResponse 接口响应给浏览器。
@Service
注解位置:业务层类(service 实现类)上方
作用:创建 service 对象。
@Repository
注解位置:持久层类(dao 实现类)上方
作用:创建 dao 对象。
@Value
注解位置:属性上方
作用:给简单基本类型的属性赋值。
@Autowired (属于 Spring 框架注解)
注解位置:属性上方
作用:原理是引用类型的自动注入原理,给引用类型的属性赋值,支持 ByType 和 ByName ,默认 ByType 。
**@Resourse ** (属于 JDK 注解而 Spring 框架提供了功能支持)
注解位置:属性上方
作用:原理是引用类型的自动注入原理,给引用类型的属性赋值,支持 ByType 和 ByName ,默认 ByName 。
@Component
注解位置:非抽象类上方
作用:创建对象。
在 JDBC 编程中,每次创建和断开 Connection 对象都会消耗一定的时间和资源,频繁地创建与断开与数据库的连接必然会影响数据库地访问效率,甚至导致数据库崩溃。
为了避免这种情况的发生,工程师们提出了数据库连接池技术。
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用现有的数据库连接,而不是重新建立。
为了获取数据库连接对象 Connection ,JDBC 提供了 DataSourse 接口(常把实现该接口的类称为数据源,数据源中存储了所有建立数据库连接的信息),它负责与数据库建立连接,并定义了返回值为 Connection 对象的方法。
常用的数据源:
DBCP:数据库连接池(DataBase Connection Pool)的简称,是 Apache 组织下的开源连接池实现,也是 Tomcat 服务器使用的连接池组件。单独使用 DBCP 数据源时,需要导入两个 jar 包:commons-pool.jar 与 commons-dbcp.jar 。
commons-dbcp.jar 包中含有两个核心类:BasicDataSourseFactory (DataSourse 接口的实现类)和 BasicDataSourse ,它们都包含获取 DBCP 数据源对象的方法。
C3P0:目前最流行的开源数据库连接池之一,它实现了 DataSourse 接口,易于扩展且性能优越
MyBatis 是服务于 Java 平台的 SQL 映射框架,是一个 ORMapping 工具,通过配置 XML 文件,使用第三方数据源,从而实现了对操作数据库的托管,相当于增强版的 JDBC 。
MyBatis 是对 JDBC 的封装,它让数据库底层操作变得透明,MyBatis 的操作都是围绕一个 sqlSessionFactory 实例展开的,MyBatis 经过配置文件关联到各实体类的 Mapper 文件。Mapper 文件中配置了每一个类对数据库所需进行的 SQL 语句映射。在每次与数据库交互时,经过 sqlSessionFactory 拿到一个 sqlSession,再执行 SQL 命令。
MyBatis 的 SQL 映射:将数据库表中的数据映射为 Java 对象,将 Java 对象拆分成数据库表中的数据。
MyBatis 的功能:
MyBatis 的动态代理:
动态代理指的是 MyBatis 创建并实例化 Dao 接口的实现类,调用 SqlSession() 方法执行 SQL 语句。
动态代理的实现方式:
SqlSession sqlSession=MyBatisUtil.getSqlSession();//获取SqlSession对象
StudentDao dao=sqlSession.getMapper(StudentDao.class);//获取接口的实现类对象
List<Student> students=dao.selectStudents();//调用dao接口的方法操作数据库
动态代理的要求:
Dao 接口和 mapper 文件的名称一致,位置一致(在同一个包下),Dao 接口不支持方法重载;
其他的要求在下面 MyBatis 的使用中说明。
在 XML 文件中配置数据源(主配置文件 beans.xml),定义数据库的配置信息和各 SQL 映射文件的位置。
在 MyBatis 的 SQL 映射配置文件(mapper.xml)中,需要使用 paramterType、resultType 来设置 SQL 语句的输入输出参数,一般参数都是基本的数据类型或封装类型,但都需要声明该类型的全路径 java.lang.String,或者 cn.njupt.po.Student, 这时就可以通过 typeAliases 别名来设置,简化复杂度。
当通过设置 MyBatis 的全局配置文件 (MyBatis.xml) 中的 typeAliases 属性后,就可以为 sql 映射文件中的输入 / 输出参数设置类型别名,然后在 SQL 映射配置文件中指定输入输出参数类型时,使用配置的别名。
在 XML 文件中配置 MyBatis 主类(mapper 文件),mapper 文件就是指 SQL 映射文件。
<mapper namespace="Dao接口的全限定名">
<select id="queryById" parameterType="String" resultType="Student">
select * from students where sid='${sid}'
select>
<insert id="RegisterInfo" parameterType="Student">
insert into students (sid,sname,age,password) values(#{sid},#{sname},#{age},#{password})
insert>
mapper>
占位符 # 和连接符 $ :
占位符 # :能够避免 SQL 注入,安全性更高,执行 SQL 语句时使用 PreparedStatement ,效率更高;
连接符 $ :具有 SQL 注入攻击风险,安全性更低,执行 SQL 语句时使用 Statement ,效率更低。
parameterType 属性:
表示 Dao 接口中对应方法的形参数据类型;值既可以是 Java 的数据类型全限定名,也可以是 MyBatis 定义的别名;该属性并非强制性的,MyBatis 通过反射机制能够发现接口方法的参数数据类型。
resultType 属性:
用于指定 SQL 语句的执行结果类型,其值既可以是 Java 的全限定名,也可以是 MyBatis 设定的别名。
resultMap 属性:
用于指定 resultMap 用于指定数据库表列名和 Java 对象属性的映射关系;主键使用标签
如果列名于属性名不一致:使用 resultMap 指定映射关系。
模糊查询:
在 Java 代码中指定 like 的内容,在 mapper 文件中指定 like 的内容(拼接)。
MyBatis 中的简单数据类型为基本数据类型和 String 类型;
自定义参数名:@Param(“参数名”)数据类型 形参名