Spring单实例、多线程安全、事务解析

原文:http://blog.csdn.net/c289054531/article/details/9196053

引言:

    在使用Spring时,很多人可能对Spring中为什么DAO和Service对象采用单实例方式很迷惑,这些读者是这么认为的:
    DAO对象必须包含一个数据库的连接Connection,而这个Connection不是线程安全的,所以每个DAO都要包含一个不同的Connection对象实例,这样一来DAO对象就不能是单实例的了。
    上述观点对了一半。对的是“每个DAO都要包含一个不同的Connection对象实例”这句话,错的是“DAO对象就不能是单实例”。
    其实Spring在实现Service和DAO对象时,使用了ThreadLocal这个类,这个是一切的核心! 如果你不知道什么事ThreadLocal,请看 深入研究java.lang.ThreadLocal类》 :。请放心,这个类很简单的。
1。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。 
2。将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。
 
    要弄明白这一切,又得明白事务管理在Spring中是怎么工作的,所以本文就对Spring中多线程、事务的问题进行解析。

Spring使用ThreadLocal解决线程安全问题:

    Spring中DAO和Service都是以单实例的bean形式存在,Spring通过ThreadLocal类将有状态的变量(例如数据库连接Connection)本地线程化,从而做到多线程状况下的安全。在一次请求响应的处理线程中, 该线程贯通展示、服务、数据持久化三层,通过ThreadLocal使得所有关联的对象引用到的都是同一个变量。 
    参考下面代码,这个是《Spring3.x企业应用开发实战中的例子》,本文后面也会多次用到该书中例子(有修改)。
[java]  view plain copy
  1. <span style="font-family:SimSun;font-size:14px;">public class SqlConnection {  
  2.     //①使用ThreadLocal保存Connection变量  
  3.     privatestatic ThreadLocal <Connection>connThreadLocal = newThreadLocal<Connection>();  
  4.     publicstatic Connection getConnection() {  
  5.        // ②如果connThreadLocal没有本线程对应的Connection创建一个新的Connection,  
  6.        // 并将其保存到线程本地变量中。  
  7.        if (connThreadLocal.get() == null) {  
  8.            Connection conn = getConnection();  
  9.            connThreadLocal.set(conn);  
  10.            return conn;  
  11.        } else {  
  12.            return connThreadLocal.get();  
  13.            // ③直接返回线程本地变量  
  14.        }  
  15.     }  
  16.     public voidaddTopic() {  
  17.        // ④从ThreadLocal中获取线程对应的Connection  
  18.        try {  
  19.            Statement stat = getConnection().createStatement();  
  20.        } catch (SQLException e) {  
  21.            e.printStackTrace();  
  22.        }  
  23.     }  
  24. }</span>  
    这个是例子展示了不同线程使用TopicDao时如何使得每个线程都获得不同的Connection实例副本,同时保持TopicDao本身是单实例。

事务管理器:

    事务管理器用于管理各个事务方法,它产生一个事务管理上下文。下文以SpringJDBC的事务管理器DataSourceTransactionManager类为例子。
    我们知道数据库连接Connection在不同线程中是不能共享的,事务管理器为不同的事务线程利用ThreadLocal类提供独立的Connection副本。事实上,它将Service和Dao中所有线程不安全的变量都提取出来单独放在一个地方,并用ThreadLocal替换。而多线程可以共享的部分则以单实例方式存在。

事务传播行为:

    当我们调用Service的某个事务方法时,如果该方法内部又调用其它Service的事务方法,则会出现事务的嵌套。Spring定义了一套事务传播行为,请参考。这里我们假定都用的REQUIRED这个类型:如果当前没有事务,就新建一个事务,如果已经存在一个事务,则加入到的当前事务。参考下面例子(代码不完整):
