高度抽取可重用代码的一种设计;高度的通用性。 书城:WebUtils.java;BaseServlet;Filter... 打包:bootstore.jar;(工具类) commons-fileupload,commons-io(工具类),commons-dbutils(方便操作数据库的工具)
抽取成一种高度课重用的;事务控制,强大的servlet,项目的一些工具。 框架:多个可重用模块的集合,形成一个某个领域的整体解决方案;
容器(可以管理所有的组件(类))框架
核心关注:IOC和AOP
三个一组:source是源码包
Spring模块划分图:
Test:Spring的单元测试模块; jar包:
Core_Contaniner:核心容器(IOC);黑色代表这部分的功能由哪些jar包组成;要使用这个部分的完整功能,这些jar都需要导入 jar(ioc)包:
AOP+Aspects(面向切面编程模块)
jar包:
Data Access(数据访问):Spirng数据库访问模块
Web:Spring开发web应用的模块
jar包:
spring官网地址 文档地址(本地):D:\software\java学习资料\ssm\Spring\Spring\lib\spring-framework-4.0.0.RELEASE\docs\spring-framework-reference\htmlsingle\index.html
开发Spring框架的应用,经常要写框架的配置文件,写起来复杂,我们需要提示; 需要给eclipse 中安装插件;(提供提示功能) 插件安装(如果用idea,不需要安装)
容器:管理所有的组件(有功能的类);假设,BookServlet受容器管理,BookService也受容器管理;容器可以自动的探查出那些组件(类)需要用到另一些组件(类);容器帮我们创建BookService对象,并把BookService对象赋值过去; 容器:主动的new资源变为被动的接受资源; 控制:资源的获取方式;
BookServlet{
BookService bs = new BookService();
AirPlane ap = new AirPlane();//复杂对象的创建是比较庞大的工程;
}
BookServlet{
BookService bs;
public void test01(){
bs.checkout();
}
}
容器能知道哪个组件(类)运行的时候,需要另外一个类(组件);容器通过反射的形式,将容器中准备好的BookService对象注入(利用反射给属性赋值)到BookServlet中; 只要容器管理的组件,都能使用容器提供的强大功能; ①IOC在Spring中的实现
以后框架编写流程:
②获取bean的方式
③给bean的属性赋值
<bean id="person01" class="com.zhm.bean.Person">
<property name="lastName">
<null/>
</property>
</bean>
<!-- 在Person中有car的属性,这里用外部引入(ref) -->
<bean id="car01" class="com.zhm.bean.Car">
<property name="carName" value="宝马">
<property name="color" value="绿色">
<property name="price" value="1000000">
</bean>
<bean id="person01" class="com.zhm.bean.Person">
<property name="car" ref="car01"></property>
</bean>
<!-- 也可以如下,引用内部bean(Car=new Car()) -->
<bean id="person01" class="com.zhm.bean.Person">
<property name="car">
<bean id="car01" class="com.zhm.bean.Car">
<property name="carName" value="奔驰"/>
<property name="color" value="黑色"/>
<property name="price" value="1000000"/>
</bean>
</property>
</bean>
<!-- 复杂属性赋值 -->
<bean id="book01" class="com.zhm.bean.Book">
<property name="bookName" value="红楼梦">
</bean>
<bean id="person01" class="com.zhm.bean.Person">
<!-- list赋值 -->
<property name="books">
<list>
<!-- 内部bean无法通过id获取,即写id与不写都一样book000x -->
<bean id="book000x" class="com.zhm.bean.Book" p:bookName="西游记"></bean>
<!-- 引用外部的bean -->
<ref bean="book01"/>
</list>
</property>
<!-- map赋值 -->
<property name="maps">
<map>
<entry key="key01" value="xxx"/>
<entry key="key02" value="19"/>
<!-- 引入外部值 -->
<entry key="key03" value-ref="book01"/>
</map>
</property>
</bean>
<!-- Property类型 -->
<property name="properties">
<!-- 所有的k=v都是string -->
<props>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
<!-- person中有个属性是car,若要修改属性的属性,如下图: -->
<bean id="studentSuper" class="com.zhm.ssm.bean.Student"
p:studentId="2002" p:stuName="Jerry2016" p:age="18" />```
<context:property-placeholder location="classpath:类路径的资源"/>
<!-- 取出值 -->
${key}:动态取出配置文件中某个key的值
注意:username是spring中key的一个关键字
<!-- 需要依赖于aop包,才能够组件扫描 -->
<context:component-scan base-package="com.zhm"/>
<!-- 组件的id为类的首字母小写且单实例
也可以在注解后面直接指定bean的id,如:@Repository("指定的id")
当然也能够用注解@Scope(value="prototype"),指定为多实例
-->
AOP:(Aspect Oriented Programming)面向切面编程 OOP:(Object Oriented Programming)面向对象编程 面向切面编程:基于OOP基础之上新的编程思想; 指在程序运行期间,将某段代码动态的切入到指定方法的指定位置进行运行的这种编程方式,面向切面编程;
AOP使用场景:
① AOP加日志保存到数据库;
② AOP做权限验证;
③ AOP做安全检查
④ AOP做事务控制
jdk反射包下基于接口的动态代理(原生):
public class CalculatorProxy{
public static Calculator getProxy(final Calculator calculator){
//方法执行器
InvocationHandler h = new InvocationHandler(){
/**
* Object proxy:代理对象;给jdk使用,任何时候都不要动这个对象
* Method method:当前将要执行的目标对象的方法
* Object[] args:这个方法调用时外界传入的参数值
*/
@override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
Object result = null;
//利用反射执行方法
//result:目标方法执行后的返回值
try{
//执行方法之前
System.out.println("["+method.getName()+"]方法开始执行,用的参数列表["+Arrays.asList(args)+"]");
//方法执行
result = method.invoke(calculator,args);
//方法执行后
System.out.println("["+method.getName()+"]方法执行完成,计算结果是:"+result);
}catch(Exception e){
//e.printStackTrace();
System.out.println("["+method.getName()+"]方法执行出现异常,异常信息是:"+e.getCause());
}finally{
System.out.println("["+method.getName()+"]方法最终结束");
}
return result;
}
};
Class<?>[] interfaces = calculator.getClass().getInterfaces();
ClassLoader loader = calculator.getClass().getClassLoader();
//创建代理对象
Object proxy = Proxy.newProxyInstance(loader,interfaces,h);
return (Calculator)proxy;
}
}
AOP的专业术语:
1、导包:
2、写配置
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
3、测试
只需要在通知方法的参数列表上写一个参数:JoinPoint joinPoint--》封装了当前目标方法的详细信息 并通过joinPoint.getXXX()获取参数等等信息(getArgs()、getSignature()-->getName())
接受返回值 切入点表达式是value="切入点表达式",returning="result":表示result接受返回值(在方法上加入该参数后在注解上告诉Spring该参数是什么),异常信息(同上):throwing="e"
1、导包同上 2、将目标类和切面类加入到ioc容器中
<bean id="目标类的id" class="xxxx.类名"></bean>
<bean id="切面类的id" class="xxx.类名"></bean>
3、AOP配置
<aop:config>
<!-- 指定切面:同@Aspect -->
<aop:aspect ref="切面类的id">
<!-- 抽取切入点表达式 -->
<aop:pointcut id="mypointcut" expression="execution(public int xxx.*.*(..))">
<!-- 配置哪个方法是前置通知:method指定方法名 pointcut:指定切入点表达式 -->
<aop:before method="方法名" pointcut="execution(public int xxx.*.*(..))"/>
<aop:after-returning method="方法名" pointcut-ref="mypointcut" returning="指定的返回值"/>
<aop:after-throwing method="方法名" pointcut-ref="mypointcut" throwing="exception"/>
<aop:after method="方法名" pointcut-ref="mypointcut"/>
</aop:aspect>
</aop:config>
事务:
操作数据库; Spirng提供了JdbcTemplate能快捷的操作数据库 JdbcTemplate和QueryRunner;
<!-- ${}:取出配置文件中的值 -->
<!-- #{}:spring的表达式语言(spel??) -->
<context:property-placeholder location="classpath:dbconfig.properties">
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
<property name="driverClass" value="${jdbc.driverClass}"/>
</bean>
<!-- 使用JdbcTemplate操作数据库需要获取数据源 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>
可以直接获取该bean(jdbcTemplate)操作数据库了
注意: 查询单个集合:jdbcTemplate.query() 查询单个对象:jdbcTemplate.queryForObject()
声明式事务 以前通过复杂的编程来编写一个事务,替换为只需要告诉Spring哪个方法是事务方法即可; Spring自动进行事务控制;
AOP:环绕通知可以去做; //获取连接 //设置非自动 提交 目标代码执行 //正常提交 //异常回滚 //最终关闭
注解配置:
<!-- 事务控制 -->
<!-- 1. 导包:aop基础包与增强包 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 控制数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 2. 开启基于注解的事务控制模式 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 3. 给事务方法加注解@Transactional -->
<!-- 注解后面的细节 -->
异常分类:
运行时异常(非检查异常):可以不用处理,默认回滚
编译时异常(检查异常):要么try-catch,要么在方法上声明throws,默认不回滚
① timeout=3:超时,秒为单位,事务超出指定执行时长后自动终止并回滚
② readOnly=true:加快查询速度(只读事务);不用管事务那一堆操作。
③ noRollbackFor={ArithmeticException.class,NullPointerException.class}:(运行异常)默认回滚的可以设置为不回滚(编译时异常)
④ noRollbackForClassName={"com.xxx.类名"},效果与上面一个一样
⑤ rollbackFor={类名.class}:原本不回滚(编译时异常)的异常指定让其回滚
@Transactional(isolation=Isolation.READ_UNCOMMITTED) 隔离级别: 一个事务与其他事务隔离的程度称为隔离级别。为什么会有这种现象??
假设现在有两个事务:Transaction01和Transaction02并发执行。
①脏读 1、Transaction01将某条记录的AGE值从20修改为30。 2、Transaction02读取了Transaction01更新后的值:30。 3、Transaction01回滚,AGE值恢复到了20。 4、Transaction02读取到的30就是一个无效的值。 ②不可重复读 1、Transaction01读取了AGE值为20。 2、Transaction02将AGE值修改为30。 3、Transaction01再次读取AGE值为30,和第一次读取不一致。 ③幻读 1、Transaction01读取了STUDENT表中的一部分数据。 2、Transaction02向STUDENT表中插入了新的行。 3、Transaction01读取了STUDENT表时,多出了一些行。 如何解决这些问题呢??? ①读未提交:READ UNCOMMITTED---->造成脏读
允许Transaction01读取Transaction02未提交的修改。
②读已提交:READ COMMITTED
要求Transaction01只能读取Transaction02已提交的修改。
③可重复读:REPEATABLE READ-->mysql下能解决不可重复读与幻读的问题
确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。
④串行化:SERIALIZABLE
确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
⑤各个隔离级别解决并发问题的能力见下表
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
READ UNCOMMITTED | 有 | 有 | 有 |
READ COMMITTED | 无 | 有 | 有 |
REPEATABLE READ | 无 | 无 | 有 |
SERIALIZABLE | 无 | 无 | 无 |
⑥各种数据库产品对事务隔离级别的支持程度
Oracle | MySQL | |
---|---|---|
READ UNCOMMITTED | × | √ |
READ COMMITTED | √ | √ |
REPEATABLE READ | × | √ (默认) |
SERIALIZABLE | √ | √ |
事务的传播行为:在@Transactional(propagation=Propagation.REQUIRED)
xml配置:
编程式事务:
TransactionFilter{ try{ //获取连接 //设置非自动提交 //chain.doFilter(); //提交 }catch(Exception e){ //回滚 }finally{ //关闭连接释放资源 } }
Spring与JavaWeb的整合使用 1)Spring来控制事务(dao--JdbcTemplate) 2) 所有的组件Autowired 3) 管理数据库
本文章使用limfx的vscode插件快速发布