Spring
Spring依赖于spring工厂,管理对象创建,可以自动new对象,是个轻量级项目(轻量级:学习成本低,记东西得少-_-!)
IoC:Inverse of Controller(控制反转)
对象创建的控制权转移到程序的外部(Spring),完成解耦
DI:Dependency Injectjion 依赖注入(依赖谁就注入谁)
IoC的目的是为了解耦,对象创建的控制权转移到程序外部,需要使用对象时,通过DI来实现。
DI的两种实现方法:set注入和构造注入
另外还可以静态工厂注入
;
通过Spring实现IoC和DI:
1、SpringIoC 对象创建的控制权转移到 Spring容器中。
2、SpringIoC容器:放各种JavaBean(Java对象的容器)/Spring bean工厂(工厂:造对象)。
使用Spring实现IoC的优势:
彻底的IoC,把对象创建的过程由硬编码改成了配置文件,进一步解耦,后期只修改配置文件即可,无需进行二次编译。
硬编码:代码中修改,硬编译,服务器需要重新部署,而配置文件则不需要
SpringIoC开发步骤:
1、加载jar包:
spring-beans-3.2.14.RELEASE.jar
spring-context-3.2.14.RELEASE.jar
spring-core-3.2.14.RELEASE.jar
spring-expression-3.2.14.RELEASE.jar
此外,Spring运行还需导入commons-logging的jar包:
com.springsource.org.apache.commons.logging-1.1.1.jar
其中spring-core,和spring-beans提供框架的基本组成部分,包括IOC和依赖注入功能。
2、编写Spring配置文件:
2.1、 applicationContext.xml(Spring上下文配置),一般放到src根目录,主要配置IoC和DI
①、打开页面:spring-framework-4.2.4.RELEASE\docs\spring-framework-reference\html\beans.html
②、在basic structure of XML-based configuration metadata中找到DTD
③、复制到applicationContext.xml中
<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.xsd">
</beans> <!-- 不要忘了加上beans的末标签 -->
<!-- xmlns的“ns”相当于struts2中的namespace-->
property标签:实现DI:默认会去调用set方法
bean class:默认调用无参构造,调用有参使用constructor-arg
constructor-arg:index 参数的个数、位置、类型、要和构造方法匹配
2.2、实现Spring DI
<!-- 一个bean对应一个Java对象 -->
<!-- name/id:对象名 -->
<!-- class:全限定名(包名+类名)默认调用无参构造实例化对象 -->
<bean name="a4" class="domain.A4"></bean>
<bean id="b5" class="domain.B5"></bean>
<bean name="colorInk" class="domain.Color"></bean>
<bean name="blackInk" class="domain.Black"></bean>
2.2.1、 set注入:
<bean name="printer1" class="domain.Printer">
<!-- set注入property标签:自定义引用类型对象注入使用ref,java数据类型用value进行注入 -->
<property name="ink" ref="blackInk"></property>
<property name="paper" ref="a4"></property>
<property name="brand" value="联想"></property>
</bean>
2.2.2、 构造注入:
<!-- index按照下标注入 -->
<bean name="printer2" class="domain.Printer">
<constructor-arg index="0" ref="a4"></constructor-arg>
<constructor-arg index="1" ref="colorInk"></constructor-arg>
</bean>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<!-- name属性按照参数名注入 -->
<bean name="printer2" class="domain.Printer">
<constructor-arg name="paper" ref="a4"></constructor-arg>
<constructor-arg name="ink" ref="colorInk"></constructor-arg>
</bean>
2.2.3、 静态工程
<!-- 通过静态工厂方法 -->
<bean name="printer3" class="beanfactory.PrinterFactory" factory-method="getPrinter">
<property name="ink" ref="blackInk"></property>
<property name="paper" ref="a4"></property>
<property name="brand" value="联想"></property>
</bean>
<!-- 通过非静态的工厂方法new对象:首先要把工厂先new出来 -->
<bean name="pf" class="beanfactory.PrinterFactory"></bean>
<!-- 使用工厂对象的intancePrinter()创建Printer对象 -->
<!-- factory-bean:指定使用的工厂对象名 -->
<bean name="printer4" factory-bean="pf" factory-method="instancePrinter">
<property name="ink" ref="blackInk"></property>
<property name="paper" ref="a4"></property>
<property name="brand" value="三星"></property>
</bean>
/*
* 打印机工厂
*/
public class PrinterFactory {
//静态工厂方法
public static Printer getPrinter(){
return new Printer();
}
//实例工厂方法
public Printer instancePrinter(){
return new Printer();
}
}
3、P命名空间:
3.1、 在DTD中加入xmlns:p="http://www.springframework.org/schema/p"
3.2、 使用P命名空间无须再使用property标签
<!-- 自定义引用类型后加-ref,普通类型:p:属性名 -->
<bean name="printer5" factory-bean="pf" factory-method="instancePrinter"
p:ink-ref="blackInk"
p:paper-ref="b5"
p:brand="佳能">
</bean>
4、C命名空间:
4.1、 在DTD中加入xmlns:c="http://www.springframework.org/schema/c"
4.2、 替换constructor-arg,使用c命名空间构造器注入
<!-- 自定义引用类型后加-ref,普通类型:c:属性名 -->
<bean name="printer6" class="domain.Printer"
c:paper-ref="a4"
c:ink-ref="colorInk">
</bean>
5、通过Spring实现各种类型的注入
/*
java基本属性类型(包括String,包装类):value
自定义应用类型:ref
Proprety:props
List集合:list
Set集合:set
Map集合:map
数组:list
*/
public class DIBean {
private List<String> list;
private Set<Integer> sets;
private Map<String, String> map;
private Properties prop;
private int nul;
······
//省略Get/Set、构造方法
}
<bean name="diBean" class="domain.DIBean">
<property name="list">
<list>
<value>abc</value>
<value>你好</value>
</list>
</property>
<property name="sets">
<set>
<value>12</value>
<value>14</value>
</set>
</property>
<property name="map">
<map>
<!-- map中要加entry子标签 一个entry对应一个KV -->
<entry key="" value=""></entry>
<!--key-ref表示 应用的类型 <entry key="" key-ref=""></entry> -->
<entry key="name" value="凯子"></entry>
<entry key="childrenSum" value="10"></entry>
</map>
</property>
<!-- Propeties对应.properties文件,其实就是map,key,value默认都是String类型 -->
<!-- 目的就是为了替换.properties -->
<property name="prop">
<props>
<prop key="url">jdbc:mysql://localhost:3306/XXX</prop>
<prop key="driverClass">com.mysql.jdbc.Driver</prop>
<prop key="username">xxoo</prop>
<prop key="password">xxoo</prop>
</props>
</property>
<!-- 基本注入使用value -->
<property name="nul" value="100"></property>
</bean>
6、测试类中获取Spring为我们创建的Bean
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
注意:
1、设值注入bean一定要有set方法,构造注入一定要有构造方法。
2、jutil测试的注解:@Before作用在@Test之前
使用注解进行IoC和DI
1、applicationContext.xml中添加context命名空间,并开启扫描
<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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启组件扫描 哪些类需要添加注解,就扫描类所在包,多个包之间用逗号分隔 -->
<context:component-scan base-package="domain"></context:component-scan>
<!-- base-package:要扫描的包(如果有其他的包可以添加逗号链接) -->
<!-- conponent:组建 -->
</beans>
2、Spring常用的注解
IoC注解:添加注解的类,spring会为我们创建对应对象
@Component:万能IoC注解,在需要实例化的类头上添加@Component,实例化默认的对象名是类名首字母小写,@Component("对象名"),相当于bean name
例:Printer--------printer
@Component的三个子类:
@Repository:dao层类
@Service:service层
@Controller:控制层
DI注解:
spring官方DI注解:
@Autowired:默认按照类型查找,查找到该类型对象,自动注入该对象
@Quarfer("对象名"):按照对象名去查询对象,找到自动注入
@Autowired和@Quarifer结合使用
JDK DI注解:
@Resource:默认按照名字进行查找
@Resource:默认去找名字为service对象,找不到再按照类型查找
private UserService service;
@Resource(name=“对象名”):按照指定名字进行查找
总结:
IoC注解添加到类头上:new 对象
DI注解添加到属性上:实现注入
使用注解无需在使用 set方法
易错点:
1、在一个配置文件中不能定义重名的对象
2、注解也不允许有同名的对象、不能注入同名的对象
3、在springIoC容器中可以有两个或多个类型相同的对象,但注入的时候一定按照名称注入,不能按类型注入(配置文件中不能定义重名的对象)
4、对普通类型的注入用:@Value("值")
使用注解为什么可以不添加set或构造方法?
因为使用注解实现DI,靠的是反射机制,通过反射得到Field,然后打开该filed的访问权限(filed.setAccessible(true)),最后通过调用filed.set(Object,value)实现注入。
为什么要使用Spring对各种框架进行整合?
1、使大量业务类的对象交由Spring实例化和DI,统一了对Bean的管理、也就是使用SpringIoC和DI实现和其他的框架的结合
2、同时Spring优化了一些框架的开发,比如Spring对Hibernate操作进行了封装(HibernateTemplate),简化了代码。
3、通过SpringAOP可以进一步对业务代码和非核心代码进行解耦,比如事物操作。
SpringAOP:
AOP:AspectOrientedPrograming(面向切面编程)Aspect:切面
根本作用是为了解耦,在不修改原业务代码的基础上增加功能通用功能(所有表都需要事物管理)。
AOP是个动态过程,一般情况下,非核心业务使用AOP:事务管理,日志记录
Spring和hibernante结合
Spring整合Hibernate,主要做两件事:提供事务级session和声明式的事务控制。
1、不保留hibernate的cfg.xml,把hibernate的配置声明到spring的配置文件中
2、保留hibernate cfg.xml(无需掌握);
Spring优化Hibernate步骤:
1、加载jar包:
c3p0-->mysql-connection-->hibernate Required-->SpringIoC基本包---->SpringAOP基本包
log4j下slf4j两个静态日志包-->spring整合事务
2、
1、加载db.properties
db.properties
# .properties是一种存储数据的配置文件
# db.properties是存数据库的配置文件
# .properties是key-value的结构的,而且KV默认都是String类型
# 使用配置文件的目的是为了解耦,后期需要修改数据配置信息无需再修改代码
URL= jdbc:mysql://127.0.0.1:3306/user_info
USER = root
PASSWORD = root
DRIVER = com.mysql.jdbc.Driver
applicationContext.xml
<!-- Spring加载.properties文件 -->
<context:property-placeholder location="classpath:db.properties"/>
2、配置C3P0数据源
<!-- 使用数据库连接池的时候首先在dataSource里注入数据库连接参数 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${DRIVER}"></property>
<property name="jdbcUrl" value="${URL}"></property>
<property name="user" value="${USER}"></property>
<property name="password" value="${PASSWORD}"></property>
</bean>
3、创建 SessionFactory : LocalSessionFactoryBean
创建的同时注入C3P0数据源dataSource
、hibernate配置参数hibernateProperties
,并且将使用注解的包进行扫描开启packagesToScan
<!-- LocalSessionFactoryBean:封装了hibernate.current_session_context_class=thread(进行了Session和线程的绑定)-->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"></bean>
3.1、 注入c3p0数据源
<!-- 注入c3p0数据源 -->
<property name="dataSource" ref="dataSource"></property>
3.2、 Spring中配置Hibernate
<!-- hibernateProperties:hibernate的配置信息 -->
<property name="hibernateProperties">
<!-- java.util.Prperties的注入可以简化为value -->
<value>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.show_sql=true
hibernate.format_sql=true
hibernate.hbm2ddl.auto=update
</value>
</property>
3.3、 开启注解
<!-- 使用hibernate注解需要开启包扫描 -->
<!-- packagesToScan:String[] 给数组注入值使用list -->
<property name="packagesToScan">
<list>
<value>需要扫描的包名</value>
</list>
</property>
3.4、 配置映射文件的包路径 如果不想配置映射文件直接省去即可
<!-- 配置映射文件的包路径 -->
<property name="mappingDirectoryLocations">
<list>
<value>com/lanou/domain</value>
</list>
</property>
4、DAO层类中使用HibernateDaoSupport注入SessionFactory的方式
主要用来获取session
如何获取Hibernate原生Session?
1、注入SessionFactory
2、通过SessiongFactory获取session
4.1、注解的方式
①、Dao层继承HibernateDaoSupport
②、使用set注入SessionFactory得到HibernateTemplate (可以把HibernateTemplate近似的理解为Hibernate.Session)
如:向数据库添加数据 getHibernateTemplate().save(user); 替换 session.save(user);
@Autowired不仅可以写在属性上,实现对属性的注入,还可以写在set方法上,实现set注入
@Autowired
@Qualifier("sessionFactory1")
private void setSuperSessionFactory(SessionFactory sessionFactory){
super.setSessionFactory(sessionFactory);
}
4.2、配置文件声明的方式(使用配置文件无需写setSuperSessionFactory方法)
不继承hibernateDaoSupport使用HibernateTemplate:
1、SpringIoC实例化HibernateTemplat,需要注入sessionFaciory
2、使用HibernateTemplate做Dao的成员变量,使用DI注入HibernaeTemplate实例
注意:
①、使用Set注入(property) 一定要有set();
②、 类的版本要一致,建议使用Hibernate5
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="userDao" class="dao.impl.UserDaoImpl" scope="prototype">
<property name="hibernateTemplate" ref="hibernateTemplate"/>
</bean>
注意:
springIoC实例化的对象默认是单例的:singleton
通过scope来配置Bean的模式
scop常见值:
singleton(单例)
prototype(每次使用都会创建一个新的实例)
request(把该对象添加到reuqest请求对象中request.setAttribute("beanName", bean))
session(session.setAttribute("beanName", bean))
//使用HibernateTemplate做dao的成员变量,后期springDI
//HibernateTemplate是线程安全的,其底层封装了currentSession(也就是和线程绑定的session)
private HibernateTemplate hibernateTemplate;
//使用set注入(property)一定要有set()
public HibernateTemplate getHibernateTemplate() {
return hibernateTemplate;
}
public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
//不用get/set,就用注释进行注入
**4.3、**Hibernate中 getCurrentSession()和openSession()的区别:
1、getCurrentSession是线程安全的,底层还是OpenSession,他需要开启事务,在事务提交的时候会自动关闭session,所以不能session.close();
不要忘了配置:hibernate.current_session_context_class=thread
2、openSession:线程非安全,一个CRUD对应一个open close,查询无须开启事务
4.3.1、 getCurrentSession()
方法获取session
public my_user findUser(my_user user) throws UserNotFoundException {
Session session = sessionFactory.getCurrentSession();
Transaction tx = session.beginTransaction(); // 开启事务
Query query = session.createQuery("from user_info where name=:name and password=:pwd");
query.setParameter("user_name", user.getUser_name());
query.setParameter("user_password", user.getUser_password());
my_user returnUser = (my_user) query.uniqueResult();
tx.commit(); // 提交事务
if(returnUser==null){
throw new UserNotFoundException("该用户不存在");
}
return returnUser;
}
//如果配置了 hibernate.current_session_context_class=thread 那么必须进行编程式事务管理
//此段代码已经进行了编程式事务管理,所以需要在applicationContext.xml中配置
4.3.2、 openSession()
方法获取session
public my_user findUser(my_user user) throws UserNotFoundException {
Session session = sessionFactory.openSession();
Query query = session.createQuery("from user_info where name=:name and password=:pwd");
query.setParameter("name", user.getName());
query.setParameter("pwd", user.getPassword());
user_info returnUser = (user_info) query.uniqueResult();
session.close(); //一个CRUD对应一个open close
if(returnUser == null){
throw new UserNotFoundException("该用户不存在");
}
return returnUser;
}
5、通过调用HibernateTemplate的API实现CRUD
hibernateTemplate的常用方法:
void delete(Object entity):删除指定持久化实例
deleteAll(Collection entities):删除集合内全部持久化类实例
find(String queryString):根据HQL查询字符串来返回实例集合
findByNamedQuery(String queryName):根据命名查询返回实例集合
findByExample:会对查询自动添加where条件,查询参数封装到user里
get(Class entityClass, Serializable id):根据主键加载特定持久化类的实例
save(Object entity):保存新的实例
saveOrUpdate(Object entity):根据实例状态,选择保存或者更新
update(Object entity):更新实例的状态,要求entity是持久状态
setMaxResults(int maxResults):设置分页的大小
注意:
HIbernateTemplate.find( )
方法返回list<?>
,即find( )
方法的返回值都是list
HibernateTemplate建议使用find(), find就是Hibernate.Query的封装
find(hql):List<?>
find(hql, Object...params)
:带查询条件的
6、使用SpringAOP进行事务管理
日后数据库事务管理一般交由spring处理,spring通过AOP的方法实现无侵入式事务。
<!-- 声明spring事务管理器:需要注入sessionFactory -->
<!-- 把SessionFactory交由了transactionManager管理,这样我们无须在关注事务 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
基于注解的Spring事务管理
1、初始化Spring事物管理器:需要注入SessionFactory
2、开启事物注解
3、在业务层(Service)加@Transactional注解
spring和hibernate结合总结
1、db.propreties---->c3p0 DataSource--->SessionFactory------>Dao
2、开启AOP事务:SessionFactory----->TransactionManager
3、dao---->service--->controller
编程技巧:
如和获取一些常见类的全限定名?
如:
ConboPooledDataSource
HibernateTransactionManager
LocalSessionFactoryBean
OpenSessionView
使用快捷键:ctrl+shift+T
(如果没反应,可能是热键冲突,比如qq的快捷键),可以找到该类的全限定名。
Spring和struts结合
优化Strurs开发(可以被替换)
Spring和Mybatis结合
优化Mybatis开发