[java]  view plain copy
  1. <span style="font-family:SimSun;font-size:14px;">@Service"userService")  
  2. public class UserService extends BaseService {  
  3.     @Autowired  
  4.     private JdbcTemplate jdbcTemplate;  
  5.   
  6.     @Autowired  
  7.     private ScoreService scoreService;  
  8.      
  9.     public void logon(String userName) {  
  10.         updateLastLogonTime(userName);         
  11.         scoreService.addScore(userName, 20);  
  12.     }  
  13.   
  14.     public void updateLastLogonTime(String userName) {  
  15.         String sql = "UPDATE t_user u SET u.last_logon_time = ? WHERE user_name =?";  
  16.         jdbcTemplate.update(sql, System. currentTimeMillis(), userName);  
  17.     }  
  18.   
  19.     public static void main(String[] args) {  
  20.         ApplicationContext ctx = new ClassPathXmlApplicationContext("com/baobaotao/nestcall/applicatonContext.xml" );  
  21.         UserService service = (UserService) ctx.getBean("userService" );  
  22.         service.logon( "tom");  
  23.   
  24.     }  
  25. }  
  26.   
  27. @Service"scoreUserService" )  
  28. public class ScoreService extends BaseService{  
  29.     @Autowired  
  30.     private JdbcTemplate jdbcTemplate;  
  31.   
  32.     public void addScore(String userName, int toAdd) {  
  33.         String sql = "UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?";  
  34.         jdbcTemplate.update(sql, toAdd, userName);  
  35.     }  
  36. }</span>  
    同时,在配置文件中指定UserService、ScoreService中的所有方法都开启事务。
    上述例子中UserService.logon()执行开始时Spring创建一个新事务,UserService.updateLastLogonTime()和ScoreService.addScore()会加入这个事务中,好像所有的代码都“直接合并”了!

多线程中事务传播的困惑:

    还是上面那个例子,加入现在我在UserService.logon()方法中手动新开一个线程,然后在新开的线程中执行ScoreService.add()方法,此时事务传播行为会怎么样?飞线程安全的变量,比如Connection会怎样?改动之后的UserService 代码大体是:
[java]  view plain copy
  1. <span style="font-family:SimSun;font-size:14px;">@Service"userService")  
  2. public class UserService extends BaseService {  
  3.     @Autowired  
  4.     private JdbcTemplate jdbcTemplate;  
  5.   
  6.     @Autowired  
  7.     private ScoreService scoreService;  
  8.   
  9.     public void logon(String userName) {  
  10.         updateLastLogonTime(userName);  
  11.         Thread myThread = new MyThread(this.scoreService , userName, 20);//使用一个新线程运行  
  12.         myThread .start();  
  13.     }  
  14.   
  15.     public void updateLastLogonTime(String userName) {  
  16.         String sql = "UPDATE t_user u SET u.last_logon_time = ? WHERE user_name =?";  
  17.         jdbcTemplate.update(sql, System. currentTimeMillis(), userName);  
  18.     }  
  19.   
  20.     private class MyThread extends Thread {  
  21.         private ScoreService scoreService;  
  22.         private String userName;  
  23.         private int toAdd;  
  24.         private MyThread(ScoreService scoreService, String userName, int toAdd) {  
  25.             this. scoreService = scoreService;  
  26.             this. userName = userName;  
  27.             this. toAdd = toAdd;  
  28.         }  
  29.   
  30.         public void run() {  
  31.             scoreService.addScore( userName, toAdd);  
  32.         }  
  33.     }  
  34.   
  35.     public static void main(String[] args) {  
  36.         ApplicationContext ctx = new ClassPathXmlApplicationContext("com/baobaotao/multithread/applicatonContext.xml" );  
  37.         UserService service = (UserService) ctx.getBean("userService" );  
  38.         service.logon( "tom");  
  39.        }  
  40. }</span>  
    这个例子中,MyThread会新开一个事务,于是UserService.logon()和UserService.updateLastLogonTime()会在一个事务中,而ScoreService.addScore()在另一个事务中,需要注意的是这两个事务都被事务管理器放在事务上下文中。
    结论是:在事务属性为REQUIRED时,在相同线程中进行相互嵌套调用的事务方法工作于相同的事务中。如果互相嵌套调用的事务方法工作在不同线程中,则不同线程下的事务方法工作在独立的事务中。

底层数据库连接Connection访问问题

    程序只要使用SpringDAO模板,例如JdbcTemplate进行数据访问,一定没有数据库连接泄露问题!如果程序中显式的获取了数据连接Connection,则需要手工关闭它,否则就会泄露!
    当Spring事务方法运行时,事务会放在事务上下文中,这个事务上下文在本事务执行线程中对同一个数据源绑定了唯一一个数据连接,所有被该事务的上下文传播的放发都共享这个数据连接。这一切都在Spring控制下,不会产生泄露。Spring提供了数据资源获取工具类DataSourceUtils来获取这个数据连接.
