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(指定某個網路資源)