微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!
spring security 图解过滤器的使用
1. HttpSessionContextIntegrationFilter位于过滤器顶端,第一个起作用的过滤器。用途一,在执行其他过滤器之前,率先判断用户的session中是否已经存在一个SecurityContext了。如果存在,就把SecurityContext拿出来,放到SecurityContextHolder中,供Spring Security的其他部分使用。如果不存在,就创建一个SecurityContext出来,还是放到SecurityContextHolder中,供Spring Security的其他部分使用。用途二,在所有过滤器执行完毕后,清空SecurityContextHolder,因为SecurityContextHolder是基于ThreadLocal的,如果在操作完成后清空ThreadLocal,会受到服务器的线程池机制的影响。.2. LogoutFilter图 11.3. org.springframework.security.ui.logout.LogoutFilter 只处理注销请求,默认为/j_spring_security_logout。用途是在用户发送注销请求时,销毁用户session,清空SecurityContextHolder,然后重定向到注销成功页面。可以与rememberMe之类的机制结合,在注销的同时清空用户cookie。3. AuthenticationProcessingFilter图 11.4. org.springframework.security.ui.webapp.AuthenticationProcessingFilter 处理form登陆的过滤器,与form登陆有关的所有操作都是在此进行的。默认情况下只处理/j_spring_security_check请求,这个请求应该是用户使用form登陆后的提交地址,form所需的其他参数可以参考。此过滤器执行的基本操作时,通过用户名和密码判断用户是否有效,如果登录成功就跳转到成功页面(可能是登陆之前访问的受保护页面,也可能是默认的成功页面),如果登录失败,就跳转到失败页面。4. DefaultLoginPageGeneratingFilter图 11.5. org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter 此过滤器用来生成一个默认的登录页面,默认的访问地址为/spring_security_login,这个默认的登录页面虽然支持用户输入用户名,密码,也支持rememberMe功能,但是因为太难看了,只能是在演示时做个样子,不可能直接用在实际项目中。 5. BasicProcessingFilter图 11.6. org.springframework.security.ui.basicauth.BasicProcessingFilter 此过滤器用于进行basic验证,功能与AuthenticationProcessingFilter类似,只是验证的方式不同。6. SecurityContextHolderAwareRequestFilter图 11.7. org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter 此过滤器用来包装客户的请求。目的是在原始请求的基础上,为后续程序提供一些额外的数据。比如getRemoteUser()时直接返回当前登陆的用户名之类的。7. RememberMeProcessingFilter图 11.8. org.springframework.security.ui.rememberme.RememberMeProcessingFilter 此过滤器实现RememberMe功能,当用户cookie中存在rememberMe的标记,此过滤器会根据标记自动实现用户登陆,并创建SecurityContext,授予对应的权限。8. AnonymousProcessingFilter图 11.9. org.springframework.security.providers.anonymous.AnonymousProcessingFilter 为了保证操作统一性,当用户没有登陆时,默认为用户分配匿名用户的权限。 ExceptionTranslationFilter图 11.10. org.springframework.security.ui.ExceptionTranslationFilter 此过滤器的作用是处理中FilterSecurityInterceptor抛出的异常,然后将请求重定向到对应页面,或返回对应的响应错误代码。 SessionFixationProtectionFilter图 11.11. org.springframework.security.ui.SessionFixationProtectionFilter 防御会话伪造攻击。有关防御会话伪造的详细信息,11. FilterSecurityInterceptor图 11.12. org.springframework.security.intercept.web.FilterSecurityInterceptor 用户的权限控制都包含在这个过滤器中。功能一:如果用户尚未登陆,则抛出AuthenticationCredentialsNotFoundException“尚未认证异常”。功能二:如果用户已登录,但是没有访问当前资源的权限,则抛出AccessDeniedException“拒绝访问异常”。功能三:如果用户已登录,也具有访问当前资源的权限,则放行。
spring security 管理会话 多个用户不可以使用同一个账号登录系统
多个用户不能使用同一个账号同时登陆系统。1. 添加监听器在web.xml中添加一个监听器,这个监听器会在session创建和销毁的时候通知Spring Security。<listener><listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class></listener>2.添加过滤器控制同步的session过滤器<http auto-config='true'><intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" /><intercept-url pattern="/**" access="ROLE_USER" /><session-management><concurrency-control /></session-management></http>3.1 后面的用户禁止登陆默认情况下,如果有一个人使用了一个账号登录了系统,其他人就不能再用这个账号登录了。这个参数用来控制是否在会话数目超过最大限制时抛出异常,默认值是false,也就是不抛出异常,而是把之前的session都销毁掉,所以之前登陆的用户就会被踢出系统了。现在我们把这个参数改为true,再使用同一个账号同时登陆一下系统,看看会发生什么现象。现在只要有一个人使用user/user登陆过系统,其他人就不能再次登录了。这样可能出现一个问题,如果有人登陆的时候因为某些问题没有进行logout就退出了系统,那么他只能等到session过期自动销毁之后,才能再次登录系统。3.2 后登陆的将先登录的踢出系统如果希望后登陆的用户会把先登录的用户踢出系统,需要为concurrent-session-control设置一个参数。<http auto-config='true'><intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" /><intercept-url pattern="/**" access="ROLE_USER" /><concurrent-session-control exception-if-maximum-exceeded="false"/></http> 想测试一下的话,先打开firefox使用user/user登陆系统,然后再打开ie使用user/user登陆系统。这时ie下的user用户会登陆成功,进入登陆成功页面。而firefox下的用户如何刷新页面,就会显示如下信息:This session has been expired (possibly due to multiple concurrent logins being attempted as the same user).这是因为先登录的用户已经被强行踢出了系统,如果他再次使用user/user登陆,ie下的用户也会被踢出系统了。
spring security remember me实现自动登录
1 默认策略在我们自定义的login中增加一个选择框<input type="submit" value="Login" /> <br/> <br/> <input type="checkbox" value=true name="_spring_security_remember_me" />记住密码<!-- 记住我功能 --><security:remember-me key="elim" />web.xml中增加成上述就可以实现了。默认有效时间是两周,启用rememberMe之后的两周内,用户都可以直接跳过系统,直接进入系统。实际上,Spring Security中的rememberMe是依赖cookie实现的,当用户在登录时选择使用rememberMe,系统就会在登录成功后将为用户生成一个唯一标识,并将这个标识保存进cookie中,我们可以通过浏览器查看用户电脑中的cookie。图 16.2. rememberMe cookie 从上图中,我们可以看到SpringSecurity生成的cookie名称是SPRING_SECURITY_REMEMBER_ME_COOKIE,它的内容是一串加密的字符串,当用户再次访问系统时,SpringSecurity将从这个cookie读取用户信息,并加以验证。如果可以证实cookie有效,就会自动将用户登录到系统中,并为用户授予对应的权限。2. 持久化策略rememberMe的默认策略会将username和过期时间保存到客户主机上的cookie中,虽然这些信息都已经进行过加密处理,不过我们还可以使用安全级别更高的持久化策略。在持久化策略中,客户主机cookie中保存的不再用username,而是由系统自动生成的序列号,在验证时系统会将客户cookie中保存的序列号与数据库中保存的序列号进行比对,以确认客户请求的有效性,之后在比对成功后才会从数据库中取出对应的客户信息,继续进行认证和授权等工作。这样即使客户本地的cookie遭到破解,攻击者也只能获得一个序列号,而不是用户的登录账号。如果希望使用持久化策略,我们需要先在数据库中创建rememberMe所需的表。create table persistent_logins (username varchar(64) not null,series varchar(64) primary key,token varchar(64) not null,last_used timestamp not null);然后要为配置文件中添加与数据库的链接。<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="org.hsqldb.jdbcDriver"/><property name="url" value="jdbc:hsqldb:res:/hsqldb/test"/><property name="username" value="sa"/><property name="password" value=""/></bean>最后修改http中的配置,为remember-me添加data-source-ref即可,Spring Security会在初始化时判断是否存在data-source-ref属性,如果存在就会使用持久化策略,否则会使用上述的默认策略。<http auto-config='true'><intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" /><intercept-url pattern="/**" access="ROLE_USER" /><remember-me data-source-ref="dataSource"/></http>默认策略和持久化策略是不能混用的,如果你首先在应用中使用过默认策略的rememberMe,未等系统过期便换成了持久化策略,之前保留的cookie也无法通过系统验证,实际上系统会将cookie当做无效标识进行清除。同样的,持久化策略中生成的cookie也无法用在默认策略下。 
spring security 匿名登录
匿名登录,即用户尚未登录系统,系统会为所有未登录的用户分配一个匿名用户,这个用户也拥有自己的权限,不过他是不能访问任何被保护资源的。设置一个匿名用户的好处是,我们在进行权限判断时,可以保证SecurityContext中永远是存在着一个权限主体的,启用了匿名登录功能之后,我们所需要做的工作就是从SecurityContext中取出权限主体,然后对其拥有的权限进行校验,不需要每次去检验这个权限主体是否为空了。这样做的好处是我们永远认为请求的主体是拥有权限的,即便他没有登录,系统也会自动为他赋予未登录系统角色的权限,这样后面所有的安全组件都只需要在当前权限主体上进行处理,不用一次一次的判断当前权限主体是否存在。这就更容易保证系统中操作的一致性1. 配置文件在配置文件中使用auto-config="true"就会启用匿名登录功能。在启用匿名登录之后,如果我们希望允许未登录就可以访问一些资源,可以在进行如下配置。<http auto-config='true'><intercept-url pattern="/" access="IS_AUTHENTICATED_ANONYMOUSLY" /><intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" /><intercept-url pattern="/**" access="ROLE_USER" /></http>在access中指定IS_AUTHENTICATED_ANONYMOUSLY后,系统就知道此资源可以被匿名用户访问了。当未登录时访问系统的“/”,就会被自动赋以匿名用户的身份。我们可以使用taglib获得用户的权限主体信息。这里的IS_AUTHENTICATED_ANONYMOUSLY将会交由AuthenticatedVoter处理,内部会依据AuthenticationTrustResolver判断当前登录的用户是否是匿名用户。在access中指定IS_AUTHENTICATED_ANONYMOUSLY后,系统就知道此资源可以被匿名用户访问了。当未登录时访问系统的“/”,就会被自动赋以匿名用户的身份。我们可以使用taglib获得用户的权限主体信息。这里的IS_AUTHENTICATED_ANONYMOUSLY将会交由AuthenticatedVoter处理,内部会依据AuthenticationTrustResolver判断当前登录的用户是否是匿名用户。当用户访问系统时,就会看到如下信息,这时他还没有进行登录。图 17.1. 匿名登录 这里显示的是分配给所有未登录用户的一个默认用户名roleAnonyMous,拥有的权限是ROLE_ANONYMOUS。我们可以看到系统已经把匿名用户当做了一个合法有效的用户进行处理,可以获得它的用户名和拥有的权限,而不需判断SecurityContext中是否为空。实际上,我们完全可以把匿名用户像一个正常用户那样进行配置,我们可以在配置文件中直接使用ROLE_ANONYMOUS指定它可以访问的资源。<http auto-config='true'><intercept-url pattern="/" access="ROLE_ANONYMOUS" /><intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" /><intercept-url pattern="/**" access="ROLE_USER" /></http>不过,为了更明显的将匿名用户与系统中的其他用户区分开,我们推荐在配置时尽量使用IS_AUTHENTICATED_ANONYMOUSLY来指定匿名用户可以访问的资源。2. 修改默认用户名我们通常可以看到这种情况,当一个用户尚未登录系统时,在页面上应当显示用户名的部分显示的是“游客”。这样操作更利于提升客户体验,他看到自己即使没有登录也可以使用“游客”的身份享受系统的服务,而且使用了“游客”作为填充内容,也避免了原本显示用户名部分留空,影响页面布局。如果没有匿名登录的功能,我们就被迫要在所有显示用户名的部分,判断当前用户是否登录,然后根据登录情况显示登录用户名或默认的“游客”字符。匿名登录功能帮我们解决了这个问题,既然未登录用户都拥有匿名用户的身份,那么在显示用户名时就不必去区分用户登录状态,直接显示当前权限主体的名称即可。我们需要做的只是为匿名设置默认的用户名而已,默认的名称roleAnonymous可以通过配置文件中的anonymous元素修改。<http auto-config='true'><intercept-url pattern="/" access="IS_AUTHENTICATED_ANONYMOUSLY" /><intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" /><intercept-url pattern="/**" access="ROLE_USER" /><anonymous username="Guest"/></http>这样,匿名用户的默认名称就变成了“Guest”,我们在页面上依然使用相同的taglib显示用户名即可。3. 匿名用户的限制虽然匿名用户无论在配置授权时,还是在获取权限信息时,都与已登录用户的操作一模一样,但它应该与未登录用户是等价,当我们以匿名用户的身份进入“/”后,点击admin.jsp链接,系统会像处理未登录用户时一样,跳转到登录用户,而不是像处理以登录用户时显示拒绝访问页面。但是匿名用户与未登录用户之间也有很大的区别,比如,我们将“/”设置为不需要过滤器保护,而不是设置匿名用户。<http security="none" pattern="/"/><http auto-config='true'><intercept-url pattern="/" access="IS_AUTHENTICATED_ANONYMOUSLY" /><intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" /><intercept-url pattern="/**" access="ROLE_USER" /></http>security="none"表示当我们访问“/”时,是不会使用任何一个过滤器去处理这个请求的,它可以实现无需登录即可访问资源的效果,但是因为没有使用过滤器对请求进行处理,所以也无法利用安全过滤器为我们带来的好处,最简单的,这时SecurityContext内再没有保存任何一个权限主体了,我们也无法从中取得主体名称以及对应的权限信息。图 17.3. 跳过过滤器的情况因此,使用security="none"忽略所有过滤器会提升性能,而是用匿名登录功能可以实现权限操作的一致性,具体应用时还需要大家根据实际情况自行选择了。 
spring security防御会话伪造session攻击
1. 攻击场景session fixation会话伪造攻击是一个蛮婉转的过程。比如,当我要是使用session fixation攻击你的时候,首先访问这个网站,网站会创建一个会话,这时我可以把附有jsessionid的url发送给你。http://unsafe/index.jsp;jsessionid=1pjztz08i2u4i你使用这个网址访问网站,结果你和我就会公用同一个jsessionid了,结果就是在服务器中,我们两人使用的是同一个session。这时我只要祈求你在session过期之前登陆系统,然后我就可以使用jsessionid直接进入你的后台了,然后可以使用你在系统中的授权做任何事情。简单来说,我创建了一个session,然后把jsessionid发给你,你傻乎乎的就使用我的session进行了登陆,结果等于帮我的session进行了授权操作,结果就是我可以使用一开始创建的session进入系统做任何事情了。与会话伪造的详细信息可以参考http://en.wikipedia.org/wiki/Session_fixation。2. 解决会话伪造解决session fix的问题其实很简单,只要在用户登录成功之后,销毁用户的当前session,并重新生成一个session就可以了。Spring Security默认就会启用session-fixation-protection,这会在登录时销毁用户的当前session,然后为用户创建一个新session,并将原有session中的所有属性都复制到新session中。如果希望禁用session-fixation-protection,可以在http中将session-fixation-protection设置为none。<http auto-config='true'><intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" /><intercept-url pattern="/**" access="ROLE_USER" /><session-management session-fixation-protection="none"/></http>session-fixation-protection的值共有三个可供选择,none,migrateSession和newSession。默认使用的是migrationSession,如同我们上面所讲的,它会将原有session中的属性都复制到新session中。上面我们也见到了使用none来禁用session-fixation功能的场景,最后剩下的newSession会在用户登录时生成新session,但不会复制任何原有属性。
spring security LDAP获取用户信息
很多企业内部使用LDAP保存用户信息,这章我们来看一下如何从LDAP中获取Spring Security所需的用户信息。首先在pom.xml中添加ldap所需的依赖。<dependency><groupId>org.apache.directory.server</groupId><artifactId>apacheds-server-jndi</artifactId><version>1.5.5</version></dependency><dependency><groupId>org.springframework.ldap</groupId><artifactId>spring-ldap</artifactId><version>1.2.1</version></dependency>然后修改配置文件,使用内嵌的ldap服务器和ldap-authentication-provider。<ldap-server ldif="classpath:users.ldif" port="33389" root="dc=family168,dc=com"/><ldap-authentication-providergroup-search-filter="member={0}"group-search-base="ou=groups"user-search-base="ou=people"user-search-filter="uid={0}"/>这里配置内嵌的ldap服务器从users.ldif文件中读取初始化数据,端口使用33389,查询目录的根目录设置为dc=family168,dc=com。ldap-authentication-provider设置查找组和用户的配置,分别使用ou=groups表示组,使用ou=people表示用户。用于保存ldap初始信息的文件内容如下:dn: ou=groups,dc=family168,dc=comobjectclass: topobjectclass: organizationalUnitou: groupsdn: ou=people,dc=family168,dc=comobjectclass: topobjectclass: organizationalUnitou: peopledn: uid=user,ou=people,dc=family168,dc=comobjectclass: topobjectclass: personobjectclass: organizationalPersonobjectclass: inetOrgPersoncn: FirstName LastNamesn: LastNameuid: useruserPassword: userdn: uid=admin,ou=people,dc=family168,dc=comobjectclass: topobjectclass: personobjectclass: organizationalPersonobjectclass: inetOrgPersoncn: FirstName LastNamesn: LastNameuid: adminuserPassword: admindn: cn=user,ou=groups,dc=family168,dc=comobjectclass: topobjectclass: groupOfNamescn: ROLE_USERmember: uid=user,ou=people,dc=family168,dc=commember: uid=admin,ou=people,dc=family168,dc=comdn: cn=admin,ou=groups,dc=family168,dc=comobjectclass: topobjectclass: groupOfNamescn: ROLE_ADMINmember: uid=admin,ou=people,dc=family168,dc=com这里在dc=family168,dc=com目录下创建了groups和people两个目录,然后在people目录下创建了user和admin两个用户。在groups目录下创建了admin和user两个目录,并将user和admin两个用户与groups的user目录关联,又将admin用户与groups的admin目录关联。在系统初始化后,Spring Security会在people下读取用户信息,而对应的权限信息是对应用户所关联的groups信息,Spring Security会将查询到的权限信息加上ROLE_前缀,如cn=admin最终会转换为ROLE_ADMIN。
spring注解实现AOP
项目结构图注意事项:我原来用的JDK1.7匹配的是aspectjrt.1.6和aspectjweaver.1.6,因此会报错。 如果要使用AspectJ完成注解切面需要注意下面的JDK与AspectJ的匹配:JDK1.6 —— aspectJ1.6JDK1.7 —— aspectJ1.7.3+1,Bean的配文件。 <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.1.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.1.xsd" ><aop:aspectj-autoproxy></aop:aspectj-autoproxy><bean id="userManager" class="com.tgb.spring.UserManagerImpl"></bean><bean id="aspectJAdvice" class="com.tgb.spring.AspectJAdvice"></bean></beans> 2.UserManager的接口实现  package com.tgb.spring;public interface UserManager {public abstract void addUser(String userName,String password);public abstract void delUser(int userId);public abstract String findUserById(int userId);public abstract void modifyUser(int userId,String userName,String password);} 3.继承UserManager接口实现UserManagerImplpackage com.tgb.spring;public class UserManagerImpl implements UserManager {@Overridepublic void addUser(String userName, String password) {// TODO Auto-generated method stubSystem.out.println("----tianjia.delUser()----");}@Overridepublic void delUser(int userId) {// TODO Auto-generated method stubSystem.out.println("----shanchu.delUser()----");}@Overridepublic String findUserById(int userId) {System.out.println("----UserManagerImpl.findUserById()----");if(userId <= 0){throw new IllegalArgumentException("该用户不存在");}return "jiuqiyuliang";}@Overridepublic void modifyUser(int userId, String userName, String password) {// TODO Auto-generated method stubSystem.out.println("----xiugai.delUser()----");}}4.切面实现package com.tgb.spring;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;@Aspectpublic class AspectJAdvice {/*** Pointcut* 定义Pointcut,Pointcut的名称为aspectjMethod(),此方法没有返回值和参数* 该方法就是一个标识,不进行调用*/@Pointcut("execution(* find*(..))")private void aspectjMethod(){};/*** Before* 在核心业务执行前执行,不能阻止核心业务的调用。* @param joinPoint*/@Before("aspectjMethod()")public void doBefore(JoinPoint joinPoint) {System.out.println("-----doBefore.invoke-----");System.out.println(" 此处意在执行核心业务逻辑前,做一些安全性的判断等等");System.out.println(" 可通过joinPoint来获取所需要的内容");System.out.println("-----End of doBefore()------");}/*** Around* 手动控制调用核心业务逻辑,以及调用前和调用后的处理,** 注意:当核心业务抛异常后,立即退出,转向AfterAdvice* 执行完AfterAdvice,再转到ThrowingAdvice* @param pjp* @return* @throws Throwable*/@Around(value = "aspectjMethod()")public Object doAround(ProceedingJoinPoint pjp) throws Throwable {System.out.println("-----doAround.invoke-----");System.out.println(" 此处可以做类似于Before的事情");//调用核心逻辑Object retVal = pjp.proceed();System.out.println(" 此处可以做类似于After的事情");System.out.println("-----End of doAround()------");return retVal;}/*** After* 核心业务逻辑退出后(包括正常执行结束和异常退出),执行此Advice* @param joinPoint*/@After(value = "aspectjMethod()")public void doAfter(JoinPoint joinPoint) {System.out.println("-----doAfter.invoke-----");System.out.println(" 此处意在执行核心业务逻辑之后,做一些日志记录操作等等");System.out.println(" 可通过joinPoint来获取所需要的内容");System.out.println("-----End of doAfter()------");}/*** AfterReturning* 核心业务逻辑调用正常退出后,不管是否有返回值,正常退出后,均执行此Advice* @param joinPoint*/@AfterReturning(value = "aspectjMethod()", returning = "retVal")public void doReturn(JoinPoint joinPoint, String retVal) {System.out.println("-----doReturn().invoke-----");System.out.println("Return Value: " + retVal);System.out.println(" 此处可以对返回值做进一步处理");System.out.println(" 可通过joinPoint来获取所需要的内容");System.out.println("-----End of doReturn()------");}/*** 核心业务逻辑调用异常退出后,执行此Advice,处理错误信息** 注意:执行顺序在Around Advice之后* @param joinPoint* @param ex*/@AfterThrowing(value = "aspectjMethod()", throwing = "ex")public void doThrowing(JoinPoint joinPoint, Exception ex) {System.out.println("-----doThrowing().invoke-----");System.out.println(" 错误信息:"+ex.getMessage());System.out.println(" 此处意在执行核心业务逻辑出错时,捕获异常,并可做一些日志记录操作等等");System.out.println(" 可通过joinPoint来获取所需要的内容");System.out.println("-----End of doThrowing()------");}}5.Client端实现package com.tgb.spring.client;import org.springframework.beans.factory.BeanFactory;import org.springframework.context.support.ClassPathXmlApplicationContext;impo
Spring依赖注入原理分析
在分析原理之前我们先回顾下依赖注入的概念:我们常提起的依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念。具体含义是:当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在 传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在Spring里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者 实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为依赖注入。其实简单的说,依赖注入起到的作用就是讲对象之间的依赖关系从原先的代码中解耦出来,通过配置文件或注解等方式加上Spring框架的处理让我们对依赖关系灵活集中的进行管理。下面谈谈Spring是如何实现反转模式IOC或依赖注入模式DI:平时,我们需要生成一个对象,使用new语法,如一个类为Apublic class A{public void myMethod(){System.out.println("hello");}}如果我们在 B中调用A,那么如下代码:public class B{public void invoke(){A a = new A();a.myMethod();}}每次执行invoke方法时,都要生成一个A对象,如果A对象代码较长,这是费时的事情。于是有如下写法:public class B{A a = new A();public void invoke(){a.myMethod();}}将A对象变成B的类属性。 如果我们不想在B中实现A的实例,也就是不想立即new A(),而是想通过外界传入, 注意,如果你想知道为什么,这里涉及到设计模式以及解耦等因素,进一步感兴趣者可学习 本站的GoF 23 种设计模式。如果想让A的实例从外界传入,有两种写法:public class B{A a;public void setA(A a){this.a = a;}public A getA(){return a;}public void invoke(){a.myMethod();}}这种写法,A并没有被实例化,需要通过外界调用setA方法,将A的对象实例赋入B中. 或者通过B的构造函数传入,如下:public class B{A a;public B(A a){this.a = a;}public void invoke(){a.myMethod();}}上述两种写法在编程中是经常发生的,B作为调用者,A是被调用者,A的实例化不在 调用者B内部中完成,而是通过构造函数或setXXX方法赋值进来,这种方式我们称为依赖 性注射(IoC 模式),B 和A 的依赖联系是通过构造函数或setXXX 方法赋值进来,这样, 最大程度解耦了调用者B和被调用者A之间的耦合联系。Spring如何实现依赖注射?上文提到:A的实例化不在调用者B内部中完成,而是通过构造函数或setXXX 方法赋 值进来,Spring实际就是完成这个赋值的过程。 为了让Spring自动完成B代码中的A的实例化,需要通过配置文件告诉Spring有关A 的类的属性,这个配置是applicationContext.xml文件。 在 applicationContext.xml中,我们先定义JavaBeans为B的配置:<beans><bean id="b" class="springsimple.B"/></beans>这是最常用的JavaBeans的定义,id相当于对象名,当前文件应该是唯一。后来Spring使用@Component替代。再在applicationContext.xml定义A的配置如下:<beans><bean id="b" class="springsimple.B"/><bean id="a" class="springsimple.A"/></beans>这样我们告诉Spring我们有两个JavaBeans,现在解决关键问题,B代码中还调用了A, 那么如何让Spring将A的实例注射到B中?使用Spring配置的property语法。具体配置如 下:<beans><bean id="b" class="springsimple.B"><property name="a"><ref local="a" /></property> <!— 增加这一行--></bean><bean id="a" class="springsimple.A" /></beans>增加一行说明:B 的属性a 指向了a,这样,Spring 会知道B 中属性a 的实例就是 springsimple.A,在B实例化时将会将B中的a 实现实例化,这是通过setA方法注射进入。 注意,property name="a"中的a 是setA字符中去掉set 后的字符串,这个字符串第一个 必须是小写,例如,如果B中有setOneA方法,那么,配置文件应该是property name="oneA"。在Spring Boot以后版本已经可以使用@Autowire进行自动匹配,无需如此繁琐配置了。参考:Spring源码剖析——依赖注入实现原理Spring学习笔记——Spring依赖注入原理分析 spring的依赖注入和工作原理
Spring事务管理详解+实例
1 初步理解理解事务之前,先讲一个你日常生活中最常干的事:取钱。 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱;然后ATM出1000元钱。这两个步骤必须是要么都执行要么都不执行。如果银行卡扣除了1000块但是ATM出钱失败的话,你将会损失1000元;如果银行卡扣钱失败但是ATM却出了1000块,那么银行将损失1000元。所以,如果一个步骤成功另一个步骤失败对双方都不是好事,如果不管哪一个步骤失败了以后,整个取钱过程都能回滚,也就是完全取消所有操作的话,这对双方都是极好的。 事务就是用来解决类似问题的。事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。 在企业级应用程序开发中,事务管理必不可少的技术,用来确保数据的完整性和一致性。 事务有四个特性:ACID原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。2 核心接口Spring事务管理的实现有许多细节,如果对整个接口框架有个大体了解会非常有利于我们理解事务,下面通过讲解Spring的事务接口来了解Spring实现事务的具体策略。 Spring事务管理涉及的接口的联系如下:2.1 事务管理器Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。 Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。此接口的内容如下:1 Public interface PlatformTransactionManager()...{2 // 由TransactionDefinition得到TransactionStatus对象3 TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;4 // 提交5 Void commit(TransactionStatus status) throws TransactionException;6 // 回滚7 Void rollback(TransactionStatus status) throws TransactionException;8 }从这里可知具体的具体的事务管理机制对Spring来说是透明的,它并不关心那些,那些是对应各个平台需要关心的,所以Spring事务管理的一个优点就是为不同的事务API提供一致的编程模型,如JTA、JDBC、Hibernate、JPA。下面分别介绍各个平台框架实现事务管理的机制。2.1.1 JDBC事务如果应用程序中直接使用JDBC来进行持久化,DataSourceTransactionManager会为你处理事务边界。为了使用DataSourceTransactionManager,你需要使用如下的XML将其装配到应用程序的上下文定义中:1 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">2 <property name="dataSource" ref="dataSource" />3 </bean>实际上,DataSourceTransactionManager是通过调用java.sql.Connection来管理事务,而后者是通过DataSource获取到的。通过调用连接的commit()方法来提交事务,同样,事务失败则通过调用rollback()方法进行回滚。2.1.2 Hibernate事务如果应用程序的持久化是通过Hibernate实习的,那么你需要使用HibernateTransactionManager。对于Hibernate3,需要在Spring上下文定义中添加如下的<bean>声明:1 <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">2 <property name="sessionFactory" ref="sessionFactory" />3 </bean>sessionFactory属性需要装配一个Hibernate的session工厂,HibernateTransactionManager的实现细节是它将事务管理的职责委托给org.hibernate.Transaction对象,而后者是从Hibernate Session中获取到的。当事务成功完成时,HibernateTransactionManager将会调用Transaction对象的commit()方法,反之,将会调用rollback()方法。2.1.3 Java持久化API事务(JPA)Hibernate多年来一直是事实上的Java持久化标准,但是现在Java持久化API作为真正的Java持久化标准进入大家的视野。如果你计划使用JPA的话,那你需要使用Spring的JpaTransactionManager来处理事务。你需要在Spring中这样配置JpaTransactionManager:<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"><property name="sessionFactory" ref="sessionFactory" /></bean>JpaTransactionManager只需要装配一个JPA实体管理工厂(javax.persistence.EntityManagerFactory接口的任意实现)。JpaTransactionManager将与由工厂所产生的JPA EntityManager合作来构建事务。2.1.4 Java原生API事务如果你没有使用以上所述的事务管理,或者是跨越了多个事务管理源(比如两个或者是多个不同的数据源),你就需要使用JtaTransactionManager:1 <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">2 <property name="transactionManagerName" value="java:/TransactionManager" />3 </bean>JtaTransactionManager将事务管理的责任委托给javax.transaction.UserTransaction和javax.transaction.TransactionManager对象,其中事务成功完成通过UserTransaction.commit()方法提交,事务失败通过UserTransaction.rollback()方法回滚。2.2 基本事务属性的定义上面讲到的事务管理器接口PlatformTransactionManager通过getTransaction(TransactionDefinition definition)方法来得到事务,这个方法里面的参数是TransactionDefinition类,这个类就定义了一些基本的事务属性。 那么什么是事务属性呢?事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面,如图所示:而TransactionDefinition接口内容如下:1 public interface TransactionDefinition {2 int getPropagationBehavior(); // 返回事务的传播行为3 int getIsolationLevel(); // 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据4 int getTimeout(); // 返回事务必须在多少秒内完成5 boolean isReadOnly(); // 事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的6 }我们可以发现TransactionDefinition正好用来定义事务属性,下面详细介绍一下各个事务属性。2.2.1 传播行为事务的第一个方面是传播行为(propagation behavior)。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。Spring定义了七种传播行为:传播行为含义PROPAGATION_REQUIRED表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务PROPAGATION_SUPPORTS表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行PROPAGATION_MANDATORY表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常PROPAGATION_REQUIRED_NEW表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManagerPROPAGATION_NOT_SUPPORTED表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManagerPROPAGATION_NEVER表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常PROPAGATION_NESTED表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务注:以下具体讲解传播行为的内容参考自Spring事务机制详解 (1)PROPAGATION_REQUIRED 如果存在一个事务,则支持当
spring boot mybatis 整合教程
本项目使用的环境:开发工具:Intellij IDEA 2017.1.3springboot: 1.5.6jdk:1.8.0_161maven:3.3.9额外功能PageHelper 分页插件mybatis generator 自动生成代码插件步骤:1. 创建一个springboot项目:2. 创建项目的文件结构以及jdk的版本3. 选择项目所需要的依赖然后点击finish4. 看一下文件的结构:5. 查看一下pom.xml:1 <?xml version="1.0" encoding="UTF-8"?>2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">4 <modelVersion>4.0.0</modelVersion>56 <groupId>com.winter</groupId>7 <artifactId>springboot-mybatis-demo</artifactId>8 <version>0.0.1-SNAPSHOT</version>9 <packaging>jar</packaging>1011 <name>springboot-mybatis-demo</name>12 <description>Demo project for Spring Boot</description>1314 <parent>15 <groupId>org.springframework.boot</groupId>16 <artifactId>spring-boot-starter-parent</artifactId>17 <version>1.5.6.RELEASE</version>18 <relativePath/> <!-- lookup parent from repository -->19 </parent>2021 <properties>22 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>23 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>24 <java.version>1.7</java.version>25 </properties>2627 <dependencies>28 <dependency>29 <groupId>org.mybatis.spring.boot</groupId>30 <artifactId>mybatis-spring-boot-starter</artifactId>31 <version>1.3.0</version>32 </dependency>33 <dependency>34 <groupId>org.springframework.boot</groupId>35 <artifactId>spring-boot-starter-thymeleaf</artifactId>36 </dependency>37 <dependency>38 <groupId>org.springframework.boot</groupId>39 <artifactId>spring-boot-starter-web</artifactId>40 </dependency>4142 <dependency>43 <groupId>org.springframework.boot</groupId>44 <artifactId>spring-boot-starter-test</artifactId>45 <scope>test</scope>46 </dependency>47 <dependency>48 <groupId>mysql</groupId>49 <artifactId>mysql-connector-java</artifactId>50 <version>5.1.35</version>51 </dependency>525354 <dependency>55 <groupId>com.fasterxml.jackson.core</groupId>56 <artifactId>jackson-core</artifactId>57 </dependency>58 <dependency>59 <groupId>com.fasterxml.jackson.core</groupId>60 <artifactId>jackson-databind</artifactId>61 </dependency>62 <dependency>63 <groupId>com.fasterxml.jackson.datatype</groupId>64 <artifactId>jackson-datatype-joda</artifactId>65 </dependency>66 <dependency>67 <groupId>com.fasterxml.jackson.module</groupId>68 <artifactId>jackson-module-parameter-names</artifactId>69 </dependency>70 <!-- 分页插件 -->71 <dependency>72 <groupId>com.github.pagehelper</groupId>73 <artifactId>pagehelper-spring-boot-starter</artifactId>74 <version>1.1.2</version>75 </dependency>76 <!-- alibaba的druid数据库连接池 -->77 <dependency>78 <groupId>com.alibaba</groupId>79 <artifactId>druid-spring-boot-starter</artifactId>80 <version>1.1.0</version>81 </dependency>82 </dependencies>8384 <build>85 <plugins>86 <plugin>87 <groupId>org.springframework.boot</groupId>88 <artifactId>spring-boot-maven-plugin</artifactId>89 </plugin>90 <!-- mybatis generator 自动生成代码插件 -->91 <plugin>92 <groupId>org.mybatis.generator</groupId>93 <artifactId>mybatis-generator-maven-plugin</artifactId>94 <version>1.3.2</version>95 <configuration>96 <configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>97 <overwrite>true</overwrite>98 <verbose>true</verbose>99 </configuration>100 </plugin>101 </plugins>102 </build>103104105 &
[Java web]Spring+Struts2+Hibernate整合过程
 摘要最近一直在折腾java web相关内容,这里就把最近学习的spring+struts2+hibernate进行一个整合,也就是大家经常说的ssh。环境工具IDE :Idea 2018  数据库:Mysql关于如何安装tomcat、mysql、idea,请自行查找。1、使用idea创建spring项目2、创建项目SSHDemo,项目结构如下3、添加三层的包。这里采用三层的架构进行项目的设计action存放struts2的action类dao 数据库操作service业务层test单元测试domain javabean4、配置struts2在web.xml中你会看到下面的红字提示,引起这个的原因是因为struts2 版本>=2.5 需要使用另外的方式进行配置<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><filter><filter-name>struts2</filter-name><filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class></filter><filter-mapping><filter-name>struts2</filter-name><url-pattern>/*</url-pattern></filter-mapping></web-app>修改后的配置如下<filter><filter-name>struts2</filter-name><filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class></filter>其他内容不变在action包 添加一个UserActionpackage com.ssh.action;import com.opensymphony.xwork2.ActionSupport;import com.opensymphony.xwork2.ModelDriven;import com.ssh.domain.User;/*** user相关action*/public class UserAction extends ActionSupport implements ModelDriven<User> {private User user = new User();@Overridepublic User getModel() {return user;}public String login() {System.out.println(user);return NONE;}}配置struts.xml<struts><package name="user" extends="struts-default" namespace="/"><action name="user_*" class="com.ssh.action.UserAction" method="{1}"/></package></struts>添加一个登录页面login.jsp测试<%--Created by IntelliJ IDEA.User: sunwyDate: 2018/6/6Time: 10:23To change this template use File | Settings | File Templates.--%><%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>登录</title></head><body><form action="${pageContext.request.contextPath}/user_login.action">用户名:<input type="text" name="name">密码:<input type="password" name="password" id="password"><input type="submit" value="登录"></form></body></html>启动在启动时发现如下内容严重: Context [] startup failed due to previous errors[2018-06-06 10:25:36,908] Artifact SSHDemo:war exploded: Error during artifact deployment. See server log for details.打开project structure修复出现的问题重启Tomcat发现如下错误六月 06, 2018 10:57:12 上午 org.apache.catalina.startup.TldConfig execute信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...ERROR Dispatcher Dispatcher initialization failedjava.lang.RuntimeException: java.lang.reflect.InvocationTargetExceptionat com.opensymphony.xwork2.inject.ContainerImpl$MethodInjector.inject(ContainerImpl.java:289)at com.opensymphony.xwork2.inject.ContainerImpl$ConstructorInjector.construct(ContainerImpl.java:422)at com.opensymphony.xwork2.inject.ContainerBuilder$5.create(ContainerBuilder.java:231)at com.opensymphony.xwork2.inject.Scope$2$1.create(Scope.java:52)at com.opensymphony.xwork2.inject.ContainerBuilder$3.create(ContainerBuilder.java:106)at com.opensymphony.xwork2.inject.ContainerBuilder$7.call(ContainerBuilder.java:584)at com.opensymphony.xwork2.inject.ContainerBuilder$7.call(ContainerBuilder.java:581)at com.opensymphony.xwork2.inject.ContainerImpl.callInContext(ContainerImpl.java:560)at com.opensymphony.xwork2.inject.ContainerBuilder.create(ContainerBuilder.java:581)at com.opensymphony.xwork2.config.impl.DefaultConfiguration.createBootstrapContainer(DefaultConfiguration.java:287)at com.opensymphony.xwork2.config.impl.DefaultConfiguration.reloadContainer(DefaultConfiguration.java:162)at com.opensymphony.xwork2.config.ConfigurationManager.getConfiguration(ConfigurationManager.java:66)at org.apache.struts2.dispatcher.Dispatcher.getContainer(Dispatcher.java:957)at org.apache.struts2.dispatcher.Dispatcher.init_PreloadConfiguration(Dispatcher.java:463)at org.apache.struts2.dispatcher.Dispatcher.init(Dispatcher.java:496)at org.apache.struts2.dispatcher.InitOperations.initDispatcher(InitOperations.java:73)at org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.init(StrutsPrepareAndExecuteFilter.java:61)at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:285)at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:266)at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:108)at