[java]  view plain copy
  1. <span style="font-family:SimSun;font-size:14px;">@Service"jdbcUserService" )  
  2. public class JdbcUserService {  
  3.     @Autowired  
  4.     private JdbcTemplate jdbcTemplate;  
  5.      
  6.     @Transactional  
  7.     public void logon(String userName) {  
  8.         try {  
  9.             Connection conn = jdbcTemplate.getDataSource().getConnection();             
  10.             String sql = "UPDATE t_user SET last_logon_time=? WHERE user_name =?";  
  11.             jdbcTemplate.update(sql, System. currentTimeMillis(), userName);  
  12.         } catch (Exception e) {  
  13.             e.printStackTrace();  
  14.         }  
  15.   
  16.     }  
  17.   
  18.     public static void asynchrLogon(JdbcUserService userService, String userName) {  
  19.         UserServiceRunner runner = new UserServiceRunner(userService, userName);  
  20.         runner.start();  
  21.     }  
  22.   
  23.     public static void reportConn(BasicDataSource basicDataSource) {  
  24.         System. out.println( "连接数[active:idle]-[" +  
  25.                        basicDataSource.getNumActive()+":" +basicDataSource.getNumIdle()+ "]");  
  26.     }  
  27.   
  28.     private static class UserServiceRunner extends Thread {  
  29.         private JdbcUserService userService;  
  30.         private String userName;  
  31.   
  32.         public UserServiceRunner(JdbcUserService userService, String userName) {  
  33.             this. userService = userService;  
  34.             this. userName = userName;  
  35.         }  
  36.   
  37.         public void run() {  
  38.             userService.logon( userName);  
  39.         }  
  40.     }  
  41.   
  42.     public static void main(String[] args) {  
  43.         ApplicationContext ctx = new ClassPathXmlApplicationContext("com/baobaotao/connleak/applicatonContext.xml" );  
  44.         JdbcUserService userService = (JdbcUserService) ctx.getBean("jdbcUserService" );  
  45.         JdbcUserService. asynchrLogon(userService, "tom");  
  46.     }  
  47. }</span>  
    在这个例子中,main线程拿到一个UserService实例,获取一个Connection的副本,它会被Spring管理,不会泄露。UserServiceRunner 线程手动从数据源拿了一个Connection但没有关闭因此会泄露。
    如果希望使UserServiceRunner能拿到UserService中那个Connection们就要使用DataSourceUtils类,DataSourceUtils.getConnection()方法会首先查看当前是否存在事务管理上下文,如果存在就尝试从事务管理上下文拿连接,如果获取失败,直接从数据源中拿。在获取连接后,如果存在事务管理上下文则把连接绑定上去。
    实际上,上面的代码只用改动一行,把login()方法中获取连接那行改成就可以做到:
Connection conn = DataSourceUtils.getConnection( jdbcTemplate .getDataSource());    
   需要注意的是:如果DataSourceUtils在没有事务上下文的方法中使用getConnection()获取连接,依然要手动管理这个连接!
    此外,开启了事务的方法要在整个事务方法结束后才释放事务上下文绑定的Connection连接,而没有开启事务的方法在调用完Spring的Dao模板方法后立刻释放。

多线程一定要与事务挂钩么?

    不是!即便没有开启事务,利用ThreadLocal机制也能保证线程安全,Dao照样可以操作数据。但是事务和多线程确实纠缠不清,上文已经分析了在多线程下事务传播行为、事务对Connection获取的影响。

