第1章:Spring之旅
- 简单得说,spring通过面向POJO编程、依赖注入、AOP和模板技术来简化Java开发的复杂性
第2章:装配Bean
<!-- 依赖注入应该引入以下库,以Maven为例,其他的包会自动下载 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
(1)声明Bean
<?xml version="1.0" encoding="UTF-8" ?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="duke" class="spring.test.bean.Juggler">
<!-- 通过构造器注入 -->
<constructor-arg value="15" />
<!-- 通过构造器注入引用对象 -->
<constructor-arg ref="sonnet29" />
</bean>
<!-- 通过工厂方法创建Bean -->
<bean id="stage" class="spring.test.bean.Stage"
factory-method="getInstance" />
<!-- Bean的作用域
singleton:在每一个Spring容器中,一个Bean定义只有一个对象实例
prototype:允许Bean的定义可以被实例化任意次
request、session、global-session -->
<bean id="duke" class="spring.test.bean.Ticket"
scope="prototype"/>
<!-- 注入Bean属性,通过setter -->
<bean id="kenny" class="spring.test.bean.Instrumentalist">
<property name="song" value="Jingle Bells" />
<property name="instrument" ref="saxophone" />
<!-- 装配集合,list、set、map、props等 -->
</bean>
<!-- 使用表达式装配 SpEL,SpEl语法跟java差不多。一些用法:
1、something?.pro,判断是否为空,再取值
2、T(java.lang.Math).PI,T()获取类 -->
<bean id="saxophone" class="spring.test.bean.Saxophone" >
<property name="saxophone" value="#{SpEL}"
</bean>
</beans>
第3章:最小化Spring XML配置
(1)自动装配Bean属性
- byName:把与Bean得属性具有相同名字的其他Bean自动装配
- byType:相同属性的
- constructor:与构造器具有相同属性的
- autodetect:首先尝试constructor,再尝试byName
<beans
<!-- 省略一大堆 -->
<!-- 默认自动装配 -->
default-autowire="byType">
<bean id="duke" class="spring.test.bean.Juggler"
autowire="byType">
<!-- 可以覆盖默认 -->
</bean>
<bean id="duke2" class="spring.test.bean.Juggler"
primary="false">
<!-- 多个Bean时候,可以设置首选,默认是true,得把其他的全设为false -->
</bean>
<bean id="duke2" class="spring.test.bean.Juggler"
autowire-candidate="false">
<!-- 不让这个Bean成为autowire的扫描对象 -->
</bean>
</beans>
(2)使用注解装配
<beans/> <!-- 省略一大堆 -->
<!-- 开启注解装配 -->
<context:annotation-config>
<bean id="duke3" class="spring.test.bean.Juggler" >
<qualifier value="test" />
<!-- 限定值设置 -->
</bean>
</beans>
- 使用@Autowired
- @Qualifier(“id”)
- @Qualifier(“value”)
- 自定义Qualifier,新建一个类
- 使用@Inject:跟Autowired用法基本一样,是Java依赖注入规范
- 配套使用的是 @Named 加限定
(3)自动检测Bean
- 转为Bean的注解
- @Component:通用的构造型注解,标识伪Spring组件
- @Controller:SpringMVC controller注解
- @Repository:标识数据仓库
- @Service:标识服务
<beans/> <!-- 省略一大堆 -->
<!-- 开启自动检测 -->
<context:component-scan base-package="spring.test.bean">
<context:include-filter type="assignable"
expression="some.class" />
<context:exclude-filter type="assignable"
expression="some.class" />
</context:component-scan>
</beans>
- 过滤组建扫描
- annotation:过滤注解的类
- assignable:过滤派生于注解的类
- 等等
(4)使用基于Java的配置
利用@Configuration注解来标识Java配置类
第4章:面向切面的Spring
<!-- 面向切面应该引入以下的库,以maven为例 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.10</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
在软件开发中,分布于应用中多处的功能被称为横切关注点。通常,这些横切关注点从概念上是与应用的业务逻辑相分离的。将这些横切关注点与业务逻辑相分离正是面向切面编程所要解决的。
(1)定义切点
execution(* spring.test.bean.Class.function(..)) && others
(2)在XML中声明切面
<?xml version="1.0" encoding="UTF-8" ?>
<beans <!-- 新添加 -->
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean
id="poeticDuke"
class="spring.test.bean.PoeticJuggler">
</bean>
<bean
id="audience"
class="spring.test.bean.Audience">
</bean>
<aop:config>
<aop:aspect ref="audience">
<!-- 定义切点 -->
<aop:pointcut
id="performance"
expression="execution(* spring.test.bean.Performer.perform(..))" />
<aop:before
method="takeSeats"
pointcut="execution(* spring.test.bean.Performer.perform(..))" /> <!-- perform为调用的方法 -->
<aop:after-returning
method="applaud" <!-- applaud为audience的方法 -->
pointcut-ref="performance" />
</aop:aspect>
</aop:config>
</beans>
- 声明环绕通知
// Audience 类中定义此方法,ProceedingJoinPoint作为方法的入参
public void watchPerformance(ProceedingJoinPoint joinPoint) {
try {
// do something
// 谨记必须调用proceed方法,否则本通知会阻塞被通知的方法,即perform()方法
joinpoint.proceed();
// do other things
}
}
<aop:config>
<aop:aspect ref="audience">
<aop:around
method="watchPerformance()" <!-- 先do something,然后调用perform(),再do other things -->
pointcut="execution(* spring.test.bean.Performer.perform(..))" /> <!-- perform为调用的方法 -->
</aop:aspect>
</aop:config>
- 为已有Bean引入接口
<aop:aspect>
<aop:declare-parents
<!-- 匹配已有的bean -->
types-matching="spring.test.bean.Performer+"
<!-- 需要添加到bean的接口 -->
implement-interface="spring.test.bean.NewInterface"
<!-- 接口的默认实现方法 -->
default-impl="spring.test.bean.InterfaceInstance"
/>
</aop:aspect>
(3)基于注解的切面
@Aspect
public class Audience {
@Pointcut("execution(* spring.test.bean.Performer.perform(..))")
public void performance() {
}
@Before("performance()")
public void takeSeats() {
System.out.println("Take seat");
}
}
<!-- 添加此配置 -->
<aop:aspectj-autoproxy />
- 基于注解的同样可以
- 注解环绕通知
- 传递参数给所标注的通知
- 引入新接口
Spring切面仍然只是基于代理的,而且限于通知方法的调用。如果需要的功能超过了Spring所支持的方法代理,那么可以考虑使用AspectJ,Spring只有简单功能的aop。
第5章:装配Bean
- JDBC只提供了SQLException异常,以至发生异常不知道问题在哪。spring提供了多个数据访问异常,分别描述他们抛出时所对应的问题
- 针对不同持久化平台,spring提供多个可选的模版
- spring针对不同模版提供了对应的DAO支持类
(1)配置数据源
- 使用JDNI数据源
- 需要在web server中配置数据源,需作相应部署
- 使用数据源连接池
- 第三方依赖包中包含了两个数据源的实现类包,其一是Apache的DBCP,其二是 C3P0(推荐使用)
- 基于JDBC驱动数据源
- 没有使用连接池,项目中少用
<bean id="dataSource class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://ip:3306/zhihu" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="mysql" class="spring.test.db.Mysql">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
(2)使用JDBC DAO支持类
- DAO支持类中定义好了获取Jdbc模版的相应方法
(3)Java持久化API
- spring还支持 java persistence API,JPA是一套规范,Hibernate,TopLink,JDO他们是一套产品,这些产品实现了这个JPA规范,JPA有点像JDBC,为各种不同的ORM技术提供一个统一的接口,方便把应用移植的不同的ORM技术上。
第6章:事务管理
Spring提供的事务管理:
- 编码式
- 对事务边界控制精确,但是Spring的编码式事务是侵入式的,需修改类的实现
- 声明式
- 通常情况下,事务需求不会要求很高的事务边界控制,故用的较多
(1)事务管理器
- Spring并不直接管理事务,而是提供了多种事务管理器,将事务管理的职责委托给其他平台相关的事务实现
example: JDBC事务
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
(2)声明式事务
- 属性
- 传播行为:新的事务应该被启动还是被挂起,或者方法是否要在事务环境中运行
- 隔离级别:一个事务可能受其他并发事务影响的程度
- 只读
- 事务超时
- 回滚规则
(3)在xml中定义事务
<tx:advice id="txAdvice" transaction-manager="yourManager">
<tx:attributes>
<!-- 采用方法名匹配 -->
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 采用切点匹配 -->
<aop:advisor
pointcut="execution(* *..SpitterService.*(..))"
advice-ref="txAdvice" />
</aop:config>
(4)在注解驱动中定义
<tx:annotation-driver transaction-manager="yourManager" />
@Transactional(propagation=Propagation.SUPPORTS, readOnly=true)
public class ServiceImpl implements Service {
...
@Transactional(propagation=Propagation.REQUIRED, readOnly=false)
public void addSomething() {
}
}
第7章:使用Spring MVC
- 与大多数基于Java的Web框架一样,Spring MVC所有的请求都会通过一个前端控制器Servlet——DispatcherServlet
(1)搭建Spring MVC
web.xml
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- 声明DispatcherServlet,通过这一前端控制器Servlet,
委托给其他组件来执行实际的处理 -->
<servlet>
<!-- DispatcherServlet将尝试从一个名为WEB-INF/weibo-servlet.xml加载应用上下文 -->
<servlet-name>weibo</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>weibo</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
weibo-servlet.xml
<!-- 静态资源请求 -->
<mvc:resources
mapping="/resources/**"
location="/resources/" />
<!-- mvc注解扫描 -->
<mvc:annotation-driven />
<context:component-scan base-package="spring.test.mvc" />
<!-- 配置view的解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 默认InternalResourceViewResolver创建的view是InternalResourceView
若需要使用jstl标签,需配置viewclass将其替换为JstlView -->
<property
name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property
name="prefix"
value="/WEB-INF/views/" />
<property
name="suffix"
value=".jsp" />
</bean>
(2)基本控制器
@Controller
public class HomeController {
public static final int DEFAULT_WEIBO_PER_PAGE = 25;
// 映射路径
@RequestMapping({"/", "/home"})
public String showHomePage(Map<String, Object> model) {
model.put("weibos", new WeiboHome());
return "home";
}
}
(3)控制器输入
@Controller
@RequestMapping("/weiboo") // 定义大的路径
public class Weiboo {
@RequestMapping(value="/boo", method=GET) // 路径为/weiboo/boo?name=lin
// 请求参数如果与下面的参数名一样,可省略@RequestParam("name")
// model是spring自己定义的map,会根据addAttribute的类,定义key
public String listWeiboo(@RequestParam("name") String name, Model model) {
model.addAttribute("name", name);
model.addAttribute("time", "2017");
return "weiboo";
}
}
weiboo.jsp
<!-- 我的情况不加这句ei表达式不起作用 -->
<%@ page isELIgnored="false" %>
<div>
<h1>This is weiboo of ${name}</h1>
<h1>Time is ${time}</h1>
</div>