#Spring框架学习笔记#
一. 环境搭建
* 1.导入Spring的必须jar包
1.其中logging为日志包,其他为_必需包_
* 2.编写配置文件,命名为applicationContext.xml(Spring框架最大的容器同名)
编写原则如图:
在beans标签后添加约束文件名
二. IoC Inversion of Context(Spring框架的控制反转,创建对象过程与开发者解耦)
- 创建对象的方法
1.有参构造创建对象方法:
配置文件代码:
测试类代码:
2.静态工厂创建对象方法:
配置文件代码:
测试类代码:
2.实例工厂创建对象方法:
配置文件代码:
测试类代码:
* 给对象赋值的方法:
1.有参赋值方法,见有参构造创建对象方法(需要在pojo类里提供有参构造器);
2.子标签赋值方法(注入)
<!-- 子标签赋值方法 -->
<bean id="peo3" class="com.test01.pojo.People">
<property name="name">
<value>accer</value>
</property>
<property name="age">
<value>10</value>
</property>
<property name="family">
<list>
<value>jack</value>
<value>jade</value>
<value>jazz</value>
</list>
</property>
<property name="workplaces">
<map>
<entry key="1" value="beijing"></entry>
<entry key="2" value="shanghai"></entry>
<entry key="3" value="shenzhen"></entry>
</map>
</property>
3.依赖注入(DI:dependent injection):把另一个对象实例化,并注入当前对象的过程
通过声明另一个当前类所持有的类的bean对象(并声明其中的参数),在当前bean标签内通过子标签调用
<property name="work" ref="work"></property>
</bean>
<bean id="work" class="com.test01.pojo.work">
<property name="workID" value="1001"></property>
<property name="salary" value="6000"></property>
</bean>
三.Spring框架整合myBatis框架
- 1.导入相关jar包
导入 mybatis 所有 jar和 spring 基本包,spring-jdbc,spring-tx,spring-aop,spring-web,spring 整合 mybatis 的包等;
-
2.在WebContent文件夹下编写配置文件web.xml
<?xml version="1.0" encoding="UTF-8"?> <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"> <!-- Spring数据源封装,用于获取数据库连接 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost/stuinfo"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <!--Spring对SqlSessionFactoy封装,用于获取myBatis的SqlSessionFactory对象 --> <!-- ref属性表示将数据库连接对象注入对象factory --> <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- Spring的扫描器,用于扫描指定包下的myBatis接口绑定文件(不能用于扫描mapper.xml!) --> <!-- id可省略 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.test.mapper"></property> <!-- 注入对象factory --> <property name="sqlSessionFactory" ref="factory"></property> </bean> <!-- Spring对Service的管理类 --> <bean id="StudentService" class="com.test.services.impl.StudentServiceImpl"> <!-- 注入对象studentMapper--> <property name="studentMapper" ref="studentMapper"></property> </bean> </beans>
-
3.编写pojo类与service类
pojo类正常编写,service类简写即可,必须要有get,set方法以便于设值注入(Spring已经进行封装,无需详细编写)
public class StudentServiceImpl implements StudentService { private StudentMapper studentMapper; public StudentMapper getStudentMapper() { return studentMapper; } public void setStudentMapper(StudentMapper studentMapper) { this.studentMapper = studentMapper; } @Override public List<student> showAll() throws IOException { return studentMapper.selAll(); } }
-
4.编写sevlet
由于Spring框架未对servlet进行封装,因此编写servlet代码相对复杂
public class StundetSevlet extends HttpServlet { private static final long serialVersionUID = 1L; private StudentService ss; @Override public void init() throws ServletException { ApplicationContext ac =WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); ss=ac.getBean("StudentService",StudentServiceImpl.class); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setAttribute("student", ss.showAll()); request.getRequestDispatcher("index.jsp").forward(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
注释:init()方法中的代码代表在当servlet初始化时,即启动tomcat时即加载项目applicationContext,读取其中的配置文件,并对其内容(service)进行初始化,这一步对于运行servlet(业务)是必需的而且是预先完成的,由于Spring框架未实现此功能,因此采用了init方法来实现
四.AoP(Aspect oreinted Programming)面向切面编程
-
1.基本概念
1.在程序原有纵向执行流程中,针对某一个或某一些方法添加通知,形成横切面过程就叫做面向切面编程。(对源程序的扩展,且不影响源程序的运行)
2.常用术语:
2.1 原有功能: 切点, pointcut 2.2 前置通知: 在切点之前执行的功能. before advice 2.3 后置通知: 在切点之后执行的功能,after advice 2.4 如果切点执行过程中出现异常,会触发异常通知.throws advice 2.5 所有功能总称叫做切面. 5.6 织入: 把切面嵌入到原有功能的过程叫做织入
-
2.Spring实现AoP方法:
1.采取Schema-based方法:
(1)导入相关jar包:
注意:多导入的aopalliance和aspectjweaver为SpringAoP的支持包,此外,图中遗漏了commonsLogging包!
(2)编写Spring配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd <!--引入AoP命名空间dtd文件--> http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 配置:前置通知与后置通知对象 --> <bean id="afterAdvice" class="com.testAOP.SchemaBased.AfterAdviceClass"></bean> <bean id="beforeAdvice" class="com.testAOP.SchemaBased.BeforeAdviceClass"></bean> <!-- 配置切面 --> <aop:config> <!-- 配置切点所在的方法 *代表通配符,可在后面的包名后进行切点的配置(如通配同一包下,同一方法名等) --> <aop:pointcut expression="execution(* com.testAOP.SchemaBased.demo.cutPoint2())" id="pointCut" /> <!-- 配置前置通知方法与后置通知方法 --> <aop:advisor advice-ref="beforeAdvice" pointcut-ref="pointCut"/> <aop:advisor advice-ref="afterAdvice" pointcut-ref="pointCut"/> </aop:config> <!-- 配置测试类 --> <bean id="testSchemaBased" class="com.testAOP.SchemaBased.demo"></bean> </beans>
(3)编写通知类:
//前置通知类 public class BeforeAdviceClass implements MethodBeforeAdvice { //arg0代表切点方法对象(qualified name);arg1 代表切点传入的参数,arg2代表切点所属于的对象 @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("执行前置通知的方法"); } } //后置通知类 public class AfterAdviceClass implements AfterReturningAdvice{ //arg0代表切点的返回值;arg1代表切点方法对象(qualified name);arg2 代表切点传入的参数,arg3代表切点所属于的对象 @Override public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { System.out.println("执行后置通知的方法"); } }
(4)测试结果
-
2.采取Aspect-J方式配置切面抛出异常类:
(1)xml配置文件代码
<!-- 配置:某个切面的抛出异常类对象-->
<bean id="throwAdvice" class="com.testAOP.SchemaBased.ThrowAdvice" ></bean>
<!-- 配置切面 -->
<aop:config>
<!-- 配置切点所在的方法 -->
<aop:pointcut expression="execution(* com.testAOP.SchemaBased.demo.cutPoint2())" id="pointCut" />
<!-- 配置前置通知方法与后置通知对象 -->
<aop:advisor advice-ref="beforeAdvice" pointcut-ref="pointCut"/>
<aop:advisor advice-ref="afterAdvice" pointcut-ref="pointCut"/>
<!-- 配置切面中抛出异常的方法位于的类对象 -->
<aop:aspect ref="throwAdvice">
<!-- after-throwing 代表通知类型,method代表方法名,pointcut-ref为方法所配置的切点对象,throwing代表所抛出的异常对象,必须添加,否则不显示异常结果 -->
<aop:after-throwing method="ThrowException" pointcut-ref="pointCut" throwing="e"/>
</aop:aspect>
</aop:config>
<!-- 配置测试类 -->
<bean id="testSchemaBased" class="com.testAOP.SchemaBased.demo"></bean>
(1)切面的抛出异常类的编写:
//传入的参数为程序执行异常时的异常对象e,名称必须与xml配置文件中的抛出异常类的声明一致
public class ThrowAdvice {
public void ThrowException(Exception e){
System.out.println("执行在切面中抛出异常的方法:"+e.getMessage());
}
}
- 3.采取schema-based方法配置环绕通知类(整合前置通知与后置通知)
(1)环绕通知类的编写
public class SurroundAdvice implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("执行环绕通知的前置通知方法");
Object proceed = arg0.proceed();
System.out.println("执行环绕通知的后置通知方法");
return proceed;
}
}
(2)xml配置文件的编写(与前置后置通知类编写原则一致)
- 4.采取Aspect-J方式配置切面
(1)编写通知类
(2)配置xml配置文件,对文件类,测试类的配置略;
【1】若切点含参,则在配置切点的方法声明中()内写入参数类型,并采用and字段连接参数名args(参数名1,参数名2),这里的参数名取决于在配置前置或后置通知里对参数的声明名称;
【2】对有参数的切点配置前置或后置通知时,需要在后面写上参数名arg-names=“设定参数名1,设定参数名2”,规则同上;
【3】切面的ref对应切面前置,后置对象方法所在的类
<!-- 配置Aspect-J的通知对象 -->
<aop:config>
<aop:aspect ref="aspectJStyle">
<!-- 配置切点 -->
<aop:pointcut expression="execution(* com.testAOP.SchemaBased.demo.cutPoint1(String,int)) and args(name,age)" id="pointCut1"/>
<aop:pointcut expression="execution(* com.testAOP.SchemaBased.demo.cutPoint3())" id="pointCut3"/>
<aop:before method="myBefore" pointcut-ref="pointCut1" arg- names="name,age"/>
<aop:after method="myAfter" pointcut-ref="pointCut1" arg-names="name,age"/>
<!--这种写法报错,如何写带参方法的环绕切面尚待解决
<aop:around method="mySurround2" pointcut-ref="pointCut1" arg-names="name,age"/>
-->
<aop:around method="mySurround" pointcut-ref="pointCut3"/>
</aop:aspect>
</aop:config>
5.采取注解方式配置切面方法
(1)配置文件的编写;
添加xmlns:context命名空间,代码如下:
xmlns:context="http://www.springframework.org/schema/context"
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">
配置<context:component-scan>标签,用于扫描指定的类,内容为切点方法的完整类名
添加的特殊代码:
//这端代码的含义:设置Spring的cglib动态代理开启,cglib动态代理的原理是基于字节码生成真实对象的子类(类似于JDK动态代理模式)。需要导入的jar包有cglib和asm(解析字节码必须包)
//由于代理类与真实类转换异常,因此需要设置动态代理开启
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
(2)添加注解;
【1】在修饰的切点类上添加@Component;切点方法上添加@PointCut(""),代表切点,其中""内部的写
法与Spring AspentJ写法一致,即("execution(* 完整包名+方法名)");
【2】在通知类上添加@Component与@Aspect;在具体的前置通知等通知方法上添加注解声明其通知类
型。如@Before(""),""内部的写法为修饰的切点方法的完整名(完整包名+方法名)
6.自动注入的实现
1.在 Spring 配置文件中对象名和 ref=”id”id 名相同使用自动注入,可以不配置<property/>
2.两种配置办法
2.1 在<bean>中通过 autowire=”” 配置,只对这个<bean>生效
2.2 在<beans>中通过 default-autowire=””配置,表当当前文件中所
有<bean>都是全局配置内容
3.autowire=“” 可取值如下:
3.1 default: 默认值,根据全局 default-autowire=””值.默认全局和局部都没有配置情况下,相当于 no
3.2 no: 不自动注入
3.3 byName: 通过名称自动注入.在 Spring 容器中找类的 Id
3.4 byType: 根据类型注入.
3.4.1 spring 容器中不可以出现两个相同类型的<bean>
3.5 constructor: 根据构造方法注入.
3.5.1 提供对应参数的构造方法(构造方法参数中包含注入对
戏那个)
3.5.2 底层使用 byName, 构造方法参数名和其他<bean>的 id
相同.
7.注入配置文件的实现
1.在xml中编写的代码
注意导入dtd文件命名空间
<!-- 读取属性文件 -->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!-- Spring数据源封装,用于获取数据库连接 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driveclass}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
2.测试类读取属性文件内容
@Value("${jdbc.username}")
private String Uname;
8.bean中对象的scope属性
作用:控制对象有效范围(单例,多例等)
1. <bean/>标签对应的对象默认是单例的 无论获取多少次,都是同一个对象
2. scope 可取值
2.1 singleton 默认值,单例
2.2 prototype 多例,每次获取重新实例化
2.3 request 每次请求重新实例化
2.4 session 每个会话对象内,对象是单例的.
4.5 application 在 application 对象内是单例
2.6 global session spring推出的一个对象, 依赖于spring-webmvc-portlet ,类似于 session
9.声明式事务
//注意导入相关的dtd代码
<!-- 实例化要声明事务的业务类 -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!--配置事务相关信息,如事务回滚,事务提交等 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- 特定方法配置事务,并带有特定的事务属性 -->
<tx:method name="get*" read-only="true"/>
<!-- 其他方法不配置特定属性时 可以采用通配符表示 -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--定义切点,切点的定义广度应该适量的大,保证事务的配置能作用于此切点内的方法 -->
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="mypoint"/>
</aop:config>
<!--数据源的使用-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/stuinfo"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 事务管理对象的使用 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
- 10:事务相关知识
-
propagation 控制事务传播行为. 当一个具有事务控制的方法被另一个有事务控制的方法调用后,需要如何管理事务(新建事务?在事务中执行?把事务挂起?报异常?)
- REQUIRED (默认值): 如果当前有事务,就在事务中执行,如果当前没有事务,新建一个事务.
- SUPPORTS:如果当前有事务就在事务中执行,如果当前没有事务,就在非事务状态下执行.
- MANDATORY:必须在事务内部执行,如果当前有事务,就在事务中执行,如果没有事务,报错.
- REQUIRES_NEW:必须在事务中执行,如果当前没有事务,新建事务,如果当前有事务,把当前事务挂起.
- NOT_SUPPORTED:必须在非事务下执行,如果当前没有事务,正常执行,如果当前有事务,把当前事务挂起.
- NEVER:必须在非事务状态下执行,如果当前没有事务,正常执行, 如果当前有事务,报错.
- NESTED:必须在事务状态下执行.如果没有事务,新建事务,如果当前有事务,创建一个嵌套事务.
-
isolation=”” 事务隔离级别
1 在多线程或并发访问下如何保证访问到的数据具有完整性的.
1.1 脏读: 一个事务(A)读取到另一个事务(B)中未提交的数据,此时A事务读取的数据可能和数据库中数据是不一致的,此时认为数据是脏数据,读取脏数据过程叫做脏读.
1.2 不可重复读:
1.2.1主要针对的是某行数据.(或行中某列) 1.2.2 主要针对的操作是修改操作. 1.2.3 两次读取在同一个事务内 1.2.4 当事务 A 第一次读取事务后,事务 B 对事务 A 读取的数据进行修改,事务 A 中再次读取的数据和之前读取的数据不一致,过程不可重复读.
1.3 幻读:
1.3.1主要针对的操作是新增或删除 1.3.2 两次事务的结果. 1.3.3 事务 A 按照特定条件查询出结果,事务 B 新增了一条符合条件的数据.事务 A 中查询的数据和数据库中的数据不一致的,事务 A 好像出现了幻觉,这种情况称为幻读.
1.4 DEFAULT: 默认值,由底层数据库自动判断应该使用什么隔离界别
1.4.1 READ_UNCOMMITTED: 可以读取未提交数据,可能出现脏读,不重复读,幻读. (效率最高). 1.4.2 READ_COMMITTED:只能读取其他事务已提交数据.可以防止脏读,可能出现不可重复读和幻读.(oracle默认) 1.4.3 REPEATABLE_READ: 读取的数据被添加锁,防止其他事务修改此数据,可以防止不可重复读.脏读,可能出现幻读. (mysql默认) 4.9 SERIALIZABLE: 排队操作,对整个表添加锁.一个事务在操作数据时,另一个事务等待事务操作完成后才能操作这个表.(最安全的但是效率最低)
1.5 rollback-for=”异常类型全限定路径”
1.5.1 当出现什么异常时需要进行回滚 1.5.2 建议:给定该属性值. 1.5.3 手动抛异常一定要给该属性值.
1.6 no-rollback-for=”” (当出现什么异常时不滚回事务)
11.Spring的常用注解
- @Component 创建类对象,相当于配置
-
@Service 与@Component 功能相同. 写在 ServiceImpl 类上.
-
@Repository 与@Component 功能相同. 3.1 写在数据访问层类上.
-
@Controller 与@Component 功能相同. 4.1 写在控制器类上.
-
@Resource(不需要写对象的 get/set)
5.1 java 中的注解
5.2 默认按照 byName 注入,如果没有名称对象,按照 byType 注入
5.2.1 建议把对象名称和 spring 容器中对象名相同
-
@Autowired(不需要写对象的 get/set)
6.1 spring 的注解
6.2 默认按照 byType 注入. 7. @Value() 获取 properties 文件中内容
-
@Pointcut() 定义切点
-
@Aspect() 定义切面类
-
@Before() 前置通知
-
@After 后置通知
-
@AfterReturning 后置通知,必须切点正确执行
-
@AfterThrowing 异常通知
-
@Arround 环绕通知