首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网站开发 > Web前端 >

spring架构受看设计模式和java web的框架原理

2012-12-27 
spring架构中看设计模式和java web的框架原理1. 一个类的私有构造函数表明这个类的实例只有本类方法中生成

spring架构中看设计模式和java web的框架原理

1. 一个类的私有构造函数表明这个类的实例只有本类方法中生成,外部类只有通过方法的调用得到该类的实例,可以在私有构造函数中完成对类成员的一些初始化的操作。

比如:request类:

private void exit(HttpServletRequest request, HttpServletResponse response)throws IOException {// 清空授权CookieSessionService.create(request, response, "auth", null).remove();// 跳转HttpServletResponse httpResponse = (HttpServletResponse) response;httpResponse.sendRedirect("/out.html");return;}

?CookieSessionService类extends SessionServiceBase<User>:

public static CookieSessionService create(HttpServletRequest request,HttpServletResponse response, String name, String domain) {CookieSessionService r = new CookieSessionService(request, response,name, domain);return r;}

?这个类有个私有构造函数和对私有成员的初始化操作:

private static final char SPLIT_CHAR = ':';private static final String ENCODING = "UTF-8";private tools.web.CookieUtil cookie = null;private String name = null;private String domain = null;Map<String, Object> extra = null;private CookieSessionService(HttpServletRequest request,HttpServletResponse response, String name, String domain) {this.cookie = new CookieUtil(request, response);this.name = name;this.domain = domain;if (tools.StringUtil.isNullOrEmpty(domain)) // 2012-02-29this.domain = (String)Global.getSettings().getParams().get("COOKIE_DOMAIN");// 2012-05-23extra = tools.MapUtil.create();extra.put("ip", tools.web.ServletUtil.getIpAddr(request));}

??? 这里可能对上面的Global.getSettings().getParams().get("COOKIE_DOMAIN")这里面的值是在什么时候放进去的,也许感到疑惑,下面就会慢慢解开其中的设计思想。

??? 这里Global的功用bean类的设计也是很不错的,设计到很多java常用的面相对象原理:它里面采用静态方法和静态成员,作为一个工程公用的参数存储配置类spring bean ,它通过init-method="init"的application.xml参数配置,使得在jboss或者comcat服务器启动的时候就会调用Global的init()方法,完成一些成员变量的初始化工作:

?

applicationContext.xml:

<bean id="global" init-method="init"><property name="keyValueDao"><ref bean="keyValueDao"/></property></bean>        <bean id="keyValueDao"name="code">private static final Log LOGGER = LogFactory.getLog("system");// 日志private static final String TYPE_CODE = "50";// KV系统类别public static final String LOCAL_IP = tools.StringUtil.getLocalIp();// 本机IPprivate static final String SETTINGS_KEY = "globalSettings";// KV配置名称/** * 用静态属性,后续还会用到该dao */private static KeyValueDao keyValueDao = null;// KV daoprivate static AppSettings settings;// 全局设置private static JobScheduler jobScheduler = null;// job调度器/** * 哈勃日志,目前供pfp项目使用 */private static final Log MONITOR_LOGGER = LogFactory.getLog("monitor");/** * 系统计数器 */private static MultiKeyedCounter counter = createCounters();private static Context<User> contextStore = new Context<User>();

??? 3.根据上面的xml中的bean的配置,Global类int()方法在服务器启动时就要调用,完成一些配置的初始化动作,已便于

其他地方获取到这些必要的配置信息:

?

Global类的Init()方法:

?

public void init() {LOGGER.info("开始初始化global");// ===初始化工作// ===创建调度器工厂LOGGER.info("开始初始化jobScheduler");try {SchedulerFactory sf = new StdSchedulerFactory();Scheduler sched = sf.getScheduler();// 创建JobScheduler,设置调度器jobScheduler = new JobScheduler(sched);jobScheduler.start();// 一定要start 2012-02-14} catch (SchedulerException e) {LOGGER.error("创建JobScheduler失败", e);throw new RuntimeException(e);}// ===加载其他内容reload();}

?

?

???? 这里要谈谈调度器的设计和使用方法,这里用到了一个抽象工厂类 public interface SchedulerFactory{。。。},这里面只声明了生成对应接口对象Scheduler(public interface Scheduler{...})的工厂方法,具体的方法实现和返回的具体的Scheduler子类在抽象工厂类SchedulerFactory的实现类中完成。具体的实现机制设计到工厂方法,同步方法,单例模式等,以后可以具体分析下调度器的实现机制。这里暂且不谈。

???? 生成了具体的schedule后,通过JobScheduler的构造函数,把他注入JobScheduler中的私有成员(private Scheduler scheduler = null;)进行具体值得初始化*(或者称为注入):

?
public class JobScheduler {private static final String DEFAULT_GROUP_NAME = "group1";private static final Log LOGGER = LogFactory.getLog(JobScheduler.class);private static final AtomicInteger COUNTER = new AtomicInteger(0);private Scheduler scheduler = null;/** * 不在构造函数里体现group,因为这不是必需的  */private String groupName = DEFAULT_GROUP_NAME;public JobScheduler(Scheduler scheduler) {this.setScheduler(scheduler);}/** * 开始调度 */public void start() {// Start up the scheduler (nothing can actually run until the// scheduler has been started)if (getScheduler() == null) {throw new IllegalArgumentException("请先设置正确的Scheduler");}try {if (!getScheduler().isStarted())getScheduler().start();} catch (SchedulerException e) {LOGGER.error(e);}}/** * 增加一个单次执行的任务,要指定jobData和job名称,名称不能重复,参数里名称可以为空 */public boolean addJob(Date startTime, Class<? extends Job> runJob,Map<String, Object> dataMap, String name) {// 处理job名称String innerJobName = getJobName(name);// 判断是否重复if (checkExists(innerJobName, getGroupName()))return false;JobDetail job = prepareJobDetail(innerJobName, getGroupName(), runJob,dataMap);// 创建triggerSimpleTriggerImpl trigger = new SimpleTriggerImpl();trigger.setKey(new TriggerKey(getTriggerName(innerJobName)));trigger.setStartTime(startTime);trigger.setGroup(getGroupName());try {getScheduler().scheduleJob(job, trigger);return true;} catch (SchedulerException e) {LOGGER.error(e);}// 如果job的名字重复,则引发Quartz.ObjectAlreadyExistsException异常return false;}}

???? 这里有一种思想,可以看作为类的包装管理思想,把具体实现操作的类隐秘封装起来,比如这里虽然暴露给外面的是JobScheduler类,调用的貌似都是他提供的方法,完成start():启动调度器和addJob():添加调度任务的作用,但是实际上是他里面的成员对象scheduler完成这些工作,在外面主要实例化出具体的scheduler子类对象,然后以set方式或者JobScheduler构造函数方式实例它,然后接管完成包装它的JobScheduler类的方法任务。打个比方来说这种关系就是,

成员变量scheduler相当是程序员,是真正完成工程实现的劳动者,而外面的JobScheduler相当于产品经理(PD),它只是需求方的接口人,需求方只要告诉它完成那些功能(也就是调用JobScheduler中的方法),具体方法的实现PD会安排好程序员来完成。

??? 下面谈谈在初始化方法init()中的reload()方法:

public static void reload() {// ===这里先不清理jobScheduler里的资源,因为下面addjob时会判重的// ===加载全局设置,从数据库里加载KeyValue kv = keyValueDao.getOne(TYPE_CODE, SETTINGS_KEY);if (kv == null) {LOGGER.error("globalSettings并不存在,请配置");return; // 直接退出}settings = tools.Json.toObject(kv.getValue(), AppSettings.class);LOGGER.info("globalSettings获取成功");// 2012-02-15if (settings.getParams() == null) {LOGGER.error("未配置params");settings.setParams(new HashMap<String, Object>());}// ===计数器持久化的job 2012-02-16jobScheduler.addJob(new Date(), SaveCounterJob.class, null,"COUNTER_JOB", tools.Convert.toInt(getSettings().getParams().get("counter_saving_interval"), 60), -1);// 2012-06-21 当机器突然被提升为master,id生成器也要重新初始化;之前不是master时的生成器可能不能产生区间tools.generator.GeneratorFacade.removeAll();LOGGER.info("global初始化结束");}
?

???? 在reload方法中,通过KeyValue kv = keyValueDao.getOne(TYPE_CODE, SETTINGS_KEY);把存储在数据库中的参数值读出来,其中KeyValue 是一个数据库层的DO类,在数据库表中的存储结构是:

'5'#id#, 'globalSettings'#key_#, '{    "params": {       "WHITE_URL_LIST":",/out.html,/403.html,/request.do,/id.do,","ADVANCED_PERMISSION_URL_LIST":",/keyValue.html,/settings.html,","COOKIE_DOMAIN":".riskm.admin.taobao.org","TITLE":"风险信息管理平台riskm","COPYRIGHT":"taobao riskm 2012"    },    "comment": ""}'#value#, '50'#typecode#, NULL#sortnumber#, '2012-05-08 18:43:53'#createtime#, '2012-08-24 16:16:44'#lastupdatetime#, '0'#status#, NULL#comment#
?

然后通过: tools.Json.toObject(kv.getValue(), AppSettings.class);把json数据转化成结构化的数据:AppSettings,这个类的结构是:

public class AppSettings {/** * 一些url,比如默认的算法的地址,不过可以在client设置里修改。【key=url名称,value=url格式】 * 只把不是特别固定的项目放到params里,其他的必须用到的,以属性方式提供 */private Map<String, Object> params;/** * 备注信息 */private String comment;。。。。}

?在上面的jobScheduler.addJob(...)中,SaveCounterJob.class中定义了具体的任务或者操作,它必须implements Job接口:

?

public class SaveCounterJob implements Job {private static final Log LOGGER = LogFactory.getLog(SaveCounterJob.class);// 日志// private static final CounterDaoImpl DAO = new CounterDaoImpl();@Overridepublic void execute(JobExecutionContext context)throws JobExecutionException {LOGGER.debug(this.getClass().getName() + " is running");// 获取当前的计数列表,并清零原来的计数器Map<String, Long> m = Global.getCounter().setAll(0);if (LOGGER.isDebugEnabled())LOGGER.debug("保存计数器列表,内容是:" + m);// 保存到mysql数据库Set<Entry<String, Long>> set = m.entrySet();List<Counter> list = new ArrayList<Counter>(set.size());int timestamp = getTimestamp();String tempKey = null;for (Entry<String, Long> item : set) {long i = item.getValue();if (i > 0) {tempKey = item.getKey();Counter entry = new Counter();entry.setKey(tempKey);entry.setValue(item.getValue());entry.setTimestamp(timestamp);// 2012-05-24 总和计数,重置timestampif (tempKey.startsWith("sum."))entry.setTimestamp(1325347200);list.add(entry);}}try {long startMS = System.currentTimeMillis();int i = SpringBeanUtil.getBean(CounterDao.class, "counterDao").save(list);LOGGER.warn("保存计数器成功,共保存:" + i + ",耗时:"+ (System.currentTimeMillis() - startMS));} catch (Exception e) {LOGGER.error(e);}}private int getTimestamp() {int i = (int) (System.currentTimeMillis() / 1000);int m = i % (60 * 2); // 对齐2分钟// System.out.println(m);// System.out.println(i - m);return i - m;}}

?? 这里通过程序SpringBeanUtil工具类,动态从sprin容器中获取具体的bean,这种方式在以后可以学习利用:

? 首先,是在application.xml中配置了counterDao的bean:

<!--计数器dao--><bean id="counterDao" name="code">   <!-- spring bean -->   <context-param>        <param-name>contextConfigLocation</param-name>        <param-value>          /WEB-INF/classes/bean/applicationContext.xml        </param-value>      </context-param>          <!--注册配置文件读取器-->    <listener>    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>   </listener> 

?? 通过查看ContextLoaderListener类,发现他下面有个私有成员private ContextLoader contextLoader;,就是通过它的initWebApplicationContext(...)方法完成applicationContext.xml中的bean的实例化工作。具体代码如下:

?

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {private ContextLoader contextLoader;public ContextLoaderListener() {}public ContextLoaderListener(WebApplicationContext context) {super(context);}/** * Initialize the root web application context. */public void contextInitialized(ServletContextEvent event) {this.contextLoader = createContextLoader();if (this.contextLoader == null) {this.contextLoader = this;}this.contextLoader.initWebApplicationContext(event.getServletContext());         }?@Deprecatedprotected ContextLoader createContextLoader() {return null;}。。。}
?
public class ContextLoader {。。。public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {throw new IllegalStateException("Cannot initialize context because there is already a root application context present - " +"check whether you have multiple ContextLoader* definitions in your web.xml!");}Log logger = LogFactory.getLog(ContextLoader.class);servletContext.log("Initializing Spring root WebApplicationContext");if (logger.isInfoEnabled()) {logger.info("Root WebApplicationContext: initialization started");}long startTime = System.currentTimeMillis();try {// Store context in local instance variable, to guarantee that// it is available on ServletContext shutdown.if (this.context == null) {this.context = createWebApplicationContext(servletContext);}if (this.context instanceof ConfigurableWebApplicationContext) {configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context, servletContext);}servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);ClassLoader ccl = Thread.currentThread().getContextClassLoader();if (ccl == ContextLoader.class.getClassLoader()) {currentContext = this.context;}else if (ccl != null) {currentContextPerThread.put(ccl, this.context);}if (logger.isDebugEnabled()) {logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");}if (logger.isInfoEnabled()) {long elapsedTime = System.currentTimeMillis() - startTime;logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");}return this.context;}catch (RuntimeException ex) {logger.error("Context initialization failed", ex);servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);throw ex;}catch (Error err) {logger.error("Context initialization failed", err);servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);throw err;}}。。。}

?而去看工程的main.log,贼日志的第二行就有:

2012-09-14 09:30:08,718 [] DEBUG core.CollectionFactory - Creating [java.util.concurrent.ConcurrentHashMap]2012-09-14 09:30:08,750 [] INFO  [localhost].[/] - Initializing Spring root WebApplicationContext2012-09-14 09:30:08,750 [] INFO  context.ContextLoader - Root WebApplicationContext: initialization started2012-09-14 09:30:08,812 [] INFO  support.XmlWebApplicationContext - Refreshing org.springframework.web.context.support.XmlWebApplicationContext@39e02d: display name [Root WebApplicationContext]; startup date [Fri Sep 14 09:30:08 CST 2012]; root of context hierarchy2012-09-14 09:30:08,859 [] DEBUG core.CollectionFactory - Creating [java.util.concurrent.ConcurrentHashMap]
?

?

? 通过实现它继承的BeanFactory接口的getBean(...)方法完成从spring中获取具体的bean,具体代码如下:

public class SpringBeanUtil implements ApplicationContextAware {private static final Log LOGGER = LogFactory.getLog(SpringBeanUtil.class);// 日志private static ApplicationContext ctx = null;public static Object getBean(String name) {return ctx.getBean(name);}/** * 2012-03-05 byxxx */@SuppressWarnings("unchecked")public static <T> T getBean(Class<T> t, String name) {// return null;if (ctx == null) {LOGGER.error("ApplicationContext is null");return null;}return (T) (ctx.getBean(name));}@Overridepublic void setApplicationContext(ApplicationContext applicationContext)throws BeansException {ctx = applicationContext;}}

?通过以上,对spring中用到的一些好的设计思想有了更好的理解,同时对java web的spring的bean注入机制原理有了更深的认识,同时也简单的学习和分析了配置类中怎么应用任务触发器进行参数或者配置信息的初始化工作的原理,对bean的加载原理,一些数据的初始化原理有更好的认识? 对以后开发项目时多考虑一些架构思想和提高编码的重用高效作用有个很好的总结学习作用。

?

?

?

?

?

?

?

?

?

?

?

?

热点排行