1. HelloWorld
(Spring核心之IoC(bean通过反射创建对象,property name根据set方法注入属性,ClassPathXMLApplication()加载xml文件,ApplicationContext ctx.getBean(),可以根据id名称指定,也可以通过HelloWorld.class) &)
HelloWorld.java
package com.atguigu.spring.helloworld;
public class HelloWorld {
private String user;
public HelloWorld() {
System.out.println("HelloWorld's constructor...");
}
public void setUser(String user) {
System.out.println("setUser:" + user);
this.user = user;
}
public HelloWorld(String user) {
this.user = user;
}
public void hello(){
System.out.println("Hello: " + user);
}
}
Main.java
package com.atguigu.spring.helloworld;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
// HelloWorld helloWorld = new HelloWorld();
// helloWorld.setUser("Tom");
// helloWorld.hello();
//1. 创建 Spring 的 IOC 容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
//2. 从 IOC 容器中获取 bean 的实例
HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloWorld3");
//根据类型来获取 bean 的实例: 要求在 IOC 容器中只有一个与之类型匹配的 bean, 若有多个则会抛出异常.
//一般情况下, 该方法可用, 因为一般情况下, 在一个 IOC 容器中一个类型对应的 bean 也只有一个.
// HelloWorld helloWorld1 = ctx.getBean(HelloWorld.class);
//3. 使用 bean
helloWorld.hello();
Car car = (Car) ctx.getBean("car");
System.out.println(car);
Car car2 = (Car) ctx.getBean("car2");
System.out.println(car2);
//4. 测试使用集合属性
User user = (User) ctx.getBean("user5");
System.out.println(user);
}
}
2. IOC&DI概述
(Ioc容器如何工作(IOC或DI称为依赖注入或反转控制,采用工厂设计模式,通过spring容器来管理bean的生命周期,降低代码耦合度)&)
3. 配置 Bean
(ApplicationContext与ApplicationContextAware(ApplicationContext初始化spring的IOC容器,之后通过IOC实例中获取bean,ClassPathXMLApplication是ApplicationContext的实现类从类路基加载配置文件)&创建Spring应用上下文(ConfigurableApplicationContext)(具有刷新和关闭上下文的功能)&带有参数的构造方法类的装配(constructor-arg value=”” index=0” type=”java.lang.String””)&)
4. 属性配置细节
(属性注入(包含特殊字符要用进行包装,通过ref引用其他bean对象,可以在property中使用内部bean,通过注入null值,级联属性注入car.price 必须先初始化之后要有get/set,通过方式注入集合属性)&)
5. 自动装配
(Spring自动装配之依赖注入()&)
Main.java
package com.atguigu.spring.annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-annotation.xml");
UserAction userAction = ctx.getBean(UserAction.class);
userAction.execute();
}
}
package com.atguigu.spring.annotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserAction {
@Autowired
private UsreService usreService;
public void execute(){
System.out.println("接受请求");
usreService.addNew();
}
}
package com.atguigu.spring.annotation;
import org.springframework.stereotype.Service;
@Service
public class UserDao {
public void save(){
System.out.println("保存新用户");
}
}
package com.atguigu.spring.annotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UsreService {
@Autowired
private UserDao userDao;
public void addNew(){
System.out.println("添加新用户");
userDao.save();
}
}
6.Bean 之间的关系
(Spring Bean作用域的区别是什么(通过parent继承其他bean,abstract=”true”定义bean为抽象bean)&)
7. Bean 的作用域
(Spring Bean作用域的区别是什么(scope=”property”可以指定是否为单例)&)
package com.atguigu.spring.ref;
import java.sql.SQLException;
import java.text.DateFormat;
import java.util.Date;
import javax.sql.DataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.atguigu.spring.helloworld.User;
public class Main {
public static void main(String[] args) throws SQLException {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-auto.xml");
Action action = ctx.getBean(Action.class);
action.execute();
//测试 bean 的作用域
Dao dao1 = (Dao) ctx.getBean("dao2");
Dao dao2 = (Dao) ctx.getBean("dao2");
System.out.println(dao1 == dao2);
//测试使用外部属性文件
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
System.out.println(dataSource.getConnection());
//测试 spEL
User boy = (User) ctx.getBean("boy");
System.out.println(boy.getUserName() + ":" + boy.getWifeName());
// DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL);
DateFormat dateFormat = (DateFormat) ctx.getBean("dateFormat");
System.out.println(dateFormat.format(new Date()));
Date date = (Date) ctx.getBean("datetime");
System.out.println(date);
User user = (User) ctx.getBean("user");
System.out.println(user);
ctx.close();
}
}
applicationContext.xml
applicationContext-annotation.xml
applicationContext-aop.xml
8. 使用外部属性文件
(PropertyPlaceholderConfigurer( 在类路径下src或resources或lib下) &)
9. SpEL
(增加SpEL语言的支持(支持逻辑运算and or not 或if/else运算符判断,正则表达式,T()调用类的静态属性和方法)&)
beans.xml
]]>
beans-annotation.xml
beans-auto.xml
db.properties
jdbc.user=root
jdbc.password=1230
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql:///test
jdbc.initPoolSize=5
jdbc.maxPoolSize=10
10. 管理 Bean 的生命周期
(请解释Spring Bean的生命周期(init-method=”initMethod”初始化方法,destory-method销毁方法)&对 Bean 的处理(BeanPostProcessor)(对容器中所有Bean实例进行前后处理,postProcessBeforeInitialization)&)
package com.atguigu.spring.ref;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import com.atguigu.spring.helloworld.User;
public class MyBeanPostProcessor implements BeanPostProcessor {
//该方法在 init 方法之后被调用
@Override
public Object postProcessAfterInitialization(Object arg0, String arg1)
throws BeansException {
if(arg1.equals("boy")){
System.out.println("postProcessAfterInitialization..." + arg0 + "," + arg1);
User user = (User) arg0;
user.setUserName("李大齐");
}
return arg0;
}
//该方法在 init 方法之前被调用
//可以工作返回的对象来决定最终返回给 getBean 方法的对象是哪一个, 属性值是什么
/**
* @param arg0: 实际要返回的对象
* @param arg1: bean 的 id 值
*/
@Override
public Object postProcessBeforeInitialization(Object arg0, String arg1)
throws BeansException {
if(arg1.equals("boy"))
System.out.println("postProcessBeforeInitialization..." + arg0 + "," + arg1);
return arg0;
}
}
11. 通过工厂方法配置 Bean
(静态工厂实例化(静态工厂方法调用静态方法 传入参数创建bean,实例工厂方法需要先创建工厂本身再创建对象的实例方法factory-bean)&)
12. 通过 FactoryBean 配置 Bean
(工厂方法与FactoryBean(通过FactoryBean来创建bean的实例,implements FactoryBean getObject返回bean实例,通过属性注入属性值)&)
package com.atguigu.spring.ref;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.FactoryBean;
import com.atguigu.spring.helloworld.Car;
import com.atguigu.spring.helloworld.User;
public class UserBean implements FactoryBean{
/**
* 返回的 bean 的实例
*/
@Override
public User getObject() throws Exception {
User user = new User();
user.setUserName("abc");
user.setWifeName("ABC");
List cars = new ArrayList();
cars.add(new Car("ShangHai", "BuiKe", 180, 300000));
cars.add(new Car("ShangHai", "CRUZE", 130, 150000));
user.setCars(cars);
return user;
}
/**
* 返回的 bean 的类型
*/
@Override
public Class getObjectType() {
return User.class;
}
/**
* 返回的 bean 是否为单例的
*/
@Override
public boolean isSingleton() {
return true;
}
}
13. 通过注解配置 Bean(1)
(注解配置(@Component)( 要扫描的基类包,resource-pattern=”resposity/*.class”扫描包下特定的类,bean的ID是第一个字母小写的类名,不包含注解exclude-filter,use-default-filter=”false”不适用默认的注解)&)
14.通过注解配置 Bean(2)
(使用 @Autowired注解向构造方法进行注(@Repository(“userRepository”),@Autowired自动装配,可以放在类属性或者set方法上bean的名字要与属性值一致,required=false没有装配上打印为null,通过@Qualifier指定要装配指定的bean)&)
15. 泛型依赖注入
(更加丰富的泛型形式(泛型依赖注入,@Autowired没有在类上加入注解,注解会被子类继承)&)
package com.atguigu.spring.annotation.generic;
public class BaseDao {
public void save(T entity){
System.out.println("Save:" + entity);
}
}
package com.atguigu.spring.annotation.generic;
import org.springframework.beans.factory.annotation.Autowired;
public class BaseService {
@Autowired
private BaseDao dao;
public void addNew(T entity){
System.out.println("addNew by " + dao);
dao.save(entity);
}
}
package com.atguigu.spring.annotation.generic;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-annotation.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.addNew(new User());
RoleService roleService = (RoleService) ctx.getBean("roleService");
roleService.addNew(new Role());
}
}
16. AOP 基础
(AOP的介绍(开发者只用关注自己的业务代码,解耦当需求变化时,AOP通过动态代理来实现)&)
package com.atguigu.spring.helloworld;
public class HelloWorld {
//字段
private String user;
public HelloWorld() {
System.out.println("HelloWorld's constructor...");
}
//JavaBean 使用 setter 和 getter 来定义属性
public void setUserName(String user) {
System.out.println("setUserName:" + user);
this.user = user;
}
public void hello(){
System.out.println("Hello:" + user);
}
public void init(){
System.out.println("init method...");
}
public void destroy(){
System.out.println("destroy method...");
}
}
package com.atguigu.spring.helloworld;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
//1. 创建 IOC 容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 从 IOC 容器中获取 bean 实例
HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloWorld");
//3. 调用 bean 的方法
helloWorld.hello();
HelloWorld helloWorld2 = (HelloWorld) ctx.getBean("helloWorld");
System.out.println(helloWorld == helloWorld2);
//4. 关闭容器
ctx.close();
}
}
17. 前置通知
(使用@AspectJ注解开发Spring AOP(@Aspect定义一个切面,@Before(“execution()”)前置通知在目标方法开始执行之前执行注解自动起作用,Joinpoint连接点,joinPoint.getSignature().getName();方法名,joinPoint.getArgs()方法参数)&)
18. 后置通知
(Before/After模式(@After目标方法执行之后执行,无论方法是否出现异常,目标方法返回结果需要在返回通知访问)&)
19. 返回通知&异常通知&环绕通知
(GPAfterThrowingAdvice(@AfterReturning返回通知,方法正常结束后执行,returning=”result”为返回值,异常通知目标方法出现异常时执行,NullPointException只有在空指针异常时才会执行,@Around环绕通知需要有ProceedingJoinPoint参数,参数可以决定是否执行目标方法,相当于动态代理全过程,pjd.proceed()执行目标方法,前面是前置通知,)&)
package com.atguigu.spring.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
// ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl();
//
// arithmeticCalculator =
// new ArithmeticCalculatorLoggingProxy(arithmeticCalculator).getLoggingProxy();
//
// int result = arithmeticCalculator.add(11, 12);
// System.out.println("result:" + result);
//
// result = arithmeticCalculator.div(21, 3);
// System.out.println("result:" + result);
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-aop.xml");
ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");
System.out.println(arithmeticCalculator.getClass().getName());
int result = arithmeticCalculator.add(11, 12);
System.out.println("result:" + result);
result = arithmeticCalculator.div(21, 3);
System.out.println("result:" + result);
}
}
package com.atguigu.spring.aop;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* AOP 的 helloWorld
* 1. 加入 jar 包
* com.springsource.net.sf.cglib-2.2.0.jar
* com.springsource.org.aopalliance-1.0.0.jar
* com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
* spring-aspects-4.0.0.RELEASE.jar
*
* 2. 在 Spring 的配置文件中加入 aop 的命名空间。
*
* 3. 基于注解的方式来使用 AOP
* 3.1 在配置文件中配置自动扫描的包:
* 3.2 加入使 AspjectJ 注解起作用的配置:
* 为匹配的类自动生成动态代理对象.
*
* 4. 编写切面类:
* 4.1 一个一般的 Java 类
* 4.2 在其中添加要额外实现的功能.
*
* 5. 配置切面
* 5.1 切面必须是 IOC 中的 bean: 实际添加了 @Component 注解
* 5.2 声明是一个切面: 添加 @Aspect
* 5.3 声明通知: 即额外加入功能对应的方法.
* 5.3.1 前置通知: @Before("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(int, int))")
* @Before 表示在目标方法执行之前执行 @Before 标记的方法的方法体.
* @Before 里面的是切入点表达式:
*
* 6. 在通知中访问连接细节: 可以在通知方法中添加 JoinPoint 类型的参数, 从中可以访问到方法的签名和方法的参数.
*
* 7. @After 表示后置通知: 在方法执行之后执行的代码.
*/
//通过添加 @Aspect 注解声明一个 bean 是一个切面!
@Aspect
@Component
public class LoggingAspect {
@Before("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(int, int))")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
Object [] args = joinPoint.getArgs();
System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
}
@After("execution(* com.atguigu.spring.aop.*.*(..))")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends");
}
}
package com.atguigu.spring.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class ArithmeticCalculatorLoggingProxy {
//要代理的对象
private ArithmeticCalculator target;
public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
super();
this.target = target;
}
//返回代理对象
public ArithmeticCalculator getLoggingProxy(){
ArithmeticCalculator proxy = null;
ClassLoader loader = target.getClass().getClassLoader();
Class [] interfaces = new Class[]{ArithmeticCalculator.class};
InvocationHandler h = new InvocationHandler() {
/**
* proxy: 代理对象。 一般不使用该对象
* method: 正在被调用的方法
* args: 调用方法传入的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String methodName = method.getName();
//打印日志
System.out.println("[before] The method " + methodName + " begins with " + Arrays.asList(args));
//调用目标方法
Object result = null;
try {
//前置通知
result = method.invoke(target, args);
//返回通知, 可以访问到方法的返回值
} catch (NullPointerException e) {
e.printStackTrace();
//异常通知, 可以访问到方法出现的异常
}
//后置通知. 因为方法可以能会出异常, 所以访问不到方法的返回值
//打印日志
System.out.println("[after] The method ends with " + result);
return result;
}
};
/**
* loader: 代理对象使用的类加载器。
* interfaces: 指定代理对象的类型. 即代理代理对象中可以有哪些方法.
* h: 当具体调用代理对象的方法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 方法
*/
proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
return proxy;
}
}
package com.atguigu.spring.aop;
public class ArithmeticCalculatorLoggingImpl implements ArithmeticCalculator {
@Override
public int add(int i, int j) {
System.out.println("The method add begins with [" + i + "," + j + "]");
int result = i + j;
System.out.println("The method add ends with " + result);
return result;
}
@Override
public int sub(int i, int j) {
System.out.println("The method sub begins with [" + i + "," + j + "]");
int result = i - j;
System.out.println("The method sub ends with " + result);
return result;
}
@Override
public int mul(int i, int j) {
System.out.println("The method mul begins with [" + i + "," + j + "]");
int result = i * j;
System.out.println("The method mul ends with " + result);
return result;
}
@Override
public int div(int i, int j) {
System.out.println("The method div begins with [" + i + "," + j + "]");
int result = i / j;
System.out.println("The method div ends with " + result);
return result;
}
}
package com.atguigu.spring.aop;
import org.springframework.stereotype.Component;
@Component("arithmeticCalculator")
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
@Override
public int add(int i, int j) {
int result = i + j;
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
return result;
}
}
package com.atguigu.spring.aop;
public interface ArithmeticCalculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
20. 切面的优先级
(Order(@Order指定通知的优先级)&)
package com.atguigu.spring.ref;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
//1. 创建 IOC 容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-annotation.xml");
UserAction userAction = (UserAction) ctx.getBean("userAction");
userAction.execute();
}
}
package com.atguigu.spring.ref;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserAction {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public void execute(){
System.out.println("execute...");
userService.addNew();
}
}
package com.atguigu.spring.ref;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
public void save(){
System.out.println("UserDao's save...");
}
}
package com.atguigu.spring.ref;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void addNew(){
System.out.println("addNew...");
userDao.save();
}
}
21. 重用切点表达式
(Pointcut(重用表达式用于声明切入点表达式@Pointcut(“execution(“”)”))&)
22. 基于配置文件的方式来配置 AOP
(Spring MVC也Convention Over Configuration(基于配置文件的方式, ref拿到切面,配置前置通知,指定要切入的方法,throwing与方法中参数名要一致)&)
23. 使用 JdbcTemplate和JdbcDaoSupport
(jdbctemplate的基本使用(配置jdbcTemplate,引入dataSource,jdbcTemplate.update(sql,”sd”,5),批量更新jdbcTemplate.batchUpdate(sql,batchArgs),queryForObject(sql,Employee.class,1),RowMapper行的映射器,sql中要完成列到属性名的映射)&)
applicationContext.xml
applicationContext-tx-xml.xml
db.properties
jdbc.user=root
jdbc.password=1230
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql:///spring
jdbc.initPoolSize=5
jdbc.maxPoolSize=10
package com.atguigu.spring.jdbc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
@Repository
public class EmployeeDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public Employee get(Integer id){
String sql = "SELECT id, last_name lastName, email FROM employees WHERE id = ?";
RowMapper rowMapper = new BeanPropertyRowMapper(Employee.class);
Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, id);
return employee;
}
}
package com.atguigu.spring.jdbc;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;
/**
* 不推荐使用 JdbcDaoSupport, 而推荐直接使用 JdbcTempate 作为 Dao 类的成员变量
*/
@Repository
public class DepartmentDao extends JdbcDaoSupport{
@Autowired
public void setDataSource2(DataSource dataSource){
setDataSource(dataSource);
}
public Department get(Integer id){
String sql = "SELECT id, dept_name name FROM departments WHERE id = ?";
RowMapper rowMapper = new BeanPropertyRowMapper(Department.class);
return getJdbcTemplate().queryForObject(sql, rowMapper, id);
}
}
package com.atguigu.spring.jdbc;
public class Department {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Department [id=" + id + ", name=" + name + "]";
}
}
package com.atguigu.spring.jdbc;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
public class JDBCTest {
private ApplicationContext ctx = null;
private JdbcTemplate jdbcTemplate;
private EmployeeDao employeeDao;
private DepartmentDao departmentDao;
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
{
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate");
employeeDao = ctx.getBean(EmployeeDao.class);
departmentDao = ctx.getBean(DepartmentDao.class);
namedParameterJdbcTemplate = ctx.getBean(NamedParameterJdbcTemplate.class);
}
/**
* 使用具名参数时, 可以使用 update(String sql, SqlParameterSource paramSource) 方法进行更新操作
* 1. SQL 语句中的参数名和类的属性一致!
* 2. 使用 SqlParameterSource 的 BeanPropertySqlParameterSource 实现类作为参数.
*/
@Test
public void testNamedParameterJdbcTemplate2(){
String sql = "INSERT INTO employees(last_name, email, dept_id) "
+ "VALUES(:lastName,:email,:dpetId)";
Employee employee = new Employee();
employee.setLastName("XYZ");
employee.setEmail("xyz@sina.com");
employee.setDpetId(3);
SqlParameterSource paramSource = new BeanPropertySqlParameterSource(employee);
namedParameterJdbcTemplate.update(sql, paramSource);
}
/**
* 可以为参数起名字.
* 1. 好处: 若有多个参数, 则不用再去对应位置, 直接对应参数名, 便于维护
* 2. 缺点: 较为麻烦.
*/
@Test
public void testNamedParameterJdbcTemplate(){
String sql = "INSERT INTO employees(last_name, email, dept_id) VALUES(:ln,:email,:deptid)";
Map paramMap = new HashMap();
paramMap.put("ln", "FF");
paramMap.put("email", "ff@atguigu.com");
paramMap.put("deptid", 2);
namedParameterJdbcTemplate.update(sql, paramMap);
}
@Test
public void testDepartmentDao(){
System.out.println(departmentDao.get(1));
}
@Test
public void testEmployeeDao(){
System.out.println(employeeDao.get(1));
}
/**
* 获取单个列的值, 或做统计查询
* 使用 queryForObject(String sql, Class requiredType)
*/
@Test
public void testQueryForObject2(){
String sql = "SELECT count(id) FROM employees";
long count = jdbcTemplate.queryForObject(sql, Long.class);
System.out.println(count);
}
/**
* 查到实体类的集合
* 注意调用的不是 queryForList 方法
*/
@Test
public void testQueryForList(){
String sql = "SELECT id, last_name lastName, email FROM employees WHERE id > ?";
RowMapper rowMapper = new BeanPropertyRowMapper(Employee.class);
List employees = jdbcTemplate.query(sql, rowMapper,5);
System.out.println(employees);
}
/**
* 从数据库中获取一条记录, 实际得到对应的一个对象
* 注意不是调用 queryForObject(String sql, Class requiredType, Object... args) 方法!
* 而需要调用 queryForObject(String sql, RowMapper rowMapper, Object... args)
* 1. 其中的 RowMapper 指定如何去映射结果集的行, 常用的实现类为 BeanPropertyRowMapper
* 2. 使用 SQL 中列的别名完成列名和类的属性名的映射. 例如 last_name lastName
* 3. 不支持级联属性. JdbcTemplate 到底是一个 JDBC 的小工具, 而不是 ORM 框架
*/
@Test
public void testQueryForObject(){
String sql = "SELECT id, last_name lastName, email, dept_id as \"department.id\" FROM employees WHERE id = ?";
RowMapper rowMapper = new BeanPropertyRowMapper(Employee.class);
Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, 1);
System.out.println(employee);
}
/**
* 执行批量更新: 批量的 INSERT, UPDATE, DELETE
* 最后一个参数是 Object[] 的 List 类型: 因为修改一条记录需要一个 Object 的数组, 那么多条不就需要多个 Object 的数组吗
*/
@Test
public void testBatchUpdate(){
String sql = "INSERT INTO employees(last_name, email, dept_id) VALUES(?,?,?)";
List batchArgs = new ArrayList();
batchArgs.add(new Object[]{"AA", "aa@atguigu.com", 1});
batchArgs.add(new Object[]{"BB", "bb@atguigu.com", 2});
batchArgs.add(new Object[]{"CC", "cc@atguigu.com", 3});
batchArgs.add(new Object[]{"DD", "dd@atguigu.com", 3});
batchArgs.add(new Object[]{"EE", "ee@atguigu.com", 2});
jdbcTemplate.batchUpdate(sql, batchArgs);
}
/**
* 执行 INSERT, UPDATE, DELETE
*/
@Test
public void testUpdate(){
String sql = "UPDATE employees SET last_name = ? WHERE id = ?";
jdbcTemplate.update(sql, "Jack", 5);
}
@Test
public void testDataSource() throws SQLException {
DataSource dataSource = ctx.getBean(DataSource.class);
System.out.println(dataSource.getConnection());
}
}
24. 使用 NamedParameterJdbcTemplate
(NamedParameterJdbcTemplate(具名参数,)&)
applicationContext.xml
applicationContext-xml.xml
package com.atguigu.spring.aop;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Order(1)
@Aspect
@Component
public class VlidationAspect {
@Before("com.atguigu.spring.aop.LoggingAspect.declareJointPointExpression()")
public void validateArgs(JoinPoint joinPoint){
System.out.println("-->validate:" + Arrays.asList(joinPoint.getArgs()));
}
}
package com.atguigu.spring.aop.xml;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-xml.xml");
ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");
System.out.println(arithmeticCalculator.getClass().getName());
int result = arithmeticCalculator.add(1, 2);
System.out.println("result:" + result);
result = arithmeticCalculator.div(1000, 0);
System.out.println("result:" + result);
}
}
package com.atguigu.spring.aop;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
*/
@Order(2)
@Aspect
@Component
public class LoggingAspect {
/**
* 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码.
* 使用 @Pointcut 来声明切入点表达式.
* 后面的其他通知直接使用方法名来引用当前的切入点表达式.
*/
@Pointcut("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
public void declareJointPointExpression(){}
/**
* 在 com.atguigu.spring.aop.ArithmeticCalculator 接口的每一个实现类的每一个方法开始之前执行一段代码
*/
@Before("declareJointPointExpression()")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
Object [] args = joinPoint.getArgs();
System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
}
/**
* 在方法执行之后执行的代码. 无论该方法是否出现异常
*/
@After("declareJointPointExpression()")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends");
}
/**
* 在方法法正常结束受执行的代码
* 返回通知是可以访问到方法的返回值的!
*/
@AfterReturning(value="declareJointPointExpression()",
returning="result")
public void afterReturning(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends with " + result);
}
/**
* 在目标方法出现异常时会执行的代码.
* 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
*/
@AfterThrowing(value="declareJointPointExpression()",
throwing="e")
public void afterThrowing(JoinPoint joinPoint, Exception e){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " occurs excetion:" + e);
}
/**
* 环绕通知需要携带 ProceedingJoinPoint 类型的参数.
* 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
* 且环绕通知必须有返回值, 返回值即为目标方法的返回值
*/
/*
@Around("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
public Object aroundMethod(ProceedingJoinPoint pjd){
Object result = null;
String methodName = pjd.getSignature().getName();
try {
//前置通知
System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
//执行目标方法
result = pjd.proceed();
//返回通知
System.out.println("The method " + methodName + " ends with " + result);
} catch (Throwable e) {
//异常通知
System.out.println("The method " + methodName + " occurs exception:" + e);
throw new RuntimeException(e);
}
//后置通知
System.out.println("The method " + methodName + " ends");
return result;
}
*/
}
package com.atguigu.spring.aop;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
*/
@Order(2)
@Aspect
@Component
public class LoggingAspect {
/**
* 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码.
* 使用 @Pointcut 来声明切入点表达式.
* 后面的其他通知直接使用方法名来引用当前的切入点表达式.
*/
@Pointcut("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
public void declareJointPointExpression(){}
/**
* 在 com.atguigu.spring.aop.ArithmeticCalculator 接口的每一个实现类的每一个方法开始之前执行一段代码
*/
@Before("declareJointPointExpression()")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
Object [] args = joinPoint.getArgs();
System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
}
/**
* 在方法执行之后执行的代码. 无论该方法是否出现异常
*/
@After("declareJointPointExpression()")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends");
}
/**
* 在方法法正常结束受执行的代码
* 返回通知是可以访问到方法的返回值的!
*/
@AfterReturning(value="declareJointPointExpression()",
returning="result")
public void afterReturning(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends with " + result);
}
/**
* 在目标方法出现异常时会执行的代码.
* 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
*/
@AfterThrowing(value="declareJointPointExpression()",
throwing="e")
public void afterThrowing(JoinPoint joinPoint, Exception e){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " occurs excetion:" + e);
}
/**
* 环绕通知需要携带 ProceedingJoinPoint 类型的参数.
* 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
* 且环绕通知必须有返回值, 返回值即为目标方法的返回值
*/
/*
@Around("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
public Object aroundMethod(ProceedingJoinPoint pjd){
Object result = null;
String methodName = pjd.getSignature().getName();
try {
//前置通知
System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
//执行目标方法
result = pjd.proceed();
//返回通知
System.out.println("The method " + methodName + " ends with " + result);
} catch (Throwable e) {
//异常通知
System.out.println("The method " + methodName + " occurs exception:" + e);
throw new RuntimeException(e);
}
//后置通知
System.out.println("The method " + methodName + " ends");
return result;
}
*/
}
package com.atguigu.spring.aop.xml;
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
@Override
public int add(int i, int j) {
int result = i + j;
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
return result;
}
}
package com.atguigu.spring.aop.xml;
public interface ArithmeticCalculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
25. 事务准备
26. 声明式事务
(datasourcetransactionmanager的实现(基于注解方法上加@Transitional)&)
27. 事务的传播行为
(required(默认事务传播特性propagation=Propagation.requeired当前事物被另外事务调用,使用已有原来方法的事务就行)&)
28. 事务其他属性(隔离级别&回滚&只读&过期)
(@Transactional的配置项(事务隔离级别脏读,幻读,不可重复读isolation=”READ_COMMIT”读已提交,rollback对异常进行回滚,noRollbackFor对某些异常不回滚,readOnly表示事务只读取数据值但不更新数据,timeout防止事务对连接占用时间过长)&)
package com.atguigu.spring.tx;
import java.util.List;
public interface Cashier {
public void checkout(String username, List isbns);
}
package com.atguigu.spring.tx;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service("cashier")
public class CashierImpl implements Cashier {
@Autowired
private BookShopService bookShopService;
@Transactional
@Override
public void checkout(String username, List isbns) {
for(String isbn: isbns){
bookShopService.purchase(username, isbn);
}
}
}
package com.atguigu.spring.tx;
import static org.junit.Assert.*;
import java.util.Arrays;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTransactionTest {
private ApplicationContext ctx = null;
private BookShopDao bookShopDao = null;
private BookShopService bookShopService = null;
private Cashier cashier = null;
{
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
bookShopDao = ctx.getBean(BookShopDao.class);
bookShopService = ctx.getBean(BookShopService.class);
cashier = ctx.getBean(Cashier.class);
}
@Test
public void testTransactionlPropagation(){
cashier.checkout("AA", Arrays.asList("1001", "1002"));
}
@Test
public void testBookShopService(){
bookShopService.purchase("AA", "1001");
}
@Test
public void testBookShopDaoUpdateUserAccount(){
bookShopDao.updateUserAccount("AA", 200);
}
@Test
public void testBookShopDaoUpdateBookStock(){
bookShopDao.updateBookStock("1001");
}
@Test
public void testBookShopDaoFindPriceByIsbn() {
System.out.println(bookShopDao.findBookPriceByIsbn("1001"));
}
}
package com.atguigu.spring.tx;
public class UserAccountException extends RuntimeException{
/**
*
*/
private static final long serialVersionUID = 1L;
public UserAccountException() {
super();
// TODO Auto-generated constructor stub
}
public UserAccountException(String message, Throwable cause,
boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}
public UserAccountException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public UserAccountException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public UserAccountException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}
package com.atguigu.spring.tx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository("bookShopDao")
public class BookShopDaoImpl implements BookShopDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int findBookPriceByIsbn(String isbn) {
String sql = "SELECT price FROM book WHERE isbn = ?";
return jdbcTemplate.queryForObject(sql, Integer.class, isbn);
}
@Override
public void updateBookStock(String isbn) {
//检查书的库存是否足够, 若不够, 则抛出异常
String sql2 = "SELECT stock FROM book_stock WHERE isbn = ?";
int stock = jdbcTemplate.queryForObject(sql2, Integer.class, isbn);
if(stock == 0){
throw new BookStockException("库存不足!");
}
String sql = "UPDATE book_stock SET stock = stock -1 WHERE isbn = ?";
jdbcTemplate.update(sql, isbn);
}
@Override
public void updateUserAccount(String username, int price) {
//验证余额是否足够, 若不足, 则抛出异常
String sql2 = "SELECT balance FROM account WHERE username = ?";
int balance = jdbcTemplate.queryForObject(sql2, Integer.class, username);
if(balance < price){
throw new UserAccountException("余额不足!");
}
String sql = "UPDATE account SET balance = balance - ? WHERE username = ?";
jdbcTemplate.update(sql, price, username);
}
}
package com.atguigu.spring.tx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service("bookShopService")
public class BookShopServiceImpl implements BookShopService {
@Autowired
private BookShopDao bookShopDao;
//添加事务注解
//1.使用 propagation 指定事务的传播行为, 即当前的事务方法被另外一个事务方法调用时
//如何使用事务, 默认取值为 REQUIRED, 即使用调用方法的事务
//REQUIRES_NEW: 事务自己的事务, 调用的事务方法的事务被挂起.
//2.使用 isolation 指定事务的隔离级别, 最常用的取值为 READ_COMMITTED
//3.默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚. 也可以通过对应的
//属性进行设置. 通常情况下去默认值即可.
//4.使用 readOnly 指定事务是否为只读. 表示这个事务只读取数据但不更新数据,
//这样可以帮助数据库引擎优化事务. 若真的事一个只读取数据库值的方法, 应设置 readOnly=true
//5.使用 timeout 指定强制回滚之前事务可以占用的时间.
// @Transactional(propagation=Propagation.REQUIRES_NEW,
// isolation=Isolation.READ_COMMITTED,
// noRollbackFor={UserAccountException.class})
@Transactional(propagation=Propagation.REQUIRES_NEW,
isolation=Isolation.READ_COMMITTED,
readOnly=false,
timeout=3)
@Override
public void purchase(String username, String isbn) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {}
//1. 获取书的单价
int price = bookShopDao.findBookPriceByIsbn(isbn);
//2. 更新数的库存
bookShopDao.updateBookStock(isbn);
//3. 更新用户余额
bookShopDao.updateUserAccount(username, price);
}
}
package com.atguigu.spring.tx;
public interface BookShopService {
public void purchase(String username, String isbn);
}
29. 使用XML文件的方式配置事务
(从Spring事务配置说起(配置事物属性 tx:advice id=”txAdice” transaction-manager=”transactionManager” tx:attributes> ,把事务切入点与事务绑定aop:advisor advice-ref=””)&)
package com.atguigu.spring.tx.xml;
public class UserAccountException extends RuntimeException{
/**
*
*/
private static final long serialVersionUID = 1L;
public UserAccountException() {
super();
// TODO Auto-generated constructor stub
}
public UserAccountException(String message, Throwable cause,
boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}
public UserAccountException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public UserAccountException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public UserAccountException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}
package com.atguigu.spring.tx.xml;
import java.util.Arrays;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.atguigu.spring.tx.xml.service.BookShopService;
import com.atguigu.spring.tx.xml.service.Cashier;
public class SpringTransactionTest {
private ApplicationContext ctx = null;
private BookShopDao bookShopDao = null;
private BookShopService bookShopService = null;
private Cashier cashier = null;
{
ctx = new ClassPathXmlApplicationContext("applicationContext-tx-xml.xml");
bookShopDao = ctx.getBean(BookShopDao.class);
bookShopService = ctx.getBean(BookShopService.class);
cashier = ctx.getBean(Cashier.class);
}
@Test
public void testTransactionlPropagation(){
cashier.checkout("AA", Arrays.asList("1001", "1002"));
}
@Test
public void testBookShopService(){
bookShopService.purchase("AA", "1001");
}
}
package com.atguigu.spring.tx.xml;
public class BookStockException extends RuntimeException{
/**
*
*/
private static final long serialVersionUID = 1L;
public BookStockException() {
super();
// TODO Auto-generated constructor stub
}
public BookStockException(String message, Throwable cause,
boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}
public BookStockException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public BookStockException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public BookStockException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}
package com.atguigu.spring.tx.xml;
import org.springframework.jdbc.core.JdbcTemplate;
public class BookShopDaoImpl implements BookShopDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public int findBookPriceByIsbn(String isbn) {
String sql = "SELECT price FROM book WHERE isbn = ?";
return jdbcTemplate.queryForObject(sql, Integer.class, isbn);
}
@Override
public void updateBookStock(String isbn) {
//检查书的库存是否足够, 若不够, 则抛出异常
String sql2 = "SELECT stock FROM book_stock WHERE isbn = ?";
int stock = jdbcTemplate.queryForObject(sql2, Integer.class, isbn);
if(stock == 0){
throw new BookStockException("库存不足!");
}
String sql = "UPDATE book_stock SET stock = stock -1 WHERE isbn = ?";
jdbcTemplate.update(sql, isbn);
}
@Override
public void updateUserAccount(String username, int price) {
//验证余额是否足够, 若不足, 则抛出异常
String sql2 = "SELECT balance FROM account WHERE username = ?";
int balance = jdbcTemplate.queryForObject(sql2, Integer.class, username);
if(balance < price){
throw new UserAccountException("余额不足!");
}
String sql = "UPDATE account SET balance = balance - ? WHERE username = ?";
jdbcTemplate.update(sql, price, username);
}
}
package com.atguigu.spring.tx.xml;
public interface BookShopDao {
//根据书号获取书的单价
public int findBookPriceByIsbn(String isbn);
//更新数的库存. 使书号对应的库存 - 1
public void updateBookStock(String isbn);
//更新用户的账户余额: 使 username 的 balance - price
public void updateUserAccount(String username, int price);
}
package com.atguigu.spring.tx.xml.service;
import java.util.List;
public interface Cashier {
public void checkout(String username, List isbns);
}
package com.atguigu.spring.tx.xml.service;
public interface BookShopService {
public void purchase(String username, String isbn);
}
package com.atguigu.spring.tx.xml.service.impl;
import java.util.List;
import com.atguigu.spring.tx.xml.service.BookShopService;
import com.atguigu.spring.tx.xml.service.Cashier;
public class CashierImpl implements Cashier {
private BookShopService bookShopService;
public void setBookShopService(BookShopService bookShopService) {
this.bookShopService = bookShopService;
}
@Override
public void checkout(String username, List isbns) {
for(String isbn: isbns){
bookShopService.purchase(username, isbn);
}
}
}
package com.atguigu.spring.tx.xml.service.impl;
import com.atguigu.spring.tx.xml.BookShopDao;
import com.atguigu.spring.tx.xml.service.BookShopService;
public class BookShopServiceImpl implements BookShopService {
private BookShopDao bookShopDao;
public void setBookShopDao(BookShopDao bookShopDao) {
this.bookShopDao = bookShopDao;
}
@Override
public void purchase(String username, String isbn) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
//1. 获取书的单价
int price = bookShopDao.findBookPriceByIsbn(isbn);
//2. 更新数的库存
bookShopDao.updateBookStock(isbn);
//3. 更新用户余额
bookShopDao.updateUserAccount(username, price);
}
}
30. 整合 Hibernate 准备
(Spring整合MyBatis&)
31. 整合 Hibernate 配置
32. 整合 Hibernate 代码测试
33. 整合 Hibernate 不适用 Hibernate 配置文件
hibernate.cfg.xml
org.hibernate.dialect.MySQL5InnoDBDialect
true
true
update
db.properties
jdbc.user=root
jdbc.password=1230
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql:///spring7
jdbc.initPoolSize=5
jdbc.maxPoolSize=10
#...
applicationContext.xml
org.hibernate.dialect.MySQL5InnoDBDialect
true
true
update
package com.atguigu.spring.hibernate.test;
import java.sql.SQLException;
import java.util.Arrays;
import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.atguigu.spring.hibernate.service.BookShopService;
import com.atguigu.spring.hibernate.service.Cashier;
public class SpringHibernateTest {
private ApplicationContext ctx = null;
private BookShopService bookShopService = null;
private Cashier cashier = null;
{
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
bookShopService = ctx.getBean(BookShopService.class);
cashier = ctx.getBean(Cashier.class);
}
@Test
public void testCashier(){
cashier.checkout("aa", Arrays.asList("1001","1002"));
}
@Test
public void testBookShopService(){
bookShopService.purchase("aa", "1001");
}
@Test
public void testDataSource() throws SQLException {
DataSource dataSource = ctx.getBean(DataSource.class);
System.out.println(dataSource.getConnection());
}
}
package com.atguigu.spring.hibernate.service;
import java.util.List;
public interface Cashier {
public void checkout(String username, List isbns);
}
package com.atguigu.spring.hibernate.service;
public interface BookShopService {
public void purchase(String username, String isbn);
}
package com.atguigu.spring.hibernate.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.atguigu.spring.hibernate.service.BookShopService;
import com.atguigu.spring.hibernate.service.Cashier;
@Service
public class CashierImpl implements Cashier{
@Autowired
private BookShopService bookShopService;
@Override
public void checkout(String username, List isbns) {
for(String isbn:isbns){
bookShopService.purchase(username, isbn);
}
}
}
package com.atguigu.spring.hibernate.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.atguigu.spring.hibernate.dao.BookShopDao;
import com.atguigu.spring.hibernate.service.BookShopService;
@Service
public class BookShopServiceImpl implements BookShopService {
@Autowired
private BookShopDao bookShopDao;
/**
* Spring hibernate 事务的流程
* 1. 在方法开始之前
* ①. 获取 Session
* ②. 把 Session 和当前线程绑定, 这样就可以在 Dao 中使用 SessionFactory 的
* getCurrentSession() 方法来获取 Session 了
* ③. 开启事务
*
* 2. 若方法正常结束, 即没有出现异常, 则
* ①. 提交事务
* ②. 使和当前线程绑定的 Session 解除绑定
* ③. 关闭 Session
*
* 3. 若方法出现异常, 则:
* ①. 回滚事务
* ②. 使和当前线程绑定的 Session 解除绑定
* ③. 关闭 Session
*/
@Override
public void purchase(String username, String isbn) {
int price = bookShopDao.findBookPriceByIsbn(isbn);
bookShopDao.updateBookStock(isbn);
bookShopDao.updateUserAccount(username, price);
}
}
package com.atguigu.spring.hibernate.exceptions;
public class UserAccountException extends RuntimeException{
/**
*
*/
private static final long serialVersionUID = 1L;
public UserAccountException() {
super();
// TODO Auto-generated constructor stub
}
public UserAccountException(String message, Throwable cause,
boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}
public UserAccountException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public UserAccountException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public UserAccountException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}
package com.atguigu.spring.hibernate.exceptions;
public class BookStockException extends RuntimeException{
/**
*
*/
private static final long serialVersionUID = 1L;
public BookStockException() {
super();
// TODO Auto-generated constructor stub
}
public BookStockException(String message, Throwable cause,
boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}
public BookStockException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public BookStockException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public BookStockException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}
Book.hbm.xml
public class Book {
private Integer id;
private String bookName;
private String isbn;
private int price;
private int stock;
public Integer getId() {
return id;
}
public class Account {
private Integer id;
private String username;
private int balance;
Account.hbm.xml
package com.atguigu.spring.hibernate.dao;
public interface BookShopDao {
//根据书号获取书的单价
public int findBookPriceByIsbn(String isbn);
//更新数的库存. 使书号对应的库存 - 1
public void updateBookStock(String isbn);
//更新用户的账户余额: 使 username 的 balance - price
public void updateUserAccount(String username, int price);
}
package com.atguigu.spring.hibernate.dao.impl;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.atguigu.spring.hibernate.dao.BookShopDao;
import com.atguigu.spring.hibernate.exceptions.BookStockException;
import com.atguigu.spring.hibernate.exceptions.UserAccountException;
@Repository
public class BookShopDaoImpl implements BookShopDao {
@Autowired
private SessionFactory sessionFactory;
//不推荐使用 HibernateTemplate 和 HibernateDaoSupport
//因为这样会导致 Dao 和 Spring 的 API 进行耦合
//可以移植性变差
// private HibernateTemplate hibernateTemplate;
//获取和当前线程绑定的 Session.
private Session getSession(){
return sessionFactory.getCurrentSession();
}
@Override
public int findBookPriceByIsbn(String isbn) {
String hql = "SELECT b.price FROM Book b WHERE b.isbn = ?";
Query query = getSession().createQuery(hql).setString(0, isbn);
return (Integer)query.uniqueResult();
}
@Override
public void updateBookStock(String isbn) {
//验证书的库存是否充足.
String hql2 = "SELECT b.stock FROM Book b WHERE b.isbn = ?";
int stock = (int) getSession().createQuery(hql2).setString(0, isbn).uniqueResult();
if(stock == 0){
throw new BookStockException("库存不足!");
}
String hql = "UPDATE Book b SET b.stock = b.stock - 1 WHERE b.isbn = ?";
getSession().createQuery(hql).setString(0, isbn).executeUpdate();
}
@Override
public void updateUserAccount(String username, int price) {
//验证余额是否足够
String hql2 = "SELECT a.balance FROM Account a WHERE a.username = ?";
int balance = (int) getSession().createQuery(hql2).setString(0, username).uniqueResult();
if(balance < price){
throw new UserAccountException("余额不足!");
}
String hql = "UPDATE Account a SET a.balance = a.balance - ? WHERE a.username = ?";
getSession().createQuery(hql).setInteger(0, price).setString(1, username).executeUpdate();
}
}
34. 在 WEB 应用中使用 Spring 的基本思路
(Spring整合Struts 2&)
35. 在 WEB 应用中使用 Spring
(使用ServletContextListener 监听器&)
1. Spring 如何在 WEB 应用中使用 ?
1). 需要额外加入的 jar 包:
spring-web-4.0.0.RELEASE.jar spring-webmvc-4.0.0.RELEASE.jar
2). Spring 的配置文件, 没有什么不同
3). 如何创建 IOC 容器 ?
①. 非 WEB 应用在 main 方法中直接创建 ②. 应该在 WEB 应用被服务器加载时就创建 IOC 容器:
在 ServletContextListener#contextInitialized(ServletContextEvent sce) 方法中创建 IOC 容器.
③. 在 WEB 应用的其他组件中如何来访问 IOC 容器呢 ?
在 ServletContextListener#contextInitialized(ServletContextEvent sce) 方法中创建 IOC 容器后, 可以把其放在 ServletContext(即 application 域)的一个属性中.
④. 实际上, Spring 配置文件的名字和位置应该也是可配置的! 将其配置到当前 WEB 应用的初始化参数中较为合适.
4). 在 WEB 环境下使用 Spring
①. 需要额外加入的 jar 包:
spring-web-4.0.0.RELEASE.jar spring-webmvc-4.0.0.RELEASE.jar
②. Spring 的配置文件, 和非 WEB 环境没有什么不同
③. 需要在 web.xml 文件中加入如下配置:
contextConfigLocation classpath:applicationContext.xml
org.springframework.web.context.ContextLoaderListener
2. Spring 如何整合 Struts2 ?
1). 整合目标 ? 使 IOC 容器来管理 Struts2 的 Action!
2). 如何进行整合 ?
①. 正常加入 Struts2
②. 在 Spring 的 IOC 容器中配置 Struts2 的 Action 注意: 在 IOC 容器中配置 Struts2 的 Action 时, 需要配置 scope 属性, 其值必须为 prototype
③. 配置 Struts2 的配置文件: action 节点的 class 属性需要指向 IOC 容器中该 bean 的 id
/success.jsp
④. 加入 struts2-spring-plugin-2.3.15.3.jar
3). 整合原理: 通过添加 struts2-spring-plugin-2.3.15.3.jar 以后, Struts2 会先从 IOC 容器中 获取 Action 的实例.
if (appContext.containsBean(beanName)) { o = appContext.getBean(beanName); } else { Class beanClazz = getClassInstance(beanName); o = buildBean(beanClazz, extraContext); }
index.jsp
Insert title here
TestServlet
web.xml
configLocation
applicationContext.xml
com.atguigu.spring.struts2.listeners.SpringServletContextListener
TestServlet
TestServlet
com.atguigu.spring.struts2.servlets.TestServlet
TestServlet
/TestServlet
applicationContext.xml
package com.atguigu.spring.struts2.servlets;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.ApplicationContext;
import com.atguigu.spring.struts2.beans.Person;
/**
* Servlet implementation class TestServlet
*/
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 从 application 域对象中得到 IOC 容器的引用
ServletContext servletContext = getServletContext();
ApplicationContext ctx = (ApplicationContext) servletContext.getAttribute("ApplicationContext");
//2. 从 IOC 容器中得到需要的 bean
Person person = ctx.getBean(Person.class);
person.hello();
}
}
package com.atguigu.spring.struts2.listeners;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Application Lifecycle Listener implementation class SpringServletContextListener
*
*/
public class SpringServletContextListener implements ServletContextListener {
/**
* Default constructor.
*/
public SpringServletContextListener() {
// TODO Auto-generated constructor stub
}
/**
* @see ServletContextListener#contextInitialized(ServletContextEvent)
*/
public void contextInitialized(ServletContextEvent arg0) {
//1. 获取 Spring 配置文件的名称.
ServletContext servletContext = arg0.getServletContext();
String config = servletContext.getInitParameter("configLocation");
//1. 创建 IOC 容器
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
//2. 把 IOC 容器放在 ServletContext 的一个属性中.
servletContext.setAttribute("ApplicationContext", ctx);
}
/**
* @see ServletContextListener#contextDestroyed(ServletContextEvent)
*/
public void contextDestroyed(ServletContextEvent arg0) {
// TODO Auto-generated method stub
}
}
package com.atguigu.spring.struts2.beans;
public class Person {
private String username;
public void setUsername(String username) {
this.username = username;
}
public void hello(){
System.out.println("My name is " + username);
}
}
36. 集成 Struts2
test.jsp
Insert title here
success.jsp
Insert title here
Success Page
index.jsp
Insert title here
Person Save
web.xml
contextConfigLocation
classpath:applicationContext.xml
org.springframework.web.context.ContextLoaderListener
struts2
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
struts2
/*
struts.xml
/success.jsp
applicationContext.xml
package com.atguigu.spring.struts2.services;
public class PersonService {
public void save(){
System.out.println("PersonService's save....");
}
}
package com.atguigu.spring.struts2.beans;
public class Person {
private String username;
public void setUsername(String username) {
this.username = username;
}
public void hello(){
System.out.println("My name is " + username);
}
}
package com.atguigu.spring.struts2.actions;
import com.atguigu.spring.struts2.services.PersonService;
public class PersonAction {
private PersonService personService;
public void setPersonService(PersonService personService) {
this.personService = personService;
}
public String execute(){
System.out.println("execute....");
personService.save();
return "success";
}
}