Spring
spring简介
spring是什么
spring是分层的javaSE/EE 应用full-stack(全栈)轻量级开源框架,以ioC(Inverse Of Control: 反转控制) 和 AOP(Aspect Oriented Programming: 面向切面编程) 为核心。提供了展现层SpringMVC 和持久层 SpringJDBCTemplate 以及业务层事务管理等众多的企业级应用技术。
spring的优势
- 方便解耦,简化开发
通过Spring提供的ioC容器,可以将对象间的依赖关系交由Spring进行控制,避免了硬编码所造成的过度耦合。用户也不必为单例模式类、属性文件解析等这些很底层的需求编写代码,可以专注于上层的应用。 - AOP编程的支持
通过Spring 的AOP功能,方便进行面向切面的编程 - 声明式事务的支持
- 方便程序的测试
- 方便集成各种优秀的框架
- 降低了javaEE API的使用难度
spring 的体系结构
Spring 快速入门
程序开发步骤
1、通过maven导入spring框架
2、实现需要的class以及相关的方法
- 创建Bean
3、创建并配置xml文件(ApplicationContext.xml)
4、得到ApplicationContext对象,并且,通过app的getBean方法以及ID拿到我们需要的对象
Spring 配置文件
Bean 标签的基本配置
用于配置对象交由Spring来创建
默认情况下,它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功
基本属性
ID:bean实例在Spring容器中的唯一标识
class:Bean的全限定名称
Bean标签的范围配置
scope:指对象的作用范围,取值如下:
- singleton ==默认值,单例的==.每次getBean时得到的是同一个对象
- prototype ==多例的==。每次getBean时得到的是不同的对象。
- request WEB项目中,Spring 创建一个Bean对象,将对象存入到request域中
- session WEB项目中,Spring创建一个Bean的对象,将对象存入session域中
- global session WEB项目中,应用在portlet环境,如果没有Portlet环境那么gobalsession相当于session
singleton:javaBean 在加载spring文件,创建spring容器时创建
prototype:在getBean时才创建bean
Bean 的声明周期配置
init-method:指定类中的初始化方法名称
destroy-method:指定类中销毁方法的名称
Bean实例化的三种方式
无参构造方法实例化
1
2<!--无参构造方法-->
<bean id="userDao" class="com.xinyu.dao.impl.UserDaoImpl" scope="singleton" init-method="init" destroy-method="destroy"></bean>工厂静态方法实例化
1
2
3
4
5
6
7
<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">
<bean id="userDao" class="com.xinyu.factory.StaticFactory" factory-method="getUserDao" scope="singleton" ></bean>
</beans>
- 工厂实例方法实例化
1 | <!-- 工厂实例方法实例化--> |
Bean的依赖注入分析
若把UserService实例与UserDao实例都存在Spring容器中,当前的做法是在容器外部获得UserService实例和UserDao实例,然后在程序中进行结合。
有更好的实现方法:
依赖注入的概念
它是Spring框架核心IOC的具体实现
在编写程序时,通过控制反转,把对象的创建交给了Spring,但是代码中不可能出现没有依赖的情况。
IOC解耦只是降低了他们的依赖关系,但是不会消除。例如:业务层仍然会调用持久层的方法
那这种业务层和持久层的依赖关系,在使用Spring之后,就让Spring来维护了。简单来说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。
依赖注入的方式
构造方法注入
1
2
3
4
5
6
7
8
9//需要在service 层,引入需要注入的成员
private UserDao userDao;
// 用于构造方法注入
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
public UserServiceImpl() {
}1
2
3
4
5
6
7<!--无参构造方法-->
<bean id="userDao" class="com.xinyu.dao.impl.UserDaoImpl" scope="singleton"></bean>
<!-- 用于构造方法注入-->
<bean id="userService" class="com.xinyu.service.impl.UserServiceImpl">
<!-- constructor-arg 后面的name是需要构造的参数的名字。我们这里是private UserDao userDao中的名字。ref 引用了上面Bean 的id-->
<constructor-arg name = "userDao" ref="userDao" ></constructor-arg>set方法
需要在UserService中引入一个set方法,得到一个UserDao。1
2
3
4
5
6
7<!--无参构造方法-->
<bean id="userDao" class="com.xinyu.dao.impl.UserDaoImpl" scope="singleton"></bean>
<!-- 生成UserService这个class-->
<bean id="userService" class="com.xinyu.service.impl.UserServiceImpl">
<!-- name代表的是Set后面的name,并且把setUserDao 中的UserDao变为userDao ref引用的是Bean的id userDao-->
<property name="userDao" ref="userDao"></property>
</bean>-
1
2
3
4
5
6
7
8
9
10public class UserServiceImpl implements UserService {
//在这个class中私用成员,并且在xml中把它依赖注入。即通过spring,实现setUserDao这个功能
private UserDao userDao;
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
public void save(){
userDao.save();
}
}
简便版本;P命名空间注入。
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
1 | <!-- p命名空间注入方式--> |
Bean依赖注入的数据类型
除了在Bean 里面注入对象的引用之外,普通数据类型、集合等都可以在容器中进行注入。
注入数据的三种数据类型
普通数据类型
1
2
3
4
5
6
7
8//在UserDaoImpl内部
int a;
public int getA(){
return a;
}
public void setA(int a){
this.a = a;
}1
2
3<bean id="userDao" class="com.xinyu.dao.impl.UserDaoImpl" scope="singleton">
<property name="a" value="3"></property>
</bean>引用数据类型(主要是Bean类型)
集合数据类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<bean id="userDao" class="com.xinyu.dao.impl.UserDaoImpl" scope="singleton">
<property name="a" value="3"></property>
<property name="stringList">
<list>
<value>aasfa</value>
<value>aasfa</value>
<value>aasfa</value>
</list>
</property>
<property name="properties">
<props>
<prop key="p1">ppt1</prop>
<prop key="p2">ppt2</prop>
</props>
</property>
</bean>
引用其他配置文件(分模块开发)
实际开发中,Spring的配置内容非常多,这就导致了Spring的配置很繁杂。所以可以将部分配置拆解到其他配置文件中,而在Spring 的主配置文件通过import标签进行加载。
1 | <import resource = "applicationContext-xxx.xml"/> |
Spring相关API
ApplicationContext 的继承体系
applicationContext:接口类型,代表应用上下文,可以通过其实例获取Spring容器中的Bean对象。
图中,紫色的是接口,绿色的浅色是抽象类,深色的绿色是两个实现。
- ClassPathXmlApplicationContext
它是从类的根路径下加载配置文件,推荐使用这种 - FileSystemXmlApplicationContext
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置 - AnnotationConfigApplicationContext
当使用注解配置容器对象时,需要使用此类来创建spring容器,它用来读取注解。
getBean 方法的使用
1、传入字符串,即xml中Bean 的id,来得到对象。
2、传入某个的class 的字面常量,即类名.class
Spring配置数据源(连接池)
数据源的作用
数据源是提高程序性能出现的
事先实例化数据源,初始化部分连接资源
使用连接资源时从数据源获取
使用完毕后,将连接资源归还给数据源
数据源的配置文件
1 | com.mysql.cj.jdbc.Driver = |
Spring配置数据源
将DataSource 的创建权交给Spring
1 |
|
Jdbc配置文件的抽取
上面的代码可知,Spring中把数据库连接的方式包括了。所以需要把数据库的配置方式抽取出来,让spring来加载这个properties文件
步骤:
1、引入context命名空间和约束路径:
命名空间
1
xmlns:context="http://www.springframework.org/schema/context"
约束路径
1
2http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 加载外部的properties文件-->
<context:property-placeholder location="classpath:c3p0.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
</beans>
Spring 注解开发
Spring是轻代码而重配置的框架,配置比较繁重,影响开发速率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。
Spring原始注解
Spring的原始注解主要是替代
使用注解开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类,字段和方法。
1 | <context:component-scan base-package="com.xinyu"/> |
注解详解
@Component(“id”) 用于创建Bean,每个层都能用,放入spring容器中
@Controller(“id”) 用于Web层创建Bean
@Service(“id”) 用于service层
@Repository(“id”) 用于dao层
注入
@Autowired:按照数据类型从Spring容器中进行匹配注入
@Qualifier:按照id值从容器中进行匹配。
注意,@Qualifier 必须搭配@Autowired一起使用
1 | //@Component("userService") |
@Resource(name = “id”)
这个注入相当于@Autowired+@Qualifier
普通字符串的注入
@Value(string)
1 | "${jdbc.driver}") ( |
@Scope(“singleton”)&@Scope(“prototype”)
控制产生单例或者多个Bean
Spring 新注解
原始注解对于非自定义的bean等无法进行注解,所以无法全部代替xml配置文件。所以需要新注解
1 | /* |
AOP
AOP(Aspect Oriented Programming) 意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP 的作用和优势
作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强。
优势:减少重复代码,提高开发效率,并且便于维护。
实现原理:当多个函数都会去调用另外一个函数时,传统选择是使用每个函数来引用。但是这样使得函数间耦合了。所以我们选择在内存中运行时把他们结合到一起,而不是引用他们。那么如何在内存中使得他们运行呢,就是使用配置文件。
AOP的底层实现
AOP的底层是通过Spring提供的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态生成代理对象,代理对象执行时进行增强功能的介入。再去调用目标对象的方法,从而完成功能的增强。
常用的动态代理技术
- JDK代理:基于接口的动态代理技术
- cglib代理:基于父类的动态代理技术
AOP的相关概念
Target:被代理的目标对象
Proxy:一个类被AOP织入 增强后,产生的一个结果代理类
JoinPoint:连接点,指的是那些被拦截到的点。在Spring 中这些点指的是方法。因为spring只支持方法类型的连接点。
PointCut:切入点。指我们要对哪些Joinpoint进行拦截的定义。即,已经被增强的点,为切入点。
Advice:通知/增强。是指拦截到Joinpoint之后要做的事情。
Aspect:切面。是指切入点和通知的结合
Weaving:织入。
基于XML的AOP开发
1、导入AOP相关坐标
2、创建目标切面和目标类
3、创建切面类(内部有增强方法)
4、将目标类和切面类的对象创建权交给spring
5、在applicationContext.xml中配置织入关系
6、测试代码
配置文件
1 |
|
切点表达式的写法
excution([修饰符]返回值类型 包名.类名.方法名(参数))
访问修饰符可以省略
返回值类型、包名、类名、方法名可以使用* 代表任意
包名与类名之间一个.代表当前包下的类,两个.表示当前包及其子包下的类
参数列表可以使用两个点.表示任意个数,任意类型的参数列表
第二个:Target类下的任意方法,参数任意
第三个:任意返回值,aop包下的任意类的任意方法,参数任意
第四个:aop及其子包下的任意类的任意方法
通知的类型
基于注解的AOP开发
1 |
|
1 | "myAspect") ( |
Spring集成Web开发环境
在servlet的doGet之类的代码中,如果每次都去加载spring的xml文件,就会很麻烦。我们只需要加载一次xml文件,从一个容器中来获取需要的类
我们可以使用ServletContextListener监听Web应用的启动。我们可以在Web应用启动的时间,就加载spring的配置文件。创建应用上下文对象ApplicationContext,在其存储到最大的域,servletContext域中。这样就可以在任意位置中获取应用上下文ApplicationContext对象了。
1 | public class ContextLoaderListener implements ServletContextListener { |
1 | <!-- 配置监听器--> |
1 | //servlet中使用 |
可以看到,我们在没有访问网页的情况下,监听器已经加载了xml文件,spring容器创建完毕了。这样在servlet
中,我们就可以通过servletContext
来得ApplicationContext
了
优化:上面的代码有一些耦合的地方,我们需要进行一些改进。
1、在listener中,我们直接把applicationContext.xml
文件名写了上去,导致耦合。我们应该把它放入WEB-INF
的全局变量中,通过代码获得文件名称来加载文件。
2、我们直接把加载的applicationContext.xml
叫做app。但是,它也可以不叫app。所以也需要我们优化。
完整版
1 | public class ContextLoaderListener implements ServletContextListener { |
1 |
|
Utils
1 | public class WebApplicationContextUtils { |
servlet
1 | public class UserServlet extends HttpServlet { |
spring 提供的监听器
spring提供的监听器:ContextLoaderListener
对上诉功能的封装。该监听器内部加载spring配置文件,创建上下文对象。并存储到ServletContext域中。提供一个客户端工具WebApplicationContextUtils供使用者获取应用上下文对象。
步骤:
- 在web.xml中配置ContextLoaderListener监听器(导入spring-web坐标)
- 使用WebApplicationContextUtils获取应用上下文对象ApplicationContext
1、web-xml中导入spring自带的ContextLoaderListener
1 | <listener> |
在userServlet中调用
1 | ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext); |
SpringMVC
SpringMVC是一种基于JAVA实现的MVC设计模型的请求驱动类型的轻量级Web框架,属于SpringFrameWork的后续产品。已经融合在Spring Web Flow中了。
我们的前端控制器就是SpringMVC。
使用过程:
- 1、导入SpringMVCjar包
1 | <dependency> |
- 2、配置servlet
1 | <!-- 配置spring-mvc的前端控制器--> |
- 3、编写Controller(Web层的javaBean)
- 4、将Controller使用注解配置到spring容器中。
1 | /* |
- 5、配置spring-mvc.xml文档
1 |
|
注意要把这个xml文档加载。需要在WEB-INF文件下的web.xml中进行配置。具体过程看上图。
注意:可能在maven中导入坐标之后,项目的artifacts的lib中没有jar包,需要我们手动添加一下。
springMVC的组件解析
==RequestMapping()==
用于建立URL和处理请求方法(Controller)直接的对应关系
在class上进行注解的话,这就是URL的第一层访问路径。如果不写的话,我们访问方法的路径就是从根目录开始。
在方法上进行注解。这用于我们具体需要访问的某个资源。和class上的注解同时使用来分开不同的访问资源。
但是当我们使用这样的方法的时候,会发现,网页报错
这是因为,我们返回的字符串是success.jsp
,它默认为我们当前路径下的地址。
所以我们需要返回/success.jsp
这相当于我们从web目录下寻找资源。
注解具体的参数
- value=”/xxx” 单独写URL时,这个value可以省略。
- method=RequestMethod.GET/POST 设定我们要访问的方法必须是GET/POST
注意,我们使用post请求的情况是:用get请求加载了登录的界面了。当我们点击登录的时候,发送post请求,然后写一个方法来接受这个post请求。网页初始的请求都是get请求,我们不能直接访问到我们所写的post请求处理的方法的。
- params = {“username”} 代表了我们访问这个网页的时候,必须带有username这个参数。
springMVC的XML文件解析
除了配置controller的组件扫描,我们还可以配置内部资源的视图解析器
数据响应
1、页面跳转
- 直接返回字符串
- 通过ModelAndView对象返回
2、回写数据
直接返回字符串
当我们想把一个类response到网页上是json格式的字符串时,我们需要在maven中导入jackson相关的坐标
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.12.0</version>
</dependency>然后,在controller中调用,使得把这个类转化为string。
1
2
3
4
5
6
7
8
9
10
11
12"/modelAndView5") (
public String write3() throws IOException {
user user = new user();
user.setAge(21);
user.setName("儿砸");
user.setGender("girl");
// jackson 关系映射
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(user);
return json;
}
- 返回对象集合
但是这个是比较麻烦的。我们每次返回json都需要去生成。所以使用在mvc.xml中配置RequestMappingHandlerAdapter,给它的property传入MappingJackson2HttpMessageConverter。这样,我们可以直接返回一个类,spring会把它转为string,传入web。并且它的默认字符集是utf-8。
1 | <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> |
1 | "/modelAndView6") ( |
但是这样还是有一点麻烦的。所以springMVC提供了注解驱动。
获取请求数据
1、获得基本请求参数
Controller中的业务方法的参数名称,要与请求参数的name一致,参数值会自动匹配
1 | "/getdata1") ( |
2、获得POJO(javaBean)类型参数
需要我们请求的参数与javaBean中参数名称对应
1 | "/getdata2") ( |
3、获取数组类型参数
1 | "/getdata3") ( |
网页上回传的数据名称都为names。
4、获取集合类型的参数
获得集合参数时,需要把集合参数,封装到jopo中,作为javaBean的参数.提交页面时,使用post方式。
静态资源的获取:通过在spring-mvc.xml中加入
<mvc:default-servlet-handler/>
。使得框架找不到的静态资源,让tomcat找
请求数据乱码问题
post表单中。我们提交中文会出现乱码问题。
解决方法: 在web.xml中配置一个全局过滤的filter
1 | <!-- 配置一个全局过滤的filter.解决post请求乱码问题--> |
参数绑定注解
用于当我们网页上返回的参数名称和接受中不一样时,我们把它们做映射。
1 | "/getdata5") ( |
@RequestParam(value = "name",required = false)
不返回数据也不会报错.若再添加defaultValue="xinyu"
,则,不输入数据,返回默认值-xinyu
获取Restful风格的参数
Restful是一种软件的架构风格。而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务端交互类的软件。基于这种风格设计的软件更简洁,更有层次。
Restful风格的请求是使用“url+请求方式”表示一次请求的目的,HTTP协议里面四个表示操作方式的动词如下:
- GET:用于获取资源
- POST:用于新建资源
- PUT:用于更新资源
- DELETE:用于删除资源
自定义类型转换器
springMVC已经提供了一些常用的类型转换器,但是不是所有的数据类型都提供了转换器。没有提供的需要我们自己写自定义转换器。例如:日期类型的数据需要我们自己写。
步骤
1、定义转换器类实现Converter接口
2、在配置文件中,声明转换器
3、在<annotation-driven
中引用转换器。
1 | //定义接口 |
1 | <!-- 声明日期转换器--> |
获取Servlet原生的API
springMVC获取请求数据
1 | // 获取请求头数据 |