spring的使用-ssh整合
Posted liuxuelin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring的使用-ssh整合相关的知识,希望对你有一定的参考价值。
ssh整合-xml方式:
1.需要记住的三个jar包:
spring-web-4.2.4.RELEASE.jar ---保证项目启动时就实例化spring配置的对象(通过一个servletContext监听器ContextLoaderListener实现),保证整个项目只有一个工厂。
struts2-spring-plugin-2.3.24.jar ---解决了struts2和spring的整合问题,将struts2中的action交给spring创建
spring-orm-4.2.4.RELEASE .jar ---解决hibernate和spring的整合,将sessionfactory交给spring创建(通过一个FactoryBean接口实现类实现),并且hibernate的事务也由spring事务管理器管理
2.FactoryBean接口 //对象工厂,可以存入对象。这个接口中的方法有getObject(); getObjectType(); ...这两个方法获取对象和对象class
如果让一个类实现这个接口,然后将这个类交给spring管理(在applicationContext.xml中配置这个bean),如:
<bean id="car" class="...CarFactoryBean"></bean>
那么在获取bean对象时:
a. 如果通过applicationContext对象调用getBean();获取:
ac.getBean("car");
此时,底层发现CarFactoryBean实现了FactoryBean接口,就会调用其中getObject();方法获取到这个类中的对象
b. 如果通过ac.getBean(Car.class);这种方式获取bean:
那么,会调用CarFactoryBean中的getObjectType();如果发现这个方法返回的是Car.class,则会接下来调用getObject()获取对象;
注意:
@autowire //这种方式是在spring容器中找到Car或其实现类实现注入的,相当于调用ac.getBean(Car.class);获取,因此使用FactoryBean获取时要重写getObjectType();
private Car car;
3.spring的hibernate整合,配置文件 //原理:通过factoryBean方式让spring来管理sessionFactory
方式一:LocalSessionFactoryBean注入configLocation方式 //LocalSessionFactoryBean实现了FactoryBean和initialize
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property> //这个属性传入的是hibernate配置文件类路径
</bean>
原理: 1. 实例化LocalSessionFactoryBean时会注入configLocation,
2. 由于LocalSessionFactoryBean底层实现了initialize,会调用初始化方法,根据配置文件路径加载和解析hibernate.cfg.xml配置文件,并创建SessionFactory对象
3. 在获取sessionFactory时会调用getObject();方法获取
缺点:依然需要hibernate.cfg.xml文件
方式二:配置LocalSessionFactoryBean,注入hibernate.cfg.xml中的连接池、属性等 //不再需要hibernate.cfg.xml文件
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> //配置LocalSessionFactoryBean
<property name="dataSource" ref="dataSource"></property> //需要注入连接池
<property name="hibernateProperties"> //注入其他属性,hibernateProperties底层为properties集合
<value>
hibernate.show_sql=true
hibernate.format_sql=true
hibernate.dialect=org.hibernate.dialect.mysqlDialect
hibernate.hbm2ddl.auto=update
</value>
</property>
<property name="hibernateProperties"> //也可通过这种方式注入其他属性
<props>
<prop key="hibernate.show_sql">true</prop>
</props>
<property>
<property name="mappingDirectoryLocations" value="classpath:cn/itheima/domain"></property> //配置映射文件(xxx.hbm.xml)路径,四种方式
<property name="mappingLocations" value="classpath:cn/itheima/domain/User.hbm.xml"></property>
<property name="mappingResources" value="cn/itheima/domain/User.hbm.xml"></property>
<property name="mappingJarLocations"></property>
</bean>
4.spring的hibernate整合,Dao的操作:
让Dao继承HibernateDaoSupport,只需要注入SessionFactory就可以获得HibernateTemplate //这里的HibernateDaoSupport类似于JdbcDaoSurport,封装了HibernateTemplate
<bean id="userDao" class="cn.itheima.dao.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory"/> //dao中注入sessionFactory
</bean>
使用hibernateTemplate实现增删改查 //底层通过session实现,得到的是getCurrentSession(); 事务默认为只读
this.getHibernateTemplate().save(user) //底层调用session.save(user)
this.getHibernateTemplate().update(user) //底层调用session.update(user)
this.getHibernateTemplate().del(user) //底层调用session.del(user)
this.getHibernateTemplate().get(User.class,id) //底层调用session.get(User.class,id)
this.getHibernateTemplate().find("hql",arg1,arg2...) //底层调用session.createQrery("hql").serParameter(...).list();
原理:
配置文件加载时创建userDao,同时注入sessionFactory,sessionFactory注入时,会直接创建HibernateTemplate对象
因此,userDao可以直接调用父类的getHibernateTemplate()来获取HibernateTemplate实现增删改查
增删改查的实现在底层均通过hibernate中的session对象实现。为getCurrentSession();得到与线程绑定的session
注意:未添加事务时有默认事务,且事务的默认为readonly,即默认只能查,不能增删改。
总结:spring整合hibernate主要包括两个方面:
1)将sessionFactory交给spring管理 //两种方式,均由spring来加载配置文件,创建sessionFactoryBean
2)dao层使用hibernateTemplate实现增删改查 //dao继承HibernateDaoSupport,注入sessionFactory实现
5.spring的hibernate整合,事务管理:
采用HibernateTransactionManager事务管理器,注入sessionFactory,其他操作(配置通知切面,<tx:advice> <aop:config>)同day3中的spring自身事务的操作:
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
总结:基于spring自身提供的事务管理模式,只是采用的事务管理器不同
6.Spring整合struts2框架 //必须导入jar包struts2-spring-plugin-2.3.24.jar,这个jar包改变了创建Action的方式
原理:
struts2框架配置文件加载顺序
a. default.properties
b. struts-default.xml
c. struts -plugin.xm
在导入的jar包中有一个plugin的配置文件,加载时会覆盖struts2中的一个常量struts.objectFactory为spring
struts2框架默认通过ObjectFactory创建对象,由于上述常量值的改变。bean的创建由spring对象工厂SpringObjectFactory来管理
SpringObjectFactory创建对象主要方法:
public Object buildBean(String beanName, Map<String, Object> extraContext, boolean injectInternal) throws Exception {
Object o;
//这个beanName是struts2框架传入的,对应struts.xml中的class属性
//把beanName作为id判断Spring容器是否有这个id对应的bean
if (appContext.containsBean(beanName)) {
//如果找到了就直接从Spring容器获取
o = appContext.getBean(beanName);
} else {
//如果没有从Spring容器找到该id对应bean,
//就把beanName作为全类名通过反射创建对象并且注入属性
Class beanClazz = getClassInstance(beanName);
o = buildBean(beanClazz, extraContext);
}
return o;
}
总结:struts2-spring-plugin-2.3.24.jar改变了struts中action的创建方式,将action创建交给spring管理
方式一:基于spring管理action :伪类名 //在applicationContext.xml文件中来声明action
1. 在applicationContext中配置action:
<bean id="userAction" class="cn.itheima.action.UserAction" scope="prototype"> //此处必须设置scope的属性为prototype,否则每次请求获取到的是同一个action
<property name="userService" ref="userService"/> //service的注入,必须在action中提供对应的set方法
</bean>
2. 在struts.xml中配置,将class 值修改为伪类名
<package name="default" extends="struts-default">
<action name="user_add" class="userAction" method="add"> //此处class不再是全类名,而是对应了applicationContext中的id
<result name="...">...</result>
</action>
</package>
原理:
当请求发出时,spring会根据class 的值,在applicationContext.xml中查找是否有对应的id,有则直接从applicationContext容器中获取对象
方式二:自动注入service
struts.xml中的class 直接用全类名,只需要在action中提供service的set方法
<action name="user_add" class="cn.itheima.action.UserAction" method="add"> //此处class是全类名
<result name="...">...</result>
</action>
public void UserAction extends ActionSupport{
private IUserService userService;
public void setUserService(IUserService us){ //会根据set方法的名称在applicationContext.xml中查找对应的id(userService),并注入bean
this.userService=us;
}
}
struts.objectFactory.spring.autoWire常量的配置:
<constant name="struts.objectFactory.spring.autoWire" value="type"/> //默认值为name
默认情况下,根据action中的set方法名称,查找applicationContext.xml对应id并实现属性注入,
常量值修改为type后,会根据set方法的参数类型,在applicationContext.xml中根据这个类型查找其实现类对象并注入属性
原理:
与伪类名方式执行流程相同,会根据class属性的值,在applicationContext.xml中查找是否有对应的id,此时,找不到对应的id,会根据这个全类名创建对象,并将对象放入spring容器,
同时,根据类中的set方法注入对应的属性
总结: 本质上是将action交给spring管理,两种方式
补充:
<context:component-scan>和<context:annotation-config>的区别:
<context:component-scan> //注解扫描,做了两件事,扫描注解,创建对象放入spring容器中,让注解生效
<context:annotation-config> //使注解生效,与注解扫描的区别?
ssh整合,注解的使用
1.实体类的注解:
1)实体类注解配置:
@Entity
@Table(name="t_user")
public class User {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
private int age;
2)applicationContext.xml中实体类的引入:
<property name="packagesToScan" value="cn.itheima.domain"></property> //与之前引入hbm.xml文件不同,在sessionFactory中注入packagesToScan,指定实体类所在的包
2.dao的注解配置:
applicationContext.xml中开启注解扫描:
<context:component-scan base-package="cn.itheima"></context:component-scan>
dao的注解配置:
@Repository
public class UserDaoImpl extends HibernateDaoSupport implements IUserDAO {
@Autowired
public void setSf(SessionFactory sf){ //将sessionFactory注入父类,前提:applicationContext.xml中配置LocalSessionFactoryBean
super.setSessionFactory(sf);
}
3.service的注解配置,事务的添加
1)service注解配置:
@Service //注解方式配置UserServiceImpl
public class UserServiceImpl implements IUserService{
@Autowired //注入dao
private IUserDAO userDao;
2)事务管理的实现:
A.applicationContext.xml:
<bean id="tm" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> //配置事务管理器
<property name="sessionFactory" ref="sessionFactory"></property> //要注入sessionFactory
</bean>
<tx:annotation-driven transaction-manager="tm"/> //配置注解驱动,需要传入事务管理器
B.类上或方法上加事务管理注解:
@Transactional
public class UserServiceImpl implements IUserService{
4.action的注解配置: //采用的是全类名方式完成整合
只需要在配置好struts中的配置的基础上,在private IUserService userService;加上@Autowired即可
@ParentPackage("struts-default")
public class UserAction extends ActionSupport implements ModelDriven<User>{
private User user=new User();
@Autowired //只需要添加这个注解即可
private IUserService userService;
@Override
public User getModel() {
return user;
}
@Action(value="user_add",results={@Result(name="success",location="/success.jsp")}) //在此处其实有一个className属性,相当于配置文件中的class,默认为UserAction全类名
public String add(){
userService.add(user);
return SUCCESS;
}
}
原理: 请求发出时,spring会代替struts2创建action对象(根据xml中的class属性值或者注解中@Action中的className属性),有一下两种方式
A.默认的全类名方式: //className属性取默认值
由于@Action中的className默认值为UserAction全类名,因此spring会根据这个全类名,创建UserAction对象并交给spring容器管理,同时根据类型注入userService。
因此在spring容器中必须有userService对象存在,即在applicationContext中必须配置这个对象
B.让程序以伪类名方式创建: //麻烦,不建议使用 //设置className属性
1)设置@Action中的className属性为userAction。此时,会根据这个名称,在applicationContext中获取对象,因此需手动将userAction交给spring容器管理:
2)在UserAction类上配置注解,实现action由spring创建并管理:
@Component //默认的id为userAction
@Scope("prototype") //必须配置这个属性
public class UserAction extends ActionSupport implements ModelDriven<User>{
5.no session 的问题:配置一个filter
问题产生的原因:默认情况下,session是在dao层通过getCurrentSession();获取到,这个session在service层事务关闭时会自动关闭。
当在dao层进行延迟查询时,不会马上从数据库查询数据。当要使用这些数据时,会再action层获取,此时会使用到session,由于session在service已经关闭,会出现no session异常
<filter> //必须放在struts2过滤器之前
<filter-name>openSessionInView</filter-name>
<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openSessionInView</filter-name>
<url-pattern> /* </url-pattern> // */
</filter-mapping>
原理:
请求发出时,会先被这个过滤器拦截,在这个过滤器中,会从spring容器中获取到sessionFactory,并调用openSession()获取session这个session会被放到sessionHolder中,被dao拿到。
这个过滤器走完后会执行struts2过滤器,以及action。service,dao层的操作,此时在service层执行完,事务提交时不会再关闭session,而是再action执行完后由过滤器关闭session
如果请求为action请求,struts2过滤器不会放行,因此必须把openSessionInView过滤器放在struts2过滤器之前
//sturts 会拦截url,不会拦截uri(指定某个网络资源)
以上是关于spring的使用-ssh整合的主要内容,如果未能解决你的问题,请参考以下文章