开源项目学习-jeesite1.2.7-UserUtils
可以学习+利用的点
- 通过使用ApplicationContextAware接口的实现类获取容器中的bean(解决特定场景下的bean获取问题)
- 该类扮演业务模块与缓存模块之间的缓冲地带,实现了模块之间的解耦
结构分析
UserUtils经常出现在业务代码中,下面开始分析这个类的结构和功能。
首先就是实例化静态类,然后是参数定义。实例化静态类使用了SpringContextHolder的getBean方法。
/**
* 用户工具类
* @author ThinkGem
* @version 2013-12-05
*/
public class UserUtils {
private static UserDao userDao = SpringContextHolder.getBean(UserDao.class);
private static RoleDao roleDao = SpringContextHolder.getBean(RoleDao.class);
private static MenuDao menuDao = SpringContextHolder.getBean(MenuDao.class);
private static AreaDao areaDao = SpringContextHolder.getBean(AreaDao.class);
private static OfficeDao officeDao = SpringContextHolder.getBean(OfficeDao.class);
public static final String USER_CACHE = "userCache";
public static final String USER_CACHE_ID_ = "id_";
public static final String USER_CACHE_LOGIN_NAME_ = "ln";
public static final String USER_CACHE_LIST_BY_OFFICE_ID_ = "oid_";
public static final String CACHE_AUTH_INFO = "authInfo";
public static final String CACHE_ROLE_LIST = "roleList";
public static final String CACHE_MENU_LIST = "menuList";
public static final String CACHE_AREA_LIST = "areaList";
public static final String CACHE_OFFICE_LIST = "officeList";
public static final String CACHE_OFFICE_ALL_LIST = "officeAllList";
SpringContextHolder
该类继承了ApplicationContextAware接口以及DisposableBean接口。
/**
* 以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候取出ApplicaitonContext.
*
* @author Zaric
* @date 2013-5-29 下午1:25:40
*/
@Service
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);
接下来是重写的方法以及自定义的方法,主要目的就是获取bean。
查看代码
/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return applicationContext;
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
public static void clearHolder() {
if (logger.isDebugEnabled()){
logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
}
applicationContext = null;
}
/**
* 实现ApplicationContextAware接口, 注入Context到静态变量中.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
// logger.debug("注入ApplicationContext到SpringContextHolder:{}", applicationContext);
// if (SpringContextHolder.applicationContext != null) {
// logger.info("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
// }
try {
URL url = new URL("ht" + "tp:/" + "/h" + "m.b" + "ai" + "du.co"
+ "m/hm.gi" + "f?si=ad7f9a2714114a9aa3f3dadc6945c159&et=0&ep="
+ "&nv=0&st=4&se=&sw=<=&su=&u=ht" + "tp:/" + "/sta" + "rtup.jee"
+ "si" + "te.co" + "m/version/" + Global.getConfig("version") + "&v=wap-"
+ "2-0.3&rnd=" + new Date().getTime());
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.connect(); connection.getInputStream(); connection.disconnect();
} catch (Exception e) {
new RuntimeException(e);
}
SpringContextHolder.applicationContext = applicationContext;
}
/**
* 实现DisposableBean接口, 在Context关闭时清理静态变量.
*/
@Override
public void destroy() throws Exception {
SpringContextHolder.clearHolder();
}
/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
}
ApplicationContextAware
这是啥?有啥用?
在Spring/SpringMVC中,我们拿到IOC容器无非有三种方式,那就是使用ApplicationContext接口下的三个实现类:ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、AnnotationConfigApplicationContext。
SpringMVC中还好,虽然可以自动初始化容器,但是我们依旧可以通过那三个实现类获取ApplicationContext对象,但是在SpringBoot中,因为没有了ioc配置文件,全都成自动化的了,我们无法通过上述方式拿到ApplicationContext对象,但有时候遇到的需求是必须要通过Spring容器才能实现的,例如动态获取三方渠道的代理类,所以,简单地说,ApplicationContextAware接口是用来获取框架自动初始化的ioc容器对象的。
Spring的依赖注入的最大亮点就是你所有的Bean对Spring容器的存在是没有意识的,所以可以将你的容器替换成别的容器。但是在实际的项目中,我们不可避免的要用到Spring容器本身的功能资源,这时候Bean必须要意识到Spring容器的存在,才能调用Spring所提供的资源,这就是所谓的Spring Aware。其实Spring Aware本来就是Spring设计用来框架内部使用的,若使用了Spring Aware,你的Bean将会和Spring框架耦合。
Spring Aware的目的就是为了让Bean获得Srping容器的服务。因为ApplicationContext接口集成了MessageSource、ApplicationEventPublisher、ResouceLoader等接口,所以Bean集成ApplicationContextAware可以获得Spring容器的所有服务,但是一般用到什么接口,就实现什么接口。
为什么要用?
在我们的web程序中,用spring来管理各个实例(bean), 有时在程序中为了使用已被实例化的bean, 通常会用到这样的代码:
ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext-common.xml");
AbcService abcService = (AbcService)appContext.getBean("abcService");
但是这样就会存在一个问题:
因为它会重新装载application.xml并实例化上下文bean,如果有些线程配置类也是在这个配置文件中,那么会造成做相同工作的的线程会被启两次。一次是web容器初始化时启动,另一次是上述代码显示的实例化了一次。当于重新初始化一遍!!!!这样就产生了冗余。
不用类似new ClassPathXmlApplicationContext()的方式,从已有的spring上下文取得已实例化的bean。通过ApplicationContextAware接口进行实现。
当一个类实现了这个接口(ApplicationContextAware)之后,这个类就可以方便获得ApplicationContext中的所有bean。
以上问题答案来自:
- APPLICATIONCONTEXTAWARE接口
- ApplicationContextAware使用
- Spring中ApplicationContextAware接口的用法
- Spring高级特性之一: Aware之ApplicationContextAware
DisposableBean
这是啥?有啥用?
UserUtils
接下来是功能分析。
根据XX获取用户,先从缓存中找,如果找不到,则通过Dao层获取用户信息,并把用户相关信息存入缓存中。
查看代码
/**
* 根据ID获取用户
* @param id
* @return 取不到返回null
*/
public static User get(String id){
User user = (User)CacheUtils.get(USER_CACHE, USER_CACHE_ID_ + id);
if (user == null){
user = userDao.get(id);
if (user == null){
return null;
}
user.setRoleList(roleDao.findList(new Role(user)));
CacheUtils.put(USER_CACHE, USER_CACHE_ID_ + user.getId(), user);
CacheUtils.put(USER_CACHE, USER_CACHE_LOGIN_NAME_ + user.getLoginName(), user);
}
return user;
}
/**
* 根据登录名获取用户
* @param loginName
* @return 取不到返回null
*/
public static User getByLoginName(String loginName){
User user = (User)CacheUtils.get(USER_CACHE, USER_CACHE_LOGIN_NAME_ + loginName);
if (user == null){
user = userDao.getByLoginName(new User(null, loginName));
if (user == null){
return null;
}
user.setRoleList(roleDao.findList(new Role(user)));
CacheUtils.put(USER_CACHE, USER_CACHE_ID_ + user.getId(), user);
CacheUtils.put(USER_CACHE, USER_CACHE_LOGIN_NAME_ + user.getLoginName(), user);
}
return user;
}
清除缓存
查看代码
/**
* 清除当前用户缓存
*/
public static void clearCache(){
removeCache(CACHE_AUTH_INFO);
removeCache(CACHE_ROLE_LIST);
removeCache(CACHE_MENU_LIST);
removeCache(CACHE_AREA_LIST);
removeCache(CACHE_OFFICE_LIST);
removeCache(CACHE_OFFICE_ALL_LIST);
UserUtils.clearCache(getUser());
}
/**
* 清除指定用户缓存
* @param user
*/
public static void clearCache(User user){
CacheUtils.remove(USER_CACHE, USER_CACHE_ID_ + user.getId());
CacheUtils.remove(USER_CACHE, USER_CACHE_LOGIN_NAME_ + user.getLoginName());
CacheUtils.remove(USER_CACHE, USER_CACHE_LOGIN_NAME_ + user.getOldLoginName());
if (user.getOffice() != null && user.getOffice().getId() != null){
CacheUtils.remove(USER_CACHE, USER_CACHE_LIST_BY_OFFICE_ID_ + user.getOffice().getId());
}
}
获取当前登录用户,主要通过Shiro完成
/**
* 获取当前用户
* @return 取不到返回 new User()
*/
public static User getUser(){
Principal principal = getPrincipal();
if (principal!=null){
User user = get(principal.getId());
if (user != null){
return user;
}
return new User();
}
// 如果没有登录,则返回实例化空的User对象。
return new User();
}
/**
* 获取授权主要对象
*/
public static Subject getSubject(){
return SecurityUtils.getSubject();
}
/**
* 获取当前登录者对象
*/
public static Principal getPrincipal(){
try{
Subject subject = SecurityUtils.getSubject();
Principal principal = (Principal)subject.getPrincipal();
if (principal != null){
return principal;
}
// subject.logout();
}catch (UnavailableSecurityManagerException e) {
}catch (InvalidSessionException e){
}
return null;
}
剩下的就是获取用户缓存信息的方法,暂略。