结论:

  • Spring中DAO和Service都是以单实例的bean形式存在,Spring通过ThreadLocal类将有状态的变量(例如数据库连接Connection)本地线程化,从而做到多线程状况下的安全。在一次请求响应的处理线程中, 该线程贯通展示、服务、数据持久化三层,通过ThreadLocal使得所有关联的对象引用到的都是同一个变量。 
  • 在事务属性为REQUIRED时,在相同线程中进行相互嵌套调用的事务方法工作于相同的事务中。如果互相嵌套调用的事务方法工作在不同线程中,则不同线程下的事务方法工作在独立的事务中。
  • 程序只要使用SpringDAO模板,例如JdbcTemplate进行数据访问,一定没有数据库连接泄露问题!如果程序中显式的获取了数据连接Connection,则需要手工关闭它,否则就会泄露!
  • 当Spring事务方法运行时,就产生一个事务上下文,它在本事务执行线程中对同一个数据源绑定了一个唯一的数据连接,所有被该事务上下文传播的方法都共享这个连接。要获取这个连接,如要使用Spirng的资源获取工具类DataSourceUtils。
  • 事务管理上下文就好比一个盒子,所有的事务都放在里面。如果在某个事务方法中开启一个新线程,新线程中执行另一个事务方法,则由上面第二条可知这两个方法运行于两个独立的事务中,但是:如果使用DataSourcesUtils,则新线程中的方法可以从事务上下文中获取原线程中的数据连接!
  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring.NET是一个应用程序框架,其目的是协助开发人员创建企业级的.NET应用程序。它提供了很多方面的功能,比如依赖注入、面向方面编程(AOP)、数据访问抽象及ASP.NET扩展等等。Spring.NET以Java版的Spring框架为基础,将Spring.Java的核心概念与思想移植到了.NET平台上。 第一章 序言 第二章 简介 2.1.概述 2.2.背景 2.3.模块 2.4.许可证信息 2.5.支持 第三章 背景 3.1.控制反转 第一部分 核心技术 第四章 对象、对象工厂和应用程序上下文 4.1.简介 4.2.IObjectFactory,IApplicationContext和IObjectDefinition接口介绍 4.2.1.The IObjectFactory和IApplicationContext 4.2.2.对象定义 4.2.3.对象的创建 4.2.3.1.通过构造器创建对象 4.2.3.2.通过静态工厂方法创建对象 4.2.3.3.通过实例工厂方法创建对象 4.2.4.泛型类的对象创建 4.2.4.1.通过构造器创建泛型类的对象 4.2.4.2.通过静态工厂方法创建泛型类的对象 4.2.4.3.通过实例工厂方法创建泛型类的对象 4.2.5.对象标识符(id和name) 4.2.6.Singleton和Prototype 4.3.属性,协作对象,自动装配和依赖检查 4.3.1.设置对象的属性和协作对象 4.3.2.构造器参数解析 4.3.2.1.根据参数类型匹配构造器参数 4.3.2.2.根据参数索引匹配构造器参数 4.3.2.3.根据名称匹配构造器参数 4.3.3.详细讨论对象属性和构造器参数 4.3.3.1.设置空值 4.3.3.2.设置集合值 4.3.3.3.设置泛型集合的值 4.3.3.4.设置索引器属性 4.3.3.5.内联对象定义 4.3.3.6.idref节点 4.3.3.7.引用协作对象 4.3.3.8.value和ref节点的简短格式 4.3.3.9.复合属性名 4.3.4.方法注入 4.3.4.1.查询方法注入 4.3.4.2.替换任意方法 4.3.5.引用其他对象或类型的成员 4.3.5.1.使用对象或类的属性值进行注入 4.3.5.2.使用字段值进行注入 4.3.5.3.使用方法的返回值进行注入 4.3.6.IFactoryObject接口的其它实现 4.3.6.1.Log4Net 4.3.7.使用depends-on 4.3.8.自动装配协作对象 4.3.9.检查依赖项 4.4.类型转换 4.4.1.枚举类型的转换 4.4.2.内置的类型转换器 4.4.3.自定义类型转换器 4.4.3.1.使用CustomConverterConfigurer类 4.5.自定义对象的行为 4.5.生命周期接口 4.5.1.1.IInitializingObject接口和init-method属性 4.5.1.2.IDisposable接口和destroy-method属性 4.5.2.让对象了解自己的容器 4.5.2.1.IObjectFactoryAware接口 4.5.2.2.IObjectNameAware接口 4.5.3.IFactoryObject接口 4.6.抽象与子对象定义 4.7.与IObjectFactory接口交互 4.7.1.获得IFactoryObject对象本身,而非其产品 4.8.使用IObjectPostProcessor接口自定义对象 4.9.使用IObjectFactoryPostProcessor定制对象工厂 4.9.1.PropertyPlaceholderConfigurer类 4.9.1.1.使用环境变量进行替换 4.9.2.PropertyOverrideConfigurer类 4.10.使用alias节点为对象添加别名 4.11.IApplicationContext简介 4.12.配置应用程序上下文 4.12.1.注册自定义解析器 4.12.2.创建自定义资源处理器 4.12.3.配置类型别名 4.12.4.注册类型转换器 4.13.IApplicationContext接口的扩展功能 4.13.1.上下文继承 4.13.2.使用IMessageSource接口 4.13.3.在Spring.NET内部使用资源 4.13.4.松耦合事件模型 4.13.5.IApplicationContext的事件通知 4.14.定制IApplicationContex中对象的行为 4.14.1.IApplicationContextAware标识接口 4.14.2.IObjectPostProcessor接口 4.14.3.IObjectFactoryPostProcessor接口 4.14.4.PropertyPlaceholderConfigurer类 4.15.从其它文件中导入对象定义 4.16.服务定位器访问 第五章. IObjectWrapper接口和类型转换 5.1.简介 5.2.使用IObjectWrapper接口管理对象 5.2.1.读、写普通及嵌套的属性 5.2.2.其它功能 5.3.类型转换 5.3.1.转换枚举类型 5.4.内置类型转换器 第六章. IResource接口 6.1.简介 6.2.IResource接口 6.3.内置的IResource实现类 6.4.IResourceLoader接口 6.5.IResourceLoaderAware接口 6.6.应用程序上下文和IResource路径 第七章. 多线程和并发操作 7.1.简介 7.2.线程本地存储 7.3.同步基础 7.3.1.ISync 7.3.2.SyncHolder 7.3.3.Latch 7.3.4.Semaphore 第八章. 对象池 8.1.简介 8.2.接口和实现 第九章. Spring.NET杂记 9.1.简介 9.2.PathMatcher 9.2.1.通用规则 9.2.2.匹配文件名 9.2.3.匹配子目录 9.2.4.大小写需要考虑,斜线可以任意 第十章. 表达式求值 10.1.简介 10.2.表达式求值 10.3.语言参考 10.3.1.文字表达式 10.3.2.属性,数组,列表,字典,索引器 10.3.2.1.定义内联的数组、列表和词典 10.3.3.方法 10.3.4.操作符 10.3.4.1.关系操作符 10.3.4.2.逻辑操作符 10.3.4.3.算术运算符 10.3.5.赋值 10.3.6.表达式列表 10.3.7.类型 10.3.8.类型注册 10.3.9.构造器 10.3.10.变量 10.3.10.1.'#this'和'#root'变量 10.3.11.三元操作符(If-Then-Else) 10.3.12.列表的投影(Projection)和选择(Selection) 10.3.13. 集合处理器和聚合器 10.3.13.1.Count聚合器 10.3.13.2.Sum聚合器 10.3.13.3.Average聚合器 10.3.13.4.Minimum聚合器 10.3.13.5.Maximum聚合器 10.3.13.6.nonNull处理器 10.3.13.7.distinct处理器 10.3.13.8.sort处理器 10.3.14.引用容器中的对象 10.3.15.Lambda表达式 10.3.16.空目标 10.4.本章使用的示例类型 第十一章. 验证框架 11.1.简介 11.2.用法示例 11.3.验证对象组 11.4.验证对象 11.4.1.条件验证对象 11.4.2.必需性验证对象 11.4.3.正则表达式验证对象 11.4.4.通用验证对象 11.4.5.条件型验证 11.5.验证行为 11.5.1.错误消息行为 11.5.2.通用行为 11.6.引用验证对象 11.7.在ASP.NET中的使用技巧 11.7.1.显示验证错误 11.7.1.1.配置错误显示类 第十二章. 使用Spring.NET进行面向方面的编程 12.1.简介 12.1.1.AOP基本概念 12.1.2.Spring.NET AOP的功能 12.1.3.Spring.NET的AOP代理 12.2.Spring.NET中的切入点 12.2.1.概念 12.2.2.切入点的操作 12.2.3.Spring.NET提供的切入点实现类 12.2.3.1.静态切入点 12.2.3.2.动态切入点 12.2.4.自定义切入点 12.3.Spring.NET的通知类型 12.3.1.通知的生命周期 12.3.2.通知类型 12.3.2.1.拦截环绕通知 12.3.2.2.前置通知 12.3.2.3.异常通知 12.3.2.4.后置通知 12.3.2.5.引入通知 12.4.Spring.NET中的Advisor 12.5.使用ProxyFactoryObject创建AOP代理 12.5.1.基本原理 12.5.2.ProxyFactoryObject的属性 12.5.3.代理接口 12.5.4.代理一个类 12.6.使用ProxyFactory类以编程方式创建AOP代理 12.7.管理目标对象 12.8.使用“自动代理”功能 12.8.1.自动代理对象的定义 12.8.1.1.ObjectNameAutoProxyCreator 12.8.1.2.DefaultAdvisorAutoProxyCreator 12.8.1.3.AbstractAutoProxyCreator 12.8.2.使用特性驱动的自动代理 12.9.使用TargetSources 12.9.1.动态切换TargetSource 12.9.2.池化TargetSource 12.9.3.PrototypeTargetSource 12.10.定义新的通知类型 12.11.参考资源 第十三章.通用日志抽象层 13.1.简介 13.1.1.Logging API 13.2.实现与配置 13.2.1.控制台Logger 13.3.Log4Net 第二部分. 中间层数据访问 第十四章. 事务管理 14.1.简介 14.2.动机 14.3.核心接口 14.4.用事务进行资源同步 14.4.1.高层次方法 14.4.2.低层次方法 14.5.声明式事务管理 14.5.1.理解Spring.NET声明式事务管理的实现 14.5.2.第一个例子 14.5.3.Transaction特性的设置 14.5.4.通过AutoProxyCreator使用声明式事务 14.5.5.通过TransactionProxyFactoryObject使用声明式事务 14.5.6. 通过ProxyFactoryObject使用声明式事务 14.5.7. Using Abstract object definitions 14.5.8. Declarative Transactions using ProxyFactoryObject 14.6. 编程方式的事务管理 14.6.1.使用TransactionTemplate 14.6.2.使用IPlatformTransactionManager 14.7.选择编程方式还是声明方式 第十五章. 数据访问对象 15.1.简介 15.2.统一的异常体系 15.3.为数据访问对象提供的统一抽象基类 第十六章. DbProvider 16.1.简介 16.1.1.IDbProvider和DbProviderFactory 16.1.2. XML配置 16.1.3.管理连接字符串 第十七章. 使用ADO.NET进行数据访问 17.1.简介 17.2.动机 17.3.Provider抽象 17.3.1.创建IDbProvider类型的实例 17.4.命名空间 17.5.数据访问的方式 17.6.AdoTemplate简介 17.6.1.执行回调 17.6.2.在.NET 2.0中执行回调 17.6.3. .NET 1.1 17.6.4.AdoTemplate方法指南 17.7.异常翻译 17.8.参数管理 17.8.1. IDbParametersBuilder 17.8.2. IDbParameters 17.9. Mapping DBNull values 17.10. Basic data access operations 17.10.1. ExecuteNonQuery 17.10.2. ExecuteScalar 17.11. Queries and Lightweight Object Mapping 17.11.1. ResultSetExtractor 17.11.2. RowCallback 17.11.3. RowMapper 17.11.4. Query for a single object 17.11.5. Query using a CommandCreator 17.12. DataTable and DataSet 17.12.1. DataTables 17.12.2. DataSets 17.13. Deriving Stored Procedure Parameters 17.14. Database operations as Objects 17.14.1. AdoNonQuery 17.14.2. AdoQuery 17.14.3. MappingAdoQuery 17.14.4. Stored Procedure 17.14.5. DataSetOperation 18. ORM集成 18.1. 简介 第三部分. Web框架 第十九章. Web框架 19.1.简介 19.2.自动装载应用程序上下文和应用程序上下文嵌套 19.2.1. 配置 19.2.2.上下文嵌套 19.3.为ASP.NET页面进行依赖注入 19.3.1.为Web控件进行依赖注入 19.4.Master Page 19.4.1.将子页面与Master Page关联 19.5.双向数据绑定 19.5.1.数据绑定的后台实现 19.5.1.1.绑定方向 19.5.1.2.Formatters 19.5.1.3.类型转换 19.5.1.4.数据绑定事件 19.6.本地化 19.6.1.使用Localizer进行自动本地化(“推”模型) 19.6.2.使用Localizer 19.6.3.手动应用资源(“拉”模型的本地化) 19.6.4.在Web应用程序中进行图像本地化 19.6.5.全局资源 19.6.6.用户语言文化管理 19.6.6.1. DefaultWebCultureResolver 19.6.6.2. RequestCultureResolver 19.6.6.3. SessionCultureResolver 19.6.6.4. CookieCultureResolver 19.6.7.更改语言文化 19.7.结果映射 19.8.客户端脚本 19.8.1.在HTML的head节点内注册客户端脚本 19.8.2.向节点中添加CSS定义 19.8.3.全局目录(Well-Known Directories) 第四部分. 服务 第二十章. .NET Remoting 20.1.简介 20.2.在服务端发布SAO 20.2.1.SAO Singleton 20.2.2.SAO SingleCall 20.2.3.IIS应用程序配置 20.3.在客户端访问SAO 20.4.CAO 最佳实践 20.5.在服务端注册CAO 20.5.1.向CAO对象应用AOP通知 20.6.在客户端访问CAO 20.6.1.向客户端的CAO对象应用AOP通知 20.7. XML Schema for configuration 20.8.参考资源 第二十一章. .NET企业服务 21.1.简介 21.2.服务组件 21.3.服务端 21.4.客户端 第二十二章. Web服务 22.1.服务端 22.1.1.消除对.asmx文件的依赖 22.1.2.向web服务中注入依赖项 22.1.3.将PONO发布为web服务 22.1.4.将AOP代理发布为web服务 22.1.5.客户端的问题 22.2.客户端 22.2.1.WebServiceProxyFactory 22.2.2.WebServiceClientFactory 第二十三章. Windows后台服务 23.1.备注 23.2.简介 23.3.Spring.Services.WindowsService.Process.exe应用程序 23.3.1.安装 23.3.2.配置 23.4.将应用程序上下文发布为Windows服务 23.4.1.service.config 23.4.1.1.让应用程序了解自身的位置 23.4.2.watcher.xml - 可选的配置 23.4.3.bin目录 - 可选 23.5.自定义或扩展 23.5.1. .config文件 第五部分. 与Visual Studio.NET集成 第二十四章. 与Visual Studio.NET集成 24.1.XML编辑与验证 24.2.XML Schema的版本 24.3.集成API文档 第六部分. 快速入门程序 第二十五章. IoC快速入门 25.1.简介 25.2.Movie Finder 25.2.1.开始建立MovieFinder应用程序 25.2.2.第一个对象定义 25.2.3.属性注入 25.2.4.构造器参数注入 25.2.5.总结 25.2.6.日志 25.3.应用程序上下文和IMessageSource接口 25.3.1.简介 25.4.应用程序上下文和IEventRegistry接口 25.4.1.简介 25.5.对象池示例 25.5.1.实现Spring.Pool.IPoolableObjectFactory 25.5.2.使用池中的对象 25.5.3.利用executor执行并行的grep 25.6.AOP 第二十六章. AOP指南 26.1.简介 26.2.基础知识 26.2.1.应用通知 26.2.2.使用切入点-基本概念 26.3.深入探讨 26.3.1.其它类型的通知 26.3.1.1.前置通知 26.3.1.2.后置通知 26.3.1.3.异常通知 26.3.1.4.引入(mixins) 26.3.1.5.通知链 26.3.1.6.配置通知 26.3.2.使用特性定义切入点 26.4.The Spring.NET AOP Cookbook 26.4.1.缓存 26.4.2.性能监视 26.4.3.重试规则 Spring.NET AOP最佳实践 第二十七章. .NET Remoting快速入门 27.1.简介 27.2.Remoting实例程序 27.3.实现 27.4.运行程序 27.5.Remoting Schema 27.6.参考资源 第二十八章. Web框架快速入门 28.1.简介 第二十九章. SpringAir - 参考程序 29.1.简介 29.2.架构 29.3.实现 29.3.1.业务层 29.3.2.服务层 29.3.3.Web层 29.4.总结 第三十章. 数据访问快速入门 30.1.简介 第三十一章. 事务管理快速入门 31.1.简介 31.2.应用程序概述 31.2.1.接口 第七部分. Java开发人员必读 第三十二章. Java开发人员必读 32.1.简介 32.2.Beans和Objects 32.3.PropertyEditor和TypeConverter 32.4.ResourceBundle和ResourceManager 32.5.异常 32.6.应用程序配置 32.7.AOP框架
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值