首页 > 代码库 > spring总结
spring总结
【类的加载器】
类的加载器就是寻找类的字节码文件并构造出类在JVM内部表示对象的组件。在Java中类装载器把一个类装入JVM,需要经过以下几个步骤:
1. 装载:查找和导入Class文件。
2. 链接:执行校验,准备和解析
a. 校验:检查载入的Class文件数据的准确性。
b. 准备:给类的静态变量分配存储空间。
c. 解析:将符号引用转成直接饮用。
3. 初始化:对类的静态变量,静态代码块执行初始化操作。
ClassLoader重要方法:
loadClass(String name) name参数指定的是类装载器的名字,必须使用全限定类名。
loadClass(String name,boolean resolve) resolve参数告诉类装载器是否需要解析该类,在类装载之前需要考虑类是否需要解析,如果jvm之需要知道该类的存在或者该类的超类,那么就不需要进行解析。
defineClass(String name,byte[]b,int off,int len) 将类的文件字节数组转换成JVM内部的class对象。字节数组可以从本地文件系统、远程网络获取。
findSystemClass(String name)从本地文件系统载入class文件,如果本地文件系统中不存在class文件,将抛出ClassNotFoundException异常,该方法是JVM默认使用的装载机制。
findLoadedClass(String name)用来查看该类是否已经被装载。如果装载则返回class对象,如果没装在则返回null。
getParent() 获取类装载器的父装载器,除了跟装载器外每个装载器都有且仅有一个父装载器。
//通过类的加载器,获取Car类对象
ClassLoader loder = Thread.currentThread().getContextClassLoader();
Class clazz = loader.loadClass("com.lct.text.Car");
//获取类的构造器,并实例化Car
Constructor cons = clazz.getDeclaredConstructor();
Car car = (Car)cons.newInstance();
//通过反射方法,设置属性值
Method a = clazz.getMethod("setA",String.class);
a.invoke(car,"给方法A赋值");
JVM在装载类的时候采用的是全盘负责委托机制,全盘负责是指:当一个ClassLoader装载一个类的时候,该类所依赖引用的类也由此ClassLoader载入。委托机制:载入的时候先从跟类向节点类依次载入。每一个类在JVM中都有一个对应的class对象,它提供了类结构信息的描述。数组、枚举、注解、基本类型(int,double)都有对应的class对象。class对象是装载类时由jvm通过调用类的装载器中的defineClass()方法自动构造的。
【资源访问利器】
spring设计了一个Resource资源接口,此接口用于装载各种资源,包括:
//①使用系统文件路径方式加载文件
Resource res1 = new FileSystemResource("d:/abc/te.doc");
//②使用类路径方式加载文件
Resource res2 = new ClassPathResource("conf/file1.txt");
//①注意文件资源地址以相对于Web应用根路径的方式表示,可以直接从jar包中访问资源
Resource res3 = new ServletContextResource(application,"/WEB-INF/classes/conf/file1.txt");
classpath:org/springframework/**/*.xml:匹配类路径org/springframework下所有以xml为后缀的文件;PathMathchingResoucePatternResolver资源实现类会扫描所有springframework包下的扩展名为xml的文件。
【BeanFactory 和 ApplicationContext】
spring通过一个配置文件描述bean与bean之间的依赖关系,利用Java自定义注解和反射技术实例化bean并构造bean之间的依赖关系。BeabFactory是Sping架构最核心的接口,提供了高级的IOC配置机制。最主要方法就是getBean(String name)获取bean。在初始化beanFactory时候,必须提供一种日志框架,我们使用log4j配置日志,这样启动spring才不会报错。在实际应用中我们用的都是ApplicationContext因为它利用Java反射的机制识别出配置文件中的processor,并将它们注入到应用上下文中。而beanfactory需要手动去处理processor。
【Spring IOC、DI】
IOC(Inverse Of Control):通常来说我们在写程序的时候,可能会在一个对象里面用到其他的对象,在IOC出现之前,我们是在哪里用就哪里去new Object(),这里new一下,那里new一下, 这些对象不仅增加了程序的耦合度,而且还不易维护。IOC的思想就是这些new Object()的事,我们来帮你管理,降低耦合度,你去专心写业务。
DI (Dependency Injection):IOC就像是人的大脑,而DI就是四肢躯干,是执行者。它根据我们在配置文件中给出的注解或者定义,然后利用反射技术,动态地帮助我们去创建需要的对象。
【Spring注解】
? 注册注解处理器
? 方式一:bean
<bean class="org.springframework.beans.factory.annotation.
AutowiredAnnotationBeanPostProcessor"/>
? 方式二: 命名空间<context:annotation-config />
<context:annotationconfig /> 将隐式地向Spring 容器注册AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor 、 PersistenceAnnotationBeanPostProcessor 以及RequiredAnnotationBeanPostProcessor 这4 个BeanPostProcessor 。
? 方式三: 命名空间<context:component-scan />
如果要使注解工作,则必须配置component-scan ,实际上不需要再配置annotation-config。
base-package 属性指定了需要扫描的类包,类包及其递归子包中所有的类都会被处理。还允许定义过滤器将基包下的某些类纳入或排除。
? 正则表达式的过滤方式举例:
<context:component-scanbase-package="com.casheen.spring.annotation">
<context:exclude-filtertype="regex"
expression="com.casheen.spring.annotation.web..*"/>
</context:component-scan>
? 注解的过滤方式举例:
<context:component-scan base-package="com.netqin" >
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Service"/>
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
@Component 是一个泛化的概念,仅仅表示一个组件 (Bean) ,可以作用在任何层次。
@Service 通常作用在业务层。
? 例如
@Service
public class SoftCreateServiceImpl implements ISoftCreateService {}
? 或者
@Service("softCreateServiceImpl")
? 说明
@Service 负责注册一个bean 到spring 上下文中,bean 的ID 默认为类名称开头字母小写
@Controller 通常作用在控制层
? 例如
@Controller
public class SoftCreateController extends SimpleBaseController {}
? 或者
@Controller("softCreateController")
? 说明
@Controller 负责注册一个bean 到spring 上下文中,bean 的ID 默认为类名称开头字母小写
@Autowired
? 例如
@Autowired
private ISoftPMService softPMService;
? 或者
@Autowired(required=false)
private ISoftPMService softPMService = new SoftPMServiceImpl();
? 说明
@Autowired 根据bean 类型从spring 上线文中进行查找,注册类型必须唯一,否则报异常。与@Resource 的区别在于,@Resource 允许通过bean 名称或bean 类型两种方式进行查找@Autowired(required=false) 表示,如果spring 上下文中没有找到该类型的bean 时, 才会使用new SoftPMServiceImpl();
@Autowired 标注作用于 Map 类型时,如果 Map 的 key 为 String 类型,则 Spring 会将容器中所有类型符合 Map 的 value 对应的类型的 Bean 增加进来,用 Bean 的 id 或 name 作为 Map 的 key。
@Autowired 还有一个作用就是,如果将其标注在 BeanFactory 类型、ApplicationContext 类型、ResourceLoader 类型、ApplicationEventPublisher 类型、MessageSource 类型上,那么 Spring 会自动注入这些实现类的实例,不需要额外的操作。
@RequestMapping
? 类
@Controller
@RequestMapping("/bbtForum.do")
public class BbtForumController {
@RequestMapping(params = "method=listBoardTopic")
public String listBoardTopic(int topicId,User user) {}
}
? 方法
@RequestMapping("/softpg/downSoftPg.do")
@RequestMapping(value="http://www.mamicode.com/softpg/ajaxLoadSoftId.do",method = POST)
@RequestMapping(value = "http://www.mamicode.com/osu/product/detail.do", params = { "modify=false" }, method =POST)
http://localhost/bbtForum.do?method=listBoardTopic&topicId=1&userId=10&userName=tom
topicId URL 参数将绑定到 topicId 入参上,而 userId 和 userName URL 参数将绑定到 user 对象的 userId 和 userName 属性中。和 URL 请求中不允许没有 topicId 参数不同,虽然 User 的 userId 属性的类型是基本数据类型,但如果 URL 中不存在 userId 参数,Spring 也不会报错,此时 user.userId 值为 0 。如果 User 对象拥有一 个 dept.deptId 的级联属性,那么它将和 dept.deptId URL 参数绑定。
@RequestParam
? 参数绑定说明
@RequestParam("id")
http://localhost/bbtForum.do?method=listBoardTopic&id=1&userId=10&userName=tom
listBoardTopic(@RequestParam("id")int topicId,User user) 中的 topicId 绑定到 id 这个 URL 参数, 那么可以通过对入参使用 @RequestParam 注解来达到目的
@RequestParam(required=false):参数不是必须的,默认为true
@RequestParam(value="http://www.mamicode.com/id",required=false)
@ModelAttribute
? 作用域:request
? 例如
@RequestMapping("/base/userManageCooper/init.do")
public String handleInit(@ModelAttribute("queryBean") ManagedUser sUser,Model model,){
? 或者
@ModelAttribute("coopMap")// 将coopMap 返回到页 面
public Map<Long,CooperatorInfo> coopMapItems(){}
? 说明
@ModelAttribute 声明在属性上,表示该属性的value 来源于model 里"queryBean" ,并被保存到model 里@ModelAttribute声明在方法上,表示该方法的返回值被保存到model 里
@Repository 、@Service、@Controller ,它们分别对应存储层Bean ,业务层Bean ,和展示层Bean 。
【Spring Bean的生命周期】
这是BeanFactoryPostProcessor实现类构造器!! BeanFactoryPostProcessor调用postProcessBeanFactory方法 这是BeanPostProcessor实现类构造器!! 这是InstantiationAwareBeanPostProcessorAdapter实现类构造器!! InstantiationAwareBeanPostProcessor调用postProcessBeforeInstantiation方法 【构造器】调用Person的构造器实例化 InstantiationAwareBeanPostProcessor调用postProcessPropertyValues方法 【注入属性】注入属性address 【注入属性】注入属性name 【注入属性】注入属性phone 【BeanNameAware接口】调用BeanNameAware.setBeanName() 【BeanFactoryAware接口】调用BeanFactoryAware.setBeanFactory() BeanPostProcessor接口方法postProcessBeforeInitialization对属性进行更改! 【InitializingBean接口】调用InitializingBean.afterPropertiesSet() 【init-method】调用<bean>的init-method属性指定的初始化方法 BeanPostProcessor接口方法postProcessAfterInitialization对属性进行更改! InstantiationAwareBeanPostProcessor调用postProcessAfterInitialization方法 容器初始化成功 Person [address=广州, name=张三, phone=110] 现在开始关闭容器! 【DiposibleBean接口】调用DiposibleBean.destory() 【destroy-method】调用<bean>的destroy-method属性指定的初始化方法
1.Singleton作用域
当一个bean的作用域为singleton, 那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。换言之,当把一个bean定义设置为singlton作用域时,Spring IoC容器只会创建该bean定义的唯一实例。这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求和引用都将返回被缓存的对象实例。
2. Prototype作用域
Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean() 方法)时都会创建一个新的bean实例 。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
下图演示了Spring的prototype作用域。请注意,通常情况下,DAO不会被配置成prototype,因为DAO通常不会持有任何会话状态,因此应该使用singleton作用域。
Bean默认是在容器初始化时初始化的,即
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");语句执行时就实例化bean了。如果把scope设成scope=”prototype” 或设置lazy-init=”true”,则会延迟bean的实例化,bean会在PersonService personService = (PersonServiceBean)ctx.getBean("personService");语句执行时才实例化。
(2)生命周期:
构造器、init方法、获取bean后的操作、destroy方法(ctx.close时执行).
注意:如果bean的scope设为prototype时,当ctx.close时,destroy方法不会被调用.
原因:对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法。但对prototype而言,任何配置好的析构生命周期回调方法都将不会 被调用。清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被prototype作用域bean占用资源的一种可行方式是,通过使用bean的后置处理器,该处理器持有要被清除的bean的引用。)
【Spring 持久化】
Spring并没有实现自己的ORM解决方案,它为许多主流的ORM框架提供了支持。例如:
Hibernate:重量级ORM框架,它实现了数据库表和POJO对象之间的映射关系,基本上无需手动编写SQL语句,通过配置可以生成表创建字段等,提供了自己的查询接口,hsql,SessionFactory工厂,jdbc事务处理。
mybatis:相对Hibernate比较轻量级,它实现了数据库表和POJO的映射,它属于半自动映射,需要手动编写SQL语句,灵活性好于Hibernate,可以写出更高效的SQL,性能好于Hibernate。同样也支持jdbc事务处理。
1. PlaceHolder扫描数据库连接资源属性文件。
2. 根据属性文件信息,构造Datasource
3. datasource 引入 sessionFactory
4. datasource 引入 jdbcTemplate
5. datasource 引入 transactionmanager
【Spring 事务处理】
spring提供了许多内置的事务管理器实现,如:DataSourceTransactionManager数据源事务管理器,提供对单个javax.DataSource管理,用于jdbc抽象事务管理,mybatis框架事务管理;HibernateTransactionManager提供SessionFactoory事务管理,用于集成hibernate事务管理。
声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。
总结一下:这里首选是对DataSource生成一个代理类,从而可以监控获取Connection的过程,在通过ThreadLocal对Connection线程级别的缓存从而促使在同一个业务处理方法相对于某个DataSource都是在一个Connection中,从而保证处于同一事务中,因为这些执行都是在一个线程中的。这里处理Spring的AOP之外,还有一个ThreadLocal的使用。在实践编程中,有时候你会发现ThreadLocal会带来很大的帮助。
比如,你要在某个操作中的每个处理流程都要知道操作人信息,而且这个流程可能不是在一个方法或者一个类中处理完,如果在session环境中,你可能会考虑用session,但不是所有的开发都是在Session环境中的,那么此时ThreadLocal边是最好的帮手,可以在用户触发这个操作时候将用户信息放在ThreadLocal中,那么后面的每个流程都可以从ThreadLocal中获取,而且这个是线程范围的,每个线程中的ThreadLocal是不相干的,这样也防止了多线程的操作。
注解形式@Transactional实现事务管理
注意@Transactional只能被应用到public方法上,对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能。
默认情况下,一个有事务的方法,遇到RuntiomeException时会回滚。遇到受检查的异常是不会回滚的,要想所有异常都回滚,要加上属性rollbackFor={Exception.class}
建议只在实现类或实现类的方法上使用@Transactional,而不要在接口上使用,这是因为如果使用JDK代理机制是没问题,因为其使用基于接口的代理;而使用使用CGLIB代理机制时就会遇到问题,因为其使用基于类的代理而不是接口,这是因为接口上的@Transactional注解是“不能继承的”。
事务隔离级别
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
- TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。
- TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
- TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
- TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
Isolation Level(事务隔离等级)的5个枚举值
为什么事务要有Isolation Level这个属性?先回顾下数据库事务的知识:
第一类丢失更新(lost update):在完全未隔离事务的情况下,两个事物更新同一条数据资源,某一事物异常终止,回滚造成第一个完成的更新也同时丢失。
第二类丢失更新(second lost updates):是不可重复读的特殊情况,如果两个事务都读取同一行,然后两个都进行写操作,并提交,第一个事务所做的改变就会丢失。
脏读(dirty read):如果第二个事务查询到第一个事务还未提交的更新数据,形成脏读。因为第一个事务你还不知道是否提交,所以数据不一定是正确的。
虚读(phantom read):一个事务执行两次查询,第二次结果集包含第一次中没有或者某些行已被删除,造成两次结果不一致,只是另一个事务在这两次查询中间插入或者删除了数据造成的。
不可重复读(unrepeated read):一个事务两次读取同一行数据,结果得到不同状态结果,如中间正好另一个事务更新了该数据,两次结果相异,不可信任。
关于propagation属性的7个传播行为
REQUIRED:表示当前方法必须在一个具有事务的上下文中运行,如有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。(如果被调用端发生异常,那么调用端和被调用端事务都将回滚)。
SUPPORTS:表示当前方法不必需要具有一个事务上下文,但是如果有一个事务的话,它也可以在这个事务中运行。
MANDATORY:表示当前方法必须在一个事务中运行,如果没有事务,将抛出异常。
REQUIRES_NEW:表示当前方法必须运行在它自己的事务中。一个新的事务将启动,而且如果有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行。
NOT_SUPPORTED:表示该方法不应该在一个事务中运行。如果有一个事务正在运行,他将在运行期被挂起,直到这个事务提交或者回滚才恢复执行。
NEVER:表示当方法务不应该在一个事务中运行,如果存在一个事务,则抛出异常。
NESTED:指定当前方法执行时,如果已经有一个事务存在,则运行在这个嵌套的事务中.如果当前环境没有运行的事务,就新建一个事务,并与父事务相互独立,这个事务拥有多个可以回滚的保证点。就是指我自己内部事务回滚不会对外部事务造成影响,只对DataSourceTransactionManager事务管理器起效。
spring事务只读属性
- spring事务只读的含义是指,如果后端数据库发现当前事务为只读事务,那么就会进行一系列的优化措施。它是在后端数据库进行实施的,因此,只有对于那些有可能启动一个新事务的传播行为(REQUIRED,REQUIRES_NEW,NESTED)的方法来说,才有意义。(测试表明,当使用JDBC事务管理器并设置当前事务为只读时,并不能发生预期的效果,即能执行删除,更新,插入操作)
spring的事务超时
- 有的时候为了系统中关键部分的性能问题,它的事务执行时间应该尽可能的短。因此可以给这些事务设置超时时间,以秒为单位。我们知道事务的开始往往都会发生数据库的表锁或者被数据库优化为行锁,如果允许时间过长,那么这些数据会一直被锁定,影响系统的并发性。
- 因为超时时钟是在事务开始的时候启动,因此只有对于那些有可能启动新事物的传播行为(REQUIRED,REQUIRES_NEW,NESTED)的方法来说,事务超时才有意义。
事务回滚规则
- spring中可以指定当方法执行并抛出异常的时候,哪些异常回滚事务,哪些异常不回滚事务。
- 默认情况下,只在方法抛出运行时异常的时候才回滚(runtime exception)。而在出现受阻异常(checked exception)时不回滚事务,这个ejb的回滚行为一致。
- 当然可以采用申明的方式指定哪些受阻异常像运行时异常那样指定事务回滚。
利用异常机制手动回滚:
【Spring 分布式】
分布式事务对性能有一定的影响,所以不是最佳的解决方案,能通过设计避免最好尽量避免。
分布式事务(Distributed transactions),也称作XA事务(XA是一个协议的名字),在spring中被称作global transaction,是指一个事务会涉及到不同的事务资源,比如不同的数据库,消息队列。事务资源都支持commit和rollback这样的事务控制命令。分布式事务是指事务的参与者、支持事务的服务器、资源管理器以及事务管理器分别位于分布系统的不同节点之上,在两个或多个网络计算机资源上访问并且更新数据,将两个或多个网络计算机的数据进行的多次操作作为一个整体进行处理。如不同银行账户之间的转账。对于在项目中接触到JTA,大部分的原因是因为在项目中需要操作多个数据库,同时,可以保证操作的原子性,保证对多个数据库的操作一致性。
按是否需要实现完整javaEE功能的应用服务器,可以将实现XA事务的方法分为两种:
1.一般情况下,XA事务是通过javaEE应用服务器实现的,即CMT(Container Managed Transaction)。应用服务器实现了JTA(Java Transaction API),应用通过JNDI获取应用服务器的JTA UserTransaction。JTA的api比较复杂,开发者需要研究较多JTA的细节,使用spring事务管理可以简化JTA的编程模型。但是这样还是需要依赖实现了对应javaEE功能的应用服务器。
2.不需要应用服务器(standalone),或者只需要轻量级的应用服务器,例如tomcat,tomcat没有实现所有的javaEE功能。在应用中加上支持jta的第三方包,例如atomikos,JOTM等。
分布式事务的结构如图所示:
图中,1表示APP与资源管理器RM之间的接口,是资源管理器的本地接口或者XA接口。如果使用事务管理器TM来管理分布式事务,
则不需要app直接调用rm,即1接口不会使用。
2表示app与TM的接口,即UserTransaction,这个接口和普通的事务管理接口类似,是一个有提交和回滚等操作的接口。使用该接口,就像事务只处理一个数据源一样。app通过该接口控制事务的提交或回滚。
3是TM与RM之间的接口,是一个两阶段提交(2 phase commit)的过程,两阶段提交简单的说就是一个数据源的事务要提交两次才算真正提交。该操作由TM控制,app不直接调用接口3。
对于一些较大规模的应用,单个数据源是无法支撑起庞大的用户量,需要引入多数据源,水平层面进行分库分表,降低单个DB的负载。接下来,我们程序里里面需 要管理不同数据源之前的程序调用,保证功能是WORK的。另外,跨库就意味着之前单DB的事务就失效了,所以J2EE提出了JTA,分布式的事务管理,往 简单了说,就是2步提交(two phase),比单步提交更苛刻。实际上他有两个容器来管理,一个是资源管理器,一个是事务管理。
<!-- mysql数据源 -->
<bean id="master" class="com.atomikos.jdbc.AtomikosDataSourceBean"
init-method="init" destroy-method="close">
<description>master xa datasource</description>
<property name="uniqueResourceName">
<value>master</value>
</property>
<property name="xaDataSourceClassName" value="http://www.mamicode.com/com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="user">root</prop>
<prop key="password"></prop>
<prop key="URL">jdbc:mysql://127.0.0.1:3306/master</prop>
</props>
</property>
<property name="poolSize" value="http://www.mamicode.com/10"/>
</bean>
<bean id="slave" class="com.atomikos.jdbc.AtomikosDataSourceBean"
init-method="init" destroy-method="close">
<description>slave xa datasource</description>
<property name="uniqueResourceName">
<value>slave</value>
</property>
<property name="xaDataSourceClassName">
<value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value>
</property>
<property name="xaProperties">
<props>
<prop key="user">root</prop>
<prop key="password"></prop>
<prop key="URL">jdbc:mysql://127.0.0.1:3306/slave</prop>
</props>
</property>
<property name="poolSize" value="http://www.mamicode.com/10"/>
</bean>
<!-- atomikos事务管理器 -->
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
init-method="init" destroy-method="close">
<description>UserTransactionManager</description>
<property name="forceShutdown">
<value>true</value>
</property>
</bean>
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="http://www.mamicode.com/300" />
</bean>
<!-- spring 事务管理器 -->
<bean id="springTransactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager">
<ref bean="atomikosTransactionManager" />
</property>
<property name="userTransaction">
<ref bean="atomikosUserTransaction" />
</property>
</bean>
<!-- spring 事务模板 -->
<bean id="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager">
<ref bean="springTransactionManager" />
</property>
</bean>
<bean id="masterJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg>
<ref bean="master" />
</constructor-arg>
</bean>
<bean id="slaveJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg>
<ref bean="slave" />
</constructor-arg>
</bean>
<context:annotation-config />
<tx:annotation-driven transaction-manager="springTransactionManager"/>
<context:component-scan base-package="com.roden.jta" />
@Controller
public class TestController {
@Autowired
private TestService testService;
//MySQL的数据库引擎必须是InnoDB,否则无法回滚
@Test
public void test(){
testService.test();
}
@Test
public void test2(){
testService.update();
}
@Test
public void test3(){
testService.test3();
}
}
package com.roden.jta.service;
public interface TestService {
public String test();
public String update();
public void test3();
}
@Service
public class TestServiceImpl implements TestService{
@Resource(name = "springTransactionManager")
private JtaTransactionManager txManager;
@Autowired
private TestMasterDao testMasterDao;
@Autowired
private TestSlaveDao testSlaveDao;
@Resource(name = "transactionTemplate")
private TransactionTemplate transactionTemplate;
//编程式
@Override
public String test() {
UserTransaction userTx = txManager.getUserTransaction();
try {
userTx.begin();
testMasterDao.master();
testSlaveDao.slave();
int a=1/0;
System.out.println(a);
userTx.commit();
} catch (Exception e) {
System.out.println("捕获到异常,进行回滚" + e.getMessage());
e.printStackTrace();
try {
userTx.rollback();
} catch (IllegalStateException e1) {
System.out.println("IllegalStateException:" + e1.getMessage());
} catch (SecurityException e1) {
System.out.println("SecurityException:" + e1.getMessage());
} catch (SystemException e1) {
System.out.println("SystemException:" + e1.getMessage());
}
}
return null;
}
//声明式
@Override
@Transactional
public String update(){
testMasterDao.master();
testSlaveDao.slave();
//int a=1/0;
//System.out.println(a);
return null;
}
//事务模板方式
@Override
public void test3() {
transactionTemplate.execute(new TransactionCallbackWithoutResult(){
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
testMasterDao.master();
testSlaveDao.slave();
int a=1/0;
System.out.println(a);
} catch (Exception ex) {
// 通过调用 TransactionStatus 对象的 setRollbackOnly() 方法来回滚事务。
status.setRollbackOnly();
ex.printStackTrace();
}
}
});
/*
//有返回值的回调
Object obj=transactionTemplate.execute(new TransactionCallback(){
@Override
public Object doInTransaction(TransactionStatus status) {
return 1;
}
}); */
}
}
package com.roden.jta.dao;
public interface TestMasterDao {
public String master();
public String update();
}
package com.roden.jta.dao;
public interface TestSlaveDao {
public String slave();
}
package com.roden.jta.dao.impl;
import javax.annotation.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.roden.jta.dao.TestMasterDao;
@Repository
public class TestMasterDaoImpl implements TestMasterDao{
@Resource(name="masterJdbcTemplate")
JdbcTemplate masterJdbcTemplate;
@Override
public String master() {
masterJdbcTemplate.execute("update teacher set name=‘master‘ where id=1");
return "success";
}
@Override
public String update() {
masterJdbcTemplate.execute("update teacher set name=‘8‘ where id=1");
System.out.println("update");
masterJdbcTemplate.execute("fff teacher set name=‘‘6‘ where id=1");
return null;
}
}
package com.roden.jta.dao.impl;
import javax.annotation.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.roden.jta.dao.TestSlaveDao;
@Repository
public class TestSlaveDaoImpl implements TestSlaveDao{
@Resource(name="slaveJdbcTemplate")
JdbcTemplate slaveJdbcTemplate;
@Override
public String slave() {
slaveJdbcTemplate.execute("update student set name=‘slave‘ where id=1");
return "success";
}
}
数据源详细参数配置
<!-- 两个数据源的功用配置,方便下面直接引用 -->
<bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init"
destroy-method="close" abstract="true">
<property name="xaDataSourceClassName" value="http://www.mamicode.com/com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
<property name="poolSize" value="http://www.mamicode.com/10" />
<property name="minPoolSize" value="http://www.mamicode.com/10"/>
<property name="maxPoolSize" value="http://www.mamicode.com/30"/>
<property name="borrowConnectionTimeout" value="http://www.mamicode.com/60"/> <!--获取连接失败重新获等待最大时间,在这个时间内如果有可用连接,将返回-->
<property name="reapTimeout" value="http://www.mamicode.com/20"/> <!--最大获取数据时间,如果不设置这个值,Atomikos使用默认的5分钟,那么在处理大批量数据读取的时候,一旦超过5分钟,就会抛出类似 Resultset is close 的错误.-->
<property name="maxIdleTime" value="http://www.mamicode.com/60"/> <!--最大闲置时间,超过最小连接池连接的连接将将关闭-->
<property name="maintenanceInterval" value="http://www.mamicode.com/60" /> <!--连接回收时间-->
<property name="loginTimeout" value="http://www.mamicode.com/60" /> <!--java数据库连接池,最大可等待获取datasouce的时间-->
<property name="logWriter" value="http://www.mamicode.com/60"/>
<property name="testQuery">
<value>select 1</value>
</property>
</bean>
<!-- 配置第一个数据源 -->
<bean id="dataSource" parent="abstractXADataSource">
<!-- value只要两个数据源不同就行,随便取名 -->
<property name="uniqueResourceName" value="http://www.mamicode.com/mysql/sitestone" />
<property name="xaDataSourceClassName"
value="http://www.mamicode.com/com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="URL">${jdbc.url}</prop>
<prop key="user">${jdbc.username}</prop>
<prop key="password">${jdbc.password}</prop>
</props>
</property>
</bean>
<!-- 配置第二个数据源-->
<bean id="dataSourceB" parent="abstractXADataSource">
<!-- value只要两个数据源不同就行,随便取名 -->
<property name="uniqueResourceName" value="http://www.mamicode.com/mysql/sitesttwo" />
<property name="xaDataSourceClassName"
value="http://www.mamicode.com/com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="URL">${jdbca.url}</prop>
<prop key="user">${jdbca.username}</prop>
<prop key="password">${jdbca.password}</prop>
</props>
</property>
</bean>
【Spring线程安全】
由于Spring MVC默认是Singleton的,所以会产生一个潜在的安全隐患。根本核心是instance变量保持状态的问题。这意味着每个request过来,系统都会用原有的instance去处理,这样导致了两个结果:
一是我们不用每次创建Controller,
二是减少了对象创建和垃圾收集的时间;
由于只有一个Controller的instance,当多个线程同时调用它的时候,它里面的instance变量就不是线程安全的了,会发生窜数据的问题。
当然大多数情况下,我们根本不需要考虑线程安全的问题,比如dao,service等,除非在bean中声明了实例变量。因此,我们在使用spring mvc 的contrller时,应避免在controller中定义实例变量。
如:
public class Controller extends AbstractCommandController {
......
protected ModelAndView handle(HttpServletRequest request,HttpServletResponse response,
Object command,BindException errors) throws Exception {
company = ................;
}
protected Company company;
}
在这里有声明一个变量company,这里就存在并发线程安全的问题。
如果控制器是使用单例形式,且controller中有一个私有的变量a,所有请求到同一个controller时,使用的a变量是共用的,即若是某个请求中修改了这个变量a,则,在别的请求中能够读到这个修改的内容。。
有几种解决方法:
1、在控制器中不使用实例变量
2、将控制器的作用域从单例改为原型,即在spring配置文件Controller中声明 scope="prototype",每次都创建新的controller
3、在Controller中使用ThreadLocal变量
这几种做法有好有坏,第一种,需要开发人员拥有较高的编程水平与思想意识,在编码过程中力求避免出现这种BUG,而第二种则是容器自动的对每个请求产生一个实例,由JVM进行垃圾回收,因此做到了线程安全。
使用第一种方式的好处是实例对象只有一个,所有的请求都调用该实例对象,速度和性能上要优于第二种,不好的地方,就是需要程序员自己去控制实例变量的状态保持问题。第二种由于每次请求都创建一个实例,所以会消耗较多的内存空间。
所以在使用spring开发web 时要注意,默认Controller、Dao、Service都是单例的。
由于 Spring 的事务管理器是通过线程相关的 ThreadLocal 来保存数据访问基础设施,再结合 IOC 和 AOP 实现高级声明式事务的功能,所以 Spring 的事务天然地和线程有着千丝万缕的联系。
我们知道 Web 容器本身就是多线程的,Web 容器为一个 Http 请求创建一个独立的线程,所以由此请求所牵涉到的 Spring 容器中的 Bean 也是运行于多线程的环境下。在绝大多数情况下,Spring 的 Bean 都是单实例的(singleton),单实例 Bean 的最大的好处是线程无关性,不存在多线程并发访问的问题,也即是线程安全的。
一个类能够以单实例的方式运行的前提是“无状态”:即一个类不能拥有状态化的成员变量。我们知道,在传统的编程中,DAO 必须执有一个 Connection,而 Connection 即是状态化的对象。所以传统的 DAO 不能做成单实例的,每次要用时都必须 new 一个新的实例。传统的 Service 由于将有状态的 DAO 作为成员变量,所以传统的 Service 本身也是有状态的。
但是在 Spring 中,DAO 和 Service 都以单实例的方式存在。Spring 是通过 ThreadLocal 将有状态的变量(如 Connection 等)本地线程化,达到另一个层面上的“线程无关”,从而实现线程安全。Spring 不遗余力地将状态化的对象无状态化,就是要达到单实例化 Bean 的目的。
由于 Spring 已经通过 ThreadLocal 的设施将 Bean 无状态化,所以 Spring 中单实例 Bean 对线程安全问题拥有了一种天生的免疫能力。不但单实例的 Service 可以成功运行于多线程环境中,Service 本身还可以自由地启动独立线程以执行其它的 Service。
HttpSession 实例,而是一个代理,是org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler代理了HttpSession ,可通过这个代码求证:System.out.println(Proxy.getInvocationHandler(session));
HttpSession 里面非java.lang.Object方法时才会真真去调用被代理的HttpSession里面的方法。
session.get ...过程:首先从对象工厂从Threadlocal中取得HttpSession实例,然后通过反射调用该实例的set方法。
【Spring AOP】
Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其它bean实例作为目标,这种关系可由IOC容器的依赖注入提供。Spring创建代理的规则为:
1、默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了
2、当需要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理,也可强制使用CGLIB
CGLIB(Code Generation Library),简单来说,就是一个代码生成类库。它可以在运行时候动态是生成某个类的子类。
AOP编程其实是很简单的事情,纵观AOP编程,程序员只需要参与三个部分:
1、定义普通业务组件
2、定义切入点,一个切入点可能横切多个业务组件
3、定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作
所以进行AOP编程的关键就是定义切入点和定义增强处理,一旦定义了合适的切入点和增强处理,AOP框架将自动生成AOP代理,即:代理对象的方法=增强处理+被代理对象的方法。
注意一下,在讲解之前,说明一点:使用Spring AOP,要成功运行起代码,只用Spring提供给开发者的jar包是不够的,请额外上网下载两个jar包:
1、aopalliance.jar
2、aspectjweaver.jar
前面说过Spring使用动态代理或是CGLIB生成代理是有规则的,高版本的Spring会自动选择是使用动态代理还是CGLIB生成代理内容,当然我们也可以强制使用CGLIB生成代理,那就是<aop:config>里面有一个"proxy-target-class"属性,这个属性值如果被设置为true,那么基于类的代理将起作用,如果proxy-target-class被设置为false或者这个属性被省略,那么基于接口的代理将起作用
<!-- 启动 @AspectJ 支持 -->
<aop:aspectj-autoproxy/>
如果不打算使用 Spring 的 XML Schema 配置方式,则应该在 Spring 配置文件中增加如下片段来启用 @AspectJ 支持。
<!-- 启动 @AspectJ 支持 -->
<bean class="org.springframework.aop.aspectj.annotation. AnnotationAwareAspectJAutoProxyCreator"/>
上面配置文件中的 AnnotationAwareAspectJAutoProxyCreator 是一个 Bean 后处理器(BeanPostProcessor),该 Bean 后处理器将会为容器中 Bean 生成 AOP 代理,
当启动了 @AspectJ 支持后,只要我们在 Spring 容器中配置一个带 @Aspect 注释的 Bean,Spring 将会自动识别该 Bean,并将该 Bean 作为方面 Bean 处理。
<!-- 启动 @AspectJ 支持 -->
<aop:aspectj-autoproxy/>
当然,如果我们希望完全启动 Spring 的“零配置”功能,则还需要启用 Spring 的“零配置”支持,让 Spring 自动搜索指定路径下 Bean 类。
所谓自动增强,指的是 Spring 会判断一个或多个方面是否需要对指定 Bean 进行增强,并据此自动生成相应的代理,从而使得增强处理在合适的时候被调用。
如果不打算使用 Spring 的 XML Schema 配置方式,则应该在 Spring 配置文件中增加如下片段来启用 @AspectJ 支持。
<!-- 启动 @AspectJ 支持 -->
<bean class="org.springframework.aop.aspectj.annotation. AnnotationAwareAspectJAutoProxyCreator"/>
上面配置文件中的 AnnotationAwareAspectJAutoProxyCreator 是一个 Bean 后处理器(BeanPostProcessor),该 Bean 后处理器将会为容器中 Bean 生成 AOP 代理,
当启动了 @AspectJ 支持后,只要我们在 Spring 容器中配置一个带 @Aspect 注释的 Bean,Spring 将会自动识别该 Bean,并将该 Bean 作为方面 Bean 处理。
在 Spring 容器中配置方面 Bean(即带 @Aspect 注释的 Bean),与配置普通 Bean 没有任何区别,一样使用 <bean.../> 元素进行配置,一样支持使用依赖注入来配置属性值;如果我们启动了 Spring 的“零配置”特性,一样可以让 Spring 自动搜索,并装载指定路径下的方面 Bean。
使用 @Aspect 标注一个 Java 类,该 Java 类将会作为方面 Bean,如下面代码片段所示:
// 使用 @Aspect 定义一个方面类
@Aspect
public class LogAspect
{
// 定义该类的其他内容
...
}
方面类(用 @Aspect 修饰的类)和其他类一样可以有方法、属性定义,还可能包括切入点、增强处理定义。
当我们使用 @Aspect 来修饰一个 Java 类之后,Spring 将不会把该 Bean 当成组件 Bean 处理,因此负责自动增强的后处理 Bean 将会略过该 Bean,不会对该 Bean 进行任何增强处理。
开发时无须担心使用 @Aspect 定义的方面类被增强处理,当 Spring 容器检测到某个 Bean 类使用了 @Aspect 标注之后,Spring 容器不会对该 Bean 类进行增强。
// 定义一个方面
@Aspect
public class AfterReturningAdviceTest
{
// 匹配 org.crazyit.app.service.impl 包下所有类的、
// 所有方法的执行作为切入点
@AfterReturning(returning="rvt",
pointcut="execution(* org.crazyit.app.service.impl.*.*(..))")
public void log(Object rvt)
{
System.out.println("获取目标方法返回值 :" + rvt);
System.out.println("模拟记录日志功能 ...");
}
}
上面 Aspect 类使用了 @Aspect 修饰,这样 Spring 会将它当成一个方面 Bean 进行处理。其中程序中粗体字代码指定将会在调用 org.crazyit.app.service.impl 包下的所有类的所有方法之后织入 log(Object rvt) 方法。
// 定义一个方面
@Aspect
public class AroundAdviceTest
{
// 匹配 org.crazyit.app.service.impl 包下所有类的、
// 所有方法的执行作为切入点
@Around("execution(* org.crazyit.app.service.impl.*.*(..))")
public Object processTx(ProceedingJoinPoint jp)
throws java.lang.Throwable
{
System.out.println("执行目标方法之前,模拟开始事务 ...");
// 执行目标方法,并保存目标方法执行后的返回值
Object rvt = jp.proceed(new String[]{"被改变的参数"});
System.out.println("执行目标方法之后,模拟结束事务 ...");
return rvt + " 新增的内容";
}
【Spring MVC】
SpringMVC接口解释
- DispatcherServlet接口:
Spring提供的前端控制器,所有的请求都有经过它来统一分发。在DispatcherServlet将请求分发给Spring Controller之前,需要借助于Spring提供的HandlerMapping定位到具体的Controller。
- HandlerMapping接口:
能够完成客户请求到Controller映射。
- Controller接口:
需要为并发用户处理上述请求,因此实现Controller接口时,必须保证线程安全并且可重用。
Controller将处理用户请求,这和Struts Action扮演的角色是一致的。一旦Controller处理完用户请求,则返回ModelAndView对象给DispatcherServlet前端控制器,ModelAndView中包含了模型(Model)和视图(View)。
从宏观角度考虑,DispatcherServlet是整个Web应用的控制器;从微观考虑,Controller是单个Http请求处理过程中的控制器,而ModelAndView是Http请求过程中返回的模型(Model)和视图(View)。
- ViewResolver接口:
Spring提供的视图解析器(ViewResolver)在Web应用中查找View对象,从而将相应结果渲染给客户。
SpringMVC运行原理
- 客户端请求提交到DispatcherServlet
- 由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
- DispatcherServlet将请求提交到Controller
- Controller调用业务逻辑处理后,返回ModelAndView
- DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
- 视图负责将结果显示到客户端
1、客户端发出一个http请求给web服务器,web服务器对http请求进行解析,如果匹配DispatcherServlet的请求映射路径(在web.xml中指定),web容器将请求转交给DispatcherServlet.
2、DipatcherServlet接收到这个请求之后将根据请求的信息(包括URL、Http方法、请求报文头和请求参数Cookie等)以及HandlerMapping的配置找到处理请求的处理器(Handler)。
3-4、DispatcherServlet根据HandlerMapping找到对应的Handler,将处理权交给Handler(Handler将具体的处理进行封装),再由具体的HandlerAdapter对Handler进行具体的调用。
5、Handler对数据处理完成以后将返回一个ModelAndView()对象给DispatcherServlet。
6、Handler返回的ModelAndView()只是一个逻辑视图并不是一个正式的视图,DispatcherSevlet通过ViewResolver将逻辑视图转化为真正的视图View。
7、Dispatcher通过model解析出ModelAndView()中的参数进行解析最终展现出完整的view并返回给客户端。
DispatcherServlet的handlerMappings集合中根据请求的URL匹配每一个handlerMapping对象中的某个handler,匹配成功之后将会返回这个handler的处理连接handlerExecutionChain对象。而这个handlerExecutionChain对象中将会包含用户自定义的多个handlerInterceptor对象。
ModelAndView对象是连接业务逻辑层与view展示层的桥梁,对spring MVC来说它也是连接Handler与view的桥梁。ModelAndView对象顾名思义会持有一个ModelMap对象和一个View对象或者View的名称。ModelMap对象就是执行模版渲染时候所需要的变量对应的实例,如jsp的通过request.getAttribute(String)获取的JSTL标签名对应的对象。velocity中context.get(String)获取$foo对应的变量实例。
ViewResolver:主要是根据用户请求的viewName创建适合的模版引擎来渲染最终的页面,
ViewResolver会根据viewName创建一个view对象,调用view对象的Void render方法渲染出页面
DefaultAnnotationHandlerMapping 通过注解,把一个URL映射到Controller类上
AnnotationMethodHandlerAdapter类,通过注解,把一个URL映射到Controller类的方法上
UrlBasedViewResolver类 通过配置文件,把一个视图名交给到一个View来处理
每一个DispatcherServlet有自己的WebApplicationContext上下文对象。
<context:component-scan/> 扫描指定的包中的类上的注解,常用的注解有:
@Controller 声明Action组件
@Service 声明Service组件 @Service("myMovieLister")
@Repository 声明Dao组件
@Component 泛指组件, 当不好归类时.
@RequestMapping("/menu") 请求映射
@Resource 用于注入,( j2ee提供的 ) 默认按名称装配,@Resource(name="beanName")
@Autowired 用于注入,(srping提供的) 默认按类型装配
@Transactional( rollbackFor={Exception.class}) 事务管理
@ResponseBody
@Scope("prototype") 设定bean的作用域
<mvc:annotation-driven /> 是一种简写形式,完全可以手动配置替代这种简写形式,简写形式可以让初学都快速应用默认配置方案。<mvc:annotation-driven /> 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,是spring MVC为@Controllers分发请求所必须的。
并提供了:数据绑定支持,@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持,读写XML的支持(JAXB),读写JSON的支持(Jackson)。
后面,我们处理响应ajax请求时,就使用到了对json的支持。
后面,对action写JUnit单元测试时,要从spring IOC容器中取DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,来完成测试,取的时候要知道是<mvc:annotation-driven />这一句注册的这两个bean。
<mvc:interceptors/> 是一种简写形式。通过看前面的大图,知道,我们可以配置多个HandlerMapping。<mvc:interceptors/>会为每一个HandlerMapping,注入一个拦截器。其实我们也可以手动配置为每个HandlerMapping注入一个拦截器。
<mvc:default-servlet-handler/> 使用默认的Servlet来响应静态文件。
<mvc:resources mapping="/images/**" location="/images/" cache-period="31556926"/> 匹配URL /images/** 的URL被当做静态资源,由Spring读出到内存中再响应http。
Spring MVC并没有总的拦截器,不能对所有的请求进行前后拦截。
Spring MVC的拦截器,是属于HandlerMapping级别的,可以有多个HandlerMapping ,每个HandlerMapping可以有自己的拦截器。
当一个请求按Order值从小到大,顺序执行HandlerMapping接口的实现类时,哪一个先有返回,那就可以结束了,后面的HandlerMapping就不走了,本道工序就完成了。就转到下一道工序了。
拦截器会在什么时候执行呢? 一个请求交给一个HandlerMapping时,这个HandlerMapping先找有没有处理器来处理这个请求,如何找到了,就执行拦截器,执行完拦截后,交给目标处理器。
如果没有找到处理器,那么这个拦截器就不会被执行。
如果使用了<mvc:annotation-driven />, 它会自动注册DefaultAnnotationHandlerMapping 与AnnotationMethodHandlerAdapter 这两个bean,所以就没有机会再给它注入interceptors属性,就无法指定拦截器。
当然我们可以通过人工配置上面的两个Bean,不使用 <mvc:annotation-driven />,就可以 给interceptors属性 注入拦截器了。
使用@ResponseBody注解 处理ajax请求
Spring Web MVC优势
1、清晰的角色划分:前端控制器(DispatcherServlet)、请求到处理器映射(HandlerMapping)、处理器适配器(HandlerAdapter)、视图解析器(ViewResolver)、处理器或页面控制器(Controller)、验证器( Validator)、命令对象(Command 请求参数绑定到的对象就叫命令对象)、表单对象(Form Object 提供给表单展示和提交到的对象就叫表单对象)。
2、分工明确,而且扩展点相当灵活,可以很容易扩展,虽然几乎不需要;
3、由于命令对象就是一个POJO,无需继承框架特定API,可以使用命令对象直接作为业务对象;
4、和Spring 其他框架无缝集成,是其它Web框架所不具备的;
5、可适配,通过HandlerAdapter可以支持任意的类作为处理器;
6、可定制性,HandlerMapping、ViewResolver等能够非常简单的定制;
7、功能强大的数据验证、格式化、绑定机制;
8、利用Spring提供的Mock对象能够非常简单的进行Web层单元测试;
9、本地化、主题的解析的支持,使我们更容易进行国际化和主题的切换。
10、强大的JSP标签库,使JSP编写更容易。
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
- <servlet>
- <servlet-name>springMVC</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath*:/springMVC.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>springMVC</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
Spring会创建一个WebApplicationContext上下文,称为父上下文(父容器) ,保存在 ServletContext中,key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。
可以使用Spring提供的工具类取出上下文对象:WebApplicationContextUtils.getWebApplicationContext(ServletContext);
- <!-- 自动扫描的包名 -->
- <context:component-scan base-package="com.app,com.core,JUnit4" ></context:component-scan>
- <!-- 默认的注解映射的支持 -->
- <mvc:annotation-driven />
- <!-- 视图解释类 -->
- <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
- <property name="prefix" value="http://www.mamicode.com/WEB-INF/jsp/"/>
- <property name="suffix" value="http://www.mamicode.com/.jsp"/><!--可为空,方便实现自已的依据扩展名来选择视图解释类的逻辑 -->
- <property name="viewClass" value="http://www.mamicode.com/org.springframework.web.servlet.view.JstlView" />
- </bean>
- <!-- 拦截器 -->
- <mvc:interceptors>
- <bean class="com.core.mvc.MyInteceptor" />
- </mvc:interceptors>
- <!-- 对静态资源文件的访问 方案一 (二选一) -->
- <mvc:default-servlet-handler/>
- <!-- 对静态资源文件的访问 方案二 (二选一)-->
- <mvc:resources mapping="/images/**" location="/images/" cache-period="31556926"/>
- <mvc:resources mapping="/js/**" location="/js/" cache-period="31556926"/>
- <mvc:resources mapping="/css/**" location="/css/" cache-period="31556926"/>
可以通过redirect/forward:url方式转到另一个Action进行连续的处理。
可以通过redirect:url 防止表单重复提交 。
redirectAttributes.addFlashAttribute("message", "操作成功");
spring总结
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。