为什么要学习Spring
1.Hibernate是一个ORM框架,Struts2是一个MVC框架,虽然可以用来开发应用,但是代码之间的耦合度过高,各层之间的依赖较强,可扩展性不强。可以说没有Spring那么Struts2和Hibernate缺乏真正的灵魂,Spring是将二者整合的核心控制框架
2.在技术发展的过程中,Struts2和Hibernate都出现了很多类似的同类型的框架,可替代性也非常强。而Spring从2003年发展至今,还没有出现能替代它的技术存在
Java应用之间的依赖关系
Java应用中各组件之间存在着大量的A组件依赖B组件的场景。代码耦合度较高,不利于项目的扩展和维护
对于这种依赖场景,可以有三种解决方案:
A组件创建B组件对象,再调用B组件的方法。A组件和B组件耦合性过高。
A组件通过工厂模式创建B组件对象,再调用B组件的方法。A组件和B组件的工厂耦合
A、B组件都有一个“容器”来管理,容器将A组件传给B组件,A组件直接调用B组件的方法,彻底解耦
什么是Spring
Spring到底是什么呢?
Spring是一个开源框架
Spring实际是一个“超级工厂”,也可以称Spring是一个“容器”,这个容器用来管理所有的对象的。
简单来说Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架
Spring的优点
方便解耦,简化开发
Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理
AOP编程的支持
Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无需手动编程
方便程序的测试
Spring对Junit4支持,可以通过注解方便的测试Spring程序
方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Strus、Hibernate、MyBatis、Quartz等)的直接支持
降低javaEE API的使用难度
Spring对javaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低
Spring体系结构
Spring IOC
Spring IOC是Spring中最核心的部分,是以Bean的方式来组织和管理Java应用中的各种组件,提供配置层次的解耦
所有的Bean都由Spring BeanFactory根据配置文件生成管理
ApplicationContext是BeanFactory的加强版,提供了更多的功能,如自动创建,国际化
Spring AOP
Spring Aop也是Spring的核心技术,AOP擅长处理一些具有横切性质的系统服务,如事务管理,安全检查,缓存等
Spring并没有提供完整的AOP实现,Spring侧重于AOP实现与Spring IOC容器之间的整合,因此Spring AOP通常和Spring IOC 一起使用
Spring Web
Spring Web提供一套基于Spring的MVC框架,相比Struts2更加方便,更加灵活。
同时也支持与主流的其他技术和框架集成,支持WebStocket,Servlet,Struts2等框架
Spring支持各种主流的表现层技术,如Velocity,XSTL等等
Spring Date Access
Spring对传统JDBC进行了封装,能以更简单的方式进行持久化操作。
Spring对各种主流的ORM框架提供了非常良好的支持,并以模版的形式提供一致的支持
Spring对提供了良好的Dao层支持,帮助了开发人员实现自己的Dao组件
总体来说,Spring框架是一个用来管理和组织其他框架的一套框架,使得其他框架可以更好的在一起进行工作,从而提高开发人员的开发效率。
使用Spring
一、定义类
public class User {
private int id;
private String name;
省略get、set方法...
}
二、添加配置文件(xxx.xml)
配置文件名称可以随便取
添加头文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
" >http://www.springframework.org/schema/beans/spring-beans.xsd">
id:标识,唯一
class:创建对象所在类的全路径
以下节点放在beans节点中
<bean id="helloWorld" class="com.java.spring.Bean.User" scope="singleton">
</bean>
三、编写测试类
@Test
public void test() {
// 通过ClassPathXmlApplicationContext实例化Spring的上下文,读取xml文件
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
// 通过ApplicationContext的getBean()方法,根据id来获取bean的实例
User hello = (User) ac.getBean("helloWorld");//自动找无参构造方法创建对象,如果没有无参构造方法会报错,除非用了构造注入
User hello2 = (User) ac.getBean("helloWorld");
System.out.println(hello+".>>>>."+hello2);//scope="singleton"只创建一个实例,所以打印的地址是一样的。如果scop=” prototype”,就可以创建任意数量实例
}
还可以同时加载多个配置文件
//方式一
ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring.xml","spring2.xml"});
//方式二
ApplicationContext context = new
ClassPathXmlApplicationContext("spring.xml","spring2.xml");
Bean的配置方式有三种
方式一:通过全类名(反射)。
建议使用此种方式
这里就不做示例了,上述使用Spring的第二个步骤就是这种方式。
方式二:静态工厂方法
public class Cat {
public void show(){
System.out.println("这是一个普通类");
}
}
public class CatFactory {
public static Cat newInstancCat(){
return new Cat();
}
}
<!--
class:工厂类的全类名
factory-method:工厂类的方法
-->
<bean id=”b1” class=”com.java.Spring.CatFactory”
factory-method=”newInstancCat”></bean>
方式三:实例工厂方法
此方式跟方式二区别不大,只是将工厂类的静态方法改为实例方法而已
<!--先实例工厂类-->
<bean id=”b2”beanFactory”class=”com.java.Spring.CatFactory”></bean>
<!--facotry-bean:要和工厂类的id值一致-->
<bean id=”cid”factory-bean=”b2” facotry-method=”newInsCat”></bean>
Spring容器
Spring容器其实就是一个超级工厂,它负责所有对象的创建。
BeanFactory:延迟初始化bean中的所有类。Bean容器的父接口。
ApplicationContext:会与初始化bean中的 scope=singleton的bean对象
ClassPathXmlApplicationContext:ApplicationContext的子类,从加载类的根目录进行查找,相对路径
FileSystemXmlApplicationContext:ApplicationContext的子类,从绝对路径进行查找
Bean简介
Bean是一个被实例化,组装,并通过Spring IOC(控制反转)容器所管理的对象
Spring将所有组件都当做bean来进行管理,所有的对象都处于Spring的管理中。
Spring负责管理和维护所有的Bean,用户无需关心Bean的实例化
开发Spring主要做两件事
创建Bean
配置Bean
Spring的本质就是根据XML管理java代码,将java代码耦合提升到XML配置中。
Bean的常用配置项
属性 | 描述 |
class | 这个属性是强制性的,并且指定用来创建 bean 的 bean 类。 |
id | 这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。 |
scope(具体配置参数请看下面的—bean的作用范围) | 这个属性指定由特定的 bean 定义创建的对象的作用域。 |
lazy-init | 延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例。 |
init-method 方法 | bean初始化时自动调用此设置的方法 |
destroy-method 方法 | bean销毁时自动调用此设置的方法。 |
constructor-arg标签 | 它是用来注入依赖关系的,并会在后面的章节中进行讨论。 |
properties标签 | 它是用来注入依赖关系的,并会在后面的章节中进行讨论。 |
autowire | 它是用来注入依赖关系的,并会在后面的章节中进行讨论。 |
Bean的作用范围
作用域 | 描述 |
singleton | 该作用域将 bean 的定义的限制在每一个 Spring IoC 容器中只能有一个实例(默认)。 |
prototype | 该作用域将单一 bean 的定义限制在任意数量的对象实例。 |
request | 该作用域将 bean 的定义限制为 HTTP 请求。只在web项目中有效。 |
session | 该作用域将 bean 的定义限制为 HTTP 会话。只在web项目中有效。 |
global-session | 该作用域将 bean 的定义限制为全局 HTTP 会话,在跨多个站点共享session时中有效。 |
Bean的初始化
默认情况下Spring容器中的所有Bean元素都会在ApplicationContext创建时默认被加载
可以通过lazy-init属性指定持久化类是否启用延迟加载,默认为false
Bean的生命周期
定义:在配置文件中定义bean
初始化:使用ApplicationContext加载配置文件时
实现initialzingBean接口,覆盖afterPropertiesSet方法
销毁:销毁bean对象
实现DisposableBean接口,覆盖destory()方法
Init-method和构造函数的区别
Init-method是对象属性完成赋值之后(调用过setter方法之后)调用
构造函数对象属性赋值之前调用
什么是IOC和DI
IoC(Inversion of Control)称为控制反转,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。简单来说就是把创建对象交给Spring来配置,也就是说创建对象的控制权被反转到了Spring框架。传统方式是:student stu=new student();
底层技术:反射,工厂模式
控制反转还有一个名字叫做DI(Dependency Injection),中文意思叫依赖注入。简单来说就是向类里面的属性设置值
两者的关系:di不能单独存在,需要在ioc基础之上完成操作
为什么使用dI
强制将组件的构建和使用分开
目的:解耦合,实现每个组件块时只关注组件内部的事情
IOC的执行原理
IOC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI来实现的。
比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了Spring我们就只需要告诉Spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制,这就是依赖注入。
Spring中的依赖注入主要通过反射来实现的,通过反射读取xml文件中配置的实体对象的类名,然后反射出需要的对象
依赖注入的方式
主要分为三种方式
设值注入:通过set方法对属性进行注入
构造注入:通过构造方法对属性进行注入
接口注入:
1. public class ClassA {
2. private InterfaceB clzB;
3. public void doSomething() {
4. Ojbect obj = Class.forName(Config.BImplementation).newInstance();
5. clzB = (InterfaceB)obj;
6. clzB.doIt();
7. }
8. ……
9. }
设值注入
基本类型注入
<bean id="helloWorld" class="com.java.spring.Bean.User" >
<property name="id" value="1" />//注意:这里的name值一定是要在类中有的,否则会报错
<property name="name" value="lei"/>
</bean>
对象类型注入
<!-- 注入对象类型属性 -->
<!-- 如果在Service里面要使用Dao里面的方法,就像下面这样做 -->
<bean id="DaoId" class="com.java.spring.Dao.UserDao"></bean>
<bean id="ServiceId" class="com.java.spring.Service.UserService">
<!-- userdao为对象类型的属性名称 -->
<property name="userdao" ref="DaoId"></property>
</bean>
嵌套Bean
嵌套Bean不是直接定义在<beans/>,而是定义在<property../>或者<constructor-arg.../>中。
嵌套Bean和外层Bean配置方式基本完全一致。
嵌套Bean不需要id属性。
<!-- 嵌套Bean --><!-- 表示在UserService里面调用UserDao -->
<bean id="did" class="com.java.spring.Service.UserService">
<property name="userdao">
<bean class="com.java.spring.Dao.UserDao"></bean>
</property>
</bean>
集合类型注入
<bean id="Uid2" class="com.java.spring.Bean.User">
<property name="list_arr">
<list>
<value>张三</value>
<value>李四</value>
</list>
</property>
<property name="map_arr">
<map>
<entry key="a" value="1"></entry>
<entry key="b" value="2"></entry>
</map>
</property>
</bean>
<bean id="cid" class="com.java.spring.Bean.Cat"></bean>
<bean id="tid" class="com.java.spring.Bean.Teacher">
<property name="cat">
<list>
<ref bean="cid"/>
</list>
</property>
</bean>
构造注入
普通构造方法
<bean id="User2" class="com.java.spring.Bean.User2">
<!-- 使用有参构造注入 ,注意:以下必须要有个构造参数为names的有参构造方法,可以没有空构造方法-->
<constructor-arg name="names" value="张三"></constructor-arg>
</bean>
当构造参数为对象时
<!-- 定义UserDao对象,并指定id为userDao -->
<bean id="userDao" class="dao.impl.UserDao"/>
<!-- 定义UserBiz对象,并指定id为userBiz -->
<bean id="userBiz" class="biz.impl.UserBiz">
<!-- 通过定义的单参构造方法为userBiz的dao属性赋值 -->
<constructor-arg>
<!-- 引用id为userDao的对象为构造方法传参 -->
<ref bean="userDao" />
</constructor-arg>
</bean>
注意:
1、一个<constructor-arg>元素表示构造方法的一个参数,且使用时不区分顺序
2、通过<constructor-arg>元素的index属性可以指定该参数的位置索引,位置从0开始
3、<constructor-arg>元素还提供了type属性,用来指定参数的类型,避免字符串和基本数据类型的混淆
注入null值和空字符串
注入null值
<bean id="..." class="exampleBean">
<property name="email"><null/></property>
</bean>
注入空字符串
<bean id="..." class="exampleBean">
<property name="email" value=""/>
</bean>
设值注入和构造注入比较
设值注入
通过set方法实现
灵活性好,但set方法数量较多
时效性差
通过无参构造实例化
构造注入
通过构造函数实现
灵活性差,仅靠重载限制太多
时效性好
通过匹配的构造函数实例化,但建议保留无参构造
P:命名空间注入
使用这种方式前,还得在头文件中添加:
xmlns:p="http://www.springframework.org/schema/p"
或者直接在<bean>标签中加上也行
基本类型属性
P:属性名=”值”;
对象类型属性
P:属性名-ref=”id”;
可以设置多个
<bean id="UId" class="com.java.spring.Bean.User" p:name="李四"></bean>
Bean自动装配
Spring可以在不使用ref下进行自动装配,可以大量减少引用的代码,可以使用元素的autowire属性进行自动装配,Spring默认情况下是关闭自动装配的。
属性值 | 描述 |
no | 这是默认的设置,它意味着没有自动装配 |
byName | 由属性名自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byName。然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。 |
byType | 由属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。 |
constructor | 类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。 |
autodetect | Spring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。 |
byName
byName规则是指通过名字注入依赖关系。
假如BeanA的实现包含setB()方法,而Spring的配置刚好包含id为b的Bean,则Spring容器会将b实例注入到BeanA中,如果容器中没有名字匹配的Bean,Spring则不会做任何事情
如果还不明白,请看下面的例子:
注意下面这个波浪线部分:
这个id必须要和service里面属性(private UserDao userdao;)名字一样,它才会自动注入
<bean id="userdao" class="com.java.spring.Dao.UserDao" ></bean>
<!--使用自动注入就不需要使用<property ref=””name=””>了-->
<bean id="ServiceId" class="com.java.spring.Service.UserService" autowire="byName" ></bean>
byType
byType规则,根据类型匹配来注入依赖关系。
假如A实例有setB()方法,而Spring配置文件中恰有一个类型B的Bean实例,容器为A注入类型匹配的Bean实例,如果容器中没有一个类型为B的实例,则什么都不会发生;但如果容器中有多个B的实例,则抛出异常。
也就是说在service里面必须要有个UserDao类型的属性,才会自动注入
<bean id="userdao" class="com.java.spring.Dao.UserDao" ></bean>
<bean id="ServiceId" class="com.java.spring.Service.UserService" autowire="byType" ></bean>
constructor
constructor:这种规则和byType很相似,但它应用于构造参数。
Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 constructor。然后,它尝试把它的构造函数的参数与配置文件中 beans 名称中的一个进行匹配和连线。如果找到匹配项,它会注入这些 bean,否则,它会抛出异常。
例如,在配置文件中,如果一个 beanA 定义设置为通过构造函数自动装配,而且它有一个带有 beanB类型的参数之一的构造函数,那么 Spring 就会查找定义名为 beanA 的 bean,并用它来设置构造函数的参数。你仍然可以使用 <constructor-arg> 标签连接其余属性。下面的例子将说明这个概念
<bean id="userDao" class="com.lxit.spring4.dao.impl.UserDaoImpl" />
<bean id="userService"
class="com.lxit.spring4.service.UserService" autowire=" constructor">
</bean>
为什么需要AOP
当需要为N个模块,需要添加一个通用的业务处理的时候,比如事务处理,日志,权限控制等等
什么是AOP
AOP(Aspect Oriented Programming):面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
AOP是对OOP的补充,是软件开发中的一个热点,也是Spring框架中的一个重要内容
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
底层是通过动态代理和反射
AOP的一些术语
目标(Target):没有被修改的对象,也是被aspectj横切的对象。
连接点(Joinpoint):类里面哪些方法可以被增强,这些方法称为连接点
切入点(Pointcut):在类里面可以有很多的方法被增强,比如实际操作中,只是增强了类里面add方法和update方法,实际增强的方法称为切入点
通知/增强(Advice):增强的逻辑,称为增强,比如扩展日志功能,这个日志功能称为增强
前置通知:在方法之前执行
后置通知:在方法之后执行
异常通知:方法出现异常时执行
最终通知:在后置之后执行
环绕通知:在方法之前和之后执行
切面(Aspect):把增强应用到具体方法上面,过程称为切面,把增强用到切入点过程
一个或多个切入点的集合,由pointcut 和advice组成
AOP代理
AOP代理:AOP框架修改原来代码之后创建的对象,它的方法即回调了目标对象的方法,也包含了增强处理的代码
AOP代理作为目标对象的替代品,包含目标对象的全部方法,而且这些方法都添加了增强处理
Spring AOP的配置
在Spring中使用AOP有两种方式
基于XML配置
基于注解的配置
要使用Spring AOP需要导入依赖的jar包
aspectjrt.jar
aspectjweaver.jar
aspectjtools.jar
org.aspectj.matcher.jar
aopalliance.jar
注意:
使用AOP操作之前,需要修改头文件为:
只要在原来的基础上添加红色部分即可
<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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
配置切入点表达式,常见的写法有:
注意空格
execution(访问修饰符 返回值类型 全类名.方法名() 异常)
execution(public com.java.spring.aop.Book.*(..))
execution(* com.java.spring.aop.Book.*(..))
execution(public *.*(..))
基于XML配置
一、编写被增强代码
public class Book {
//以下方法为连接点,意思是这些方法可以被增强
public void save(){
System.out.println("this is save method");
}
public void queyr(){
System.out.println("this is save method");
}
}
二、编写增强代码
public class MyBook {
public void afters(){
System.out.println("this is afters method--------后置通知");
}
public void befores(){
System.out.println("this is before method--------前置通知");
}
//环绕通知
public void around1(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("之前执行。。。。。");
proceedingJoinPoint.proceed();
System.out.println("之后执行........");
}
}
三、配置xml文件
<bean id="book" class="com.java.spring.aop.Book"></bean>
<bean id="mybook" class="com.java.spring.aop.MyBook"></bean>
<!-- 配置AOP操作 -->
<aop:config>
<!--配置切入点 -->
<aop:pointcut expression="execution(* com.java.spring.aop.Book.*(..))" id="bid"/>
<!--
配置切面
把增强用到方法上面
-->
<aop:aspect ref="mybook">
<!--
配置增强类型
method:增强类里面使用哪个方法作为通知
-->
<aop:before method="befores" pointcut-ref="bid"/>
<aop:after method="after1" pointcut-ref="bid"></aop:after>
<aop:around method="around1" pointcut-ref="bid"></aop:around>
</aop:aspect>
</aop:config>
测试
public void test7(){
ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
Book hello = (Book) context.getBean("book");
hello.save();
}
基于注解配置
一、编写被增强代码
@Service(value="houseId")
public class House {
//以下方法为连接点,意思是这些方法可以被增强
public void fan1(){
System.out.println("this is fan1");
}
public void Exception() throws Exception{
throw new Exception();
}
}
二、编写增强代码
@Service(value="MyHouseId")
@Aspect //必须
public class MyHouse {
@After(value="execution(* com.java.spring.aop.House.*(..))")
public void after(){
System.out.println("后置通知.......");
}
@AfterThrowing(pointcut = "execution(* com.java.spring.aop.House.*(..))")
public void exception(){
System.out.println("异常通知");
}
}
三、配置xml文件
<!-- 开启aop操作 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
@AfterReturning
注意:
在@Before、@AfterThrowing也有JoinPoint jp参数,但没有Object returnVlaue这个参数
@AfterReturning(value="execution(* com.java.work.main.Main.*(..))", returning = "returnVlaue")
public void after(JoinPoint jp,Object returnVlaue) throws Throwable{
jp.getTarget();获取是哪个类里面使用了该增强
jp.getSignature().getName();获取是哪个方法使用了该增强
jp.getArgs();获取使用该增强的方法传入了什么参数
returnVlaue;获取使用该增强的方法的返回值
System.out.println(
"调用 " + jp.getTarget() + " 的 " +
jp.getSignature().getName() + " 方法。
方法入参:" + Arrays.toString(jp.getArgs())
+"方法返回值为:"+returnVlaue
);
}
@Around
@Around("execution(* biz.IUserBiz.*(..))")
public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable {
log.info(
"调用" +jp.getTarget()+" 的 " +
jp.getSignature().getName() + " 方法。
方法入参:" + Arrays.toString(jp.getArgs())
);
try {
Object result = jp.proceed();
log.info(
"调用 " + jp.getTarget() + " 的 "
+ jp.getSignature().getName() + " 方法。
方法返回值:" + result
);
return result;
} catch (Throwable e) {
log.error(jp.getSignature().getName() + " 方法发生异常:" + e);
throw e;
}
}
AOP配置标签和注解
使用xml方式时的标签
<aop:before> 前置增强
<aop:after> 后置增强
<aop:after-throwing> 抛出异常时增强
<aop:after-returning> 在目标方法成功执行之后增强
<aop:around> 环绕增强
使用注解方式时的注解
@Before 前置增强
@AfterReturning 在目标方法成功执行之后增强
@Around 环绕增强
@AfterThrowing 抛出异常时增强
@After 后置增强 无论方法抛出异常还是正常退出,该增强都会得到执行,类似于异常处理机制中finally块的作用,一般用于释放资源
整合SSH框架
SSH系统程序架构
Struts2+Spring+Hibernate
以Spring作为核心框架,数据持久化使用Hibernate完成,web层使用Struts2
schema头文件
SpringDao中用到许多spring的特性,比如aop,tx,context等.所以需要导入schema头文件
<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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx " >http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
首先整合Spring与Struts2
一、配置web.xml
<!-- Struts2的配置 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Spring的配置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<!-- classpath:表示类路径,就是src下面的都是用类路径来获取,
classpath:要扫描的配置文件,多个配置文件用逗号隔开。
如果文件放置在/WEB-INF/目录下且名称为applicationContext.xml,可以不配置此项
-->
<param-value>classpath:spring.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
二、配置Spring.xml
方式一:使用自动注入
在头文件中添加:
default-autowire="byName"
使用这种方式就无需在Action注入Service了,因为它会自动查找Spring容器中是否有与Action中属性(private Service service)同名的bean。如果没有找到,就不执行注入。
方式二:手动注入
在Action中注入Service,以便可以在Action中访问Service对象
<!--注意:使用这种方式时:Action一定要设置为多实例的(scope="prototype"),否则它只会创建一个对象-->
<bean id="userAction" class="com.java.action.UserAction" scope="prototype">
<property name="userservice" ref="userService"></property>
</bean>
这种方式的优点是:
采用此种配置方式时,Action的实例由Spring创建,Struts 2插件的工厂类只需根据Action Bean的id查找该组件使用即可。
这种方式的弊端是:
此种方式可以为Action进行更灵活的配置,但代价是在Spring配置文件中需要定义很多Action Bean,增加了配置工作量,如非必需并非首选
三、配置Struts.xml文件
如果在Spring.xml使用的是方式一配置,那么在这里要这样配置:
<action name="UserAction" class="com.java.action.UserAction" >
<result name="welcome">welcome.jsp</result>
</action>
如果在Spring.xml使用的是方式二配置,那么在这里要这样配置:
<!--
class属性里面不用写action全路径了,因为写的话,action对象创建两次 。
写spring配置action的bean的id值即可
-->
<action name="UserAction" class="userAction" >
<result name="welcome">welcome.jsp</result>
</action>
再整合Spring和Hibernate
以下配置都在Spring.xml中配置
c3p0连接池配置
<!-- 通过bean元素声明需要Spring创建的实例。-->
<bean id="dataSourse" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_ssh" />
<property name="user" value="root" />
<property name="password" value="123" />
</bean>
sessionFactory工厂配置
<!-- session工厂由spring来管理 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" >
//注意:在这里有两种方式:如果没有配置c3p0连接池,就可以使用红色波浪线部分来直接读取Hibernate.cfg.xml文件,并且下面加灰的配置也不需要了
<!-- <property name="configLocation" value="classpath:hibernate.cfg.xml" /> -->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
<!--引用连接池-->
<property name="dataSource" ref="dataSourse"></property>
<!--读取映射文件-->
<property name="mappingLocations"
value="classpath:com/java/vo/UserVo.hbm.xml"></property>
</bean>
读取映射文件
读取映射文件的方式有多种
//以下为注解方式
<!-- 第一种使用注解类 -->
<property name="annotatedClasses">
<list>
//加载指定的映射类,多个就加多个”<value>”
<value>com.lxitedu.ssh.entities.UserVO</value>
</list>
</property>
<!-- 第二种使用注解类 -->
<property name="packagesToScan" >
<list>
<value>com.ssh.vo.</value>//表示读取vo下面的所有映射文件
</list>
</property>
<!-- 自动加载映射文件 *表示匹配该文件下的所有映射文件 -->
<property name="mappingLocations">
<list>
<value>classpath:com/lxitedu/ssh/entities/*.hbm.xml</value>
</list>
</property>
配置事务
<!-- 配置事务 -->
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
//注意:在这里有两种方式配置事务:
<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="txManager">
</tx:annotation-driven>
使用这种方式还需在Service类名上面加上@Transactional
使用了这种方式以下加灰的操作就都不要做了
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="submit*" propagation="REQUIRED" />
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="modify*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!-- AOP切面拦截事务 -->
<aop:config>
<aop:pointcut id="serviceMethod" expression="execution(* com.java.Service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
</aop:config>
最后还得在impl引用事务
<bean id="userimpl" class="com.java.Dao.Userimpl">
<property name="hibernateTransactionManager" ref="txManager"></property>
</bean>
<tx:method>属性介绍
<tx:method />是Spring中事务通知类标签,这里用来定义Hibernate中的事务。
常用属性如下
属性 | 默认值 | 描述 |
name(必选) |
| 必选,与事务关联的方法名,通配符(*)可以用来指定一批关联到相同的事务属性的方法,如"add*"、"get*"、"select*"等。 |
propagation | REQUIRED | 事务传播行为 |
isolation | DEFAULT | 事务隔离级别 |
timeout | -1 | 超时事件,单位为秒 |
read-only | false | 是否只读 |
事务隔离级别
数据库并发操作存在的异常情况:
更新丢失
脏读取
不可重复读取
两次更新问题
幻读
数据库隔离级别:
未授权读
授权读取
可重复读
串行
HibernateTemplate类
在要使用模板的类里面写上:
private HibernateTemplate hibernateTmplate;
省略get、set方法...
在要使用模板的类里面注入:
<bean id="userimpl" class="com.java.Dao.Userimpl">
<property name="hibernateTmplate" ref="hibernateTmplate"></property>
</bean>
测试
public void add(UserVo user) {
this.hibernateTmplate.save(user);
}
还可以使用以下这种方式来使用HibernateTemplate类
常用方法
get方法:
User user=hibernateTemplate.get(User.class,2)
find方法:
hibernateTemplate.find(“from User”);
find方法条件查询
hibernateTemplate.find(“from User where username=?”,“leiq”);
jdbcTemplate
public void test10(){
//注意使用jdbc模板并不会自动创建表,所以需要自己创建
//设置数据库信息
DriverManagerDataSource dataSource=new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/spring_work");
dataSource.setUsername("root");
dataSource.setPassword("123");
//创建jdbc模板,设置数据源
JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource);
//调用jdbcTemplate对象里面的方法实现操作
String sql="insert into student values(?,?)";
//update第二个参数为可变长参数,如果要insert多个自段,就可以加多个参数
jdbcTemplate.update(sql,"3","周小狗");//增删改都调用update语句
}
Spring引入其他配置文件
当一个配置文件,配置的代码太多时,就不便于维护了,此时可以采用下面的方式引入其他配置文件
<import resource=“classpath:spring2.xml”>
使用注解实现IOC
注解方式将Bean的定义信息和Bean实现类结合在一起,Spring提供的注解有
@Component:一个普通的Bean类。
@Repository :用于标注持久层DAO类
@Service:用于标注业务层类
@Controller :用于标注控制器类
这四个注解功能是一样的,都是创建对象
使用注解
使用注解前必须在头文件中加上
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
" >http://www.springframework.org/schema/context/spring-context-3.1.xsd"
还需在spring配置文件中加上
<!-- 指定要扫描的某个文件夹中的Bean,多个包用逗号隔开,可以不用写全类名,com.java这样也可以 -->
<context:component-scan base-package="com.java.spring.Dao,com.java.spring.Service" />
<!--只扫描属性上面的注解 ,一般不用这种-->
<context:annotation-config></context:annotation-config>
方式一
@Repository(value="userDao")//这一个步骤相当于<bean id=“xx” class=“xx”>
public class Userimpl implements UserDao {
}
@Service(value="userservice")
public class UserService {
@Resource(name="userDao")//红色波浪线部分必须相等
private UserDao userdao;
public void add(UserVo user){
userdao.add(user);
System.out.println("service");
}
}
方式二
@Repository(value="userDao")
public class Userimpl implements UserDao {
}
@Service(value="userservice")
public class UserService {
@Autowired //此注解和@Resource功能都是一样的
//@Inject //此注解和@Resource、@Autowired 功能都是一样的
private UserDao userDao;
public void add(UserVo user){
userDao.add(user);
System.out.println("service");
}
}
扫描过滤
方式一:使用扫描注解标签的属性
<context:component-scan base-package="com.java.Dao,com.java.Service" resource-pattern="repository/*.class">//表示过滤调没有用@Repository注解的类
</context:component-scan>
方式二:使用扫描注解标签的子标签
<content:include-filter />:只检索指定的规则
<content:exclude-filter />:排除指定的规则
类别 | 表达式 | 说明 |
annotation | org.springframework.stereotype.Repository | 所有标注了@Repository的类,该类型采用目标类是否标注了某个注解进行过滤 |
assinable | com.lxit.XxxService | 所有继承或扩展了XxxService的类,该类型采用目标是否继承或者扩展了某个类进行过滤。 |
aspectj | com.lxit.*Service | 所有类名以Service结尾的类以及继承它或扩展它的类,该类型采用aspectj表示式进行过滤。 |
regex | com\.lxit\.Service | 所有com.lxit.Service包下的类,该类型采用正则表达式进行过滤。 |
custom | com.lxit.XxxtypeFilter | 采用XxxTypeFilter进行过滤,通过代码方式定义过滤规则,该类必须实现org.springframework.core.type.typeFilter接口。 |
例:
注意红色波浪线部分,使用<context:include-filter />时,必须将系统默认的过滤器关闭。
<context:component-scan base-package="com.java.Dao,com.java.Service" resource-pattern="repository/*.class" use-default-filters="false">
<!-- 表示只接受@Respository修饰的类 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
@Autowired注解
@Autowired注解根据类型匹配(byType)的方式自动装配属性。当存在多个兼容的bean时,会查找跟属性名同名的bean,如果找不到会抛出异常,也可以添加@Qualifier标签指定要加载的bean的名称(byName)
构造器,普通字段(非public属性),一切具有参数的方法都可以应用@Autowired注解
默认情况下,所有使用@Autowired注解的属性都需要被设置,当Spring找不到匹配的Bean来装配属性时,会抛出异常,若想某个属性允许不被设置,可以将其required属性设置为false
@Autowired注解放在数组或集合的属性上时,spring容器会将所有匹配的bean进行自动装载
@Autowired注解放在Map上时,若该Map的键值为String,那么Spring会自动将所有匹配的bean进行自动加载,此时key为类的名称
@Resource
@Resource 的作用相当于 @Autowired,只不过 @Autowired 按 byType 自动注入,而@Resource 默认按 byName 自动注入罢了。
@Resource 有两个属性是比较重要的,
name :指定自动注入的名称
Type:注入类型,byName或者byType,默认byName。
@Inject
@Inject注解使用方式和@Autowired一样,也是根据类型匹配进行注入,只是@Inject没有reqired属性。所以建议使用@Autowired注解
其他注解
@Scope(“prototype”)作用域
Public class userVo
@PostConstruct初始化
Public void init(){}
@PreDestroy销毁
Public void destory(){}
@Lay延迟加载
Public class userVo