文章目录
- 1.转账业务
- 2.jdk动态代理原理
- 3.cglib动态代理
- 4.动态代理实现service层事物的管理
- 5.aop入门
- 6.AOP半注解半xml:applicationContext.xml,UserService.java,UserServiceImpl.java,LogUtils.java,UserServiceTest.java
- 7.纯注解开发
- 8.JdbcTemplate
- 9.事务控制
- 10.spring的xml方式配置事务:applicationContext.xml
- 11.spring的半xml半注解方式配置事务
- 12.spring的纯注解方式配置事务:AccountServiceTest.java,JdbcConfig.java
- 13. 配置文件:pom.xml,applicationContext.xml,web.xml
- 14.service:UserService.java,UserServiceImpl.java
- 15.web:DemoServlet.java
- 16.listener:MyServletContextListener.java
1.转账业务
1.1 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>spring_day03_01</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<!-- java编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
</project>
//applicationContext.xml
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClassName" value="${jdbc.driverClass}"></property>
</bean>
<bean id="factoryBean" class="com.itheima.utils.ProxyBeanFactory">
<property name="accountService" ref="accountService"></property>
</bean>
<bean id="proxyAccountService" factory-bean="factoryBean" factory-method="getAccountServiceProxyObject"></bean>
</beans>
//jdbc.properties
jdbc.url=jdbc:mysql://localhost:3306/spring_day02
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=root
1.2 service
package com.itheima.service;
public interface AccountService {
public void transfer(String outName,String inName,double money);
public void update();
public void delete();
}
package com.itheima.service.impl;
import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
import com.itheima.service.AccountService;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void update() {
System.out.println("更新");
}
@Override
public void delete() {
System.out.println("删除");
}
@Override
public void transfer(String outName, String inName, double money) {
//对update,delete,transfer三个方法都加上事务,用动态代理对AccountServiceImpl进行增强
//创建AccountServiceImpl对象的代理对象, 我们调用这些update方法执行代理对象的方法。
//动态代理方式没有impl实现类,怎么装到spring容器中?以前不管是注解还是bean标签都要求有实现类
//装配对象即创建对象的三种方式
// try {
// System.out.println("开启事务");
//1.查询付款人的账户信息
Account outAccount = accountDao.findAccountByName(outName);
//2.查询收款人的账户信息
Account inAccount = accountDao.findAccountByName(inName);
//对2人的账户进行添加和减少的操作
outAccount.setMoney(outAccount.getMoney()-money);
inAccount.setMoney(inAccount.getMoney()+money);
//3.将更新后的账户信息更新到数据库
accountDao.update(outAccount);
int i = 1/0;
accountDao.update(inAccount);
// System.out.println("提交事务");
// } catch (Exception e) {
// System.out.println("回滚事务");
// e.printStackTrace();
// }
}
}
1.3 dao
package com.itheima.dao;
import com.itheima.domain.Account;
public interface AccountDao {
public Account findAccountByName(String name);
public void update(Account account);
}
package com.itheima.dao.impl;
import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate ;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public Account findAccountByName(String name) {
String sql = "select * from account where name = ?";
Account account = null;
try {
account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), name);
} catch (DataAccessException e) {
e.printStackTrace();
}
return account;
}
@Override
public void update(Account account) {
String sql = "update account set money = ? where name = ?";
jdbcTemplate.update(sql,account.getMoney(),account.getName());
}
}
1.4 test
package com.itheima.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {
@Resource(name = "proxyAccountService")
private AccountService accountService;
@Test
public void transfer() {
String outName = "aaa";
String inName ="bbb";
double money = 500;
accountService.transfer(outName,inName,money);
}
@Test
public void update(){
accountService.update();
}
@Test
public void delete(){
accountService.delete();
}
}
transfer执行结果如下:
2.jdk动态代理原理
package com.itheima.proxy;
public interface IActor {
public void sing();
public void active();
}
package com.itheima.proxy;
public class ActorImpl implements IActor {
@Override
public void sing() {
System.out.println("唱歌");
}
@Override
public void active() {
System.out.println("表演");
}
}
动态代理:不修改源码情况下,对现有方法进行增强,要增强的功能只要写一遍就可以了。
package com.itheima.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyTest {
public static void main(String[] args) {
IActor actor = new ActorImpl(); //需要创建一个对象来代理需要增强的actor对象
/**
* 参数1:类加载器,一般写被代理对象的类加载器
* 参数2:接口,被代理对象实现的接口。
* 参数3:处理器
*/
IActor iActor = (IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(), actor.getClass().getInterfaces(),
new InvocationHandler() { //选中方法名,按ctrl+q查看方法注释
/**
* @param proxy 被代理对象本身。没用。
* @param method 被调用的方法。
* @param args 调用方法时传递的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("增强的功能");
/**
* 参数1:方法调用时依赖的对象
* 参数2:调用方法时需要传递的参数
*/
return method.invoke(actor,args); //调用原有方法的功能
}
});
iActor.sing();
iActor.active();
// IActor actor2 = new $Proxy0();
// actor2.sing();
// actor2.active();
}
}
3.cglib动态代理
jdk动态代理产生iActor对象必须要有接口IActor,但很多时候我们要增强的对象ActorImpl没有接口IActor ,用cglib动态代理。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
package com.itheima.proxy;
public class Actor2 {
public void dance(){
System.out.println("跳舞");
}
}
package com.itheima.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyTest {
public static void main(String[] args) {
/**
* 参数1: 需要代理的实现类。
* 参数2: 回调函数,作用和jdk动态代理中的InvocationHandler一模一样。
*/
Actor2 actor2 = (Actor2)Enhancer.create(Actor2.class, new MethodInterceptor() {
/**
* 此方法和 jdk动态代理中的 处理器的 invoke方法一样
* @param o 代理对象本身
* @param method 将要调用的方法
* @param objects 调用方法时传递的参数
* @param methodProxy 代理后的方法。 没用
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("功能的增强");
return method.invoke(new Actor2(),objects); //调用原有方法的功能
}
});
actor2.dance();
}
}
4.动态代理实现service层事物的管理
要创建AccountServiceImpl实现类对象
的代理类对象
对其进行增强:创建一个工具类:1.
提供一个静态或者动态方法用来返回AccountServiceImpl的动态代理对象。2.
并且使用工厂创建方式装配到spring容器中。
<bean id="factoryBean" class="com.itheima.utils.ProxyBeanFactory">
<property name="accountService" ref="accountService"></property>
</bean>
<bean id="proxyAccountService" factory-bean="factoryBean" factory-method="getAccountServiceProxyObject"></bean>
package com.itheima.utils;
import com.itheima.service.AccountService;
import com.itheima.service.impl.AccountServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyBeanFactory {
private AccountService accountService = null;
public void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
public AccountService getAccountServiceProxyObject(){ //创建动态代理对象
AccountService proxyAccountService = (AccountService) Proxy.
newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
try {
System.out.println("开启事务");
obj = method.invoke(accountService,args); //这一行原有功能不变
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("回滚事务");
e.printStackTrace();
}
return obj;
}
});
return proxyAccountService;
}
}
package com.itheima.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {
@Resource(name = "proxyAccountService")
private AccountService accountService;
@Test
public void transfer() {
String outName = "aaa";
String inName ="bbb";
double money = 500;
accountService.transfer(outName,inName,money);
}
@Test
public void update(){
accountService.update();
}
@Test
public void delete(){
accountService.delete();
}
}
update执行结果如下:
5.aop入门
如下printLog()比作System.out.println(“功能的增强”);的封装。Aspect就是Advice加在joinPoint的前面还是后面等。
使用AOP完整一个快速入门,在业务层的前后加上日志的输出,掌握spring的xml方式的AOP配置。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>spring_day03_02</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<!-- java编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- ioc相关 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- spring单元测试 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<!-- aop相关 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
</dependencies>
</project>
package com.itheima.service;
public interface UserService {
public void findUserById();
}
package com.itheima.service.impl;
import com.itheima.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public void findUserById() {
System.out.println("根据用户的id查询用户的信息");
//int i = 1/0;
}
}
package com.itheima.utils;
import org.aspectj.lang.ProceedingJoinPoint;
public class LogUtils { //增强
public void printLog(){
System.out.println("打印日志信息.......");
}
public void printLog2(){
System.out.println("打印日志信息2222222.......");
}
public void printLog3(){
System.out.println("打印日志信息3333333.......");
}
public void printLog4(ProceedingJoinPoint point){ //@param point 环绕通知必要的参数 ,切入点。
try {
System.out.println("自定义环绕通知,开启事务。"); //这一行作用和前置通知一样
point.proceed(); //调用原有功能的方法,在原有功能基础上自己定义在哪个地方增强。
System.out.println("自定义环绕通知,提交事务"); //这一行作用和后置通知一样
} catch (Throwable throwable) {
System.out.println("自定义环绕通知,回滚事务"); //这一行作用和异常通知一样
throwable.printStackTrace();
}finally {
System.out.println("自定义环绕通知,释放资源"); //这一行作用和最终通知一样
}
}
}
接下里创建代理对象将LogUtils中方法应用到UserServiceImpl中:以前自己写JDK或cglib动态代理实现,现在只要配置告诉spring(spring自动根据是否有实现类
,使用jdk动态代理还是cglib动态代理)。
//applicationContext.xml
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>
<!-- 将增强所在的类,创建一个对象,并且装配到spring容器 -->
<bean id="logUtils" class="com.itheima.utils.LogUtils"></bean>
<!-- 配置aop -->
<!-- 声明开始配置aop -->
<aop:config>
<!-- 切面:具体配置切入点和增强的关系的
id: 切点的唯一标识,不需要
ref: 指定增强所在的对象,增强方法前提是new出一个对象
-->
<aop:aspect ref="logUtils">
<!-- 切入点和增强
method: 指的就是增强所在的方法
pointcut: 切入点,将要被增强的方法
AspectJ表达式语法: 修饰符 返回值 包名.类名.方法名()
1.修饰符可以省略
execution(void com.itheima.service.impl.UserServiceImpl.findUserById())
2. *可以代表任意返回值
execution(* com.itheima.service.impl.UserServiceImpl.findUserById())
3. *可以代表任意包名,有一级包,就必须有一个*
execution(* *.*.*.*.UserServiceImpl.findUserById())
4. ..代表任意包和多级包
execution(* *..UserServiceImpl.findUserById())
5. * 可以代表任意类名
execution(* *..*.findUserById())
6. * 可以代表任意方法名
execution(* *..*.*())
7. * 可以代表任意参数,必须有参数
execution(* *..*.*(*))
8. .. 可以代表任意参数,包括无参
execution(* *..*.*(..))
实际企业开发中,常用的AspectJ表达式是:
execution(* com.itheima.service.impl.*.*(..))
-->
<!-- 抽取切入点,方便复用
expression:切入点的AspectJ表达式
id:切入点的唯一标识
-->
<aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>
<!--<aop:before method="printLog" pointcut-ref="pt1"></aop:before>--> <!--pt1就是上一行-->
<!--<aop:before method="printLog2" pointcut-ref="pt1"></aop:before>-->
<!--<aop:before method="printLog3" pointcut-ref="pt1"></aop:before>-->
<!--
<aop:before> : 前置通知,增强在核心功能之前执行。
<aop:after-returning> : 后置通知,在核心功能之后执行.如果出现异常,后置通知不会执行
<aop:after-throwing>: 异常通知,当出现异常时,执行通知。
<aop:after> : 最终通知,无论是否有异常,都会执行通知
<aop:around> : 环绕通知,自定义通知的位置
-->
<!-- <aop:after-returning method="printLog" pointcut-ref="pt1"></aop:after-returning>-->
<!--<aop:after-throwing method="printLog" pointcut-ref="pt1"></aop:after-throwing>-->
<!--<aop:after method="printLog" pointcut-ref="pt1"></aop:after>-->
<aop:around method="printLog4" pointcut-ref="pt1"></aop:around>
</aop:aspect>
</aop:config>
</beans>
6.AOP半注解半xml:applicationContext.xml,UserService.java,UserServiceImpl.java,LogUtils.java,UserServiceTest.java
//applicationContext.xml
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启IOC注解扫描的包 -->
<context:component-scan base-package="com.itheima"></context:component-scan>
<!-- 开启注解AOP -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
package com.itheima.service;
public interface UserService {
public void findUserById();
}
package com.itheima.service.impl;
import com.itheima.service.UserService;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserServiceImpl implements UserService {
@Override
public void findUserById() {
System.out.println("根据用户的id查询用户的信息");
//int i = 1/0;
}
}
package com.itheima.utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component // 当前增强类装配到spring容器
@Aspect // 声明当前类是一个切面类
public class LogUtils {
/**
* xml中开启aop注解方式:
* <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
* 在通知类上添加
* @Aspect 声明当前类是一个切面类
* @Component //当前增强类装配到spring容器
*
* @Before : 配置前置通知
* @AfterReturning: 配置后置通知
* @After: 配置最终通知
* @Around : 配置环绕通知
* @AfterThrowing: 配置异常通知
*
* 注解方式抽取切入点:
* 1.声明一个空实现的方法:pt1()
* 2.在方法上添加@Pointcut注解,value属性值就是切入点的AspectJ表达式
* 3.方法名就是切入点的 id
*/
//@Before("execution(* com.itheima.service.impl.*.*(..))")
public void printLog(){
System.out.println("前置通知");
}
//@AfterReturning("execution(* com.itheima.service.impl.*.*(..))")
public void printLog2(){
System.out.println("后置通知");
}
//@AfterThrowing("execution(* com.itheima.service.impl.*.*(..))")
public void printLog3(){
System.out.println("异常通知");
}
@Pointcut("execution(* com.itheima.service.impl.*.*(..))")
public void pt1(){
}
@Around("pt1()")
public void printLog4(ProceedingJoinPoint point){
try {
System.out.println("自定义环绕通知,开启事务。");
point.proceed(); //调用原有功能的方法。
System.out.println("自定义环绕通知,提交事务");
} catch (Throwable throwable) {
System.out.println("自定义环绕通知,回滚事务");
throwable.printStackTrace();
}finally {
System.out.println("自定义环绕通知,释放资源");
}
}
}
package com.itheima.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.swing.*;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void findUserById() {
userService.findUserById();
}
}
7.纯注解开发
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.itheima")
public class SpringConfig {
/**
* 开发步骤: 1. @Configuration 标注当前类是一个配置类
* 2.@ComponentScan 开启注解扫描(ioc)
* 3.@EnableAspectJAutoProxy 开启注解aop即可
*/
}
package com.itheima.service;
import com.itheima.config.SpringConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.swing.*;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void findUserById() {
userService.findUserById();
}
}
8.JdbcTemplate
8.1 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>spring_day04_01</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<!-- java编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--IOC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<!-- 事务相关的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<!-- aop 相关依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
</dependencies>
</project>
<?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" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<!-- <property name="jdbcTemplate" ref="jdbcTemplate"></property>-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClass}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
</bean>
<!-- spring事务的配置步骤
1.在applicationContext.xml中配置事务管理器的bean
2.配置事务的策略
3.配置事务的aop
-->
<!--
声明对于jdbc进行事务管理的 事务管理器对象
id建议叫做 transactionManager,如果不叫这个名字,我们配置策略是,需要指定事务管理器对象的名字
-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--
配置spring事务的策略
tx:advice: 声明开始配置事务的策略
id: 当前策略的唯一标识
transaction-manager: 指定事务管理器对象。默认值为transactionManager
-->
<tx:advice id="tx1" >
<!-- 声明具体的策略 -->
<tx:attributes>
<!--
tx:method : 指定对于哪些方法进行事务的管理
name:方法名的表达式
read-only: 事务是否只读,默认是false
timeout:超时时间,默认为-1,永不超时。
isolation: 事务的隔离级别,默认采用数据库默认的隔离级别
propagation: 事务的传播行为
rollback-for="": 针对指定的异常进行回滚
no-rollback-for="": 针对指定的异常不进行回滚
-->
<tx:method name="*" />
<!-- <tx:method name="select*" read-only="true"/>-->
</tx:attributes>
</tx:advice>
<!-- 配置事务的aop
AspectJ 表达式的语法 修饰符 返回值 包名.类型.方法名()
-->
<aop:config>
<aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>
<!--
advice-ref="tx1":使用哪个策略
pointcut-ref="pt1" : 事务切入到哪个切入点
-->
<aop:advisor advice-ref="tx1" pointcut-ref="pt1"></aop:advisor>
</aop:config>
</beans>
//jdbc.properties
jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/spring_day02
jdbc.driverClass=com.mysql.jdbc.Driver
8.2 domain
package com.itheima.domain;
public class Account {
private int id;
private String name;
private double money;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
8.3 dao
package com.itheima.dao;
import com.itheima.domain.Account;
public interface AccountDao {
public Account findAccountByName(String name);
public void update(Account account);
}
package com.itheima.dao;
import org.springframework.jdbc.core.JdbcTemplate;
public class BaseDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
}
任意dao用到jdbcTemplate的话都要声明这对象,冗余复杂。所以抽成父类BaseDao被继承。
package com.itheima.dao.impl;
import com.itheima.dao.AccountDao;
import com.itheima.dao.BaseDao;
import com.itheima.domain.Account;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import java.sql.Connection;
import java.sql.Savepoint;
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { //BaseDao换成JdbcDaoSupport
@Override
public Account findAccountByName(String name) {
String sql = "select * from account where name = ?";
Account account = null;
try {
account = getJdbcTemplate().queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), name);
} catch (DataAccessException e) {
e.printStackTrace();
}
return account;
}
@Override
public void update(Account account) {
String sql = "update account set money = ? where name = ?";
getJdbcTemplate().update(sql, account.getMoney(), account.getName());
}
}
8.4 service
package com.itheima.service;
import com.itheima.domain.Account;
public interface AccountService {
public Account findAccountByName(String name);
public void transfer(String outName,String inName,double money);
}
package com.itheima.service.impl;
import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
import com.itheima.service.AccountService;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public Account findAccountByName(String name) {
return accountDao.findAccountByName(name);
}
@Override
public void transfer(String outName, String inName, double money) {
//1.查询用户的账户信息
Account outAccount = accountDao.findAccountByName(outName);
Account inAccount = accountDao.findAccountByName(inName);
//2.转账,
outAccount.setMoney(outAccount.getMoney()-money);
inAccount.setMoney(inAccount.getMoney()+money);
//3.将数据更新到数据库
accountDao.update(outAccount);
int i = 1/0;
accountDao.update(inAccount);
}
}
8.5 test
package com.itheima.service;
import com.itheima.domain.Account;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {
@Autowired
private AccountService accountService;
@Test
public void findAccountByName() {
Account aaa = accountService.findAccountByName("aaa");
System.out.println(aaa);
}
@Test
public void transfer(){
accountService.transfer("aaa","bbb",200);
}
}
问题:使用JdbcDaoSupport能够使用注解的方式吗?不能,无法将dataSouce注入到JdbcDaoSupport中(源码不能加注解)。
jdbcTemplate和JdbcDaoSupport的区别?
1.在dao层定义jdbcTemplate可以使用注解和xml配置方式。而且jdbcTemplate需要被spring的容器管理。
2.继承JdbcDaoSupport的话,这种方式只适用于xml配置方式,并且,只需要在applicationContext.xml中将dataSource注入给dao的实现类即可。不需要管理jdbcTemplate对象。
9.事务控制
第一:JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案。
第二:spring框架为我们提供了一组事务控制的接口。这组接口是在spring-tx-4.3.13.RELEASE.jar中。
第三:spring的事务控制都是基于AOP的,它既可以使用编程的方式实现,也可以使用配置的方式实现。重点是使用配置的方式实现。有了spring后,事务就从编程中脱离出来。
PlatformTransactionManage接口
是平台事务管理器,是Spring真正管理事务的对象,常用实现类DataSourceTransactionManager
(针对JDBC和mybatis事务管理)。
9.1 TransactionDefinition:
事务定义对象有如下api:
9.1.1 定义事务隔离级别
事务四大特性:1.原子性(A)
:如转账,一个人转出,一个人转入,这两步操作是不能分的,要么都做,要么都不做。
2.一致性(C)
:事务的成功与否,数据库的数据都满足生活正常逻辑,如转账,总额不会变。
3.隔离性(I)
:并发访问数据库时,一个用户的事务不被其他事务干扰。
4.持久性(D)
:事务提交后,它对数据库的改变是持久的,断电也变。
注意隔离性,虽然事务间没有影响,但两个事务同时并发且操作同一条数据会出现如下问题:
1.脏读
:一个事务读到另一个事务未提交的数据。
2.不可重复读
:事务A第一次读账户余额为1000,事务B这时操作了这条数据并且提交+1000的事务。事务A又去读,变为2000了,读不到重复的1000了。
3.幻读
:不可重复读强调读取内容【select *,update】
不一致,幻读强调读取数量【select count(*),delete或insert】
不一致。
用事务隔离级别解决如上问题:
read uncommitted:不会去设的,未提交会出现脏读。
read committed:解决脏读,还是存在不可重复读和幻读。
repeatable-read:mysql数据库默认隔离级别,解决不可重复读,还是存在幻读。
serialize:串行化,解决所有。
上面4个,spring在TransactionDefinition设置事务隔离级别时定义如下5个值:多了一个DEFAULT,默认采用数据库隔离级别。
9.1.2 事务传播行为
事务传播行为
解决的问题: 一个业务层事务 调用 另一个业务层事务时,事务之间关系如何处理。
事务传播行为PROPAGATION的取值:
REQUIRED: 一个事务, 要么都成功,要么都失败。
REQUIRES_NEW :两个不同事务,彼此之间没有关系 一个事务失败了 不影响另一个事务。
NESTED :一个事务, 在A事务 调用 B过程中, B失败了, 回滚事务到 之前SavePoint , 用户可以选择提交事务或者回滚事务。
10.spring的xml方式配置事务:applicationContext.xml
<?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" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<!-- <property name="jdbcTemplate" ref="jdbcTemplate"></property>-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClass}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
</bean>
<!-- spring事务的配置步骤
1.在applicationContext.xml中配置事务管理器的bean
2.配置事务的策略
3.配置事务的aop
-->
<!--
声明对于jdbc进行事务管理的 事务管理器对象
id建议叫做 transactionManager,如果不叫这个名字,我们配置策略时,需要指定事务管理器对象的名字
-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--
配置spring事务的策略
tx:advice:声明开始配置事务的策略
id:当前策略的唯一标识
transaction-manager: 指定事务管理器对象。默认值为transactionManager
-->
<tx:advice id="tx1" >
<!-- 声明具体的策略 -->
<tx:attributes>
<!--
tx:method : 指定对于哪些方法进行事务的管理
name:方法名的表达式
read-only: 事务是否只读,默认是false
timeout:超时时间,默认为-1,永不超时。
isolation: 事务的隔离级别,默认采用数据库默认的隔离级别
propagation: 事务的传播行为
rollback-for="": 针对指定的异常进行回滚
no-rollback-for="": 针对指定的异常不进行回滚
-->
<tx:method name="*" />
<!-- <tx:method name="select*" read-only="true"/>-->
</tx:attributes>
</tx:advice>
<!-- 配置事务的aop
AspectJ 表达式的语法 修饰符 返回值 包名.类型.方法名()
-->
<aop:config>
<aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>
<!--
advice-ref="tx1":使用哪个策略
pointcut-ref="pt1" : 事务切入到哪个切入点
-->
<aop:advisor advice-ref="tx1" pointcut-ref="pt1"></aop:advisor>
</aop:config>
</beans>
11.spring的半xml半注解方式配置事务
ioc注解要用包扫描开启ioc注解,aop也要开启,事务也要开启,
<?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" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- bean definitions here -->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClass}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
</bean>
<!--
1.开启注解方式的事务
2.开启ioc注解
3.在service层的实现类上添加@Transactional 即可
@Transactional: 即可用在方法上,也可用在实现类上,也可用在接口上。
执行方法是,寻找的顺序是 方法->实现类->接口
如果需要配置事务的信息,那么直接通过@Transactional注解的相关属性去配置事务的策略信息
-->
<context:component-scan base-package="com.itheima"></context:component-scan>
<tx:annotation-driven></tx:annotation-driven>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
package com.itheima.service.impl;
import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
import com.itheima.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public Account findAccountByName(String name) {
return accountDao.findAccountByName(name);
}
@Override
@Transactional
public void transfer(String outName, String inName, double money) {
//1.查询用户的账户信息
Account outAccount = accountDao.findAccountByName(outName);
Account inAccount = accountDao.findAccountByName(inName);
//2.转账,
outAccount.setMoney(outAccount.getMoney()-money);
inAccount.setMoney(inAccount.getMoney()+money);
//3.将数据更新到数据库
accountDao.update(outAccount);
int i = 1/0;
accountDao.update(inAccount);
}
}
12.spring的纯注解方式配置事务:AccountServiceTest.java,JdbcConfig.java
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
需要使用一个配置类来代替xml核心配置
1.创建一个类,通过@Configuration来声明当前类是一个配置类
2.引入外部配置文件
3.将jdbcTemplate和dataSource,和事务管理器对象 装配到spring容器中
4.开启包扫描
5.开启注解事务
*/
@Configuration
@Import(JdbcConfig.class)
@ComponentScan("com.itheima")
@EnableTransactionManagement //开启注解事务
public class SpringConfig {
}
package com.itheima.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.driverClass}")
private String driverClass;
//下面是三个对象的装配
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClass);
dataSource.setUrl(url);
dataSource.setPassword(password);
dataSource.setUsername(username);
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
return jdbcTemplate;
}
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
package com.itheima.service;
import com.itheima.config.SpringConfig;
import com.itheima.domain.Account;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {
@Autowired
private AccountService accountService;
@Test
public void findAccountByName() {
Account aaa = accountService.findAccountByName("aaa");
System.out.println(aaa);
}
@Test
public void transfer(){
accountService.transfer("aaa","bbb",200);
}
}
13. 配置文件:pom.xml,applicationContext.xml,web.xml
//pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>spring_day04_web</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<!--监听器与web相关-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
</dependencies>
<packaging>war</packaging>
<build>
<plugins>
<!-- java编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
//applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>
</beans>
//web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<!--
<context-param>
<param-name>classPath</param-name>
<param-value>applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>com.itheima.listener.MyServletContextListener</listener-class>
</listener>
-->
<!-- 告诉spring的监听器,核心配置文件的位置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- spring的监听器,不是上面自定义的-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
14.service:UserService.java,UserServiceImpl.java
package com.itheima.service;
public interface UserService {
public void login();
}
package com.itheima.service.impl;
import com.itheima.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public void login() {
System.out.println("处理登录业务");
}
}
15.web:DemoServlet.java
package com.itheima.web;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/demoServlet")
public class DemoServlet extends HttpServlet {
private UserService userService;
@Override
public void init() throws ServletException {
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/**
* 问题:
* 1.如果有多个servlet,那么这个容器就被创建了多次。而spring容器只需要创建一次即可。
* 2.代码冗余
*
* 最主要解决的问题:
* 1.我们需要创建一个spring容器,并且只创建一次。
* 2.创建的spring容器应改被所有的servlet所共有。 servletContext 域对象。
* 3.这个容器应该随着服务器启动就自动创建。 servletContextListener监听器 或者servlet的启动时加载<load-on-startup></>
*/
//创建spring容器
//第一次:自定义容器
//ApplicationContext ac = (ApplicationContext) getServletContext().getAttribute("ac");
//第二次:使用spring的容器
//ApplicationContext ac = (ApplicationContext) getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
//第三次: 优化后的
ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
userService = (UserService) ac.getBean("userService");
userService.login();
}
}
16.listener:MyServletContextListener.java
package com.itheima.listener;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class MyServletContextListener implements ServletContextListener {
/**
* ServletContextListener:用来监听servletContext对象的创建和销毁的
* servletContext:随着服务器启动创建,随着服务器销毁而销毁
*/
/**
* 如果监听到servletContext对象创建,调用当前方法
* @param servletContextEvent 事件监听对象,保存着被监听的对象对象
*/
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
// ServletContext servletContext = servletContextEvent.getServletContext();
// System.out.println("servletContext对象创建了");
System.out.println("初始化spring容器,并且将spring容器保存到servletContext域中");
ServletContext servletContext = servletContextEvent.getServletContext();
//servletContext对象保存的是当前项目相关的信息,能读取web.xml
String classPath = servletContext.getInitParameter("classPath");
System.out.println("读取web.xml中配置的全局的初始化参数:"+classPath);
//1.创建spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext(classPath);
//2.添加到servletContext域中
servletContext.setAttribute("ac",ac);
}
/**
* 如果监听到servletContext对象销毁,调用当前方法
* @param servletContextEvent
*/
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("servletContext对象销毁了");
}
}