一、spring概述
spring是一个开源的控制反转(inversion of control,IOC)和面向切面(AOP)的容器框架,主要目的是简化企业开发。
二、IOC控制反转
public class PersonService {
private PersonDao personDao = new PersonDao();
public void save(Person person)
{
personDao.save(person);
}
}
PersonDao是在应用内部创建及维护的,所谓控制反转就是应用本身不负责依赖对象的创建及维护,而是由外部容器负责,这样控制权就由应用转移到了外部容器。
三、依赖注入
public class PersonService {
private PersonDao personDao;
// 构造器注入,容器把创建好的依赖对象注入进PersonService,也可使用setter方法进行注入。
public PersonService(PersonDao personDao)
{
this.personDao = personDao;
}
public void save(Person person)
{
personDao.save(person);
}
}
所谓依赖注入,就是指在运行期,由外部容器动态的将依赖对象注入到组件中。
四、spring的优点
1、降低组件之间的耦合度,实现软件各层之间的解耦。
2、可以使用容器提供的众多服务,如事务管理服务,消息服务等等,当我们使用容器管理事务时,就不再需要手工控制事务,也不需要处理复杂的事务传播,只需要通过声明式的事务属性配置就可实现。
3、容器提供单例模式支持,开发人员不再需要自己编写实现代码。
4、容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能。
5、容器提供众多的辅助类,使用这些类能加速应用的开发,如JdbcTemplate、HibernateTemplate。
6、spring对于主流的应用框架提供了集成支持,如集成Hibernate、JPA、Struts等,更便于应用的开发。
五、使用spring需要的jar
1、基本应用需要下列jar文件:
dist\spring.jar
lib\jakarta-commons\commons-logging.jar
2、如果使用了切面编程,还需要下列jar文件:
lib\aspectj\aspectjweaver.jar和aspectjrt.jar
lib\cglib\cglib-nodep-2.1.3.jar
3、如果使用了JSR-250的注解,如@Resource/@PostConstruct/@PReDestroy,还需要下列jar文件
lib\j2ee\common-annotations.jar。
六、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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
七、实例化spring容器
实例化spring容器常用的两种方式:
1、在类路径下寻找配置文件来实例化容器。
ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"beans.xml"});
2、在文件系统中寻找文件来实例化容器,通用性差,一般少用。
ApplicationContext ctx = new FileSystemXmlApplicationContext(new String[]{"d:\\beans.xml"});
spring的配置文件可以指定多个,可以通过String数组传入。
八、编写spring配置文件时,不能出现帮助信息的解决办法
1、联网,eclipse会自动从网络下载scheme文件并缓存到硬盘。
2、手动添加scheme文件,方法如下:
windows->preferences->myeclipse->files and editors->xml->xmlcatalog,点击"add",在出现的窗口中,Key Type中选择URI,在location中选择“File System”,然后在spring解压目录的dist/resources目录中选择spring-beans-2.5.xsd,Key Type修改为Scheme location,key修改为http://www.springframework.org/schema/beans/spring-beans-2.5.xsd。
id不能包含特殊字符,如果包含用到特殊字符,需要配置name属性。
九、仿spring容器代码
1、类BeanDefinition用于定义bean对象,包括id,类名。
public class BeanDefinition {
private String id;
private String className;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public BeanDefinition(String id, String className) {
this.id = id;
this.className = className;
}
}
2、ItcastClassPathXmlApplicationContext主要功能包括:读取xml配置文件,实例化bean,获取bean实例等功能。代码如下:
/**
* 仿spring容器
*/
public class ItcastClassPathXmlApplicationContext {
private List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();
private Map<String, Object> sigletons = new HashMap<String, Object>();
public ItcastClassPathXmlApplicationContext(String fileName) {
this.readXml(fileName);
this.instanceBeans();
}
/**
* 完成bean的实例化
*/
private void instanceBeans() {
try {
for (BeanDefinition beanDefinition : beanDefinitions) {
if (null != beanDefinition.getClassName()
&& !"".equals(beanDefinition.getClassName())) {
sigletons.put(beanDefinition.getId(), Class.forName(
beanDefinition.getClassName()).newInstance());
}
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 读取配置文件
* @param fileName 配置文件名称
*/
private void readXml(String fileName) {
SAXReader saxReader = new SAXReader();
Document document = null;
try {
URL xmlPath = this.getClass().getClassLoader()
.getResource(fileName);
document = saxReader.read(xmlPath);
Map<String, String> nsMap = new HashMap<String, String>();
nsMap.put("ns", " 加入命名空间
XPath xsub = document.createXPath("//ns:beans/ns:bean");// 创建beans/bean查询路径
xsub.setNamespaceURIs(nsMap);// 设置命名空间
List<Element> beans = xsub.selectNodes(document);// 获取文档下所有bean节点
for (Element element : beans) {
String id = element.attributeValue("id");// 获取id属性值
String clazz = element.attributeValue("class");// 获取class属性值
BeanDefinition beanDefinition = new BeanDefinition(id, clazz);
beanDefinitions.add(beanDefinition);
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
/**
* 获取bean实例
* @param beanName bean名称
* @return
*/
public Object getBean(String beanName)
{
return this.sigletons.get(beanName);
}
}
十、三种实例化bean的方式
1、使用类构造器实例化
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean"></bean>
2、使用静态工厂方法实例化
<bean id="personService2" class="cn.itcast.service.impl.PersonServiceBeanFactory" factory-method="createPersonServiceBean"></bean>
public class PersonServiceBeanFactory {
public static PersonServiceBean createPersonServiceBean()
{
return new PersonServiceBean();
}
}
3、使用实例工厂方法实例化
<bean id="personServiceFactory" class="cn.itcast.service.impl.PersonServiceBeanFactory"></bean>
<bean id="personService3" factory-bean="personServiceFactory" factory-method="createPersonServiceBean2"></bean>
public class PersonServiceBeanFactory {
public PersonServiceBean createPersonServiceBean2()
{
return new PersonServiceBean();
}
}
十一、bean的作用域
1、singleton
在每个spring ioc容器中一个bean定义只有一个对象实例,默认情况下会在容器启动时初始化bean,但可指定bean节点的lazy-init="true"来延迟初始化bean,这时只有第一次获取bean时才会初始化bean,例如:
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" lazy-init="true"></bean>
如果想对所有bean都应用延迟初始化,可以在根节点beans设置:
<beans default-lazy-init="true"/>
注意:延迟加载一般不建议使用,启动时加载各个类,出现问题可以及时处理,延迟加载后,错误只能在运行时才能发现,不利于维护。
2、prototype
每次从容器获取bean都是新的对象。
3、request
4、session
5、global session
十二、指定bean的初始化方法和销毁方法
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" init-method="init" destroy-method="destory"></bean>
十三、依赖注入方法
1、使用构造器注入
public PersonServiceBean(PersonDao personDao, String name) {
this.personDao = personDao;
this.name = name;
}
<bean id="personServiceConstructor" class="cn.itcast.service.impl.PersonServiceBean">
<constructor-arg index="0" type="cn.itcast.dao.PersonDao" ref="personDao"></constructor-arg>
<constructor-arg index="1" value="itcast"></constructor-arg>
</bean>
2、使用属性setter方法注入
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
<bean id="personServiceSetter" class="cn.itcast.service.impl.PersonServiceBean">
<property name="personDao" ref="personDao"></property>
</bean>
3、使用Field注入(用于注解方式)
注入依赖对象可以采用手工装配和自动装配,在实际应用中建议使用手工装配,因为自动装配会产生未知情况,开发人员无法预见最终的装配结果。
十四、依赖注入---手工装配
手工装配依赖对象,有两种编程方式
1、在xml配置文件中,通过在bean节点下配置,例如:
<bean id="personServiceConstructor" class="cn.itcast.service.impl.PersonServiceBean" init-method="init" destroy-method="destory">
<constructor-arg index="0" type="cn.itcast.dao.PersonDao" ref="personDao"></constructor-arg> //构造器注入
<property name="name" value="itcast"></property> //属性setter方法注入
</bean>
2、在java代码中使用@Autowired或@Resource注解方式进行装配,使用注解方式需要在xml配置文件中配置如下信息:
<beans xmlns="
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:annotation-config></context:annotation-config>
</beans>
这个配置隐式注册了多个对注释进行解析处理的处理器:AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor。
注意:@Resource注解在spring安装目录下的lib\j2ee\common-annotations.jar中。
@Autowired和@Resource注解的区别:
@Autowired默认按类型装配,@Resource默认按名称装配,当找不到与名称匹配的bean时才按类型装配。
@Autowired
private PersonDao personDao;//用于字段上
@Autowired
public void setPersonDao(PersonDao personDao) {//用于属性的setter方法上
this.personDao = personDao;
}
@Autowired注解按类型装配依赖对象,默认情况下要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用,例如:
@Autowired @Qualifier("personDaoBean")
private PersonDao personDao;
@Resource注解和@Autowired注解一样,也可以标注在字段属性的setter方法上,但他默认按名称装配。名称可以通过@Resource的name属性指定,如果没有指定name属性,当注解标注在字段上,则默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,则默认取属性名作为bean名称寻找依赖对象。
@Resource(name="personDaoBean")
private PersonDao personDao;//用于字段上
注意:如果没有指定name属性,并且按照默认的名称依然找不到依赖对象时,@Resource注解会回退到按类型装配,但一旦指定了name属性,就只能按名称装配了,此时如果根据name找不到依赖对象,则会报错。
@AutoWired是spring注解,@Resource是j2ee注解,建议使用@Resource。
十五、集合类型的装配
set、list、properties、map集合的注入配置:
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean">
<property name="personDao" ref="personDao"></property>
<property name="sets">
<set>
<value>set1</value>
<value>set2</value>
<value>set3</value>
</set>
</property>
<property name="lists">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
<property name="properties">
<props>
<prop key="key1">value1</prop>
<prop key="key2">value2</prop>
<prop key="key3">value3</prop>
</props>
</property>
<property name="maps">
<map>
<entry key="map_key1" value="map_value1"></entry>
<entry key="map_key2" value="map_value2"></entry>
<entry key="map_key3" value="map_value3"></entry>
</map>
</property>
</bean>
十六、通过在classpath自动扫描方式把组件纳入spring容器中管理
在一个大型项目中,通常会有成百上千个组件,如果这些组件采用xml的bean定义来配置,显然会增加配置文件的体积,查找及维护起来也不太方便。spring2.5为我们引入了组件自动扫描机制,它可以在类路径下寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入spring容器中管理。
要使用自动扫描机制,需要打开以下配置信息:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package="cn.itcast"></context:component-scan>
</beans>
其中base-package为需要扫描的包(含子包)。
@Service用于标注业务层组件;
@Controller用于标注控制层组件;
@Repository用于标注数据访问组件;
@Component泛指组件,当组件不好归类时,可以使用这个注解进行标注。
十七、依赖注入---自动装配依赖对象
无需手工配置,系统自配装配,由于存在不可预知性,不推荐使用。
<bean id="" class="" autowire="byType"/>
autowire属性取值如下:
1、byType:按类型装配,可以根据属性的类型,在容器中寻找跟该类型匹配的bean,如果发现多个,将抛出异常,如果没有找到,则属性值为null。
2、byName:按名称装配,可以根据属性的名称,在容器中寻找跟该属性同名的bean,如果没有找到,则属性值为null。
3、constructor:与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,将抛出异常。
4、autodetect:通过bean类的自省机制来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式。
十八、AOP---代理对象
请求---代理对象---目标对象
jdk代理类:
public class JDKProxyFactory implements InvocationHandler {
private Object targetObject;
public Object createProxyInstance(Object targetObject)
{
this.targetObject = targetObject;
return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
PersonServiceBean personServiceBean = (PersonServiceBean) this.targetObject;
Object result = null;
if(null != personServiceBean.getUser())
{
result = method.invoke(targetObject, args);//调用目标对象targetObject的method方法,传入参数args
}
return result;
}
}
cglib代理类:
public class CglibProxyFactory implements MethodInterceptor {
private Object targetObject;
public Object createProxyInstance(Object targetObject)
{
this.targetObject = targetObject;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.targetObject.getClass());//代理继承目标类,对目标类中所有非final方法进行覆盖
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
PersonServiceBean personServiceBean = (PersonServiceBean) this.targetObject;
Object result = null;
// 环绕通知
if(null != personServiceBean.getUser())
{
// 前置通知
try
{
result = methodProxy.invoke(targetObject, args);//调用目标对象targetObject的method方法,传入参数args
// 后置通知
}
catch(Exception e)
{
// 例外通知
}
finally
{
// 最终通知
}
}
return result;
}
}
十九、AOP---基本概念
1、Aspect(切面):横切性关注点的抽象即为切面,它与类相似只是两者的关注点不一样,类是对事物特征的抽象,切面是横切性关注点的抽象。
2、joinpoint(连接点):那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是属性或类构造器。
3、Pointcut(切入点):指我们要对那些joinpoint进行拦截的定义。
4、Advice(通知):只拦截到joinpoint之后所要做的事情。分为前置通知,后置通知,异常通知,最终通知,环绕通知。
5、Target(目标对象):代理的目标对象。
6、Weave(织入):指将aspect应用到target对象并导致proxy对象创建的过程。
7、Introduction(引入):在不修改代码的前提下,Introduction可以在运行期为类动态的添加一些方法或属性。
二十、使用spring进行面向切面编程
要进行AOP编程,首先我们需要需要在spring配置文件中引入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-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<beans>
spring提供了两种切面使用方式:
1、基于xml配置方式进行AOP开发。
2、基于注解方式进行AOP开发。
二十一、基于注解方式声明切面
首先启动对@Aspect注解的支持,配置<aop:aspectj-autoproxy></aop:aspectj-autoproxy>。
<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-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<beans>
自定义拦截器代码:
@Aspect
public class MyInterceptor {
@Pointcut("execution (* cn.itcast.service.impl.PersonServiceBean.*(..))")
private void anyMethod(){}//声明一个切入点
@Before("anyMethod() && args(name)")//拦截参数只有一个,并且必须是字符串类型的方法
public void doAccessCheck(String name)
{
System.out.println("前置通知:"+name);
}
@AfterReturning(pointcut="anyMethod()",returning="result")//拦截返回类型为字符串的方法
public void doAfterReturning(String result)
{
System.out.println("后置通知:"+result);
}
@After("anyMethod()")
public void After()
{
System.out.println("最终通知");
}
@AfterThrowing(pointcut="anyMethod()",throwing="e")
public void doAfterThrowing(Exception e)
{
System.out.println("例外通知:"+e);
}
@Around("anyMethod()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable
{
System.out.println("进入方法");
Object result = pjp.proceed();
System.out.println("退出方法");
return result;
}
}
没有异常时输出:
前置通知:zhangsan
进入方法
我是save()方法
最终通知
后置通知:null
退出方法
发生异常时输出:不会执行后置通知和环绕通知的退出通知。
前置通知:zhangsan
进入方法
最终通知
例外通知:java.lang.RuntimeException: Exception
二十二、基于xml配置方式声明切面
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean"></bean>
<bean id="myXmlInterceptor" class="cn.itcast.service.MyXmlInterceptor"></bean>
<aop:config>
<aop:aspect id="asp" ref="myXmlInterceptor">
<aop:pointcut expression="execution(* cn.itcast.service.impl.PersonServiceBean.*(..))" id="mycut"/>
<aop:before method="doAccessCheck" pointcut-ref="mycut"/>
<aop:after-returning method="doAfterReturning" pointcut="mycut"/>
<aop:after method="after" pointcut="mycut"/>
<aop:after-throwing method="doAfterThrowing" pointcut="mycut"/>
<aop:around method="doBasicProfiling" pointcut="mycut"/>
</aop:aspect>
</aop:config>
二十三、声明切面表达式
execution(返回值 包名.类名.方法名(参数类型))
execution(* cn.itcast.service..*.*(..))//拦截某包及其子包下的所有类所有方法
execution(!void cn.itcast.service.impl.PersonServiceBean.*(..))//拦截PersonServiceBean类中返回值类型不为void的方法
二十四、spring+jdbc整合开发
使用spring+jdbc集成步骤如下:
1、配置数据源:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.gjt.mm.mysql.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/itcast?useUnicode=true&character=UTF-8"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
<!-- 连接池启动时的初始值 -->
<property name="initialSize" value="1"></property>
<!-- 连接池的最大值 -->
<property name="maxActive" value="500"></property>
<!-- 最大空闲值 -->
<property name="maxIdle" value="2"></property>
<!-- 最小空闲值 -->
<property name="minIdle" value="1"></property>
</bean>
2、配置事务
配置事务需要在xml配置文件中引入用于声明事务的命名空间,事务的配置方式有两种:注解方式和基于XML配置方式。
1>采用注解方式配置事务,精确控制,灵活,建议使用。
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- 指定属性文件存放路径 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${driverClassName}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
<!-- 连接池启动时的初始值 -->
<property name="initialSize" value="${initialSize}"></property>
<!-- 连接池的最大值 -->
<property name="maxActive" value="${maxActive}"></property>
<!-- 最大空闲值 -->
<property name="maxIdle" value="${maxIdle}"></property>
<!-- 最小空闲值 -->
<property name="minIdle" value="${minIdle}"></property>
</bean>
<!-- 声明事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 注解方式配置事务 -->
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="personService" class="cn.itcast.bean.impl.PersonServiceBean">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
PersonServiceBean代码:
@Transactional
public class PersonServiceBean implements PersonService {
// private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Transactional(noRollbackFor=RuntimeException.class)
public void delete(Integer personid) {
jdbcTemplate
.update("delete from person where id=?", new Object[] { personid },
new int[] { java.sql.Types.INTEGER });
throw new RuntimeException("运行期异常");
}
@Transactional(propagation=Propagation.NOT_SUPPORTED)
public Person getPerson(Integer personid) {
return (Person)jdbcTemplate.queryForObject("select * from person where id=?",
new Object[] { personid },
new int[] { java.sql.Types.INTEGER }, new PersonRowMapper());
}
public List<Person> getPersons() {
return ( List<Person>)jdbcTemplate.query("select * from person",
new PersonRowMapper());
}
public void save(Person person) {
jdbcTemplate.update("insert into person(name) values(?)",
new Object[] { person.getName() },
new int[] { java.sql.Types.VARCHAR });
}
public void update(Person person) {
jdbcTemplate.update("update person set name=? where id=?",
new Object[] { person.getName(), person.getId() }, new int[] {
java.sql.Types.VARCHAR, java.sql.Types.INTEGER });
}
}
2>采用基于XML方式配置事务:
<!-- xml方式配置事务 -->
<aop:config>
<aop:pointcut expression="execution(* cn.itcast.service..*.*(..))" id="transactionPointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="transactionPointcut"/>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" propagation="NOT_SUPPORTED"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
二十四、事务传播属性
REQUIRED:业务方法需要在一个事物中运行,如果方法运行时,已经处在一个事物中,那么假如该事物,否则为自己创建一个新的事务。
NOT_SUPPORTED:声明方法不需要事务,如果方法没关联到一个事物,容器不会为它开启事务,如果方法在一个事物中被调用,该事物会被挂起,在方法调用结束后,原先的事务便会恢复执行。
REQUIRESNEW:不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事物中,则原有事务会被挂起,新的事务会被创建,知道方法执行结束,新事务才算结束,原有的事务才会会发执行。
MANDATORY:指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务,如果业务方法在没有事务的环境下调用,容器就会抛出异常。
SUPPORTS:如果业务方法在某个事务范围内被调用,则方法成为事务的一部分,如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。
Never:执行业务方法绝对不能在事务范围内执行,如果业务方法在某个事务中执行,容器会抛出异常,只有业务方法没有关联到任何事务,才能正常执行。
NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按required属性执行,它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点,内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器有效。
二十五、spring2.5+hibernate3.3+struts1.3整合开发
1、导入ssh框架包:
1>hibernate核心安装包下的:
hibernate3.jar
lib\required\*.jar
lib\option\ehcache-1.2.3.jar
hibernate注解安装包下的:
lib\test\slf4j-log4j12.jar
2>spring安装包下的:
dist\spring.jar
dist\modules\spring-webmvc-struts.jar
lib\jakarta-commons\commons-logging.jar、commons-dbcp.jar、commmons-pool.jar
lib\aspectj\aspectjweaver.jar、aspectjrt.jar
lib\cglib\cglib-nodep-2.1.3.jar
lib\j2ee\common-annotations.jar
lib\log4j\log4j-1.2.15.jar
3>struts安装包下:
所有jar包,建议把jstl-1.0.2.jar和standard-1.0.2.jar更换为1.1版本。spring中已经存在一个anltr-2.7.6.jar,所以把struts中的anltr-2.7.2.jar删除,避免jar包冲突。
4>数据库驱动jar包
mysql-connector-java-5.1.18-bin
2、spring配置文件beans.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- 打开注解方式 -->
<context:annotation-config></context:annotation-config>
<!-- 指定属性文件存放路径 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${driverClassName}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
<!-- 连接池启动时的初始值 -->
<property name="initialSize" value="${initialSize}"></property>
<!-- 连接池的最大值 -->
<property name="maxActive" value="${maxActive}"></property>
<!-- 最大空闲值 -->
<property name="maxIdle" value="${maxIdle}"></property>
<!-- 最小空闲值 -->
<property name="minIdle" value="${minIdle}"></property>
</bean>
<!-- 配置hibernate二级缓存 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="mappingResources">
<list>
<value>cn/itcast/bean/Person.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.hbm2ddl.auto=update
hibernate.show_sql=false
hibernate.format_sql=false
</value>
</property>
</bean>
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 配置注解方式声明事务 -->
<tx:annotation-driven transaction-manager="txManager"/>
<!-- 配置业务bean -->
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean"></bean>
</beans>
3、编写Person.java和Person.hbm.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.bean">
<class name="Person" table="person">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name" length="10" not-null="true"></property>
</class>
</hibernate-mapping>
4、编写PersonServiceBean代码,抽取PersonService接口:
@Transactional
public class PersonServiceBean implements PersonService {
@Resource
private SessionFactory sessionFactory;
public void save(Person person) {
// 获取容器中被管理的session
sessionFactory.getCurrentSession().persist(person);
}
public void update(Person person) {
sessionFactory.getCurrentSession().merge(person);
}
@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
public Person getPerson(Integer personid) {
return (Person) sessionFactory.getCurrentSession().get(Person.class,
personid);
}
public void delete(Integer personid) {
sessionFactory.getCurrentSession()
.delete(
sessionFactory.getCurrentSession().load(Person.class,
personid));
}
@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
public List<Person> getPersons() {
return sessionFactory.getCurrentSession().createQuery("from Person")
.list();
}
}
5、单元测试hibernate和spring整合:
public class PersonServiceTest {
private static PersonService personService = null;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
personService = (PersonService) ctx.getBean("personService");
}
@Test
public void testSave() {
personService.save(new Person("ssh"));
}
@Test
public void testUpdate() {
Person person = personService.getPerson(1);
person.setName("张三");
personService.update(person);
}
@Test
public void testGetPerson() {
Person person = personService.getPerson(1);
System.out.println(person.getName());
}
@Test
public void testDelete() {
personService.delete(1);
}
@Test
public void testGetPersons() {
List<Person> persons = personService.getPersons();
for(Person person : persons)
{
System.out.println(person.getId()+":"+person.getName());
}
}
}
6、如果action没有交给spring管理,可通过如下语句获取spring容器实例:
WebApplicationContext ctx = WebApplicationContextUtils
.getWebApplicationContext(this.getServlet().getServletContext());
把action交给spring管理后,可以使用依赖注入在action中注入业务层bean,确保action的path属性值与bean的名称相同。
struts配置:
<action path="/person/list"></action>
spring配置:
<bean name="/person/list" class="cn.itcast.web.action.PersonAction"></bean>
在struts配置文件中添加进spring的请求控制器。该请求控制器会根据action的path属性到spring容器中寻找和该属性值同名的bean,如果找到则使用该bean处理用户请求。
<controller>
<set-property property="processorClass" value="org.springframework.web.struts.DelegatingRequestProcessor" />
</controller>
二十六、hibernate二级缓存配置
hibernate二级缓存配置文件名称为ehcache.xml,存放在类路径下,配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<!--
diskStore 缓存对象存放在硬盘上的位置
defaultCache 节点为默认的缓存策略
maxElementsInMemory 内存中最大允许存在的对象数量
eternal 设置缓存中的对象是否永远不过期
overflowToDisk 把溢出的对象存放到硬盘上
timeToIdleSeconds 指定缓存对象空闲多长时间就过期,过期的对象会被清除掉
timeToLiveSeconds 指定缓存对象总的存活时间
diskPersistent 当jvm结束是否持久化对象
diskExpiryThreadIntervalSecond 指定专门用于清除过期对象的监听线程的轮询时间
-->
<ehcache>
<diskStore path="D:\cache"/>
<defaultCache maxElementsInMemory="1000" eternal="false" overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="180"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="60"/>
<cache name="cn.itcast.bean.Person" maxElementsInMemory="100" eternal="false" overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="180"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="60"/>
</ehcache>
测试二级缓存:从数据库中取出一条记录,关闭数据库之后,依然能读取出正确结果。
public void testGetPerson() {
Person person = personService.getPerson(3);
System.out.println(person.getName());
try {
System.out.println("请关闭数据库");
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第二次开始获取");
Person person1 = personService.getPerson(3);
System.out.println(person1.getName());
}
二十七、ssh整合开发常见问题解决
1、使用spring解决struts1.3乱码问题
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2、使用spring解决hibernate因session关闭导致的延迟加载异常问题
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
二十八、spring2.5+hibernate3.3+struts2整合开发
和整合struts1差不多,区别在于:
1、导入struts2的jar包:
struts2的lib目录下所有不带-plugin结尾的jar文件,除了struts2-spring-plugin-2.0.11.1.jar。
2、web.xml配置文件:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans.xml</param-value>
</context-param>
<!-- 对spring容器进行实例化 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置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>
3、struts.xml配置文件:
<!-- 指定action对象由spring容器创建 -->
<constant name="struts.objectFactory" value="spring"/>