一、概述
a>、核心技术 : IOC与AOP
b>、开发为什么需要面向接口而不是实现
接口降低一个组件与整个系统的藕合程度,当该组件不满足系统需求时,可以很容易的将该组件从系统中替换掉,而不会对整个系统产生大的影响
c>、面向接口编口编程的难点在于如何对接口进行初始化,(使用工厂设计模式)
二、搭建开发环境
a>、下载开发包,在工程中添加jar包
aa>、IOC : spring-core.jar,spring-context.jar,spring-beans.jar,spring-expression.jar,spring-test.jar,commons-logging.jar
bb>、AOP : spring-aop.jar,spring-aspects.jar,cglib.jar,aopalliance.jar,aspectjrt.jar,aspectjweaver.jar
cc>、整合hibernate : spring-jdbc.jar,spring-tx.jar,string-orm.jar
b>、准备一个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.xsd">
</beans>
c>、从容器中获取bean的方法
UserBean user1 = (UserBean)context.getBean("user1");
UserBean user2 = contextBean(UserBean.class);
UserBean user3 = contextBean("user2",UserBean.class); //容器中有多个UserBean类型对象时使用
三、为什么要用Spring
a>、解藕,项目中各层代码之间不需要再写连接性的代码,能轻松完成面向接口开发
b>、Spring框架提供了AOP的功能,可以方便的为系统中的组件添加服务逻辑
c>、Spring框架提供了声明式的事务管理功能,在项目开发中不需要考虑复杂的事务控制问题
d>、Sprng框架对其它注流框架如Hibernate,Struts等提供了很好的支持,可以降低项目中使用这些框架的难度,且更容易被替换
四、关于IOC
a>、spring框架只服务于IOC容器中的对象,如果对象不在IOC容器中,不管在对象做了什么与spring有关的设定,都与spring
b>、ioc : Inversion of Control 控制反转
c> ID : dependence Injection 依赖注入
五、<bean>的主要属性
id : 获取Bean的ID号,spring的早期版中,该属性的值不能有特殊字符
name : 与ID一样标识对象的,其值可以包含特殊字符
class :
scope : (singleton:单例,只创建该类的一个对象/prototype : 每次从容器中取对象时都创建该类的一个新的实例) bean的作用域
lazy-init : 延迟初始化,指的是ApplicationContext启动时是否创建该对象
init-method : 生命周期回调方法
destroy-method :
六、BeanFactory与ApplicationContext的差别:
a>、在实际应用中,用户有时候不知道到底是选择BeanFactory接口 还是ApplicationContext接口。 BeanFactory接口擅长于处理bean的初始化和配置,而 ApplicationContext接口除了处理上述工作外,它还针对企业引用 提供了许多基础支持,比如事务处理和AOP。
简而言之,一般把ApplicationContext接 口作为首选。
b>、创建对象的时机:BeanFactory在第一次取对象时才创建bean对象,而ApplicationContext在容器启动时就创建所有singleton作用域的对象
七、集合属性注入 : 如何向一个对象的集合属性(数组,Set,List,Map,Properties)上直接注入值
八、依赖注入的方式
a>、set方法注入:调用属性的set方法
<bean id="addr" class="com.sxt.spring.bean.Address">
<property name="province" value="陕西省"/>
</bean>
b>、构造方法注入: 调用类的指定构造方法,在创建对象时注入依赖资源
<bean name="user2" class="com.sxt.spring.bean.UserBean" >
<constructor-arg value="李四"/>
<constructor-arg value="女"/>
<constructor-arg value="25"/>
<constructor-arg>
<bean class="java.util.Date"></bean>
</constructor-arg>
<constructor-arg ref="addr"/>
</bean>
c>、p名称空间注入: set注入的简写形式
a>、打开p注入:在<beans>的开始标记中添加p名称空间:
<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">
<bean name="user3"
class="com.sxt.spring.bean.UserBean"
p:name = "王麻子"
p:sex = "男"
p:age = "30"
p:address-ref="addr"
>
<property name="birth"><bean class="java.util.Date"/></property>
</bean>
d>、field注入(仅针对引用类型),使用@Resource,@Autowired注解进行注入
a>、打开注解注入:声明context名称空间,添加<context:annotation-config/>
<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"
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-4.0.xsd">
b>、在需要注入的属性或属性的set方法上,使用@Autowired或@Resource注解
public class LoginAction {
@Autowired
private IUserService userService ;
}
c>、@Resource : 默认按名称装配,如果按名称装配失败,会回退到按类型装配,在使用该注解时,可使用name属性,指定bean的ID,使用该属性后只能按名称装配
@Autowired : 按类型装配,默认情况下要求所依赖的对象必须存在,也可以使用@Autowired(request=false),所依赖的资源可以不存在
该注解可以配置@Qualifier("uuDao")一起使用,实现按名称装配
e>、自动装配(仅针对引用类型)
<bean name="user4" class="com.sxt.spring.bean.UserBean" autowire="byName/byType/constructor"></bean>
九、自动扫描
a>、打开自动扫描
<context:component-scan base-package="com.sxt.spring"/>
b>、在需要被扫描的类上使用以下注解:
@Controller : 标注控制器
@Service : 标注业务组件
@@Repository : 标注数据访问层
@Component :泛指组件,当组件不好归类时使用
c>、默认情况下,对象在容器中的id为简单类名,首字毋小写,我们可以在使用注解时指定id,方法为:@Service("userService")
十、在测试用例中直接注入依赖对象
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/injection.xml")
public class LoginActionTest {
@Autowired
private LoginAction loginAction;
@Test
public void testLogin() {
loginAction.execute();
}
}
十一、AOP:面向切面编程
a>、Filter,Struts2的Interceptor就是AOP编程
b>、AOP的技术需要用到代理(Proxy)设计模式
原对象(Target) --> 代理对象(Proxy)
aa>、代理对象具有被代理对象的所有功能
bb>、代理对象不能脱离被代理对象而独立存在的,业务逻辑代码由被代理对象完成
cc>、对接口的代理:代理类与被代理类实现了同一接口,在开发时只能使用接口操作
dd>、对类的代理:代理类是被代理类的子类
ee>、AOP框架必须支持动态代理(提前不知道要代理谁,可以动态生成一个类的代理类)
aaa>、JDK的API只支持接口的动态代理,即可以动态生成接口的实现类(类名格式为:$Proxy10),
要对类进行动态代理,必须使用第三方库动态生成类的子类的字节码,如CGLib库(类名格式为:DeptServiceBean$$EnhancerBySpringCGLIB$$e5477967),javassist库
c>、AOP的术语
aa>、JoinPoint : 连接点
bb>、Advice : 通知
cc>、PointCut : 切点
dd>、Aspect = JoinPoint + Advice + PointCut
ee>、Advisor = Advice(含JoinPoint) + PointCut;
十二、在Spring框架中实施AOP
a>、添加aop相关的jar
b>、在spring配置文件中打开AOP的支持
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" >
<property name="proxyTargetClass" value="true"/>
</bean>
c>、开发切面Aspect
@Aspect
public class TimerAspect {
@Around("test()")
public Object advice(ProceedingJoinPoint pjp)throws Throwable {
long start = System.currentTimeMillis();
System.out.println( "方法" + pjp.getSignature().getName() + "开始于:" + String.format("%tF %<tT", start));
Object obj = pjp.proceed(); //执行被代理对象的方法
long end = System.currentTimeMillis();
System.out.println("方法" + pjp.getSignature().getName() + "结束于:" + String.format("%tF %<tT", end)+ ",共执行" + (end - start) + "毫秒\n");
return obj;
}
@Pointcut("execution(* *(..))")
public void test() {}
}
d>、在spring的配置文件中声明切面类
<bean class="com.sxt.spring.aspect.TimerAspect"></bean>
e>、连接点的类型
@Before : 前置通知
@AfterReturning : 后置通知
@After : 最终通知
try {
}finally {
//..........
}
@AfterThrowing : 异常处理
try {
}catch(Throwable e) {
//.............
throw e;
}
@Around : 环绕通知
十三、切点函数execution()的使用
@Before("execution(* *(..))") : execution()是一个切点函数,* * (..)是该函数的参数,其格式为:
<访问权限>? 返回值类型 包名+类名+方法名(参数类型) <throws 异常类型声明>
十四、基于Schema的方式进行AOP编程
a>、添加AOP名称空间
<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"
xmlns:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
b>、开发切面类:不需要任何注解
public class AfterAspect {
public void aaa() {
System.out.println("--------------------------");
System.out.println(" 请关注:www.xasxt.com");
System.out.println("--------------------------\n");
}
}
c>、在配置文件中将类转换为Aspect
<aop:config>
<aop:pointcut expression="execution(public void *())" id="pp"/>
<aop:aspect ref="afterAspect">
<aop:after method="aaa" pointcut-ref="pp"/>
</aop:aspect>
</aop:config>
十五、全用PropertyPlaceholderConfigurer将配置值写入properties文件
a>、在spring的配置文件中注册PropertyPlaceholderConfigurer类,指定其所使用的properties
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/jdbc.properties"/>
</bean>
或使用context名称空间下的元素
<context:property-placeholder location="jdbc.properties"/>
b>、在其它bean的定义中就可以直接使用properties文件中的key来引用其value
<bean id="jdbcConfig" class="com.sxt.spring.test.JdbcConfig">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
十六、使用FactoryBean创建对象
<!-- 如果一个类实现了FactoryBean接口,那么id标识的不再是该类的对象,而是它的getObject方法返回的对象 -->
<bean id="jdbcConfig2" class="com.sxt.spring.test.JdbcConfigFactoryBean"/>
十七、Spring整合Hibernate
1>、添加所有框架的jar文件
2>、准备Spring的配置文件,官方建议以applicationContext.xml
3>、由Spring创建数据源(DBCP:添加dbcp的jar包)
4>、由Spring创建SessionFactory
5>、开发Dao
6>、为业务对象的方法使用声明式的事务管理
十八、数据访问层的开发及异常处理
jdbc : saveEmp()throws SQLExeption;
hibernate : saveEmp() throws HibernateException;
JPA : saveEmp() throws PersistenceException;
不管数据访问层使用什么样的技术,抛出的异常类型必须固定,否则的话数据访问层的变化可能就会影响业务逻辑层
解决方案 : 自定义DaoExeption: saveEmp() throws DaoException
saveEmp()throws DaoExcepton {
try {
session.save(emp);
}catch(HibernateException e) {
throw new DaoException(e);
}
}
业务层处理异常:
try {
saveEmp()
} catch(?) {
}
Spring的解决方案
public class HibernateTemplate {
private SessionFactory sf;
public void save(Object obj)throws DataAccessException {
try {
sf.getCurrentSession().save(emp);
}catch(HibernateException e) {
throw new DataAccessException(e);
}
}
}
1>、
public class EmpDao {
private HibernateTemplate ht;
public void save(Employee emp) throws DataAccessException {
ht.save(emp);
}
}
2>、
public class EmpDao extends HibernateDaoSupport {
public void save(Employee emp) throws DataAccessException {
this.getHibernateTemplate().save(emp);
}
}
3>、使用Hibernate原生API
public class EmpDao {
private SessionFactory sf;
public void save(Employee emp) {
sf.getCurrentSession().save(emp);
}
}
优点:这种DAO访问方式的主要优势在于它仅仅依赖于Hibernate API本身而无需引入任何Spring的类。 从无入侵性的角度来看,这一点非常吸引人。对于Hibernate开发人员来说也无疑更加自然
缺点:这样的DAO访问方式会抛出原生 HibernateException (这是一个无需声明或捕获的unchecked exception), 这意味着,DAO的调用者只能以致命的错误来处理这些异常,除非完全依赖Hibernate自身的异常体系。
因而,除非你将DAO的调用者绑定到具体的实现策略上去,否则你将无法捕获特定的异常原因,诸如乐观锁异常。 这种折中平衡或许可以被接受,如果你的应用完全基于Hibernate或者无需进行特殊的异常处理
十九:声明式事务处理
a>、对于Hibernate,事务处理这样做
Session session = sf.getCurrentSession();
Transaction tx = session.beginTransaction();
try {
session.save(obj);
tx.commit();
}catch(HibernateException e) {
tx.rollback();
throw e;
}
b>、事务要加在业务方法上,而不是数据访问方法上
public void 转账(Integer userId1,Integer userId2,double money) { //业务方法
-- 开始事务
//session.beginTransaction();
User user1 = userDao.findById(userId1);
if(user1.getBalance() < money) {
throw ServiceException("余额不足");
}
user1.setBalance(user1.getBalance() - money);
userDao.update(user1);
User user2 = userDao.findById(userId2);
user2.setBalance(user2.getBalance() + money);
userDao.update(user2);
logDao.log(userId1 + "向" + userId2 + "转账" + money);//写日志
--结束事务
// tx.commit();
}
c>、
<!-- 1、创建事务管理器对象 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory"/>
<!-- 2、声明事务属性Advice -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1" />
<tx:method name="update*"/>
<tx:method name="delete*" />
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 3、使用aop,将事务管理的代码加到业务对象的方法上 -->
<aop:config proxy-target-class="true">
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.sxt.ems.service.*Service.*(..))"/>
</aop:config>
二十、Spring整合Struts2
a>、为什么要整合? 因为Action需要的业务对象在Spring的ioc容器中
b>、如何整合(Action如何拿到业务对象),最理想的方式就是由spring的依赖注入机制自动注入
c>、要使用自动注入,Action必须由Spring创建和管理
d>、整合的要点就在于如何让struts2框架使用Spring容器中的Action,而不是自己创建Action
e>、整合步骤
aa>、在应用程序启动时启动spring容器,在web.xml中配置spring框架提供的一个监听器ContextLoaderListener,在spring-web.jar中
<!-- 指定spring的配置文件,供ContextLoaderListener使用 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 在应用启动时启动spring -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
bb>、让struts2使用spring的action:由struts2完成,添加struts2-spring-plugin.jar
在spring框架中创建Action,注意scope属性
<bean id="employeeAction" class="com.sxt.ems.web.action.EmployeeAction" scope="prototype">
<property name="empService" ref="empService"/>
<property name="deptService" ref="deptService"/>
</bean>
在struts.xml中添加以下常量<constant name="struts.objectFactory" value="spring" />
action的配置中class属性的值不再写Action的类名,而是Action对象在spring中id
二一、如何在Filter或Servlet中访问Spring容器中的对象
WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
EmployeeService empService = context.getBean(EmployeeService.class);
二二、OpenSessionInViewFilter的使用
a>、在正常情况下,业务方法运行结束后,session就关闭了,通过业务方法所得到的对象如果其关联的属性没有被加载,这些属性就不能在jsp页面中被访问
b>、在业务方法中就要将之后可能用到的属性进行初始化:Hibernate.initialize(dept.getEmps());
c>、使用OpenSessionInViewFilter : 通过一个Filter拉长了session的作用域,它会在请求到达Servlet/Action之前打开session,在响应生成之后关闭session
<filter>
<filter-name>openSessionInView</filter-name>
<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openSessionInView</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>