Spring解决对象创建以及对象之间依赖关系的一种框架。
组件/框架设计
侵入式设计: 引入了框架,对现有的类的结构有影响;即需要实现或继承某些特定类。例如: Struts框架
非侵入式设计: 引入了框架,对现有的类结构没有影响。
控制反转: 对象的创建交给外部容器完成,这个就做控制反转.
依赖注入: 在创建完对象后, 对象的关系的处理就是依赖注入
Spring提供了一站式解决方案:
1) Spring Core spring的核心功能: IOC容器, 解决对象创建及依赖关系
2) Spring Web Spring对web模块的支持。
3) Spring DAO Spring 对jdbc操作的支持 【JdbcTemplate模板工具类】
4) Spring ORM spring对orm的支持:
5)Spring AOP 切面编程
6)SpringEE spring 对javaEE其他模块的支持
// 1. 通过工厂类得到IOC容器创建的对象
public void testIOC() throws Exception {
// 现在,把对象的创建交给spring的IOC容器
Resource resource = new ClassPathResource("cn/itcast/a_hello/applicationContext.xml");
// 创建容器对象(Bean的工厂), IOC容器 = 工厂类 + applicationContext.xml
BeanFactory factory = new XmlBeanFactory(resource);
// 得到容器创建的对象
User user = (User) factory.getBean("user");
System.out.println(user.getId());
}
//2. (方便)直接得到IOC容器对象
public void testAc() throws Exception {
// 得到IOC容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/a_hello/applicationContext.xml");
// 从容器中获取bean
User user = (User) ac.getBean("user");
System.out.println(user);
}
}
IOC容器创建对象:有几种方式:
1) 调用无参数构造器
2) 带参数构造器
3) 工厂创建对象
工厂类,静态方法创建对象
工厂类,非静态方法创建对象
<!-- 1. 默认无参数构造器 -->
<bean id="user1" class="cn.itcast.b_create_obj.User"></bean>
<!-- 2. 带参数构造器 -->
<bean id="user2" class="cn.itcast.b_create_obj.User">
<constructor-arg index="0" type="int" value="100"></constructor-arg>
<constructor-arg index="1" type="java.lang.String" value="Jack"></constructor-arg>
</bean>
<!-- # 3.1 工厂类,实例方法 -->
<!-- 先创建工厂 -->
<bean id="factory" class="cn.itcast.b_create_obj.ObjectFactory"></bean>
<!-- 在创建user对象,用factory方的实例方法 -->
<bean id="user4" factory-bean="factory" factory-method="getInstance"></bean>
<!-- # 3.2 工厂类: 静态方法 -->
<!-- class 指定的就是工厂类型
factory-method 一定是工厂里面的“静态方法” -->
<bean id="user" class="cn.itcast.b_create_obj.ObjectFactory" factory-method="getStaticInstance"></bean>
对象依赖关系:
1) 通过构造函数
2) 通过set方法给属性注入值
3) p名称空间
4)自动装配(自动去IOC容器中找与属性名同名的引用的对象,并自动注入)
5) 注解
<!-- 给对象属性注入值:
# p 名称空间给对象的属性注入值
(spring3.0以上版本才支持)-->
<bean id="userDao" class="cn.itcast.c_property.UserDao"></bean>
<bean id="userService" class="cn.itcast.c_property.UserService" p:userDao-ref="userDao"></bean>
<bean id="userAction" class="cn.itcast.c_property.UserAction" p:userService-ref="userService"></bean>
<!-- 传统的注入:
<bean id="user" class="cn.itcast.c_property.User" >
<property name="name" value="xxx"></property>
</bean>
-->
<!-- p名称空间优化后 -->
<bean id="user" class="cn.itcast.c_property.User" p:name="Jack0001"></bean>
注解方式可以简化spring的IOC容器的配置-----使用注解步骤:
1)先引入context名称空间 xmlns:context="http://www.springframework.org/schema/context"
2)开启注解扫描 <context:component-scan base-package="cn.itcast.e_anno2"></context:component-scan>
3)使用注解 通过注解的方式,把对象加入ioc容器。相关的注解:
@Component 指定把一个对象加入IOC容器
@Repository 作用同@Component; 在持久层使用
@Service 作用同@Component; 在业务逻辑层使用
@Controller 作用同@Component; 在控制层使用
@Resource 属性注入
总结:
1) 使用注解,可以简化配置,且可以把对象加入IOC容器,及处理依赖关系(DI)
2) 注解可以和XML配置一起使用。
代理(Proxy)
是一种设计模式, 提供了对目标对象另外的访问方式;即通过代理访问目标对象。 这样好处: 可以在目标对象实现的基础上,增强额外的功能操作。关键点: 代理对象与目标对象
静态代理: 代理对象,要实现与目标对象一样的接口;
1)可以做到在不修改目标对象的功能前提下,对目标对象功能扩展。
2)缺点: 因为代理对象,需要与目标对象实现一样的接口。所以会有很多代理类,类太多。一旦接口增加方法,目标对象与代理对象都要维护。
动态代理:
1)代理对象,不需要实现接口;
2)代理对象的生成,是利用JDKAPI, 动态的在内存中构建代理对象(需要我们指定创建 代理对象/目标对象 实现的接口的类型;)
3) 动态代理, JDK代理, 接口代理;
代理对象不需要实现接口,但是目标对象一定要实现接口;否则不能用动态代理!
Cglib代理: 也叫做子类代理。在内存中构建一个子类对象从而实现对目标对象功能的扩展。
如果加入容器的目标对象有实现接口,用JDK代理;如果目标对象没有实现接口,用Cglib代理;
AOP编程
实现“业务代码”与“关注点代码”分离:(重复代码就叫做关注点)
切面:关注点形成的类,就叫切面(类) 面向切面编程,就是指 对很多功能都有的重复的代码抽取,再在运行的时候往业务方法上动态植入“切面类代码”。
切入点: 执行目标对象方法,动态植入切面代码。切入点表达式,指定拦截哪些类的哪些方法; 给指定的类在运行的时候植入切面类代码。
@Aspect 指定一个类为切面类
@Pointcut(“execution(* cn.itcast.e_aop_anno..(…))”) 指定切入点表达式
@Before(“pointCut_()”) 前置通知: 目标方法之前执行
@After(“pointCut_()”) 后置通知:目标方法之后执行(始终执行)
@AfterReturning(“pointCut_()”) 返回后通知: 执行方法结束前执行(异常不执行)
@AfterThrowing(“pointCut_()”) 异常通知: 出现异常时候执行
@Around(“pointCut_()”) 环绕通知: 环绕目标方法执行
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
切入点表达式:对指定的“方法”进行拦截
<!-- Aop配置 -->
<aop:config>
<!-- 定义一个切入点表达式: 拦截哪些方法 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.*.*(..))" id="pt"/>-->
<!-- 【拦截所有public方法】 -->
<!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>-->
<!-- 【拦截所有save开头的方法 】 -->
<!--<aop:pointcut expression="execution(* save*(..))" id="pt"/>-->
<!-- 【拦截指定类的指定方法, 拦截时候一定要定位到方法】 -->
<!--<aop:pointcut expression="execution(public * cn.itcast.g_pointcut.OrderDao.save(..))" id="pt"/>-->
<!-- 【拦截指定类的所有方法】 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.*(..))" id="pt"/>-->
<!-- 【拦截指定包,以及其自包下所有类的所有方法】 -->
<!--<aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>-->
<!-- 【多个表达式】 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) || execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) or execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!-- 下面2个且关系的,没有意义 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) && execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) and execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!-- 【取非值】 -->
<!--<aop:pointcut expression="!execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<aop:pointcut expression=" not execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>
<!-- 切面 -->
<aop:aspect ref="aop">
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
事务控制:
编程式事务控制: 自己手动控制事务 细粒度的事务控制: 可以对指定的方法、指定的方法的某几行添加事务控制
声明式事务控制: Spring提供了对事务控制的实现(基于Aop)。如果想用Spring的声明式事务管理,只需要在配置文件中配置即可; 不想使用时直接移除配置。实现了对事务控制的最大程度的解耦。 粗粒度的事务控制: 只能给整个方法应用事务,不可以对方法的某几行应用事务。 (因为aop拦截的是方法。)
XML方式实现:
<!-- #############5. Spring声明式事务管理配置############### -->
<!-- 5.1 配置事务管理器类 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 5.2 配置事务增强(如果管理事务?) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="*" read-only="false"/>
</tx:attributes>
</tx:advice>
<!-- 5.3 Aop配置: 拦截哪些方法(切入点表表达式) + 应用上面的事务增强配置 -->
<aop:config>
<aop:pointcut expression="execution(* cn.itcast.a_tx.DeptService.*())" id="pt"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
注解方式实现:需要添加事务控制的地方,写上: @Transactional
<tx:annotation-driven transaction-manager="txManager"/>
@Transactional(
readOnly = false, // 读写事务
timeout = -1, // 事务的超时时间不限制
noRollbackFor = ArithmeticException.class, // 遇到数学异常不回滚
isolation = Isolation.DEFAULT, // 事务的隔离级别,数据库的默认
propagation = Propagation.REQUIRED // 事务的传播行为
)
public void save(Dept dept){
deptDao.save(dept);
int i = 1/0;
deptDao.save(dept);
}
事务的隔离级别分为:
未提交读(read uncommitted)
原先name的值是小刚,然后有一个事务B修改了name=小明还没提交事务。同时事务A在查询name,在这个隔离级别下获取到的name的值是小明而不是小刚。若事务B回滚了,读到了事务B回滚前的脏数据,这称之为脏读。
已提交读(read committed)
一个事务只能读到另一个事务修改的已经提交了事务的数据。如果事务B 在这时候隐式提交了时候,事务A的结果就小明,但是事务A没结束,事务B又name=小红并隐式提交了。然后事务A又执行查询结果=小红。这种现象叫不可重复读。
可重复读(repeatable read)
一个事务只能读到另一个事务修改的已提交了事务的数据,但是第一次读取的数据,即使别的事务修改的这个值,这个事务再读取这条数据的时候还是和第一次获取的一样,不会随着别的事务的修改而改变。
串行化(serializable)
只能进行读-读并发。只要有一个事务操作一条记录的写,那么其他要访问这条记录的事务都得等着。
数据库事务的错误情况
脏读:事务A读到了事务B未提交的数据。
不可重复读:事务A第一次查询得到一行记录row1,事务B提交修改后,事务A第二次查询得到row1,但列内容发生了变化。
幻读:事务A第一次查询得到一行记录row1,事务B提交修改后,事务A第二次查询得到两行记录row1和row2。
spring bean作用域有以下5个:
singleton:单例模式,当spring创建applicationContext容器的时候,spring会欲初始化所有的该作用域实例,加上lazy-init就可以避免预处理;
prototype:原型模式,每次通过getBean获取该bean就会新产生一个实例,创建后spring将不再对其管理;
==下面是在web项目下才用到的=
request:搞web的大家都应该明白request的域了吧,就是每次请求都新产生一个实例,和prototype不同就是创建后,接下来的管理,spring依然在监听
session:每次会话,同上
global session:全局的web域,类似于servlet中的application