中文html5网站欣赏,文章采集网站,北京建站模板厂家,广安百度推广代理商目录
什么是Spring事务为什么需要Spring事务Spring事务的实现 Spring事务的传播机制Spring事务的底层原理 EnableTransactionManagement --开启Spring管理事务Import(TransactionManagementConfigurationSelector.class) --提供两个beanAutoProxyRegistrar --启用AOP的功能EnableTransactionManagement --开启Spring管理事务Import(TransactionManagementConfigurationSelector.class) --提供两个beanAutoProxyRegistrar --启用AOP的功能创建一个bean后置处理器会拦截所有bean的创建对符合条件的bean创建代理。ProxyTransactionManagementConfiguration --添加事务事务拦截器TransactionInterceptorTransactionInterceptor --负责拦截Transaction方法的执行在方法执行之前开启spring事务方法执行完毕之后提交或者回滚事务invokeWithinTransaction --事务拦截器的入口determineTransactionManager --获取事务管理器completeTransactionAfterThrowing --异常如何处理Spring事务失效的场景 扩展多数据源的事务管理 什么是Spring事务 事务其实是一个并发控制单位是用户定义的一个操作序列这些操作要么全部完成要不全部不完成是一个不可分割的工作单位。事务有 ACID 四个特性即
原子性Atomicity事务中的所有操作或者全部完成或者全部不完成不会结束在中间某个环节。一致性Consistency在事务开始之前和事务结束以后数据库的完整性没有被破坏。隔离性Isolation多个事务之间是独立的不相互影响的。持久性Durability事务处理结束后对数据的修改就是永久的即便系统故障也不会丢失。
而我们说的 Spring 事务其实是事务在 Spring 中的实现。 为什么需要Spring事务 为了解释清楚这个问题我们举个简单的例子银行里树哥要给小黑转 1000 块钱这时候会有两个必要的操作
将树哥的账户余额减少 1000 元。将小黑的账户余额增加 1000 元。
这两个操作要么一起都完成要么都不完成。如果其中某个成功另外一个失败那么就会出现严重的问题。而我们要保证这个操作的原子性就必须通过 Spring 事务来完成这就是 Spring 事务存在的原因。
如果你深入了解过 MySQL 事务那么你应该知道MySQL 默认情况下对于所有的单条语句都作为一个单独的事务来执行。我们要使用 MySQL 事务的时候可以通过手动提交事务来控制事务范围。Spring 事务的本质其实就是通过 Spring AOP 切面技术在合适的地方开启事务接着在合适的地方提交事务或回滚事务从而实现了业务编程层面的事务操作。 Spring事务的实现 在此之前需要提前讲解一下下面三个接口
TransactionDefinition 定义一些基本的事务属性 public interface TransactionDefinition {int PROPAGATION_REQUIRED 0;int PROPAGATION_SUPPORTS 1;int PROPAGATION_MANDATORY 2;int PROPAGATION_REQUIRES_NEW 3;int PROPAGATION_NOT_SUPPORTED 4;int PROPAGATION_NEVER 5;int PROPAGATION_NESTED 6;int ISOLATION_DEFAULT -1;int ISOLATION_READ_UNCOMMITTED 1;int ISOLATION_READ_COMMITTED 2;int ISOLATION_REPEATABLE_READ 4;int ISOLATION_SERIALIZABLE 8;int TIMEOUT_DEFAULT -1;// 返回事务的传播行为默认值为 REQUIRED。int getPropagationBehavior(); // 返回事务的隔离级别默认值是 DEFAULTint getIsolationLevel(); // 返回事务的超时时间默认值为-1。如果超过该时间限制但事务还没有完成则自动回滚事务。int getTimeout(); // 返回是否为只读事务默认值为 falseboolean isReadOnly();NullableString getName();
}PlatformTransactionManager事务管理器接口Spring 为各个平台如JDBC、MyBatisDataSourceTransactionManager、HibernateHibernateTransactionManager、JPAJpaTransactionManager等都提供了对应的事务管理器但是具体的实现就是各个平台自己的事情了。 public interface PlatformTransactionManager {// 获得事务TransactionStatus getTransaction(Nullable TransactionDefinition var1) throws TransactionException;// 提交事务void commit(TransactionStatus var1) throws TransactionException;// 回滚事务void rollback(TransactionStatus var1) throws TransactionException;
}TransactionStatus接口用来记录事务的状态该接口定义了一组方法用来获取或判断事务的相应状态信息 public interface TransactionStatus{// 是否是新的事务boolean isNewTransaction();// 是否有恢复点boolean hasSavepoint();// 设置为只回滚void setRollbackOnly();// 是否为只回滚boolean isRollbackOnly();// 是否已完成boolean isCompleted;
}回归正文Spring事务的实现方式主要有两种
①编程式事务这种方式通过编码的方式直接管理事务通常是使用PlatformTransactionManager接口的实现通过手动的开启事务、关闭事务以及回滚事务。编程式事务管理提供了最细粒度的控制但代码复杂性较高且容易出错。示例代码
Service
public class AccountService {private AccountMapper accountmapper;//事务管理器private PlatformTransactionManager transactionManager;//构造器方式注入public AccountService(AccountMapper accountmapper, PlatformTransactionManager transactionManager) {this.accountmapper accountmapper;this.transactionManager transactionManager;}//转账方法使用编程式事务管理。public void transferMoneyProgrammatic(Long fromAccountId, Long toAccountId, BigDecimal amount) {//TransactionDefinition定义一些基本的事务属性TransactionDefinition definition new DefaultTransactionDefinition();definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//通过事务管理器TransactionManager获取到一个事务TransactionStatus包含了事务的当前状态和一些其他与事务相关的信息//在这里开启了事务TransactionStatus status transactionManager.getTransaction(definition);try {//执行业务逻辑Account fromAccount accountmapper.findById(fromAccountId).orElseThrow();Account toAccount accountmapper.findById(toAccountId).orElseThrow();fromAccount.setBalance(fromAccount.getBalance().subtract(amount));toAccount.setBalance(toAccount.getBalance().add(amount));accountmapper.save(fromAccount);accountmapper.save(toAccount);//提交事务transactionManager.commit(status);} catch (Exception e) {//回滚事务transactionManager.rollback(status);throw e;}}
}
②声明式事务这是Spring推荐的方式通过在方法上使用Transactional注解来管理事务。声明式事务管理使得事务管理变得声明化代码更加简洁且减少了出错的可能性。Transactional注解可以指定传播行为、隔离级别等事务属性从而控制事务的行为 。示例代码
Service
public class AccountService {private AccountMapper accountmapper;//构造器方式注入public AccountService(AccountMapper accountmapper) {this.accountmapper accountmapper;}//转账方法使用声明式式事务管理。Transactionalpublic void transferMoneyProgrammatic(Long fromAccountId, Long toAccountId, BigDecimal amount) {//执行业务逻辑Account fromAccount accountmapper.findById(fromAccountId).orElseThrow();Account toAccount accountmapper.findById(toAccountId).orElseThrow();fromAccount.setBalance(fromAccount.getBalance().subtract(amount));toAccount.setBalance(toAccount.getBalance().add(amount));accountmapper.save(fromAccount);accountmapper.save(toAccount);}
} Spring事务的传播机制 事务的传播行为用来描述系统中的一些方法交由spring来管理事务当这些方法之间出现嵌套调用的时候事务所表现出来的行为是什么样的例如 Service1中的m1方法和Service2中的m2方法上面都有Transactional注解说明这2个方法由spring来控制事务。但是注意m1中2行代码先执行了一个insert然后调用service2中的m2方法service2中的m2方法也执行了一个insert。 那么大家觉得这2个insert会在一个事务中运行么也就是说此时事务的表现行为是什么样的呢这个就是spring事务的传播行为来控制的事情不同的传播行为表现会不一样可能他们会在一个事务中执行也可能不会在一个事务中执行这就需要看传播行为的配置了。 注意这7种传播行为有个前提他们的事务管理器是同一个的时候才会有上面描述中的表现行为
推荐去看这篇文章讲述了事务传播行为之间的各种搭配情况分析 Spring事务的底层原理 我们知道声明式事务的实现是通过通过aop的功能通过拦截器拦截 Transaction 方法在方法前后添加事务功能。具体步骤①第一步在启动类上加上EnableTransactionmanagement注解 ②第二步在目标方法上加上Transaction注解即可实现 EnableTransactionManagement --开启Spring管理事务 EnableTransactionManagement注解会开启spring自动管理事务的功能有了这个注解之后spring容器启动的过程中会拦截所有bean的创建过程判断bean 是否需要让spring来管理事务即判断bean中是否有Transaction注解如果找到就会被spring容器通过aop的方式创建代理代理中会添加一个拦截器通过拦截器在方法前后添加事务的功能
Target(ElementType.TYPE)
Retention(RetentionPolicy.RUNTIME)
Documented
Import(TransactionManagementConfigurationSelector.class) //核心
public interface EnableTransactionManagement {// 是基于类的代理cglib还是基于接口的代理jdk动态代理默认为false表示是基于jdk动态代理boolean proxyTargetClass() default false;//通知的模式默认是通过aop的方式AdviceMode mode() default AdviceMode.PROXY;// 我们知道这个注解的功能最终是通过aop的方式来实现的对bean创建了一个代理代理中添加了一个拦截器// 当代理中还有其他拦截器的是时候可以通过order这个属性来指定事务拦截器的顺序// 默认值是 LOWEST_PRECEDENCE Integer.MAX_VALUE,拦截器的执行顺序是order升序int order() default Ordered.LOWEST_PRECEDENCE;
}
Import(TransactionManagementConfigurationSelector.class) --提供两个bean
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelectorEnableTransactionManagement {//这个方法会返回一个类名数组spring容器启动过程中会自动调用这个方法//将这个方法指定的类注册到spring容器中方法的参数是AdviceMode这个就是 //EnableTransactionManagement注解中mode属性的值默认是PROXYprotected String[] selectImports(AdviceMode adviceMode) {switch (adviceMode) {case PROXY: -----//默认会走这里return new String[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};case ASPECTJ:return new String[]{this.determineTransactionAspectClass()};default:return null;}}
}
最终会在Spring中注册下面两个bean
AutoProxyRegistrar
ProxyTransactionManagementConfiguration
AutoProxyRegistrar --启用AOP的功能创建一个bean后置处理器会拦截所有bean的创建对符合条件的bean创建代理。
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {boolean candidateFound false;SetString annTypes importingClassMetadata.getAnnotationTypes();Iterator var5 annTypes.iterator();while(var5.hasNext()) {String annType (String)var5.next();AnnotationAttributes candidate AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);if (candidate ! null) {Object mode candidate.get(mode);Object proxyTargetClass candidate.get(proxyTargetClass);if (mode ! null proxyTargetClass ! null AdviceMode.class mode.getClass() Boolean.class proxyTargetClass.getClass()) {candidateFound true;if (mode AdviceMode.PROXY) {//这个代码的作用就是在容器中做了一个非常关键的beanInfrastructureAdvisorAutoProxyCreator//这个类是bean后置处理器会拦截所有bean的创建对符合条件的bean创建代理。AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);if ((Boolean)proxyTargetClass) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);return;}}}}}
}
ProxyTransactionManagementConfiguration --添加事务事务拦截器TransactionInterceptor
//部分源码
Configuration(proxyBeanMethods false)
Role(2)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {BeanRole(2)public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {TransactionInterceptor interceptor new TransactionInterceptor();interceptor.setTransactionAttributeSource(transactionAttributeSource);if (this.txManager ! null) {interceptor.setTransactionManager(this.txManager);}return interceptor;}
}
二者结合起来的效果就是对Transaction标注的bean创建代理对象代理对象中通过TransactionInterceptor拦截器来实现事务管理的功能。
TransactionInterceptor --负责拦截Transaction方法的执行在方法执行之前开启spring事务方法执行完毕之后提交或者回滚事务
//部分源码
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {Nullablepublic Object invoke(MethodInvocation invocation) throws Throwable {Class? targetClass invocation.getThis() ! null ? AopUtils.getTargetClass(invocation.getThis()) : null;Method var10001 invocation.getMethod();invocation.getClass();return this.invokeWithinTransaction(var10001, targetClass, invocation::proceed);}private void writeObject(ObjectOutputStream oos) throws IOException {oos.defaultWriteObject();oos.writeObject(this.getTransactionManagerBeanName());oos.writeObject(this.getTransactionManager());oos.writeObject(this.getTransactionAttributeSource());oos.writeObject(this.getBeanFactory());}private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {ois.defaultReadObject();this.setTransactionManagerBeanName((String)ois.readObject());this.setTransactionManager((PlatformTransactionManager)ois.readObject());this.setTransactionAttributeSource((TransactionAttributeSource)ois.readObject());this.setBeanFactory((BeanFactory)ois.readObject());}
}
invokeWithinTransaction --事务拦截器的入口
protected Object invokeWithinTransaction(Method method, Nullable Class? targetClass,final InvocationCallback invocation) throws Throwable {TransactionAttributeSource tas getTransactionAttributeSource();//6-1获取事务属性配置信息通过TransactionAttributeSource.getTransactionAttribute解析Trasaction注解得到事务属性配置信息final TransactionAttribute txAttr (tas ! null ? tas.getTransactionAttribute(method, targetClass) : null);//6-2获取事务管理器final TransactionManager tm determineTransactionManager(txAttr);//将事务管理器tx转换为 PlatformTransactionManagerPlatformTransactionManager ptm asPlatformTransactionManager(tm);if (txAttr null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {// createTransactionIfNecessary内部这里就不说了内部主要就是使用spring事务硬编码的方式开启事务最终会返回一个TransactionInfo对象TransactionInfo txInfo createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);// 业务方法返回值Object retVal;try {//调用aop中的下一个拦截器最终会调用到业务目标方法获取到目标方法的返回值retVal invocation.proceedWithInvocation();}catch (Throwable ex) {//6-3异常情况下如何走可能只需提交也可能只需回滚这个取决于事务的配置completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {//清理事务信息cleanupTransactionInfo(txInfo);}//6-4业务方法返回之后只需事务提交操作commitTransactionAfterReturning(txInfo);//返回执行结果return retVal;}
}
这个方法的执行流程非常像我们编程式事务的实现方式
1、定义事务属性信息TransactionDefinition transactionDefinition new DefaultTransactionDefinition();
2、定义事务管理器PlatformTransactionManager platformTransactionManager new DataSourceTransactionManager(dataSource);
3、获取事务TransactionStatus transactionStatus platformTransactionManager.getTransaction(transactionDefinition);
4、执行sql操作比如上面通过JdbcTemplate的各种方法执行各种sql操作
5、提交事务(platformTransactionManager.commit)或者回滚事务(platformTransactionManager.rollback)
determineTransactionManager --获取事务管理器
我们再具体来看首先是如何通过determineTransactionManager()方法获取事务管理器的
Nullable
protected TransactionManager determineTransactionManager(Nullable TransactionAttribute txAttr) {if (txAttr ! null this.beanFactory ! null) {String qualifier txAttr.getQualifier();if (StringUtils.hasText(qualifier)) {return this.determineQualifiedTransactionManager(this.beanFactory, qualifier);} else if (StringUtils.hasText(this.transactionManagerBeanName)) {return this.determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);} else {TransactionManager defaultTransactionManager this.getTransactionManager();if (defaultTransactionManager null) {defaultTransactionManager (TransactionManager)this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);if (defaultTransactionManager null) {defaultTransactionManager (TransactionManager)this.beanFactory.getBean(TransactionManager.class);this.transactionManagerCache.putIfAbsent(DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);}}return defaultTransactionManager;}} else {return this.getTransactionManager();}
从上面可知事务管理器的查找顺序
1、先看Transactional中是否通过value或者transactionManager指定了事务管理器
2、TransactionInterceptor.transactionManagerBeanName(即我们在TransactionInterceptor类中通过构造方法传进来的事务管理器)是否有值如果有将通过这个值查找事务管理器
3、如果上面2种都没有将从spring容器中查找TransactionManager类型的事务管理器 completeTransactionAfterThrowing --异常如何处理
然后再来看completeTransactionAfterThrowing()方法中出现异常该如何处理
protected void completeTransactionAfterThrowing(Nullable TransactionInfo txInfo, Throwable ex) {if (txInfo ! null txInfo.getTransactionStatus() ! null) {//6-3-1判断事务是否需要回滚if (txInfo.transactionAttribute ! null txInfo.transactionAttribute.rollbackOn(ex)) {//通过事务管理器回滚事务txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());}else {//通过事务管理器提交事务txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}}
}//跟着进入rollbackOn方法
public boolean rollbackOn(Throwable ex) {RollbackRuleAttribute winner null;int deepest Integer.MAX_VALUE;//Trasaction中可以通过rollbackFor指定需要回滚的异常列表通过noRollbackFor属性指定不需要回滚的异常//根据Transactional中指定的回滚规则判断ex类型的异常是否需要回滚if (this.rollbackRules ! null) {for (RollbackRuleAttribute rule : this.rollbackRules) {int depth rule.getDepth(ex);if (depth 0 depth deepest) {deepest depth;winner rule;}}}//若Transactional注解中没有匹配到这走默认的规则将通过super.rollbackOn来判断if (winner null) {return super.rollbackOn(ex);}return !(winner instanceof NoRollbackRuleAttribute);
}//跟着进入rollbackOn方法
Override
public boolean rollbackOn(Throwable ex) {return (ex instanceof RuntimeException || ex instanceof Error);
}
根据上面的源码分析可以看出默认情况下异常类型是RuntimeException或者Error的情况下事务才会回滚。 Trasaction中可以通过rollbackFor指定需要回滚的异常列表通过noRollbackFor属性指定不需要回滚的异常。
源码干货暂时分析到这已经干的不行了
这里推荐看一篇非常详细且为参考的文章Transaction源码深度解析 Spring事务失效的场景 扩展多数据源的事务管理 文章转载自: http://www.morning.zrmxp.cn.gov.cn.zrmxp.cn http://www.morning.jfcbz.cn.gov.cn.jfcbz.cn http://www.morning.yqsq.cn.gov.cn.yqsq.cn http://www.morning.qnwyf.cn.gov.cn.qnwyf.cn http://www.morning.mnjyf.cn.gov.cn.mnjyf.cn http://www.morning.fnkcg.cn.gov.cn.fnkcg.cn http://www.morning.wgkz.cn.gov.cn.wgkz.cn http://www.morning.hksxq.cn.gov.cn.hksxq.cn http://www.morning.xjpnq.cn.gov.cn.xjpnq.cn http://www.morning.xkhhy.cn.gov.cn.xkhhy.cn http://www.morning.rlwgn.cn.gov.cn.rlwgn.cn http://www.morning.wctqc.cn.gov.cn.wctqc.cn http://www.morning.qdlr.cn.gov.cn.qdlr.cn http://www.morning.nwnbq.cn.gov.cn.nwnbq.cn http://www.morning.ndrzq.cn.gov.cn.ndrzq.cn http://www.morning.qjngk.cn.gov.cn.qjngk.cn http://www.morning.wfyqn.cn.gov.cn.wfyqn.cn http://www.morning.kscwt.cn.gov.cn.kscwt.cn http://www.morning.qwpyf.cn.gov.cn.qwpyf.cn http://www.morning.jfbgn.cn.gov.cn.jfbgn.cn http://www.morning.bxsgl.cn.gov.cn.bxsgl.cn http://www.morning.xywfz.cn.gov.cn.xywfz.cn http://www.morning.rkdzm.cn.gov.cn.rkdzm.cn http://www.morning.fewhope.com.gov.cn.fewhope.com http://www.morning.yymlk.cn.gov.cn.yymlk.cn http://www.morning.rxydr.cn.gov.cn.rxydr.cn http://www.morning.ljygq.cn.gov.cn.ljygq.cn http://www.morning.rlhh.cn.gov.cn.rlhh.cn http://www.morning.gpryk.cn.gov.cn.gpryk.cn http://www.morning.qlpq.cn.gov.cn.qlpq.cn http://www.morning.yjtnc.cn.gov.cn.yjtnc.cn http://www.morning.zfqr.cn.gov.cn.zfqr.cn http://www.morning.nffwl.cn.gov.cn.nffwl.cn http://www.morning.jhkzl.cn.gov.cn.jhkzl.cn http://www.morning.qbnfc.cn.gov.cn.qbnfc.cn http://www.morning.dbqg.cn.gov.cn.dbqg.cn http://www.morning.wqgr.cn.gov.cn.wqgr.cn http://www.morning.wnmdt.cn.gov.cn.wnmdt.cn http://www.morning.pxbrg.cn.gov.cn.pxbrg.cn http://www.morning.ljfjm.cn.gov.cn.ljfjm.cn http://www.morning.junmap.com.gov.cn.junmap.com http://www.morning.qwgct.cn.gov.cn.qwgct.cn http://www.morning.ydtdn.cn.gov.cn.ydtdn.cn http://www.morning.cfybl.cn.gov.cn.cfybl.cn http://www.morning.dzzjq.cn.gov.cn.dzzjq.cn http://www.morning.lzwfg.cn.gov.cn.lzwfg.cn http://www.morning.yrsg.cn.gov.cn.yrsg.cn http://www.morning.fesiy.com.gov.cn.fesiy.com http://www.morning.etsaf.com.gov.cn.etsaf.com http://www.morning.nmngq.cn.gov.cn.nmngq.cn http://www.morning.slkqd.cn.gov.cn.slkqd.cn http://www.morning.qwrb.cn.gov.cn.qwrb.cn http://www.morning.rhfh.cn.gov.cn.rhfh.cn http://www.morning.lbgsh.cn.gov.cn.lbgsh.cn http://www.morning.tmpsc.cn.gov.cn.tmpsc.cn http://www.morning.wjhnx.cn.gov.cn.wjhnx.cn http://www.morning.qkrz.cn.gov.cn.qkrz.cn http://www.morning.lnnc.cn.gov.cn.lnnc.cn http://www.morning.pcrzf.cn.gov.cn.pcrzf.cn http://www.morning.kpgbz.cn.gov.cn.kpgbz.cn http://www.morning.lzdbb.cn.gov.cn.lzdbb.cn http://www.morning.gjqnn.cn.gov.cn.gjqnn.cn http://www.morning.rstrc.cn.gov.cn.rstrc.cn http://www.morning.qmmfr.cn.gov.cn.qmmfr.cn http://www.morning.xdjsx.cn.gov.cn.xdjsx.cn http://www.morning.hnhgb.cn.gov.cn.hnhgb.cn http://www.morning.gtjkh.cn.gov.cn.gtjkh.cn http://www.morning.phnbd.cn.gov.cn.phnbd.cn http://www.morning.qflcb.cn.gov.cn.qflcb.cn http://www.morning.qqpg.cn.gov.cn.qqpg.cn http://www.morning.sfyqs.cn.gov.cn.sfyqs.cn http://www.morning.jnptt.cn.gov.cn.jnptt.cn http://www.morning.tjwlp.cn.gov.cn.tjwlp.cn http://www.morning.guangda11.cn.gov.cn.guangda11.cn http://www.morning.qncqd.cn.gov.cn.qncqd.cn http://www.morning.lkcqz.cn.gov.cn.lkcqz.cn http://www.morning.pmxw.cn.gov.cn.pmxw.cn http://www.morning.lyldhg.cn.gov.cn.lyldhg.cn http://www.morning.dyxlj.cn.gov.cn.dyxlj.cn http://www.morning.njddz.cn.gov.cn.njddz.cn