一、什么是spring
1.什么是Spring
spring是一个开源的轻量级的企业级框架;目前最流行的一个框架;
两个核心的思想是IOC控制反转(DI)和AOP面向切面编程;
spring是一个一站式的框架;
2.spring的特点
非侵入式的设计:pojo(plain old java object);
ioc:反转控制;Inversion of Control
aop:面向切面编程;Aspect Oriented Programming
容器:spring就是一个大容器,用来创建所有bean,并且可以设置bean之间的依赖关系;
一站式:既包括控制器,又包括业务层和持久层,同时可整合其它框架;
3.开发第一个spring项目
创建一个maven项目(jar)
在maven项目中导入 spring-context(aop,core,spel,beans)
创建一个配置文件;applicationContext.xml
创建一个工厂;ApplicationContext factory=new ClassPathXmlApplicationContext("applicationcontext.xml");
二、IOC部分
4.ioc
ioc:反转控制;
对象与对象之间的依赖关系不是由一方来控制的,而是由第三方来控制;
IOC(Inversion of Control):其思想是反转资源获取的方向. 传统的资源查找方式要求组件向容器发起请求查找资源. 作为回应, 容器适时的返回资源. 而应用了 IOC 之后, 则是容器主动地将资源推送给它所管理的组件, 组件所要做的仅是选择一种合适的方式来接受资源. 这种行为也被称为查找的被动形式
DI(Dependency Injection) : IOC 的另一种表述方式:即组件以一些预先定义好的方式(例如: setter 方法)接受来自如容器的资源注入. 相对于 IOC 而言,这种表述更直接
servlet 与 service;两者之间是关联或依赖的关系
如果service变化,servlet代码必须修改;
如果想在servlet中使用service,可以由第三方注入;
在Spring的IOC容器里配置bean:
1.在xml中通过bean节点配置
<!-- 通过类名的方式配置bean -->
<bean id="school" class="com.oracle.vo.School">
<property name="name" value="东北林业大学"></property>
<property name="year" value="1972"></property>
</bean>
id:Bean 的名称。
- 在 IOC 容器中必须是唯一的
- 若 id 没有指定,Spring 自动将权限定性类名作为 Bean 的名字
- name 可以指定多个名字,名字之间可用逗号、分号、或空格分隔
2.通过java注解配置bean
例如@Service、@Component、@Repository、@Controller
5.Spring为我们提供了两种类型的容器;
- BeanFactory;(IOC容器的基本实现,是Spring框架的基础设施,面向Spring本身)
- ApplicationContext;(是BeanFactory的子接口,提供了更多高级特性,面向Spring框架的开发者)
在 Spring IOC 容器 读取 Bean 配置 创建 Bean 实例之前, 必须对它进行实例化. 只有在容器实例化后, 才可以从 IOC 容器里获取 Bean 实例并使用.
三个常用子接口:
ClassPathXmlApplicationContext:从类路径中加载配置文件的工厂;
FileSystemXmlApplicatonContext:从文件系统中加载配置文件的工厂;(项目的可移植性会较差)
WebApplicationContext:为web应用准备的工厂,从web目录加载配置文件;
6.常用方法:
只有类中仅含一个实例时才可以通过类名获取对象,否则会报错,应使用id获取。
也可根据名字获取对象,一个对象可以有多个名字。
创建对象是无状态的,默认单例。
7.依赖注入的三种方式
属性注入(setter方法)实际上最常用
构造器注入(构造方法)
工厂注入(很少使用,不推荐)
8.属性注入
1.使用setter方法对属性赋值
2.在xml中使用<property>标记进行注入(做了两件事:1创建一个user 2.set其属性)
<bean id="" class="">
<property name="属性名" value="属性值"/>(通过set方法)
</bean>
若无setName方法,则<property name="属性名" value="属性值"/>无效(xml标记其实也是使用setter)
若value中有特殊符号如<,可使用<value>,<![CDATA[]]>
9.构造器注入
使用构造方法为属性赋值,属性事先定义好构造方法,方可使用
在bean中使用<constructor-arg>标记进行注入
<bean id="p" name="zhang,sky" class="com.oracle.vo.Person" scope="prototype">
<constructor-arg index="0" type="String" value="张三丰"></constructor-arg>
<constructor-arg index="1" type="int" value="1"></constructor-arg>
</bean>
在标签中使用type属性决定调用哪个构造方法
10.注入字面值
1.可以使用value属性
2.可以使用value子标记,且可以使用<![CDATA[]]>结来注入特殊字符
基本数据类型及其封装类、String等都可采取字面注入方式
11.注入引用
使用ref属性(引入其他bean)
<bean id="p" class="com.oracle.vo.Person">
<property name="name" value="小猴"></property>
<property name="age" value="18"></property>
<property name="dept" ref="dept"></property>
</bean>
12.内部bean;(不能被定义在其他地方),只定义在某一个bean中。(定义在外部的bean是其他所有bean都可引用的)
<bean id="s" class="com.oracle.vo.Student">
<property name="name" value="shy"></property>
<property name="age" value="21"></property>
<property name="school">
<bean class="com.oracle.vo.School">
<property name="name" value="北京大学"></property>
<property name="year" value="1963"></property>
</bean>
</property>
</bean>
13.<null>标记,给属性赋null值
<bean id="s2" class="com.oracle.vo.Student">
<property name="name" value="shy"></property>
<property name="age" value="21"></property>
<property name="school">
<null></null>
</property>
</bean>
14.级联赋值:spring给属性赋值时,支持级联赋值,但被赋值的对象不能为null
<bean id="s2" class="com.oracle.vo.Student">
<property name="name" value="shy"></property>
<property name="age" value="21"></property>
<property name="dept.dname" value="开发部"></property>
</bean>
15.注入集合
<array>
<list>
<set>
<map> <entry>
<props>(支持持久化,可用来连接数据库)
如果集合中的元素类型是引用类型时,可使用ref或bean
<!-- 注入技能,技能是list类型 -->
<property name="skills">
<list>
<value>java</value>
<value>mysql</value>
<value>mybatis</value>
<value>junit</value>
</list>
</property>
<property name="edus">
<list>
<value>东北林业大学</value>
<value>哈尔滨第三中学</value>
<value>哈尔滨花园小学</value>
</list>
</property>
<!-- 注入map类型 -->
<property name="subjects">
<map>
<entry key="mysql" value="90"></entry>
<entry key="javase" value="89"></entry>
<entry key="spring" value="93"></entry>
</map>
</property>
<property name="weapons">
<list>
<bean class="com.oracle.vo.Weapon">
<property name="name" value="丈八蛇矛"></property>
<property name="power" value="3"></property>
</bean>
<bean class="com.oracle.vo.Weapon">
<property name="name" value="青龙偃月刀"></property>
<property name="power" value="4"></property>
</bean>
<ref bean="bow"/>
</list>
</property>
<property name="props">
<props>
<prop key="name">mysql</prop>
<prop key="password">123456</prop>
<prop key="driver">com.mysql.jdbc.Driver</prop>
</props>
</property>
16.如果想重复的使用一个集合引用时,可以通过util命名空间(现在nameSpace中勾选util)来给集合引用命名;这样就可以通过<ref bean="名称"/>来重复使用此集合了。否则集合无法重复使用
<util:list id="list">
<value>东北林业大学</value>
<value>哈尔滨第三中学</value>
<value>哈尔滨花园小学</value>
</util:list>
<property name="edus">
<ref bean="list"/>
</property>
17.p 命名空间可以简化属性的注入;语法如下(需在nameSpace中勾选)
<!-- 定义一个武器bean -->
<bean id="bow" class="com.oracle.vo.Weapon" p:name="麒麟弓" p:power="20">
</bean>
18.自动注入
spring支持属性的自动注入,可以根据类型,名称来完成自动注入;
若上下文中与属性中有相同类型或名称,则自动装配
自动注入的类型有:
1.byType:按类型
2.byName:按名称
3.constructor(不推荐)
<bean id="p" class="com.oracle.vo.Person" autowire="byName">
<property name="name" value="赵云"></property>
</bean>
19.bean的作用域;
可以在bean标记中使用scope来设置bean作用域;scope一共有四个值,分别是;
singleton:单例
prototype:原型,每次调用getBean时,都会创建一个新对象;
request:当次请求
session:当前会话
20.导入属性文件(想额外定义时可用此方法)
使用context:placeholder标记,同时使用${}语句引用;
<context:property-placeholder location="classpath:db.properties"/>
<bean id="p" class="com.oracle.vo.Person" autowire="byName" scope="prototype" >
<property name="name" value="${name}"></property>
<property name="age" value="${power}"></property>
</bean>
21.SpEL(了解即可)
Spring表达式语句,可以在Spring的配置文件中使用,语法类似于EL;
22.ioc容器创建的bean的生命周期
1.实例化
2.注入属性
3.初始化方法(如果有),init-method属性指定;
4.使用;
5.销毁方法(如果有),destory-method属性指定
23.使用注解来创建bean实例;
@Component: 代表任何组件;
@Repository:代表持久层组件,Dao
@Service:代表业务层组件,Service
@Controller:代表控制层组件,Conroller;
24.关于context-scan标记的用法
可以使用base-backage来设置需要扫描的包,如果base-backage="com.oracle",那么将会扫描com.oracle中所有子包的类;
如果想扫描多个包,那么可以在base-backage中用,分隔;
可以通过resource-pattern来进一步过滤
*.class:只代表当前包上的类
*/*.class:代表下一层子包的所有类;
**/*.class:代表所有子包中的类;
25.使用context:include-filter及context:exclude-filter进行过滤;
可以使用的过滤类型有annotation及assignable;
annotation:annotaion进行过滤
assignable:类的继承关系;
注意,component-scan使用了一个默认的过滤器,此过滤器包含了@Component,@Repostitory,@Service,@Controller四种注解,如果不想使用默认的过滤器,则可以使用use-default-filters="false"来进行修改;
26.@scope注解可以设计bean的作用域,值分别是singleton,prototype,request,session
@Repository
@Scope("prototype") 一般来说都创建无状态的singleton
public class PersonDao
27.自动装配的注解;
@Autowired (用的最多,Spring提供的)Spring;
@Resource JSR250规范
@Inject JSR330规范(不建议使用,使用时需要导包,且包是2009年的)
ps:可以在@Autowired后加(required=false),即能在上下文中找到就找,找不到不注入也不报错。但一般不用。
28.@Autowired的装配规则
1.@Autowired会自动完成装配;如果在容器没找到,则会抛出异常,但我们可以使用@Autowired(required=false)来设置是否需要装配;
2.@Autowired先按类型匹配,如果找到多个bean,则按名称匹配,如果没有相同的名称,则抛出异常
3.可以使用@Qualifier("name")来指定注入的bean的名称; (若同一类型有多个)
29.@Resource的作用与@Autowired相同;区别是
@Resource先匹配名称,再匹配类型;如果有相同类开的多个bean时,可以使用@Resource("名称来指定")
AOP部分
1.代理模式
解决的问题:当无法直接访问或没有权限访问真实对象时,可以使用代理模式,代理对象包含了真实的引用,同时还可以增加一些额外的功能;
2.代理模式结构
抽象角色:
具体角色:
代理角色:
3.房屋出租的案例;
Rantable:可出租的;
Houser:房东;(目标对象)
Proxy:中介(代理对象)
动态代理
如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决
Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下几个类和接口
class Proxy: 动态代理类,程序运行时动态创建代理对象
interface InvocationHandler: 调用处理接口
class Method:接口或类的方法信息
4.动态代理API;
Proxy:用来生成代理对象;(需要提供抽象角色和真实对象,该类即可生成代理对象)
InvocationHandler:调用处理接口;(调用代理对象.某方法时,会调用该接口中的invoke方法)
invoke(); (可以将重复的代码写入该方法)
5.如何开发一个动态代理类
1.定义一个调用处理器(实现InvocationHandler接口),并重写invoke方法;
2.利用Proxy 提供的newProxyInstance()来创建代理对象;
6.SqlSession.getMapper(接口);
接口+annotation;(jdk的动态代理没有接口无法实现)
public interface BookDao{
@Sql("insert into book values(null,'',48)")
public void save();
}
7.实现动态代理的两种技术;
jdk动态代理;必须实现接口;
InvocationHandler;
Proxy;
cglib;创建一个类的子类;不需要实现接口;
MethodInterceptor;
Enchacer;
8.Aop:面向切面编程;
Aop是面向切面编程,可以将分散在各个模块(方法)中相同的功能抽取出来;
OOP是纵向的抽取,可以抽取出类,方法,属性;但并不能将方法中相同的相同的部分(功能代码)抽取出来.
因此AOP并不能取代OOP,它只是对OOP的补充;
9.AOP要相关概念;
1.增强(advice):额外增加的功能;Spring中支持5种增强;分别是;前置,后置,环绕,返回,异常;
2.连接点(joinPoint):加入增强(advice)的位置;目前Spring只支持方法;
3.切入点(PointCut):加入增强(advice)的具体位置;一般通过一个表达式来表示;
4.切面(Aspcet): 增强+切入点;
5.目标对象(Target):没加入切面的真实对象
6.代理对象(Proxy):加入切面后的对象;
7.引介(Introduction):动态增加功能;
8.织入(weawing):将切面加入到目标对象中,形成代理对象的过程;
织入有三种类型,分别是:
①编译期:
②类加载期:
③运行期:动态代理;
10.AOP的实现者
AspectJ:(开源组件,Spring支持,是Java社区中最完整最流行的AOP框架)
SpringAOP:(Spring提供的)
11.使用AspectJ来实现AOP
1.导包;
2.定义切面(advice+pointcut)
3.利用spring来创建代理对象;
12.在Spring中启用AspectJ注解支持
1.要在 Spring 应用中使用 AspectJ 注解, 必须在 classpath 下包含 AspectJ 类库: aopalliance.jar、aspectj.weaver.jar 和 spring-aspects.jar
2.将 aop Schema 添加到 <beans> 根元素中
3.要在 Spring IOC 容器中启用 AspectJ 注解支持, 只要在 Bean 配置文件中定义一个空的 XML 元素 <aop:aspectj-autoproxy>
4.当 Spring IOC 容器侦测到 Bean 配置文件中的 <aop:aspectj-autoproxy> 元素时, 会自动为与 AspectJ 切面匹配的 Bean 创建代理.
13.用AspectJ注解声明切面
1.要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ 切面之后, Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 Bean 创建代理.
2.在 AspectJ 注解中, 切面只是一个带有 @Aspect 注解的 Java 类.
3.通知是标注有某种注解的简单的 Java 方法.
4.AspectJ 支持 5 种类型的通知注解:
@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行
14.切入点重复使用
如果一个切入点需要重复使用,则可以定义一个切入点(使用@Pointcut注解),然后在增加中使用这个定义好的切入点
@Pointcut("execution(* com.oracle.vo.*.*(..)) || execution(* com.oracle.dao.*.*(..))")
private void mypointCut() {}
@Before("mypointCut()")
public void before2() {
System.out.println("这是一个前置方法");
}
15.定义多个切入点
如果定义多个切面,可以使用@Order()来确定增加切面的顺序;(放在Aspect后,Order值小的先加)
@Component
@Aspect
@Order(3)
public class MyAdvice {}
16.JdbcTemplate JDBC模板(比Mybatis更易使用一些,但功能没那么强大)
JdbcTemplate是Spring为支持数据访问提供的一个棋牌类,这个类中封装了很多操作数据的方法;例如;
update():主要执行insert,update,delete;
query()
queryForMap
queryForList
queryForObject()
17.Spring创建数据源与JdbcTemplate的xml
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/books"></property>
<property name="username" value="root"></property>
<property name="password" value="tiger"></property>
<property name="initialSize" value="2"></property>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
18.完整的Dao
@Repository
public class BookDao {
@Autowired
JdbcTemplate jdbc;
public void save(Book book) {
jdbc.update("insert into book values(null,?,?)", book.getBookName(),book.getPrice());
}
public int getBooksCount() {
return jdbc.queryForObject("select count(*) from book", Integer.class);
}
public void getBookById(Integer isbn) {
System.out.println(jdbc.queryForMap("select * from book where isbn=?", isbn));
}
/**
* 将查询的结果转换成Vo
* @return
*/
public List<Book> getBooks(){
return jdbc.query("select * from book", new RowMapper<Book>() {
@Override
public Book mapRow(ResultSet rs, int arg1) throws SQLException {
Book b=new Book();
b.setIsbn(rs.getInt(1));
b.setBookName(rs.getString(2));
b.setPrice(rs.getInt(3));
return b;
}});
}
}
事务部分
1.Spring的事务控制,分为两种
编程式事务;
声明式事务
2.Spring控制事务的简单步骤
1:声明一个事务管理器
要写id
<!-- 声明一个事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 增加事务管理器的Annotation驱动 -->
<tx:annotation-driven />
2:在service 方法中加入@Transactional注解
@Transactional
public void save(Book book) {
System.out.println("bookservice...插入图书");
dao.save(book);
book.setIsbn(book.getIsbn()+1);
dao.save(book);
}
3.事务管理器的父接口(了解)
Spring对所有的事务管理进行抽象,所有的事务管理器的父接口是PlatformTransactionManager
4.事务的传播特性
事务传播特性指的是 当两个事务方法组合在一起使用,如何进行传播;
5.事务的传播特性有7个值;
①REQUIRED:需要一个事务;如果在一个事务中运行,则使用原的事务,否则创建一个新事务,并在新事务中运行;
②REQUIRED_NEW:必须在一个新事务中运行,如果在一个事务中运行,会将原事务挂起,并开启新事务,如不在一个事务中运行,也会开启新事务;
③SUPPORTS:支持事务,在一个事务中运行,则使用原事务,如不在一个事务中,则不支持事务(autocommit)
④NOT_SUPPORTED:不支持事务,无论是否存在事务,都不支持;
⑤MADATORY:必须放在一个事务方法中,否则抛出异常
⑥NEVER:不能放在事务方法,否则抛出异常;
⑦NESTED:嵌入一个事务,相当于设置一个保存点;
6.事务并发时可能发生的问题
1.脏读:对于两个事物 T1, T2, T1 读取了已经被 T2 更新但 还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的.
2.不可重复读:对于两个事物 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了
3.幻读:对于两个事物 T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行.
(不可重复读:一个数据;幻读:整体数据)这两个问题有时是允许出现的
7.事务隔离级别(4个)
未提交读:所有问题均会发生;但并发性最好;(极端)
已提交读:不会出现脏读;(只能读已提交的数据)默认的,一般用它
可重复读:解决脏读,不可重复读的问题,幻读依然存在
序列化:解决所有问题,但并发性最差;(极端)
隔离级别与并发性相互制约,隔离性越好,并发性越差;
可以通过@Transactional(isolation=隔离级别)来控制
8.设置回滚事务属性;
Spring默认情况下;事务方法中只有出现未检查异常(Error或RuntimeException)时会回退事务,如果出现受检查异常(Exception)时则会提交事务;
如果想修改回滚事务属性,可以使用rollbackFor与noRollbackFor来指定,例如;
@Transactional(rollbackFor=Exception.class,noRollbackFor=RuntimeException.class)
9.超时事务属性与只读事务属性
只读:如果当前的事务方法中只有查询语句,那么可以使用readonly=true属性来设计只读,Spring通过个属性的设置来提高性能
超时:statement与statement的间隔(两个操作的间隔)
10.通过timeout可以设计超时时间,时间的单位是秒
Spring事务超时 = 事务开始时到最后一个Statement创建时时间 + 最后一个Statement的执行时超时时间(即其queryTimeout)。所以在在执行Statement之外的超时无法进行事务回滚
11.整合mybatis 的步骤;
可以访问mabits.org/spring查看
①导包,mybatis,连接池,事务;驱动,spring-mybatis插件;
②在application中定义数据源及sqlSessionFactory;
<!-- 定义数据源; -->
<bean id="dataSource"
class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close">
<property name="driverClassName"
value="com.mysql.jdbc.Driver"></property>
<property name="url"
value="jdbc:mysql://localhost:3306/books"></property>
<property name="username" value="root"></property>
<property name="password" value="tiger"></property>
<property name="initialSize" value="2"></property>
</bean>
<!-- 创建mybatis的session工厂,需要数据源,映射文件,别名包等信息 -->
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="mapperLocations" value="classpath*:com/oracle/mapper/*.xml"></property>
<property name="typeAliasesPackage" value="com.oracle.vo"></property>
</bean>
③使用mybatis插件来扫描需要绑定的dao接口;
<!-- 扫描所有的接口;用来做接口绑定 -->
<mybatis-spring:scan base-package="com.oracle.dao"/>
④定义事务管理器,并启动事务;
<!-- 声明一个事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 增加事务管理器的Annotation驱动 -->
<tx:annotation-driven />
⑤扫描dao及service包
<!-- 扫描包 -->
<context:component-scan base-package="com.oracle"></context:component-scan>
或
<!-- 扫描包 -->
<context:component-scan base-package="com.oracle.dao,com.oracle.service"></context:component-scan>
⑥定义mybatis的mapper映射文件及dao接口
<mapper namespace="com.oracle.dao.BookDao">
<insert id="save" parameterType="book">
insert into book values(null,#{bookName},#{price})
</insert>
<select id="getAll" resultType="book">
select * from book
</select>
</mapper>
⑦定义service后,进行测试;
@Test
public void test() {
BookService dao=c.getBean(BookService.class);
Book book=new Book();
book.setBookName("天道");
book.setPrice(48);
dao.save(book);
System.out.println(dao.getAll());
}
Spring学习笔记结束
来源:CSDN
作者:孙宏宇51
链接:https://blog.csdn.net/Sunhongyu51/article/details/98646872