[Java web]Spring+Struts2+Hibernate整合过程2
摘要上篇文章介绍了一种整合方式,不妨就叫做有hibernate配置文件的方式,这里介绍一种不用hibernate.cfg.xml的一种配置方式,为了方便,就仍在上篇的demo中,继续修改了。步骤因为hibernate.cfg.xml中配置的是数据库的相关配置,以及映射关系,关于这部分配置完全可以在applicationContext.xml中进行整合。数据库的4要素也可以单独提取到jdbc.properties文件中。在src目录下新建jdbc.properties文件,将4要素配置上jdbc.driverClass=com.mysql.jdbc.Driverjdbc.jdbcUrl=jdbc:mysql:///sshdemojdbc.user=rootjdbc.password=abcd在applicationContext.xml引入该文件即可修改后配置如下<?xml version="1.0" encoding="UTF-8"?><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"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><!--第一种方式 --><!--加载sessionFactory配置--><!--<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">--><!--<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>--><!--</bean>--><!--第二种--><!--配置连接池,以及引入jdbc.properties属性文件--><context:property-placeholder location="classpath:jdbc.properties"/><bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="${jdbc.driverClass}"/><property name="user" value="${jdbc.user}"/><property name="password" value="${jdbc.password}"/><property name="jdbcUrl" value="${jdbc.jdbcUrl}"/></bean><bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"><property name="dataSource" ref="c3p0DataSource"/><property name="hibernateProperties"><props><prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop><prop key="hibernate.show_sql">true</prop><prop key="hibernate.hbm2ddl.auto">update</prop><prop key="hibernate.format_sql">true</prop><!--可以添加其他的配置,这里不再写了,可以参考hibernate.cfg.xml中的配置--></props></property><!--映射关系--><property name="mappingResources"><list><value>com/ssh/domain/User.hbm.xml</value></list></property></bean><!-- 注入事务管理器,提交事务和回滚事务 --><bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"><property name="sessionFactory" ref="sessionFactory"/></bean><!--开启事务注解方式--><tx:annotation-driven transaction-manager="transactionManager"/><!--配置userDao--><bean class="com.ssh.dao.UserDao" id="userDao"><!--注入sessionFactory--><property name="sessionFactory" ref="sessionFactory"></property></bean><!--配置userService--><bean id="userService" class="com.ssh.service.UserService"><!--注入userDao--><property name="userDao" ref="userDao"></property></bean><!--将action的创建交给spring ioc 注意action是多例的 一定要将scope设置为prototype--><bean class="com.ssh.action.UserAction" id="userAction" scope="prototype"/></beans>这时完全可以把hibernate.cfg.xml删除测试结果
spring 中StoredProcedure的用法--转载
 StoredProcedure是一个抽象类,必须写一个子类来继承它,这个类是用来简化JDBCTemplate执行存储过程操作的。首先我们写一个实现类:package com.huaye.framework.dao;import java.sql.Types;import java.util.HashMap;import java.util.Map;import org.springframework.jdbc.core.RowMapper;import org.springframework.jdbc.core.SqlOutParameter;import org.springframework.jdbc.core.SqlParameter;import org.springframework.jdbc.core.SqlReturnResultSet;import org.springframework.jdbc.object.StoredProcedure;/*** Name:StoredProcedureTemplate User: HP Date: 2007-7-21 Time: 7:40:01* Description:*/public class StoredProcedureTemplate extends StoredProcedure {private HashMap<String, Object> map = new HashMap<String, Object>();public StoredProcedureTemplate() {super();}public HashMap getMap(){return this.map;}public void setValue(String key, Object obj) {map.put(key, obj);}public Map execute() {if (this.getSql() == null || this.getSql().equals(""))return null;this.compile();return execute(map);}public void setVarcharParam(String param) {this.declareParameter(new SqlParameter(param, Types.VARCHAR));}public void setDoubleParam(String param) {this.declareParameter(new SqlParameter(param, Types.DOUBLE));}public void setIntegerParam(String param) {this.declareParameter(new SqlParameter(param, Types.INTEGER));}public void setVarcharOutParam(String param) {this.declareParameter(new SqlOutParameter(param, Types.VARCHAR));}public void setDoubleOutParam(String param) {this.declareParameter(new SqlOutParameter(param, Types.DOUBLE));}public void setIntegerOutParam(String param) {this.declareParameter(new SqlOutParameter(param, Types.INTEGER));}public void setInParam(String param,int valueType){this.declareParameter(new SqlParameter(param, valueType));}public void setOutParam(String param,int valueType){this.declareParameter(new SqlOutParameter(param, valueType));}public void setReturnParam(String param, RowMapper rowMapper) {this.declareParameter(new SqlReturnResultSet(param,rowMapper));}}写一个测试:public void test2() {ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-base.xml");JdbcTemplate jdbc = (JdbcTemplate) context.getBean("jdbcTemplate");StoredProcedureTemplate template = new StoredProcedureTemplate();template.setJdbcTemplate(jdbc);template.setSql("testproc");//注意有返回结果集的时候,第一个参数必须设置为返回结果集参数,不然会报错。template.setReturnParam("rows", new FirstReportRowMapper());template.setIntegerParam("@parama");template.setValue("@parama", 9);Map map = template.execute();Object o = map.get("rows");List<FirstReportVO> list = (List<FirstReportVO>)o;for (FirstReportVO vo : list) {System.out.println(vo.getSortID()+","+vo.getSortName());}}唯一要注意的地方就是测试里备注的地方,我测试了好久才发现,郁闷的一塌糊涂,老是莫名其妙的错,原来将参数互换一下位置就OK了,比如你把template.setIntegerParam("@parama");写在前面然后再写template.setReturnParam("rows", new FirstReportRowMapper());的话,就会报空指针错误。这个“rows”可以随便取名字,不过下面map.get("rows")要和你取的名字一致,因为StoredProcedureTemplate会将结果集以这个名字保存在map中返回。还有要注意的就是设置sqlparamter的顺序要和存储过程中参数的顺序要一致,不然也会报错. 原文:http://blog.csdn.net/xiao_jun_0820/article/details/7268219 http://forum.spring.io/forum/spring-projects/data/13984-multiple-calls-to-storedprocedure-using-same-connection
mysql测试spring事务是否生效
同时对三张表进行插入操作,事务保证完整性。下面进行简单测试:1. 锁定表锁定用户表LOCK TABLES user WRITE;查看表是否锁定:show OPEN TABLES where In_use > 0;显示被锁定的表。2. 验证在同一session下是否可以插入操作insert into user(userId,ip,type,terminal) values(10002,'127.0.0.1',7,'pc');可以操作成功。3. 启动spring应用,执行插入操作,不能成功。4,解锁UNLOCK TABLES;查看被锁定的表:show OPEN TABLES where In_use > 0;上述表不在锁定中。5. 调用spring应用,执行插入操作,操作可以成功。所以,应用spring的@transactional进行事务管理是ok的。附录: SHOW OPEN TABLES Syntax:https://dev.mysql.com/doc/refman/5.0/en/show-open-tables.htmlSHOW OPEN TABLES [{FROM | IN} db_name][LIKE 'pattern' | WHERE expr]SHOW OPEN TABLES lists the non-TEMPORARY tables that are currently open in the table cache. SeeSection 8.4.3.1, “How MySQL Opens and Closes Tables”. The WHERE clause can be given to select rows using more general conditions, as discussed in Section 19.18, “Extensions to SHOW Statements”.The FROM and LIKE clauses may be used as of MySQL 5.0.12. The LIKE clause, if present, indicates which table names to match. The FROM clause, if present, restricts the tables shown to those present in the db_namedatabase.SHOW OPEN TABLES output has the following columns:DatabaseThe database containing the table.TableThe table name.In_useThe number of table locks or lock requests there are for the table. For example, if one client acquires a lock for a table using LOCK TABLE t1 WRITE, In_use will be 1. If another client issues LOCK TABLE t1 WRITE while the table remains locked, the client will block waiting for the lock, but the lock request causes In_use to be 2. If the count is zero, the table is open but not currently being used. In_use is also increased by the HANDLER ... OPENstatement and decreased by HANDLER ... CLOSE.Name_lockedWhether the table name is locked. Name locking is used for operations such as dropping or renaming tables.
改造继续之eclipse集成tomcat开发spring mvc项目配置一览
 在上一篇的环境配置中,你还只能基于maven开发一个javase的项目,如果要开发一个web项目,还得配置一下tomcat和spring mvc,集成一览表如下。 一:Tomcat安装      在.net web开发中,微软再一次向你展示了一站式马赛克配置,你只需要轻轻一点按钮,发射。。。一个带有bootstrap框架的页面就呈现在你的面前,在java中就没有这么好的事情了,基本都是高清无码。 1. 下载地址     http://mirrors.hust.edu.cn/apache/tomcat/tomcat-8/v8.5.20/bin/apache-tomcat-8.5.20.zip,目前tomcat最新版是 9.0 ,这里我选择的8.5版本。   2. 配置环境变量   <1>  在windows平台下,一般会默认安装在:C:Program FilesApache Software FoundationTomcat 8.5 目录下,在你的windows服务列表中会有一个Apache Tomcat 8.5 Tomcat8服务项,这样就算安装完成了。   <2> centos平台下,因为项目需要部署在linux上,wget url,解压,最后执行startup.sh。[root@localhost myapp]# cd tomcat[root@localhost tomcat]# lsbin conf lib LICENSE logs NOTICE RELEASE-NOTES RUNNING.txt temp webapps work[root@localhost tomcat]# cd bin[root@localhost bin]# lsbootstrap.jar commons-daemon.jar daemon.sh setclasspath.sh startup.sh tool-wrapper.shcatalina.bat commons-daemon-native.tar.gz digest.bat shutdown.bat tomcat-juli.jar version.batcatalina.sh configtest.bat digest.sh shutdown.sh tomcat-native.tar.gz version.shcatalina-tasks.xml configtest.sh setclasspath.bat startup.bat tool-wrapper.bat[root@localhost bin]# ./startup.shUsing CATALINA_BASE: /usr/myapp/tomcatUsing CATALINA_HOME: /usr/myapp/tomcatUsing CATALINA_TMPDIR: /usr/myapp/tomcat/tempUsing JRE_HOME: /usr/mysoft/java/jdk1.8Using CLASSPATH: /usr/myapp/tomcat/bin/bootstrap.jar:/usr/myapp/tomcat/bin/tomcat-juli.jarTomcat started.[root@localhost bin]#          从上面可以看到,tomcat已经启动了,对了,汤姆猫的默认端口是8080,可以通过netstat -tln 验证一下,最后检查一下是否启动正常。1 [root@localhost bin]# netstat -tln2 Active Internet connections (only servers)3 Proto Recv-Q Send-Q Local Address Foreign Address State4 tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN5 tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN6 tcp 0 0 192.168.122.1:53 0.0.0.0:* LISTEN7 tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN8 tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN9 tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN10 tcp6 0 0 :::8009 :::* LISTEN11 tcp6 0 0 :::111 :::* LISTEN12 tcp6 0 0 :::8080 :::* LISTEN13 tcp6 0 0 :::22 :::* LISTEN14 tcp6 0 0 ::1:631 :::* LISTEN15 tcp6 0 0 ::1:25 :::* LISTEN16 tcp6 0 0 127.0.0.1:8005 :::* LISTEN二: eclipse 和 tomcat 的集成        两者集成起来,相对还是比较简单的,一般来说做下面三件事情就基本可以搞定了。1.  在eclipse 的 windows -> references -> server  ->  enveriment runtime 中先指定你的tomcat版本,比如下图中的    apache tomcat 8.5版本,点击‘next’后指定一下tomcat的安装路径,指定你的jre运行版本即可。    好了,接下来你可以新建一个 maven project -> 选择模式为:’‘maven-archetype-webapp’ ,然后填写好公司的域名和项目名称,最后就完成一个web项目的创建,详细如下图: 2.  右击web工程 -> Properties -> Java Build Path -> Libraries-> Add Library -> Server Runtime -> 添加tomcat -> 切到order and export 勾选tomcat。  当你终于创建好web工程之后,你会发现项目有‘错误信息’:The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path。      这是因为你的web工程还没有感知到jsp容器tomcat,这个时候你需要在 右击Web工程,在Properties面板中的Java Build Path添加tomcat的library。详细步骤看一下标题即可,当一切做完之后,就能解决这个问题了。 3. 添加jstl模板引擎      这个是可选项,如果你创建的 spring mvc工程运行的时候如果报错说缺少jstl模板,那么在pom.xml引用一下即可。1 <!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->2 <dependency>3 <groupId>javax.servlet</groupId>4 <artifactId>jstl</artifactId>5 <version>1.1.2</version>6 </dependency>  三:安装spring mvc        到目前为止,tomcat和eclipse的集成算是告一段落了,接下来要做的就是安装配置spring mvc。大家可以在maven仓库去拉一下即可,像spring-core,spring-aop 这些必备依赖jar包会全部给你安装完毕。 1. web.xml的配置    在src-> main -> webapp -> WEB-INF 下有一个web.xml文件,这个就相当于.net 中的web.config,在asp.net mvc 中最后是通过mvchandler进行了请求接管,这种模式在spring mvc中同样适用,比如:接管的Servlet是DispatcherServlet,web.xml的详细配置如下:1 <?xml version="1.0" encoding="UTF-8"?>2 <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">5 <display-name>Archetype Created Web Application</display-name>67 <!-- url请求拦截器 -->8 <servlet>9 <servlet-name>spring</servlet-name>10 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>11 </servlet>1213 <servlet-mapping>14 <servlet-name>spring</servlet-name>15 <url-pattern>/</url-pattern>16 </servlet-mapping>1718 <!-- 字符集过滤器 -->19 <
Spring boot Value注入 未整理 待完善
功能 @ConfigurationProperties @Value注入方式 批量注入配置文件中的属性 一个个指定松散绑定(松散语法) 支持 不支持SpEL 不支持 支持JSR303数据校验(@Validated) 支持 不支持复杂类型封装 支持 不支持应用场景:配置文件yml还是properties它们都能获取到值若只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value若专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties@ConfigurationProperties该注解可以将配置文件中配置的每一个属性的值,通过set方法映射到被注释的组件中(因此不可以缺少setter方法)/*** 批量注入、松散绑定、数据校验、复杂类型封装*/@Component@ConfigurationProperties(prefix = "person1") //批量注入@Validated //数据校验public class Person {@Emailprivate String lastName;private Integer age;private Boolean boss;private Date birth;private Map<String,Object> maps;private List<Object> lists;private Dog dog;//复杂类型封装public void setLastName(String lastName) {this.lastName = lastName;}......}public class Dog {private String name;private Integer age;public void setName(String name) {this.name = name;}public void setAge(Integer age) {this.age = age;}}applicaton.ymlperson1:lastName: [email protected]#以下二种写法与上述等同(松散绑定):#last-name: [email protected]#last_name: [email protected]: 18boss: falsebirth: 2018/04/04maps: {k1: v1,k2: v2}lists:- lisi- zhaoliudog:name: 小狗age: 12@Value@Component@Validatedpublic class Person {@Email//数据校验无效@Value("${person1.lastName}")//从环境变量、配置文件中获取值private String lastName;@Value("#{11*2}")//使用SpEL表达式private Integer age;@Value("true")//直接赋值private Boolean boss;private Date birth;@Value("${person1.maps}")//不支持复杂类型,报错private Map<String,Object> maps;private List<Object> lists;private Dog dog;......}@PropertySource、@ImportResource、@Bean//指明当前类为配置类,替代之前的Spring配置文件@Configuration//加载指定的配置文件@PropertySource(value = {"classpath:person.properties"})//SpringBoot项目没有Spring的配置文件,若要想自己编写Spring配置文件加载并生效,需要使用@ImportResoure注解标注在一个配置类上@ImportResource(locations = {"classpath:application.xml"})public class MyAppConfig {@Bean //注册bean,默认是方法名作为idpublic Person person() {return new Person();}}SpringBoot给容器中添加组件的方式(推荐使用全注解的方式):配置类@Configuration标注@ImportResoure加载Spring配置文件使用@Bean给容器中添加组件3.3 配置文件属性3.3.1 随机数RandomValuePropertySource:配置文件中可以使用随机数${random.value}:随机数字与字母组合的字符串${random.uuid}:随机uuid${random.long}:随机long值${random.int}:随机int值${random.int(value)}:0~value之间的随机数${random.int(value,max)}:value~max之间的随机数这些随机数可以使用在配置文件或@Value注解中person1:lastName: ${random.value}age: ${random.int(0,100)}#如果birthday不存在使用默认值2014/1/1birth: ${birthday:2014/1/1}3.3.2 profileProfile是Spring对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速切换环境,多环境有以下二种方式:多个profile文件:格式是application-{profile}.properties/yml,例如application-dev.properties,SpringBoot默认加载application.propeties/yml配置文件,可以在该文件中激活不同的profile:spring.profiles.active=dev多个profile文档块模式application.yml:#激活指定环境spring:profiles:active: dev---#开发环境spring:profiles: devserver:port: 8090---#生产环境spring:profiles: productserver:port: 9090---#默认环境spring:profiles: defaultserver:port: 8080配置环境也可以在外部文件、命令行或jvm参数中指定,如使用命令行(- -spring.profiles.active=dev)或jvm参数(-Dspring.profiles.active=dev)来激活指定的profile。3.3.3 配置文件加载顺序springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件file:./config/file:./classpath:/config/classpath:/上述优先级由高到低,高优先级的配置会覆盖低优先级的配置,同时配置互补。项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来可以使用spring.config.location指定外部的配置文件位置:java -jar spring-boot-0.0.1-SNAPSHOT.jar --spring.config.location=D:/application.properties1springboot的所有配置都可以在命令行上指定,多个配置使用空格分开,spring配置的优先级由高到低:命令行参数来自java:comp/env的JNDI属性Java系统属性(System.getProperties())操作系统环境变量RandomValuePropertySource配置的random.*属性值jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件jar包外部的application.properties或application.yml(不带spring.profile)配置文件jar包内部的application.properties或application.yml(不带spring.profile)配置文件@Configuration注解类上的@PropertySource通过SpringApplication.setDefaultProperties指定的默认属性4. 自动配置原理SpringBoot启动的时候加载主配置类(@SpringBootApplication),开启了自动配置功能@EnableAutoConfiguration@EnableAutoConfiguration的组合注解@AutoConfigurationPackage将该配置类包及子包路径下的所有组件扫描进Spring容器中@EnableAutoConfiguration的组合注解 @Import(AutoConfigurationImportSelector.class)注解导入AutoConfigurationImportSelector类,AutoConfigurationImportSelector类实现了DeferredImportSelector接口重写了selectImports方法,SpringFactoriesLoader.loadFactoryNames()扫描所有jar包类路径下 META-INF/spring.factories把扫描到的这些文件的内容包装成List对象,selectImports将这些自动配置类注册到容器中4.1 自动配置类案例分析以HttpEncodingAutoConfiguration 这个自动配置类为例:@Configuration //标注该类为配置类@EnableConfigurationProperties(HttpEncodingProperties.class) //启动指定类的ConfigurationProperties功能,将配置文件中对应的值和HttpEncodingProperties绑定起来,并把HttpEncodingProperties加入到ioc容器中@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) //只有基于Servlet的web环境配置类才会生效@ConditionalOnClass(CharacterEncodingFilter.class) //判断当前项目有没有CharacterEncodingFilter这个类,CharacterEncodingFilter是SpringMVC中进行乱码解决的过滤器@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)//判断配置文件中是否存在spring.http.encoding.enabled这个配置,如果缺失则默认为truepublic class HttpEncodingAutoConfiguration {private final HttpEncodingProperties properties;//将与SpringBoot配置文件映射过的HttpEncodingProperties注入public HttpEncodingAutoConfiguration(
Spring Boot 2.1.1.RELEASE 多数据源配置与使用
有时候,一个系统的功能,需要两个或两个以上的数据库,在Spring Boot 中要如何配置? How to?#primaryprimary.spring.datasource.jdbc-url=jdbc:mysql://localhost:3316/test1primary.spring.datasource.username=rootprimary.spring.datasource.password=rootprimary.spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver#secondarysecondary.spring.datasource.jdbc-url=jdbc:mysql://localhost:3316/test2secondary.spring.datasource.username=rootsecondary.spring.datasource.password=rootsecondary.spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 新建一个类DataSourceConfig@Configurationpublic class DataSourceConfig {@Bean(name = "primaryDataSource")@Qualifier("primaryDataSource")@Primary@ConfigurationProperties(prefix="primary.spring.datasource")public DataSource primaryDataSource() {return DataSourceBuilder.create().build();}@Bean(name = "secondaryDataSource")@Qualifier("secondaryDataSource")@ConfigurationProperties(prefix="secondary.spring.datasource")public DataSource secondaryDataSource() {return DataSourceBuilder.create().build();}@Bean(name = "primaryJdbcTemplate")public JdbcTemplate primaryJdbcTemplate(@Qualifier("primaryDataSource") DataSource dataSource) {return new JdbcTemplate(dataSource);}@Bean(name = "secondaryJdbcTemplate")public JdbcTemplate secondaryJdbcTemplate(@Qualifier("secondaryDataSource") DataSource dataSource) {return new JdbcTemplate(dataSource);}}  之后在运用的时候,注意@Qualifier@RunWith(SpringJUnit4ClassRunner.class)@SpringBootTestpublic class ApplicationTests {@Autowired@Qualifier("primaryJdbcTemplate")protected JdbcTemplate jdbcTemplate1;@Autowired@Qualifier("secondaryJdbcTemplate")protected JdbcTemplate jdbcTemplate2;@Beforepublic void setUp() {jdbcTemplate1.update("DELETE FROM USER ");jdbcTemplate2.update("DELETE FROM USER ");}@Testpublic void test() throws Exception {// 往第一个数据源中插入两条数据jdbcTemplate1.update("insert into user(id,name,age) values(?, ?, ?)", 1, "aaa", 20);jdbcTemplate1.update("insert into user(id,name,age) values(?, ?, ?)", 2, "bbb", 30);}}  java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.修改application.xml中的数据库的url, 用spring.datasource.jdbc-url,而不是通常使用的spring.datasource.url。  之前用的如下截图,总是报错 
Spring3 整合Hibernate3.5 动态切换SessionFactory 切换数据库方言
一、缘由上一篇文章Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法介绍到了怎么样在Sping、MyBatis、Hibernate整合的应用中动态切换DataSource数据源的方法,但最终遗留下一个问题:不能切换数据库方言。数据库方言可能在当前应用的架构中意义不是很大,但是如果单纯用MyBatis或Hibernate做数据库持久化操作,还是要处理这一问题。 那么下面将介绍怎么样动态切换SessionFactory,为什么要切换SessionFactory?因为这里切换SessionFactory就可以实现多数据源和多个SessionFactory,每个SessionFactory有自己独立的数据库配置和SessionFactory的相关配置。我们的数据库方言就配置在SessionFactory这里,所以实现了切换SessionFactory也就实现了切换数据库方言的问题。这个主要是针对Hibernate来操作的,而MyBatis则需要动态切换SqlSessionFactory才行。 二、实现代码1、定义全局切换SessionFactory的工具package com.hoo.framework.spring.support; /*** <b>function:</b> 多数据源* @author hoojo* @createDate 2013-9-27 上午11:36:57* @file CustomerContextHolder.java* @package com.hoo.framework.spring.support* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email [email protected]* @version 1.0*/public abstract class CustomerContextHolder { public final static String SESSION_FACTORY_MYSQL = "mysql";public final static String SESSION_FACTORY_ORACLE = "oracle";private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();public static void setCustomerType(String customerType) {contextHolder.set(customerType);}public static String getCustomerType() {return contextHolder.get();}public static void clearCustomerType() {contextHolder.remove();}}同样上面的静态变量和前一文章中介绍的一致,它需要和下面配置文件中的SessionFactory的key对应。2、实现自己的SessionFactory定义好接口package com.hoo.framework.spring.support.core; import org.hibernate.SessionFactory; /*** <b>function:</b> 动态SessionFactory接口* @author hoojo* @createDate 2013-10-12 下午03:29:52* @file DynamicSessionFactory.java* @package com.hoo.framework.spring.support.core* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email [email protected]* @version 1.0*/public interface DynamicSessionFactory extends SessionFactory {public SessionFactory getHibernateSessionFactory();} 实现接口package com.hoo.framework.spring.support.core; import java.io.Serializable;import java.sql.Connection;import java.util.Map;import java.util.Set;import javax.naming.NamingException;import javax.naming.Reference;import org.hibernate.Cache;import org.hibernate.HibernateException;import org.hibernate.Interceptor;import org.hibernate.SessionFactory;import org.hibernate.StatelessSession;import org.hibernate.TypeHelper;import org.hibernate.classic.Session;import org.hibernate.engine.FilterDefinition;import org.hibernate.metadata.ClassMetadata;import org.hibernate.metadata.CollectionMetadata;import org.hibernate.stat.Statistics;import com.hoo.framework.spring.support.CustomerContextHolder; /*** <b>function:</b> 动态数据源实现* @author hoojo* @createDate 2013-10-12 下午03:31:31* @file DynamicSessionFactoryImpl.java* @package com.hoo.framework.spring.support.core* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email [email protected]* @version 1.0*/@SuppressWarnings({ "unchecked", "deprecation" })public class DynamicSessionFactoryImpl implements DynamicSessionFactory { private static final long serialVersionUID = 5384069312247414885L;private Map<Object, SessionFactory> targetSessionFactorys;private SessionFactory defaultTargetSessionFactory;/*** @see com.hoo.framework.spring.support.core.DynamicSessionFactory#getHibernateSessionFactory()* <b>function:</b> 重写这个方法,这里最关键* @author hoojo* @createDate 2013-10-18 上午10:45:25*/@Overridepublic SessionFactory getHibernateSessionFactory() {SessionFactory targetSessionFactory = targetSessionFactorys.get(CustomerContextHolder.getCustomerType());if (targetSessionFactory != null) {return targetSessionFactory;} else if (defaultTargetSessionFactory != null) {return defaultTargetSessionFactory;}return null;}  @Overridepublic void close() throws HibernateException {this.getHibernateSessionFactory().close();} @Overridepublic boolean containsFetchProfileDefinition(String s) {return this.getHibernateSessionFactory().containsFetchProfileDefinition(s);} @Overridepublic void evict(Class clazz) throws HibernateException {this.getHibernateSessionFactory().evict(clazz);} @Overridepublic void evict(Class clazz, Serializable serializable) throws HibernateException {this.getHibernateSessionFactory().evict(clazz, serializable);} @Overridepublic void evictCollection(String s) throws HibernateException {this.getHibernateSessionFactory().evictCollection(s);} @Overridepublic void evictCollection(String s, Serializable serializable) throws HibernateException {this.getHibernateSessionFactory().evictCollection(s, serializable);} @Overridepublic void evictEntity(String entity) throws HibernateException {this.getHibernateSessionFactory().evictEntity(entity);} @Overridepublic void evictEntity(String entity, Serializable serializable) throws HibernateException {this.getHibernateSessionFactory().evictEntity(entity, serializable);} @Overridepublic void evictQueries() throws HibernateException {this.getHibernateSessionFactory().evictQueries();} @Overridepublic void evictQueries(String queries) throws HibernateException {this.getHibernateSessionFactory().evictQueries(queries);} @Overridepublic Map<String, ClassMetadata> getAllClassMetadata() {return this.ge
Struts2、Spring、Hibernate整合ExtJS
SSHExtTree百度文库提供下载:http://wenku.baidu.com/view/485e4d36f111f18583d05aed.html开标题就知道是Struts、Spring、Hibernate、ExtJS的Tree实例文档,其中包括Filter功能的checkNodeTree、comboBoxCheckNodeTree、comboBoxTree这三种扩展的tree。不错,如果你不了解Struts、Spring、Hibernate、ExtJS,也不要紧。跟着我做,做出这个例子应该木有问题。生产环境:System:WindowsWebBrowser:IE6+、Firefox3+JavaEE Server:tomcat5.0.2.8、tomcat6IDE:eclipse、MyEclipse6.1+Database:Ms SQLServer2000、2005开发依赖库版本:ExtJS:lib 2.1Struts: 2.1.4Hibernate:3.2Spring:2.0Email:[email protected]:http://blog.csdn.net/IBM_hoojohttp://hoojo.cnblogs.com/如果你具备上面的环境后,下面就可以跟着我一步步的完成这个SSHExtTree的示例。1、 添加Hibernate支持,这里我用MyEclipse的工具直接添加jar包和链接数据库的配置文件,这一步如果你熟悉可以跳过。A、 首先你选择你的MyEclipse中的MyEclipse Database Explorer这个视图B、 进入视图后,点击newC、 进入new视图后,你就可以添加你的DB的链接数据库的种类、端口、地址、数据库名称我上面选择的是MsSQL2005Connect URL:jdbc:jtds:sqlserver://localhost:1433/jc2009_gdszzjc2009_gdszz是数据库的名称,是建科院的数据库用户名、密码就是你链接数据库的密码注意的是你还要添加你的链接数据库的driver,我这里用jdts驱动上面的完成后,你可以点击Test Driver就可以测试你的链接对不对。如果对的话就会出现上面的successfully!D、 继续Next你可以选择第三个单选按钮,就是下面的1。然后点击Add按钮选择你的数据库。这样后面链接数据库的时候就不会出现其他的数据库了;点击FinishE、 下面开始链接数据库上面的sql_2005就是我们刚才建的数据库链接了。右键sql_2005,然后点击Open connection,就可以链接到你的数据库了F、 然后展开数据库,选择dbo展开,选择table。你就看到你的数据库中的表了至此数据链接创建成功,下面我们添加Hibernate的支持。G、 下面切换到MyEclipse JavaEE视图,如果你喜欢Java视图也可以。这里用MyEclipse JavaEE视图。然后右键点击你建好的SSHExtTree这个WebProject选择MyEclipse,选择Add HibernateH、进入添加Hibernate支持的视图后,选择Hibernate3.2;然后记得选择添加jar包到你的工程lib中I、 继续Next,选择New。我喜欢单独的Hibernate配置文件,如果你不喜欢你可以在Spring中添加配置。J、 点击Next,进入选择数据库链接的视图,当然是选择我们刚才创建的sql_2005这个视图了。记得选择你的数据库方言,数据库方言对于不同的数据库是不同的。K、 点击Next你就可以看到创建SessionFactory,我们用Spring就不要这个了。点击Finish;就可以看到配置文件hibernate.cfg.xml我们还得添加2条:<property name="format_sql">true</property><property name="show_sql">true</property>这个在调试程序的时候有用,可以格式化输入sql语句综上所述,上面的主要完成的就是添加Hibernate的jar包和数据库链接配置文件。你也可以手动添加Hibernate的数据库配置文件和jar包。那样就不需要上面的步骤。2、 下面我们添加Spring的支持,请跟着我做A、 右键SSHExtTree项目,选择MyEclipse然后选择Add SpringB、 点击后你可以看到Spring的支持了,Spring选择2.0的版本。然后就是选择你要的jar包。C、 我们选择完后,继续下一步NextD、 继续下一步Next上面的SessionFactory Bean id就是你的applicationContext.xml这个文件中的SessionFactory的bean的id。在后面我们需要为其他使用Hibernate模版的文件件注入sessionFactory。至此Spring的支持添加完毕,现在我们需要配置Spring的Aop管理我们的事务。E、 首先修改applicationContext.xml为applicationContext-common.xml,然后我们添加aop、tx的命名空间。你也可以手动添加,如果你不记得Sping的aop、tx的命名空间,请跟着我做。F、 右键我们的Project,选择New然后选择xml basic templateG、 进入页面后,直接next。选择第二项H、进入后,看下面的选择schema文件先选择aop2.0,点击nextI、 进入视图后你可以看到选择p,点击Edit 也就是1处。然后将改成aop。然后点击AddJ、 进入视图后,你就可以继续选择tx这个命名空间的schameK、 点击ok后,你可以选择刚才的tx。然后点击edit。为它添加xsd将ns name复制粘贴到Location Hint中,然后在后面添加/spring-tx-2.0.xsd完了后,点击Finish。然后copy里面的内容到刚才的applicationContext-common.xml中,内容如下:xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.0.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.0.xsd ">刚才这么多步骤就是要这些文件的内容最后common文件的的头部就是这样的内容:代码<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.0.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.0.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.0.xsd">L、 下面配置事务的传播特性Hibernate的事务管理机制代码<!-- 配置事务管理器,并注入sessionFactory --><bean name="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"><property name="sessionFactory" ref="sessionFactory"/></bean>上面的配置注入了sessionFactory,property中的name是HibernateTransactionManager中的setSessionFactory这个setter方法,后面的ref是上面的bean的引用。事务的传播特性代码<!-- 配置事务的传播特性 --><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="add*" propagation="REQUIRED"/><tx:method name="edit*" propagation="REQUIRED"/><tx:method name="remove*" propagation="REQUIRED"/><tx:method name="execute*" propagation="REQUIRED"/><tx:method name="*" read-only="false"/></tx:attributes></tx:advice>配置哪些方法参与事务的管理,add*代码addXXX开头的方法,后面的propagation是事务的传播特性、级别,最后的*代表所有方法,对于查询的方法read-only只读就可以了。查询不需要参与事务。配置在哪里启用事务,哪些包或类、方法参与事务管理:代码<!-- 配置哪些方法纳入事务管理 --><aop:config><aop:pointcut id="filterMethod" expression="execution(* com.hhh.biz.impl.*.*(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="filterMethod"/></aop:config>上面的pointcut称作切面,expression是表达式(关于表达式更多内容可以参考spring官方文档)execution(* com.hhh.biz.impl.*.*(..))前面的execution是固定的,括号里面的内容是第一个*代表任意返回值,com.hhh.biz.impl代表包。在这一层的包被纳入到事务的管理,impl.*代表impl下面的所有的class,impl.*.*代表class中所有方法,impl.*.*(..)代表的是方法