虽然Spring用bean或者JavaBean来表示应用组件,但并不意味着Spring组件必须要遵循JavaBean规范。一个Spring组件可以是任何形式的POJO。
三个主要内容:依赖注入,切面,模板代码
表达式的语法采用的是AspectJ的切点表达式语言。
常用的应用上下文:
AnnotationConfigApplicationContext
AnnotationConfigWebApplicationContext
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
XmlWebApplicationContext
Bean的生存周期:
实例化 => 填充属性 => BeanNameAware => BeanFactoryAware => ApplicationContextAware
=> BeanPostProcessor的预初始化方法 => InitializingBean的afterPropertiesSet方法 => 调用自定义初始化方法 => 调用BeanPostProcessor的初始化后方法
=> Bean可以使用了
=> 调用DisposableBean的destroy()方法 => 调用自定义销毁方法
Spring框架的模块分类:
1. Spring核心容器: Beans, Core, Context, Expression, Context support
2. 面向切面编程: AOP, Aspects
3. 数据访问与集成:JDBC, Transaction, ORM, OXM, Messaging, JMS
4. Web与远程调用:Web, Web servlet, Web portlet, WebSocket
5. Instrumentation: Instrument, Instrument Tomcat
6. Test
Spring提供了三种主要的装配机制:
1. 在XML中进行显式配置
2. 在Java中进行显式配置
3. 隐式的bean发现机制和自动装配。
自动发现:
@Configuration
@ComponentScan
public class CDPlayerConfig{
}
按照默认规则,它会以配置类所在的包作为基础包来扫描组件。
@ComponentScan("soundsystem")
@ComponentScan(basePackages={"soundsystem", "video"})
@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})
profile bean。
Spring为环境相关的bean所提供的解决方案其实与构建时的方案没有太大的差别。当然,在这个过程中需要根据环境决定该创建哪个bean和不创建哪个bean。不过Spring并不是在构建的时候做出这样的决策,而是等到运行时再来确定。这样的结果就是同一个部署单元(可能会是WAR文件)能够适用于所有的环境,没有必要进行重新构建。
Spring在确定哪个profile处于激活状态时,需要依赖两个独立的属性:spring.profiles.active和spring.profiles.default。
有多种方式来设置这两个属性:
作为DispatcherServlet的初始化参数。
作为Web应用的上下文参数
作为JNDI条目
作为环境变量
作为JVM的系统属性
在集成测试类上,使用@ActiveProfiles注解设置。
Spring定义了多种作用域,可以基于这些作用域创建bean,包括:
1. 单例(Singleton):在整个应用中,只创建bean的一个实例。
2. 原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
3. 会话(Session):在Web应用中,为每个会话创建一个bean实例。
4. 请求(Request):在Web应用中,为每个请求创建一个bean实例。
proxyMode这个属性解决了将会话或请求作用域的bean注入到单例bean中所遇到的问题。
在@Configuration类上可以指定@PropertySource注解来指定属性来源,之后就可以注入Environment来调用它的getProperty方法获取属性性。
Spring切面可以应用5种类型的通知:
1. 前置通知
2. 后置通知
3. 返回通知
4. 异常通知
5. 环绕通知
第10章
在开发应用的持久层的时候,会面临多种选择,我们可以使用JDBC、Hibernate、Java持久化API(JPA)或者其他任意的持久化框架。
为了避免持久化的逻辑分散到应用的各个组件中,最好将数据访问的功能放到一个或多个专注于此项任务的组件中。这样的组件通常称为数据访问对象(DAO)或Repository。
为了避免应用与特定的数据访问策略耦合在一起,编写良好的Repository应该以接口的方式暴露功能。
如果在阅读了上面几段文字之后,你能感受到我倾向于将持久层隐藏在接口之后,那很高兴我的目的达到了。我相信接口是实现松耦合代码的关键,并且应将其用于应用程序的各个层,而不仅仅是持久化层。
Spring提供与平台无关的持久化异常,这意味着我们可以使用Spring抛出一致的异常,而不用关心所选择的持久化方案。
这些异常都继承自DataAccessException,DataAccessException的特殊之处在于它是一个非检查型异常。换句话说,没有必要捕获Spring所抛出的数据访问异常(当然,如果你想捕获的话也是完全可以的)。
配置数据源的三种方式:
1. 通过JDBC驱动程序定义的数据源。
2. 通过JNDI查找的数据源。
3. 连接池的数据源。
对于即将发布到生产环境中的应用程序,我建议使用从连接池获取连接的数据源。如果可能的话,我倾向于通过应用服务器的JNDI来获取数据源。
1.在Spring中,通过JDBC驱动定义数据源是最简单的配置方式。Spring提供了三个这样的数据源类:
DriverManagerDataSource:在每个连接请求时都会返回一个新建的连接
SimpleDriverDataSource:与DriverManagerDataSource的工作方式类似,但是它直接使用JDBC驱动,来解决在特定环境下的类加载问题。
SingleConnectionDataSource:在每个连接请求时都会返回同一个连接。
2.通过JNDI查找数据源。
利用Spring,我们可以像使用Spring bean那样配置JNDI中数据源的引用并将其装配到需要的类中。
3.如果你不能从JNDI中查找数据源,那么下一个选择就是直接在Spring中配置数据源连接池。尽管Spring并没有提供数据源连接池的实现,但是我们有多项可用的方案,包括如下开源的实现:
Apache Commons DBCP
c3p0
BoneCP
Spring为JDBC提供的模板类:JdbcTemplate和NamedParameterJdbcTemplate。
在这里,JdbcSpitterRepository类上使用了@Repository注解,这表明它将会在组件扫描的时候自动创建。它的构造器上使用了@Inject注解,因此在创建的时候,会自动获得一个JdbcOperations对象。JdbcOperations是一个接口,定义了JdbcTemplate所实现的操作。
Hibernate是在开发者社区很流行的开源持久化框架。它不仅提供了基本的对象关系映射,还提供了ORM工具所应具有的所有复杂功能,比如缓存、延迟加载、预先抓取以及分布式缓存。
使用Hibernate所需的主要接口是org.hibernate.Session。Session接口提供了基本的数据访问功能,如保存、更新、删除以及从数据库加载对象的功能。通过Hibernate的Session接口,应用程序的Repository能够满足所有的持久化需求。
获取Hibernate Session对象的标准方式是借助于Hibernate SessionFactory接口的实现类。除了一些基他的任务,SessionFactory主要负责Hibernate Session的打开、关闭以及管理。
基于JPA的应用程序需要使用EntityManagerFactory的实现类来获取EntityManager实例。JPA定义了两种类型的实体管理器:应用程序管理类型,容器管理类型。
以上的两种实体管理器实现了同一个EntityManager接口。关键的区别不在于EntityManager本身,而是在于EntityManager的创建和管理方式。应用程序管理类型的EntityManager是由EntityManagerFactory创建的,而后者是通过PersistenceProvider的createEntityManagerFactory()方法得到的。与此相对,容器管理类型的EntityManagerFactory是通过PersistenceProvider的createContainerEntityManagerFactory()方法获得的。
这两种实体管理器工厂分别由对应的Spring工厂Bean创建:
LocalEntityManagerFactoryBean生成应用程序管理类型的EntityManagerFactory。
LocalContainerEntityManagerFactoryBean生成容器管理类型的EntityManagerFactory。
三个主要内容:依赖注入,切面,模板代码
<beans>
<aop:config>
<aop:aspect ref="minstrel">
<aop:pointcut id="embark" expression="execution(* *.embarkOnQuest(...))" />
<aop:before pointcut-ref="embark" method="singBeforeQuest" />
<aop:after pointcut-ref="embark" method="singAfterQuest">
</aop:aspect>
</aop:config>
</beans>
表达式的语法采用的是AspectJ的切点表达式语言。
public Employee getEmployeeById(long id){
return jdbcTemplate.queryForObject(
"select id, firstname, lastname, salary from employee where id=?",
new RowMapper<Employee>(){
public Employee mapRow(ResultSet rs, int rowNum) throws SQLException {
Employee employee = new Employee();
employee.setId(rs.getLong("id"));
employee.setFirstName(rs.getString("firstname"));
employee.setLastName(rs.getString("lastname"));
employee.setSalary(rs.getBigDecimal("salary"));
return employee;
}
}
)
}
常用的应用上下文:
AnnotationConfigApplicationContext
AnnotationConfigWebApplicationContext
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
XmlWebApplicationContext
Bean的生存周期:
实例化 => 填充属性 => BeanNameAware => BeanFactoryAware => ApplicationContextAware
=> BeanPostProcessor的预初始化方法 => InitializingBean的afterPropertiesSet方法 => 调用自定义初始化方法 => 调用BeanPostProcessor的初始化后方法
=> Bean可以使用了
=> 调用DisposableBean的destroy()方法 => 调用自定义销毁方法
Spring框架的模块分类:
1. Spring核心容器: Beans, Core, Context, Expression, Context support
2. 面向切面编程: AOP, Aspects
3. 数据访问与集成:JDBC, Transaction, ORM, OXM, Messaging, JMS
4. Web与远程调用:Web, Web servlet, Web portlet, WebSocket
5. Instrumentation: Instrument, Instrument Tomcat
6. Test
Spring提供了三种主要的装配机制:
1. 在XML中进行显式配置
2. 在Java中进行显式配置
3. 隐式的bean发现机制和自动装配。
自动发现:
@Configuration
@ComponentScan
public class CDPlayerConfig{
}
按照默认规则,它会以配置类所在的包作为基础包来扫描组件。
@ComponentScan("soundsystem")
@ComponentScan(basePackages={"soundsystem", "video"})
@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest{
@Test
public void fn(){
System.out.println(1);
}
}
profile bean。
Spring为环境相关的bean所提供的解决方案其实与构建时的方案没有太大的差别。当然,在这个过程中需要根据环境决定该创建哪个bean和不创建哪个bean。不过Spring并不是在构建的时候做出这样的决策,而是等到运行时再来确定。这样的结果就是同一个部署单元(可能会是WAR文件)能够适用于所有的环境,没有必要进行重新构建。
Spring在确定哪个profile处于激活状态时,需要依赖两个独立的属性:spring.profiles.active和spring.profiles.default。
有多种方式来设置这两个属性:
作为DispatcherServlet的初始化参数。
作为Web应用的上下文参数
作为JNDI条目
作为环境变量
作为JVM的系统属性
在集成测试类上,使用@ActiveProfiles注解设置。
Spring定义了多种作用域,可以基于这些作用域创建bean,包括:
1. 单例(Singleton):在整个应用中,只创建bean的一个实例。
2. 原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
3. 会话(Session):在Web应用中,为每个会话创建一个bean实例。
4. 请求(Request):在Web应用中,为每个请求创建一个bean实例。
@Component
@Scope(
value=WebApplicationContext.SCOPE_SESSION,
proxyMode=ScopedProxyMode.INTERFACES)
public ShoppingCart cart(){ ... }
proxyMode这个属性解决了将会话或请求作用域的bean注入到单例bean中所遇到的问题。
在@Configuration类上可以指定@PropertySource注解来指定属性来源,之后就可以注入Environment来调用它的getProperty方法获取属性性。
Spring切面可以应用5种类型的通知:
1. 前置通知
2. 后置通知
3. 返回通知
4. 异常通知
5. 环绕通知
第10章
在开发应用的持久层的时候,会面临多种选择,我们可以使用JDBC、Hibernate、Java持久化API(JPA)或者其他任意的持久化框架。
为了避免持久化的逻辑分散到应用的各个组件中,最好将数据访问的功能放到一个或多个专注于此项任务的组件中。这样的组件通常称为数据访问对象(DAO)或Repository。
为了避免应用与特定的数据访问策略耦合在一起,编写良好的Repository应该以接口的方式暴露功能。
如果在阅读了上面几段文字之后,你能感受到我倾向于将持久层隐藏在接口之后,那很高兴我的目的达到了。我相信接口是实现松耦合代码的关键,并且应将其用于应用程序的各个层,而不仅仅是持久化层。
Spring提供与平台无关的持久化异常,这意味着我们可以使用Spring抛出一致的异常,而不用关心所选择的持久化方案。
这些异常都继承自DataAccessException,DataAccessException的特殊之处在于它是一个非检查型异常。换句话说,没有必要捕获Spring所抛出的数据访问异常(当然,如果你想捕获的话也是完全可以的)。
配置数据源的三种方式:
1. 通过JDBC驱动程序定义的数据源。
2. 通过JNDI查找的数据源。
3. 连接池的数据源。
对于即将发布到生产环境中的应用程序,我建议使用从连接池获取连接的数据源。如果可能的话,我倾向于通过应用服务器的JNDI来获取数据源。
1.在Spring中,通过JDBC驱动定义数据源是最简单的配置方式。Spring提供了三个这样的数据源类:
DriverManagerDataSource:在每个连接请求时都会返回一个新建的连接
SimpleDriverDataSource:与DriverManagerDataSource的工作方式类似,但是它直接使用JDBC驱动,来解决在特定环境下的类加载问题。
SingleConnectionDataSource:在每个连接请求时都会返回同一个连接。
2.通过JNDI查找数据源。
利用Spring,我们可以像使用Spring bean那样配置JNDI中数据源的引用并将其装配到需要的类中。
<jee:jndi-lookup id="dataSource" jndi-name="/jdbc/SpitterDSxx" resource-ref="true">
3.如果你不能从JNDI中查找数据源,那么下一个选择就是直接在Spring中配置数据源连接池。尽管Spring并没有提供数据源连接池的实现,但是我们有多项可用的方案,包括如下开源的实现:
Apache Commons DBCP
c3p0
BoneCP
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
p:driverClassName="org.h2.Driver"
p:url="jdbc:h2:tcp://localhost/~/spitter"
p:username="sa"
p:password=""
p:initialSize="5"
p:maxActive="10" />
@Bean
public BasicDataSource dataSource(){
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("org.h2.Driver");
ds.setUrl("jdbc:h2:tcp://localhost/~/spitter");
ds.setUsername("sa");
ds.setPassword("");
ds.setInitialSize(5);
ds.setMaxActive(10);
return ds;
}
嵌入式的数据源
<jdbc:embedded-database id="dataSource" type="H2">
<jdbc:script location="com/habuma/spitter/db/jdbc/schema.sql" />
<jdbc:script location="com/habuma/spitter/db/jdbc/test-data.sql" />
</jdbc:embedded-database>
@Bean
public DataSource dataSource(){
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(SQL_SELECT_SPITTER);
stmt.setLong(1, id)
ResultSet rs = stmt.executeQuery();
Spring为JDBC提供的模板类:JdbcTemplate和NamedParameterJdbcTemplate。
配置JdbcTemplate bean:
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
使用:
@Repository
public class JdbcSpitterRepository implements SpitterRepository{
private JdbcOperations jdbcOperations;
@Inject
public JdbcSpitterRepository(JdbcOperations jdbcOperations) {
this.jdbcOperations = jdbcOperations;
}
}
在这里,JdbcSpitterRepository类上使用了@Repository注解,这表明它将会在组件扫描的时候自动创建。它的构造器上使用了@Inject注解,因此在创建的时候,会自动获得一个JdbcOperations对象。JdbcOperations是一个接口,定义了JdbcTemplate所实现的操作。
public void addSpitter(Spitter spitter){
jdbcOperations.update(INSERT_SPITTER,
spitter.getUsername(),
spitter.getPassword(),
spitter.getFullName(),
spitter.getEmail(),
spitter.isUpdateByEmail());
}
public Spitter findOne(long id) {
return jdbcOperations.queryForObject(
SELECT_SPITTER_BY_ID,
(rs, rowNum) -> {
return new Spitter(xxx);
}, id);
}
或用方法引用
public Spitter findOne(long id) {
return jdbcOperations.queryForObject(SELECT_SPITTER_BY_ID, this::mapSpitter, id);
}
public Spitter mapSpitter(ResultSet rs, int row) throws SQLException{
xxx
}
Hibernate是在开发者社区很流行的开源持久化框架。它不仅提供了基本的对象关系映射,还提供了ORM工具所应具有的所有复杂功能,比如缓存、延迟加载、预先抓取以及分布式缓存。
使用Hibernate所需的主要接口是org.hibernate.Session。Session接口提供了基本的数据访问功能,如保存、更新、删除以及从数据库加载对象的功能。通过Hibernate的Session接口,应用程序的Repository能够满足所有的持久化需求。
获取Hibernate Session对象的标准方式是借助于Hibernate SessionFactory接口的实现类。除了一些基他的任务,SessionFactory主要负责Hibernate Session的打开、关闭以及管理。
@Bean
public LocalSessionFactoryBean sessionFactory(DataSource ds) {
LocalSessionFactoryBean sfb = new LocalSessionFactoryBean();
sfb.setDataSource(ds);
sfb.setPackagesToScan(new String[]{"com.habuma.spittr.domain"});
Properties props = new Properties();
props.setProperty("dialect", "org.hibernate.dialect.H2Dialect");
sfb.setHibernateProperties(props);
return sfb;
}
基于JPA的应用程序需要使用EntityManagerFactory的实现类来获取EntityManager实例。JPA定义了两种类型的实体管理器:应用程序管理类型,容器管理类型。
以上的两种实体管理器实现了同一个EntityManager接口。关键的区别不在于EntityManager本身,而是在于EntityManager的创建和管理方式。应用程序管理类型的EntityManager是由EntityManagerFactory创建的,而后者是通过PersistenceProvider的createEntityManagerFactory()方法得到的。与此相对,容器管理类型的EntityManagerFactory是通过PersistenceProvider的createContainerEntityManagerFactory()方法获得的。
这两种实体管理器工厂分别由对应的Spring工厂Bean创建:
LocalEntityManagerFactoryBean生成应用程序管理类型的EntityManagerFactory。
LocalContainerEntityManagerFactoryBean生成容器管理类型的EntityManagerFactory。