Spring学习笔记
个人笔记,以后有时间再优化本文
理解概念
struts:web层,比较简单(ValueStack值栈,拦截器)hibernate:dao层,知识点杂- spring:service层,重要,讲多少用多少
学习进度
- spring day01:基础(IOC控制反转、DI依赖注入)、整合Junit、整合web
- spring day02:AOP切面编程、JdbcTemplate
- spring day03:事务管理
1 spring框架概述
1.1 什么是spring
- Spring是一个
开源框架
,Spring是于2003 年兴起的一个轻量级
的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构
,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean
来完成以前只可能由EJB
完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)。
简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。- 轻量级:与EJB对比,依赖资源少,销毁的资源少。
- 分层: 一站式,每一个层都提供的解决方案
- web层:
struts,spring-MVC
- service层:
spring
- dao层:
hibernate,mybatis
, jdbcTemplate --> spring-data (有时间就学)
- web层:
1.2 spring由来
- Expert One-to-One J2EE Design and Development
- Expert One-to-One J2EE Development without EJB
1.3 spring核心
- Spring的核心是
控制反转(IoC)
和面向切面(AOP)
1.4 spring优点
方便解耦,简化开发 (高内聚低耦合)
- Spring就是一个大工厂(容器),可以将所有对象创建和依赖关系维护,交给Spring管理
- spring工厂是用于生成bean
-
AOP编程的支持 Spring提供 面向切面编程,可以方便的实现对程序进行 权限拦截 、运行监控 等 功能。——声明式事务的支持
-
只需要通过配置就可以完成对事务的管理,而无需手动编程。——方便程序的测试
-
Spring对Junit4支持,可以通过注解方便的测试Spring程序——方便集成各种优秀框架
-
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持——降低JavaEE API的使用难度
-
Spring 对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低
1.5 spring体系结构
核心容器:beans、core、context、expression
2 入门案例:IoC【掌握】
2.1 导入jar包
- 4 + 1 : 4个核心(beans、core、context、expression) + 1个依赖(commons-loggins…jar)
2.2 目标类
- 提供UserService接口和实现类
- 获得UserService实现类的实例
之前开发中,直接new一个对象即可。
学习spring之后,将由Spring创建对象实例即 IoC 控制反转(Inverse of Control
)之后需要实例对象时,从spring工厂(容器)中获得,需要将实现类的全限定名称配置到xml
文件中
public interface UserService {
public void addUser();
}
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("a_ico add user");
}
}
2.3 配置文件
- 位置:任意,开发中一般在classpath下(src)
- 名称:任意,开发中常用
applicationContext.xml
- 内容:添加
schema
约束
约束文件位置:88spring-framework-3.2.0.RELEASE\docs\spring-framework-reference\html\ xsd-config.html
<?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">
<!-- 配置service
<bean> 配置需要创建的对象
id :用于之后从spring容器获得实例时使用的
class :需要创建实例的**全限定**类名
-->
<bean id="userServiceId" class="com.itheima.a_ioc.UserServiceImpl"></bean>
</beans>
2.4 测试
@Test
public void demo02(){
//从spring容器获得
//1 获得容器
String xmlPath = "com/itheima/a_ioc/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//2获得内容 --不需要自己new,都是从spring容器获得
UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser();
}
3 入门案例:DI 【掌握】
DI
Dependency Injection ,依赖注入- is a :是一个,继承。
- has a:有一个,成员变量,依赖。
class B {
private A a; //B类依赖A类
}
依赖:一个对象需要使用另一个对象
注入:通过setter
方法进行另一个对象实例设置。
例如:
class BookServiceImpl{
//之前开发:接口 = 实现类 (service和dao耦合)
//private BookDao bookDao = new BookDaoImpl();
//spring之后 (解耦:service实现类使用dao接口,不知道具体的实现类)
private BookDao bookDao;
setter方法
}
模拟spring执行过程
创建service实例:BookService bookService = new BookServiceImpl() IoC
<bean>
创建dao实例:BookDao bookDao = new BookDaoImple() IoC
将dao设置给service:bookService.setBookDao(bookDao); DI
<property>
3.1 目标类
- 创建BookService接口和实现类
- 创建BookDao接口和实现类
- 将dao和service配置 xml文件
- 使用api测试
3.1.1 dao
public interface BookDao {
public void addBook();
}
public class BookDaoImpl implements BookDao {
@Override
public void addBook() {
System.out.println("di add book");
}
}
3.1.2 service
public interface BookService {
public abstract void addBook();
}
public class BookServiceImpl implements BookService {
// 方式1:之前,接口=实现类
// private BookDao bookDao = new BookDaoImpl();
// 方式2:接口 + setter
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
@Override
public void addBook(){
this.bookDao.addBook();
}
}
3.2 配置文件
<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">
<!--
模拟spring执行过程
创建service实例:BookService bookService = new BookServiceImpl() IoC <bean>
创建dao实例:BookDao bookDao = new BookDaoImpl() IoC
将dao设置给service:bookService.setBookDao(bookDao); DI <property>
<property> 用于进行属性注入
name: bean的属性名,通过setter方法获得
setBookDao ##> BookDao ##> bookDao
ref :另一个bean的id值的引用
-->
<!-- 创建service -->
<bean id="bookServiceId" class="com.itheima.b_di.BookServiceImpl">
<property name="bookDao" ref="bookDaoId"></property>
</bean>
<!-- 创建dao实例 -->
<bean id="bookDaoId" class="com.itheima.b_di.BookDaoImpl"></bean>
3.3 测试
@Test
public void demo01(){
//从spring容器获得
String xmlPath = "com/itheima/b_di/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
BookService bookService = (BookService) applicationContext.getBean("bookServiceId");
bookService.addBook();
}
4 myeclipse schema xml提示
-
步骤一:确定xsd文件位置
spring-framework-3.2.0.RELEASE\schema\beans
-
步骤二:复制路径
-
步骤三:搜索“xml catalog”
-
步骤四:添加约束提示
5 核心API
api整体了解,之后不使用,在学习过程需要。
BeanFactory
:这是一个工厂,用于生成任意bean。
采取延迟加载,第一次getBean时才会初始化BeanApplicationContext
:是BeanFactory的子接口,功能更强大。
(国际化处理、事件传递、Bean自动装配、各种不同应用层的Context实现)。当配置文件被加载,就进行对象实例化。ClassPathXmlApplicationContext
用于加载classpath(类路径、src)下的xml
加载xml运行时位置 --> /WEB-INF/classes/…xmlFileSystemXmlApplicationContext
用于加载指定盘符下的xml
加载xml运行时位置 --> /WEB-INF/…xml
通过java webServletContext.getRealPath()
获得具体盘符
@Test
public void demo02(){
//使用BeanFactory --第一次条用getBean实例化
String xmlPath = "com/itheima/b_di/beans.xml";
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(xmlPath));
BookService bookService = (BookService) beanFactory.getBean("bookServiceId");
bookService.addBook();
}
6 装配Bean 基于XML
6.1 实例化方式
- 3种bean实例化方式:默认构造、静态工厂、实例工厂
6.1.1 默认构造
<bean id="" class=""> 必须提供默认构造
6.1.2 静态工厂
- 常用与spring整合其他框架(工具)
- 静态工厂:用于生成实例对象,所有的方法必须是
static
<bean id="" class="工厂全限定类名" factory-method="静态方法">
6.1.2.1 工厂
public class MyBeanFactory {
/**
* 创建实例
* @return
*/
public static UserService createService(){
return new UserServiceImpl();
}
}
6.1.2.2 spring配置
<!-- 将静态工厂创建的实例交予spring
class 确定静态工厂全限定类名
factory-method 确定静态方法名
-->
<bean id="userServiceId" class="com.itheima.c_inject.b_static_factory.MyBeanFactory" factory-method="createService">
</bean>
6.1.3 实例工厂
- 实例工厂:必须先有工厂实例对象,通过实例对象创建对象。提供所有的方法都是“非静态”的。
6.1.3.1 工厂
/**
* 实例工厂,所有方法非静态
*
*/
public class MyBeanFactory {
/**
* 创建实例
* @return
*/
public UserService createService(){
return new UserServiceImpl();
}
}
6.1.3.2 spring配置
<!-- 创建工厂实例 -->
<bean id="myBeanFactoryId" class="com.itheima.c_inject.c_factory.MyBeanFactory"></bean>
<!-- 获得userservice
* factory-bean 确定工厂实例
* factory-method 确定普通方法
-->
<bean id="userServiceId" factory-bean="myBeanFactoryId" factory-method="createService"></bean>
6.2 Bean种类
- 普通bean:之前操作的都是普通bean。<bean id="" class=“A”> ,spring直接创建A实例,并返回
- FactoryBean:是一个特殊的bean,具有工厂生成对象能力,只能生成特定的对象。
bean必须使用FactoryBean
接口,此接口提供方法getObject()
用于获得特定bean。
<bean id="" class=“FB”> 先创建FB实例,使用调用getObject()方法,并返回方法的返回值
FB fb = new FB();
return fb.getObject();
- BeanFactory 和 FactoryBean 对比?
BeanFactory
:工厂,用于生成任意bean。
FactoryBean
:特殊bean,用于生成另一个特定的bean。例如:ProxyFactoryBean ,此工厂bean用于生产代理。<bean id=" " class="…ProxyFactoryBean"> 获得代理对象实例。AOP使用
6.3 作用域
- 作用域:用于确定spring创建bean实例个数
- 取值:
singleton
单例,默认值。
prototype
多例,每执行一次getBean将获得一个实例。例如:struts整合spring,配置action多例。 - 配置信息
<bean id="" class="" scope="">
<bean id="userServiceId" class="com.itheima.d_scope.UserServiceImpl" scope="prototype" >
</bean>
6.4 生命周期
6.4.1 初始化和销毁
- 目标方法执行前后执行后,将进行初始化或销毁。
<bean id="" class="" init-method="初始化方法名称" destroy-method="销毁的方法名称">
6.4.1.1 目标类
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("e_lifecycle add user");
}
public void myInit(){
System.out.println("初始化");
}
public void myDestroy(){
System.out.println("销毁");
}
}
6.4.1.2 spring配置
<!--
init-method 用于配置初始化方法,准备数据等
destroy-method 用于配置销毁方法,清理资源等
-->
<bean id="userServiceId" class="com.itheima.e_lifecycle.UserServiceImpl"
init-method="myInit" destroy-method="myDestroy" ></bean>
6.4.1.3 测试
@Test
public void demo02() throws Exception{
//spring 工厂
String xmlPath = "com/itheima/e_lifecycle/beans.xml";
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser();
//要求:1.容器必须close,销毁方法执行; 2.必须是单例的
// applicationContext.getClass().getMethod("close").invoke(applicationContext);
// * 此方法接口中没有定义,实现类提供
applicationContext.close();
}
6.4.2 BeanPostProcessor 后处理Bean
-
spring 提供一种机制,只要实现此接口
BeanPostProcessor
,并将实现类提供给spring容器,spring容器将自动执行,在初始化方法前执行before()
,在初始化方法后执行after()
。 配置<bean class=""> -
Factory hook(勾子) that allows for custom modification of new bean instances, e.g. checking for marker interfaces or wrapping them with proxies.
-
spring提供工厂勾子,用于修改实例对象,可以生成代理对象,是
AOP
底层。
模拟
A a =new A();
a = B.before(a) --> 将a的实例对象传递给后处理bean,可以生成代理对象并返回。
a.init();
a = B.after(a);
a.addUser(); //生成代理对象,目的在目标方法前后执行(例如:开启事务、提交事务)
a.destroy()
6.4.2.1 编写实现类
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("前方法 : " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
System.out.println("后方法 : " + beanName);
// bean 目标对象
// 生成 jdk 代理
return Proxy.newProxyInstance(
MyBeanPostProcessor.class.getClassLoader(),
bean.getClass().getInterfaces(),
new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------开启事务");
//执行目标方法
Object obj = method.invoke(bean, args);
System.out.println("------提交事务");
return obj;
}});
}
}
6.4.2.2 配置
<!-- 将后处理的实现类注册给spring -->
<bean class="com.itheima.e_lifecycle.MyBeanPostProcessor"></bean>
- 问题1:后处理bean作用某一个目标类,还是所有目标类?
所有 - 问题2:如何只作用一个?
通过“参数2”beanName进行控制
6.5 属性依赖注入
- 依赖注入方式:手动装配 和 自动装配
- 手动装配:一般进行配置信息都采用手动
基于xml装配**:构造方法、setter方法
基于注解装配: - 自动装配:
struts和spring 整合可以自动装配
byType
:按类型装配
byName
:按名称装配
constructor
:构造装配,
auto
: 不确定装配。
6.5.1 构造方法
6.5.1.1 目标类
public class User {
private Integer uid;
private String username;
private Integer age;
public User(Integer uid, String username) {
super();
this.uid = uid;
this.username = username;
}
public User(String username, Integer age) {
super();
this.username = username;
this.age = age;
}
6.5.1.2 spring配置
<!-- 构造方法注入
* <constructor-arg> 用于配置构造方法一个参数argument
name :参数的名称
value:设置普通数据
ref:引用数据,一般是另一个bean id值
index :参数的索引号,从0开始 。如果只有索引,匹配到了多个构造方法时,默认使用第一个。
type :确定参数类型
例如:使用名称name
<constructor-arg name="username" value="jack"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
例如2:【类型type 和 索引 index】
<constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
-->
<bean id="userId" class="com.itheima.f_xml.a_constructor.User" >
<constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
</bean>
6.5.2 setter方法
<!-- setter方法注入
* 普通数据
<property name="" value="值">
等效
<property name="">
<value>值
* 引用数据
<property name="" ref="另一个bean">
等效
<property name="">
<ref bean="另一个bean"/>
-->
<bean id="personId" class="com.itheima.f_xml.b_setter.Person">
<property name="pname" value="阳志"></property>
<property name="age">
<value>1234</value>
</property>
<property name="homeAddr" ref="homeAddrId"></property>
<property name="companyAddr">
<ref bean="companyAddrId"/>
</property>
</bean>
<bean id="homeAddrId" class="com.itheima.f_xml.b_setter.Address">
<property name="addr" value="阜南"></property>
<property name="tel" value="911"></property>
</bean>
<bean id="companyAddrId" class="com.itheima.f_xml.b_setter.Address">
<property name="addr" value="北京八宝山"></property>
<property name="tel" value="120"></property>
</bean>
6.5.3 P命令空间 [了解]
- 对“setter方法注入”进行简化,替换<property name=“属性名”>,而是在
<bean p:属性名=“普通值” p:属性名-ref=“引用值”> - p命名空间使用前提,必须添加命名空间
<bean 在这里插入代码片id="personId" class="com.itheima.f_xml.c_p.Person"
p:pname="禹太璞" p:age="22"
p:homeAddr-ref="homeAddrId" p:companyAddr-ref="companyAddrId">
</bean>
<bean id="homeAddrId" class="com.itheima.f_xml.c_p.Address"
p:addr="DG" p:tel="湖北">
</bean>
<bean id="companyAddrId" class="com.itheima.f_xml.c_p.Address"
p:addr="DG" p:tel="湖南">
</bean>
6.5.4 SpEL [了解]
- 对<property>进行统一编程,所有的内容都使用value
<property name="" value="#{表达式}">
#{123}、#{'jack'} : 数字、字符串
#{beanId} :另一个bean引用
#{beanId.propName} :操作数据
#{beanId.toString()} :执行方法
#{T(类).字段|方法} :静态方法或字段
<!--
<property name="cname" value="#{'jack'}"></property>
<property name="cname" value="#{customerId.cname.toUpperCase()}"></property>
通过另一个bean,获得属性,调用的方法
<property name="cname" value="#{customerId.cname?.toUpperCase()}"></property>
?. 如果对象不为null,将调用方法
-->
<bean id="customerId" class="com.itheima.f_xml.d_spel.Customer" >
<property name="cname" value="#{customerId.cname?.toUpperCase()}"></property>
<property name="pi" value="#{T(java.lang.Math).PI}"></property>
</bean>
6.5.5 集合注入
<!--
集合的注入都是给<property>添加子标签
数组:<array>
List:<list>
Set:<set>
Map:<map> ,map存放k/v 键值对,使用<entry>描述
Properties:<props> <prop key=""></prop> 【】
普通数据:<value>
引用数据:<ref>
-->
<bean id="collDataId" class="com.itheima.f_xml.e_coll.CollData" >
<property name="arrayData">
<array>
<value>DS</value>
<value>DZD</value>
<value>屌丝</value>
<value>屌中屌</value>
</array>
</property>
<property name="listData">
<list>
<value>于嵩楠</value>
<value>曾卫</value>
<value>杨煜</value>
<value>曾小贤</value>
</list>
</property>
<property name="setData">
<set>
<value>停封</value>
<value>薄纸</value>
<value>关系</value>
</set>
</property>
<property name="mapData">
<map>
<entry key="jack" value="杰克"></entry>
<entry>
<key><value>rose</value></key>
<value>肉丝</value>
</entry>
</map>
</property>
<property name="propsData">
<props>
<prop key="高富帅">嫐</prop>
<prop key="白富美">嬲</prop>
<prop key="男屌丝">挊</prop>
</props>
</property>
</bean>
7 装配Bean 基于注解
- 注解:就是一个类,使用
@
注解名称 - 开发中:使用注解 取代 xml配置文件。
@Component 取代\<bean class="">
@Component("id") 取代 \<bean id="" class="">
- web开发,提供3个@Component注解衍生注解(功能一样)取代
<bean class="">
@Repository :dao层
@Service:service层
@Controller:web层
- 依赖注入,给私有字段设置,也可以给setter方法设置
普通值:@Value("")
引用值:
1. 方式1:按照【类型】注入
@Autowired
2. 方式2:按照【名称】注入1
@Autowired
@Qualifier("名称")
3. 方式3:按照【名称】注入2
@Resource("名称")
- 生命周期
初始化:@PostConstruct
销毁:@PreDestroy
- 作用域
@Scope("prototype")
多例
- 注解使用前提,添加命名空间,让spring扫描含有注解类
<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:component-scan base-package="com.itheima.g_annotation.a_ioc"></context:component-scan>
</beans>
1 spring day01回顾
1.1 编写流程(基于xml)
- 导入jar包:4+1 beans/core/context/expression | commons-logging
- 编写目标类:dao 和 service
- spring配置文件
IoC:
<bean id="" class="" >
DI:
<bean> <property name="" value="" | ref="">
-
实例化方式:
默认构造
静态工厂:
<bean id="" class="工厂类" factory-method="静态方法">
实例工厂:
<bean id="工厂id" class="工厂类"> <bean id="" factory-bean="工厂id" factory-method="方法">
- 作用域:
<bean id="" class="" scope="singleton | prototype">
- 生命周期:
<bean id="" class="" init-method="" destroy-method="">
- 后处理bean
BeanPostProcessor
接口, ,对容器中所有的bean都生效
属性注入 - 构造方法注入:
<bean><constructor-arg index="" type="" >
- setter方法注入:
<bean><property>
- p命名空间:
- 简化<property>
<bean p:属性名="普通值" p:属性名-ref="引用值"> 注意声明命名空间
SpEL:
<property name="" value="#{表达式}">
#{123} #{'abc'}
#{beanId.propName?.methodName()}
#{T(类).静态方法|字段}
- 集合
数组
List
Set
Map
- 核心api
BeanFactory
,延迟实例化bean,第一次调用getBean
ApplicationContext
一般常用,功能更强
ClassPathXmlApplicationContext
加载 classpath xml文件
FileSystemXmlApplicationContext
加载指定盘符文件 ,ServletContext.getRealPath()
1.2 后处理bean 对一个生效
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if("userServiceId".equals(beanName)){
System.out.println("前方法 : " + beanName);
}
return bean;
}
1.3 注解
- 扫描含有注解的类
<context:component-scan base-package="....">
- 常见的注解
@Component
组件,任意bean
WEB
@Controller
web层
@Service
service层
@Repository
dao层
- 注入 字段或setter方法
普通值:@Value
引用值:
类型:@Autowired
名称1:@Autowired
@Qualifier("名称")
名称2:@Resource("名称")
作用域:@Scope("prototype")
生命周期:
初始化:@PostConstruct
销毁方法:@PreDestroy
1.4 注解和xml混合使用
- 将所有的bean都配置xml中
<bean id="" class="">
- 将所有的依赖都使用注解
@Autowired
默认不生效。为了生效,需要在xml配置:<context:annotation-config>
总结:
注解1:<context:component-scan base-package=" ">
注解2:<context:annotation-config>
- 一般情况两个注解不一起使用。
- “注解1”扫描含有注解(@Component 等)类,注入注解自动生效。
“注解2”只在xml和注解(注入)混合使用时,使注入注解生效。
2 AOP
2.1 AOP介绍
2.1.1 什么是AOP
- 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP
采取横向抽取机制,取代了传统纵向继承体系重复性代码- 经典应用:事务管理(最多)、性能监视、安全检查、缓存 、日志等
- Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码
AspectJ
是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入
2.1.2 AOP实现原理
- aop底层将采用代理机制进行实现。
- 接口 + 实现类:spring采用
jdk
的动态代理Proxy。 - 实现类:spring 采用
cglib
字节码增强。
2.1.3 AOP术语 【掌握】
target
目标类:需要被代理的类。例如:UserServiceJoinpoint
连接点:所谓连接点是指那些可能被拦截到的方法。例如:所有的方法PointCut
切入点:已经被增强的连接点。例如:addUser()advice
通知/增强:增强代码。例如:after、beforeWeaving
织入:是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.proxy
代理类Aspect
切面:是切入点pointcut和通知advice的结合
一个线是一个特殊的面。
一个切入点和一个通知,组成成一个特殊的面。
2.2 手动方式
2.2.1 JDK动态代理
- JDK动态代理 对“装饰者”设计模式 简化。使用前提:必须有接口
- 目标类:接口 + 实现类
- 切面类:用于存通知 MyAspect
- 工厂类:编写工厂生成代理
- 测试
2.2.1.1 目标类
public interface UserService {
public void addUser();
public void updateUser();
public void deleteUser();
}
2.2.1.2 切面类
public class MyAspect {
public void before(){
System.out.println("鸡首");
}
public void after(){
System.out.println("牛后");
}
}
2.2.1.3 工厂
public class MyBeanFactory {
public static UserService createService(){
//1 目标类
final UserService userService = new UserServiceImpl();
//2切面类
final MyAspect myAspect = new MyAspect();
/* 3 代理类:将目标类(切入点)和 切面类(通知) 结合 --> 切面
* Proxy.newProxyInstance
* 参数1:loader ,类加载器,动态代理类 运行时创建,任何类都需要类加载器将其加载到内存。
* 一般情况:当前类.class.getClassLoader();
* 目标类实例.getClass().get...
* 参数2:Class[] interfaces 代理类需要实现的所有接口
* 方式1:目标类实例.getClass().getInterfaces() ;注意:只能获得自己接口,不能获得父元素接口
* 方式2:new Class[]{UserService.class}
* 例如:jdbc 驱动 --> DriverManager 获得接口 Connection
* 参数3:InvocationHandler 处理类,接口,必须进行实现类,一般采用匿名内部
* 提供 invoke 方法,代理类的每一个方法执行时,都将调用一次invoke
* 参数31:Object proxy :代理对象
* 参数32:Method method : 代理对象当前执行的方法的描述对象(反射)
* 执行方法名:method.getName()
* 执行方法:method.invoke(对象,实际参数)
* 参数33:Object[] args :方法实际参数
*
*/
UserService proxService = (UserService)Proxy.newProxyInstance(
MyBeanFactory.class.getClassLoader(),
userService.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//前执行
myAspect.before();
//执行目标类的方法
Object obj = method.invoke(userService, args);
//后执行
myAspect.after();
return obj;
}
});
return proxService;
}
}
2.2.1.4 测试
@Test
public void demo01(){
UserService userService = MyBeanFactory.createService();
userService.addUser();
userService.updateUser();
userService.deleteUser();
}
2.2.2 CGLIB字节码增强
-
没有接口,只有实现类。
-
采用字节码增强框架cglib,在运行时创建目标类的子类,从而对目标类进行增强。
-
导入jar包:
自己导包(了解):
核心:hibernate-distribution-3.6.10.Final\lib\bytecode\cglib\cglib-2.2.jar
依赖:struts-2.3.15.3\apps\struts2-blank\WEB-INF\lib\asm-3.3.jar
spring-core…jar 已经整合以上两个内容
2.2.2.1 工厂类
public class MyBeanFactory {
public static UserServiceImpl createService(){
//1 目标类
final UserServiceImpl userService = new UserServiceImpl();
//2切面类
final MyAspect myAspect = new MyAspect();
// 3.代理类 ,采用cglib,底层创建目标类的子类
//3.1 核心类
Enhancer enhancer = new Enhancer();
//3.2 确定父类
enhancer.setSuperclass(userService.getClass());
/* 3.3 设置回调函数 , MethodInterceptor接口 等效 jdk InvocationHandler接口
* intercept() 等效 jdk invoke()
* 参数1、参数2、参数3:以invoke一样
* 参数4:methodProxy 方法的代理
*/
enhancer.setCallback(new MethodInterceptor(){
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//前
myAspect.before();
//执行目标类的方法
Object obj = method.invoke(userService, args);
// * 执行代理类的父类 ,执行目标类 (目标类和代理类 父子关系)
methodProxy.invokeSuper(proxy, args);
//后
myAspect.after();
return obj;
}
});
//3.4 创建代理
UserServiceImpl proxService = (UserServiceImpl) enhancer.create();
return proxService;
}
}
2.3 AOP联盟通知类型
AOP联盟为通知Advice定义了org.aopalliance.aop.Advice
- Spring按照通知Advice在目标类方法的连接点位置,可以分为5类
• 前置通知 org.springframework.aop.MethodBeforeAdvice
• 在目标方法执行前实施增强
• 后置通知 org.springframework.aop.AfterReturningAdvice
• 在目标方法执行后实施增强
• 环绕通知 org.aopalliance.intercept.MethodInterceptor
• 在目标方法执行前后实施增强
• 异常抛出通知 org.springframework.aop.ThrowsAdvice
• 在方法抛出异常后实施增强
• 引介通知 org.springframework.aop.IntroductionInterceptor
• 在目标类中添加一些新的方法和属性
环绕通知,必须手动执行目标方法
try{
//前置通知
//执行目标方法
//后置通知
} catch(){
//抛出异常通知
}
2.4 spring编写代理:半自动
- 让spring 创建代理对象,从spring容器中手动的获取代理对象。
- 导入jar包:
核心:4+1
AOP:AOP联盟(规范)、spring-aop (实现)
2.4.1 目标类
public interface UserService {
public void addUser();
public void updateUser();
public void deleteUser();
}
2.4.2 切面类
/*
* 切面类中确定通知,需要实现不同接口,接口就是规范,从而就确定方法名称。
* 采用“环绕通知” MethodInterceptor
*/
public class MyAspect implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
System.out.println("前3");
//手动执行目标方法
Object obj = mi.proceed();
System.out.println("后3");
return obj;
}
}
2.4.3 spring配置
<!-- 1 创建目标类 -->
<bean id="userServiceId" class="com.itheima.b_factory_bean.UserServiceImpl"></bean>
<!-- 2 创建切面类 -->
<bean id="myAspectId" class="com.itheima.b_factory_bean.MyAspect"></bean>
<!-- 3 创建代理类
* 使用工厂bean FactoryBean ,底层调用 getObject() 返回特殊bean
* ProxyFactoryBean 用于创建代理工厂bean,生成特殊代理对象
interfaces : 确定接口们
通过<array>可以设置多个值
只有一个值时,value=""
target : 确定目标类
interceptorNames : 通知 切面类的名称,类型String[],如果设置一个值 value=""
optimize :强制使用cglib
<property name="optimize" value="true"></property>
底层机制
如果目标类有接口,采用jdk动态代理
如果没有接口,采用cglib 字节码增强
如果声明 optimize = true ,无论是否有接口,都采用cglib
-->
<bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interfaces" value="com.itheima.b_factory_bean.UserService"></property>
<property name="target" ref="userServiceId"></property>
<property name="interceptorNames" value="myAspectId"></property>
</bean>
2.4.4 测试
@Test
public void demo01(){
String xmlPath = "com/itheima/b_factory_bean/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//获得代理类
UserService userService = (UserService) applicationContext.getBean("proxyServiceId");
userService.addUser();
userService.updateUser();
userService.deleteUser();
}
2.5 spring aop编程:全自动 【掌握】
- 从spring容器获得目标类,如果配置aop,spring将自动生成代理。
- 要确定目标类,aspectj 切入点表达式,导入jar包
spring-framework-3.0.2.RELEASE-dependencies\org.aspectj\com.springsource.org.aspectj.weaver\1.6.8.RELEASE
2.5.1 spring配置
<?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">
<!-- 1 创建目标类 -->
<bean id="userServiceId" class="com.itheima.c_spring_aop.UserServiceImpl"></bean>
<!-- 2 创建切面类(通知) -->
<bean id="myAspectId" class="com.itheima.c_spring_aop.MyAspect"></bean>
<!-- 3 aop编程
3.1 导入命名空间
3.2 使用 <aop:config>进行配置
proxy-target-class="true" 声明时使用cglib代理
<aop:pointcut> 切入点 ,从目标对象获得具体方法
<aop:advisor> 特殊的切面,只有一个通知 和 一个切入点
advice-ref 通知引用
pointcut-ref 切入点引用
3.3 切入点表达式
execution(* com.itheima.c_spring_aop.*.*(..))
选择方法 返回值任意 包 类名任意 方法名任意 参数任意
-->
<aop:config proxy-target-class="true">
<aop:pointcut expression="execution(* com.itheima.c_spring_aop.*.*(..))" id="myPointCut"/>
<aop:advisor advice-ref="myAspectId" pointcut-ref="myPointCut"/>
</aop:config>
</beans>
2.5.2 测试
@Test
public void demo01(){
String xmlPath = "com/itheima/c_spring_aop/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//获得目标类
UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser();
userService.updateUser();
userService.deleteUser();
}
3 AspectJ
3.1 介绍
- AspectJ是一个基于Java语言的AOP框架
- Spring2.0以后新增了对AspectJ切点表达式支持
- @AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
新版本Spring框架,建议使用AspectJ方式来开发AOP - 主要用途:自定义开发
3.2 切入点表达式 【掌握】
- execution() 用于描述方法 【掌握】
语法:
execution(修饰符 返回值 包.类.方法名(参数) throws异常)
修饰符 | 一般省略 |
---|---|
public | 公共方法 |
* | 任意 |
返回值 | 不能省略 |
---|---|
void | 返回没有值 |
String | 返回值字符串 |
* | 任意 |
包 | 可省略 |
---|---|
com.itheima.crm | 固定包 |
com.itheima.crm.*.service | crm包下面子包任意 (例如:com.itheima.crm.staff.service) |
com.itheima.crm… | crm包下面的所有子包(含自己) |
com.itheima.crm.*.service… | crm包下面任意子包,固定目录service,service目录任意包类,[省略] |
UserServiceImpl | 指定类 |
*Impl | 以Impl结尾 |
User* | 以User开头 |
* | 任意 |
方法名 | 不能省略 |
---|---|
addUser | 固定方法 |
add* | 以add开头 |
*Do | 以Do结尾 |
* | 任意 |
(参数) | |
---|---|
() | 无参 |
(int) | 一个整型 |
(int ,int) | 两个 |
(…) | 参数任意 |
throws ,可省略,一般不写。
综合1
execution(* com.itheima.crm.*.service..*.*(..))
综合2
<aop:pointcut expression="execution(* com.itheima.*WithCommit.*(..)) ||
execution(* com.itheima.*Service.*(..))" id="myPointCut"/>
- within:匹配包或子包中的方法 (了解)
within(com.itheima.aop…*) - this:匹配实现接口的代理对象中的方法 (了解)
this(com.itheima.aop.user.UserDAO) - target:匹配实现接口的目标对象中的方法 (了解)
target(com.itheima.aop.user.UserDAO) - args:匹配参数格式符合标准的方法 (了解)
args(int,int) - bean(id) 对指定的bean所有的方法 (了解)
bean(‘userServiceId’)
3.3 AspectJ 通知类型
- aop联盟定义通知类型,具有特性接口,必须实现,从而确定方法名称。
- aspectj 通知类型,只定义类型名称。已经方法格式。
- 个数:6种,知道5种,掌握1中。
before
:前置通知(应用:各种校验)
在方法执行前执行,如果通知抛出异常,阻止方法运行
afterReturning
:后置通知(应用:常规数据处理)
方法正常返回后执行,如果方法中抛出异常,通知无法执行
必须在方法执行后才执行,所以可以获得方法的返回值。
around
:环绕通知(应用:十分强大,可以做任何事情)
方法执行前后分别执行,可以阻止方法的执行
必须手动执行目标方法
afterThrowing
:抛出异常通知(应用:包装异常信息)
方法抛出异常后执行,如果方法没有抛出异常,无法执行
after
:最终通知(应用:清理现场)
方法执行完毕后执行,无论方法中是否出现异常
环绕
try{
//前置:before
//手动执行目标方法
//后置:afterRetruning
} catch(){
//抛出异常 afterThrowing
} finally{
//最终 after
}
3.4 导入jar包
- 4个:
- aop联盟规范
- spring aop 实现
- aspect 规范
- spring aspect 实现
3.5 基于xml
- 目标类:接口 + 实现
- 切面类:编写多个通知,采用aspectj 通知名称任意(方法名任意)
- aop编程,将通知应用到目标类
- 测试
3.5.1 切面类
/**
* 切面类,含有多个通知
*/
public class MyAspect {
public void myBefore(JoinPoint joinPoint){
System.out.println("前置通知 : " + joinPoint.getSignature().getName());
}
public void myAfterReturning(JoinPoint joinPoint,Object ret){
System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
}
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("前");
//手动执行目标方法
Object obj = joinPoint.proceed();
System.out.println("后");
return obj;
}
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("抛出异常通知 : " + e.getMessage());
}
public void myAfter(JoinPoint joinPoint){
System.out.println("最终通知");
}
}
3.5.2 spring配置
<!-- 1 创建目标类 -->
<bean id="userServiceId" class="com.itheima.d_aspect.a_xml.UserServiceImpl"></bean>
<!-- 2 创建切面类(通知) -->
<bean id="myAspectId" class="com.itheima.d_aspect.a_xml.MyAspect"></bean>
<!-- 3 aop编程
<aop:aspect> 将切面类 声明“切面”,从而获得通知(方法)
ref 切面类引用
<aop:pointcut> 声明一个切入点,所有的通知都可以使用。
expression 切入点表达式
id 名称,用于其它通知引用
-->
<aop:config>
<aop:aspect ref="myAspectId">
<aop:pointcut expression="execution(* com.itheima.d_aspect.a_xml.UserServiceImpl.*(..))" id="myPointCut"/>
<!-- 3.1 前置通知
<aop:before method="" pointcut="" pointcut-ref=""/>
method : 通知,及方法名
pointcut :切入点表达式,此表达式只能当前通知使用。
pointcut-ref : 切入点引用,可以与其他通知共享切入点。
通知方法格式:public void myBefore(JoinPoint joinPoint){
参数1:org.aspectj.lang.JoinPoint 用于描述连接点(目标方法),获得目标方法名等
例如:
<aop:before method="myBefore" pointcut-ref="myPointCut"/>
-->
<!-- 3.2后置通知 ,目标方法后执行,获得返回值
<aop:after-returning method="" pointcut-ref="" returning=""/>
returning 通知方法第二个参数的名称
通知方法格式:public void myAfterReturning(JoinPoint joinPoint,Object ret){
参数1:连接点描述
参数2:类型Object,参数名 returning="ret" 配置的
例如:
<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" />
-->
<!-- 3.3 环绕通知
<aop:around method="" pointcut-ref=""/>
通知方法格式:public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
返回值类型:Object
方法名:任意
参数:org.aspectj.lang.ProceedingJoinPoint
抛出异常
执行目标方法:Object obj = joinPoint.proceed();
例如:
<aop:around method="myAround" pointcut-ref="myPointCut"/>
-->
<!-- 3.4 抛出异常
<aop:after-throwing method="" pointcut-ref="" throwing=""/>
throwing :通知方法的第二个参数名称
通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
参数1:连接点描述对象
参数2:获得异常信息,类型Throwable ,参数名由throwing="e" 配置
例如:
<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
-->
<!-- 3.5 最终通知 -->
<aop:after method="myAfter" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>
3.6 基于注解
3.6.1 替换bean
<!-- 1 创建目标类 -->
<bean id="userServiceId" class="com.itheima.d_aspect.b_anno.UserServiceImpl"></bean>
<!-- 2 创建切面类(通知) -->
<bean id="myAspectId" class="com.itheima.d_aspect.b_anno.MyAspect"></bean>
- 注意:扫描
<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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1.扫描 注解类 -->
<context:component-scan base-package="com.itheima.d_aspect.b_anno"></context:component-scan>
3.6.2 替换aop
- 必须进行aspectj 自动代理
<!-- 2.确定 aop注解生效 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- 声明切面
<aop:aspect ref="myAspectId">
- 替换前置通知
<aop:before method="myBefore" pointcut="execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))"/>
//切入点当前有效
@Before("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))")
public void myBefore(JoinPoint joinPoint){
System.out.println("前置通知 : " + joinPoint.getSignature().getName());
}
- 替换 公共切入点
<aop:pointcut expression="execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))" id="myPointCut"/>
//声明公共切入点
@Pointcut("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))")
private void myPointCut(){
}
- 替换后置
<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" />
@AfterReturning(value="myPointCut()" ,returning="ret")
public void myAfterReturning(JoinPoint joinPoint,Object ret){
System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
}
- 替换环绕
<aop:around method="myAround" pointcut-ref="myPointCut"/>
@Around(value = "myPointCut()")
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("前");
//手动执行目标方法
Object obj = joinPoint.proceed();
System.out.println("后");
return obj;
}
- 替换抛出异常
<aop:after-throwing method="myAfterThrowing" pointcut="execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))" throwing="e"/>
@AfterThrowing(value="execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))" ,throwing="e")
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("抛出异常通知 : " + e.getMessage());
}
3.6.3 切面类
/**
* 切面类,含有多个通知
*/
@Component
@Aspect
public class MyAspect {
//切入点当前有效
// @Before("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))")
public void myBefore(JoinPoint joinPoint){
System.out.println("前置通知 : " + joinPoint.getSignature().getName());
}
//声明公共切入点
@Pointcut("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))")
private void myPointCut(){
}
// @AfterReturning(value="myPointCut()" ,returning="ret")
public void myAfterReturning(JoinPoint joinPoint,Object ret){
System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
}
// @Around(value = "myPointCut()")
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("前");
//手动执行目标方法
Object obj = joinPoint.proceed();
System.out.println("后");
return obj;
}
// @AfterThrowing(value="execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))" ,throwing="e")
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("抛出异常通知 : " + e.getMessage());
}
@After("myPointCut()")
public void myAfter(JoinPoint joinPoint){
System.out.println("最终通知");
}
}
3.6.4 spring配置
<!-- 1.扫描 注解类 -->
<context:component-scan base-package="com.itheima.d_aspect.b_anno"></context:component-scan>
<!-- 2.确定 aop注解生效 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
3.6.5 aop注解总结
@Aspect 声明切面,修饰切面类,从而获得 通知。
通知
@Before 前置
@AfterReturning 后置
@Around 环绕
@AfterThrowing 抛出异常
@After 最终切入点
@PointCut ,修饰方法 private void xxx(){} 之后通过“方法名”获得切入点引用
4 JdbcTemplate
- spring 提供用于操作JDBC工具类,类似:DBUtils。
- 依赖 连接池DataSource (数据源)
4.1 环境搭建
4.1.1 创建表
create database ee19_spring_day02;
use ee19_spring_day02;
create table t_user(
id int primary key auto_increment,
username varchar(50),
password varchar(32)
);
insert into t_user(username,password) values('jack','1234');
insert into t_user(username,password) values('rose','5678');
4.1.2 导入jar包
4.1.3 javabean
package com.itheima.domain;
public class User {
private Integer id;
private String username;
private String password;
4.2 使用api(了解)
public static void main(String[] args) {
//1 创建数据源(连接池) dbcp
BasicDataSource dataSource = new BasicDataSource();
// * 基本4项
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/ee19_spring_day02");
dataSource.setUsername("root");
dataSource.setPassword("1234");
//2 创建模板
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
//3 通过api操作
jdbcTemplate.update("insert into t_user(username,password) values(?,?);", "tom","998");
}
4.3 配置DBCP
<bean id="dataSourceId" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/ee19_spring_day02"></property>
<property name="username" value="root"></property>
<property name="password" value="1234"></property>
</bean>
<!-- 创建模板 ,需要注入数据源-->
<bean id="jdbcTemplateId" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSourceId"></property>
</bean>
<!-- 配置dao -->
<bean id="userDaoId" class="com.itheima.c_dbcp.UserDao">
<property name="jdbcTemplate" ref="jdbcTemplateId"></property>
</bean>
4.4 配置C3P0
<bean id="dataSourceId" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ee19_spring_day02"></property>
<property name="user" value="root"></property>
<property name="password" value="1234"></property>
</bean>
4.5 使用JdbcDaoSupport
4.5.1 dao层
4.5.2 spring配置文件
4.5.3 源码分析
4.6 配置properties
4.6.1 properties文件
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/ee19_spring_day02
jdbc.user=root
jdbc.password=1234
4.6.2 spring配置
<context:property-placeholder location="classpath:com/itheima/f_properties/jdbcInfo.properties"/>
<!-- 创建数据源 c3p0-->
<bean id="dataSourceId" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
5 要求
properties + JdbcDaoSupport + c3p0
UserDao --> api ( update / query / queryForObject)
1 spring day02回顾
AOP :切面编程
切面:切入点 和 通知 结合
spring aop 编程
aop:config
方法1:
<aop:pointcut expression=“切入点表达式” id="">
<aop:advisor advice-ref=“通知引用” pointcut-ref=“切入点的引用”>
方法2:
<aop:advisor advice-ref=“通知引用” pointcut=“切入点表达式”>
AspectJ xml
aop:config
<aop:aspect ref=“切面类”>
aop:pointcut
aop:before 前置
<aop:afterReturning returning=“第二个参数名称”> 后置
aop:around 环绕
<aop:afterThrowing throwing=“第二。。。”> 抛出异常
aop:after 最终
AspectJ annotation
@Aspect
@Pointcut(“表达式”) private void xxx(){}
@Before @…
切入点表达式
<aop:pointcut expression=“execution(* com.itheima.crm..service….*(…))” id="">
2 事务管理
2.1 回顾事务
事务:一组业务操作ABCD,要么全部成功,要么全部不成功。
特性:ACID
原子性:整体
一致性:完成
隔离性:并发
持久性:结果
隔离问题:
脏读:一个事务读到另一个事务没有提交的数据
不可重复读:一个事务读到另一个事务已提交的数据(update)
虚读(幻读):一个事务读到另一个事务已提交的数据(insert)
隔离级别:
read uncommitted:读未提交。存在3个问题
read committed:读已提交。解决脏读,存在2个问题
repeatable read:可重复读。解决:脏读、不可重复读,存在1个问题。
serializable :串行化。都解决,单事务。
mysql 事务操作–简单
ABCD 一个事务
Connection conn = null;
try{
//1 获得连接
conn = …;
//2 开启事务
conn.setAutoCommit(false);
A
B
C
D
//3 提交事务
conn.commit();
} catche(){
//4 回滚事务
conn.rollback();
}
mysql 事务操作–Savepoint
需求:AB(必须),CD(可选)
Connection conn = null;
Savepoint savepoint = null; //保存点,记录操作的当前位置,之后可以回滚到指定的位置。(可以回滚一部分)
try{
//1 获得连接
conn = …;
//2 开启事务
conn.setAutoCommit(false);
A
B
savepoint = conn.setSavepoint();
C
D
//3 提交事务
conn.commit();
} catche(){
if(savepoint != null){ //CD异常
// 回滚到CD之前
conn.rollback(savepoint);
// 提交AB
conn.commit();
} else{ //AB异常
// 回滚AB
conn.rollback();
}
}
2.2 事务管理介绍
2.2.1 导入jar包
transaction --> tx
2.2.2 三个顶级接口
PlatformTransactionManager 平台事务管理器,spring要管理事务,必须使用事务管理器
进行事务配置时,必须配置事务管理器。
TransactionDefinition:事务详情(事务定义、事务属性),spring用于确定事务具体详情,
例如:隔离级别、是否只读、超时时间等
进行事务配置时,必须配置详情。spring将配置项封装到该对象实例。
TransactionStatus:事务状态,spring用于记录当前事务运行状态。例如:是否有保存点,事务是否完成。
spring底层根据状态进行相应操作。
2.2.3 PlatformTransactionManager 事务管理器
导入jar包:需要时平台事务管理器的实现类
常见的事务管理器
DataSourceTransactionManager ,jdbc开发时事务管理器,采用JdbcTemplate
HibernateTransactionManager,hibernate开发时事务管理器,整合hibernate
api详解
TransactionStatus getTransaction(TransactionDefinition definition) ,事务管理器 通过“事务详情”,获得“事务状态”,从而管理事务。
void commit(TransactionStatus status) 根据状态提交
void rollback(TransactionStatus status) 根据状态回滚
2.2.4 TransactionStatus
2.2.5 TransactionDefinition
传播行为:在两个业务之间如何共享事务。
PROPAGATION_REQUIRED , required , 必须 【默认值】
支持当前事务,A如果有事务,B将使用该事务。
如果A没有事务,B将创建一个新的事务。
PROPAGATION_SUPPORTS ,supports ,支持
支持当前事务,A如果有事务,B将使用该事务。
如果A没有事务,B将以非事务执行。
PROPAGATION_MANDATORY,mandatory ,强制
支持当前事务,A如果有事务,B将使用该事务。
如果A没有事务,B将抛异常。
PROPAGATION_REQUIRES_NEW , requires_new ,必须新的
如果A有事务,将A的事务挂起,B创建一个新的事务
如果A没有事务,B创建一个新的事务
PROPAGATION_NOT_SUPPORTED ,not_supported ,不支持
如果A有事务,将A的事务挂起,B将以非事务执行
如果A没有事务,B将以非事务执行
PROPAGATION_NEVER ,never,从不
如果A有事务,B将抛异常
如果A没有事务,B将以非事务执行
PROPAGATION_NESTED ,nested ,嵌套
A和B底层采用保存点机制,形成嵌套事务。
掌握:PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED
2.3 案例:转账
2.3.1 搭建环境
2.3.1.1 创建表
create database ee19_spring_day03;
use ee19_spring_day03;
create table account(
id int primary key auto_increment,
username varchar(50),
money int
);
insert into account(username,money) values(‘jack’,‘10000’);
insert into account(username,money) values(‘rose’,‘10000’);
2.3.1.2 导入jar包
核心:4+1
aop : 4 (aop联盟、spring aop、aspectj规范、spring aspect)
数据库:2 (jdbc/tx)
驱动:mysql
连接池:c3p0
2.3.1.3 dao层
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Override
public void out(String outer, Integer money) {
this.getJdbcTemplate().update("update account set money = money - ? where username = ?", money,outer);
}
@Override
public void in(String inner, Integer money) {
this.getJdbcTemplate().update("update account set money = money + ? where username = ?", money,inner);
}
}
2.3.1.4 service层
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String outer, String inner, Integer money) {
accountDao.out(outer, money);
//断电
// int i = 1/0;
accountDao.in(inner, money);
}
}
2.3.1.5 spring配置
<?xml version="1.0" encoding="UTF-8"?><!-- 1 datasource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ee19_spring_day03"></property>
<property name="user" value="root"></property>
<property name="password" value="1234"></property>
</bean>
<!-- 2 dao -->
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3 service -->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
2.3.1.6 测试
@Test
public void demo01(){
String xmlPath = “applicationContext.xml”;
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
AccountService accountService = (AccountService) applicationContext.getBean(“accountService”);
accountService.transfer(“jack”, “rose”, 1000);
}
2.3.2 手动管理事务(了解)
spring底层使用 TransactionTemplate 事务模板进行操作。
操作
1.service 需要获得 TransactionTemplate
2.spring 配置模板,并注入给service
3.模板需要注入事务管理器
4.配置事务管理器:DataSourceTransactionManager ,需要注入DataSource
2.3.2.1 Dao类
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class AccountDaoImple extends JdbcDaoSupport implements AccountDao {
@Override
public void out(String outer, int money) {
this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,outer);
}
@Override
public void in(String inner, int money) {
this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money,inner);
}
}
2.3.2.2 修改service
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
//需要spring注入模板
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
@Override
public void transfer(final String outer,final String inner,final Integer money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
accountDao.out(outer, money);
//断电
// int i = 1/0;
accountDao.in(inner, money);
}
});
}
}
2.3.2.3 修改spring配置
<?xml version="1.0" encoding="UTF-8"?><!-- 创建数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/study"></property>
<property name="user" value="root"></property>
<property name="password" value="10086"></property>
</bean>
<!-- 配置dao -->
<bean id="accountDaoImple" class="cn.lm.tx01.AccountDaoImple">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置service -->
<bean id="accountServiceImplId" class="cn.lm.tx01.AccountServiceImpl">
<property name="accountDao" ref="accountDaoImple"></property>
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
<!-- 创建模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="txManager"></property>
</bean>
<!-- 配置事务管理器 ,管理器需要事务,事务从Connection获得,连接从连接池DataSource获得 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
2.3.2.4 测试 import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
String xmlPath = "cn/lm/tx01/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
AccountService accountService = (AccountService) applicationContext.getBean("accountServiceImplId");
accountService.transfer("jack", "rose", 1000);
}
}
2.3.3 工厂bean 生成代理:半自动
spring提供 管理事务的代理工厂bean TransactionProxyFactoryBean
1.getBean() 获得代理对象
2.spring 配置一个代理
2.3.3.1 Dao类
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class AccountDaoImple extends JdbcDaoSupport implements AccountDao {
@Override
public void out(String outer, int money) {
this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,outer);
}
@Override
public void in(String inner, int money) {
this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money,inner);
}
}
2.3.3.2 Service类
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String outer, String inner, int money) {
accountDao.out(outer, money);
int i = 1 / 0;
accountDao.in(inner, money);
}
}
2.3.3.3 spring配置
<?xml version="1.0" encoding="UTF-8"?><!-- 创建数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/study"></property>
<property name="user" value="root"></property>
<property name="password" value="10086"></property>
</bean>
<!-- 配置dao -->
<bean id="accountDaoImple" class="cn.lm.tx02.AccountDaoImple">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置service -->
<bean id="accountServiceImplId" class="cn.lm.tx02.AccountServiceImpl">
<property name="accountDao" ref="accountDaoImple"></property>
</bean>
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 4 service 代理对象
4.1 proxyInterfaces 接口
4.2 target 目标类
4.3 transactionManager 事务管理器
4.4 transactionAttributes 事务属性(事务详情)
prop.key :确定哪些方法使用当前事务配置
prop.text:用于配置事务详情
格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
传播行为 隔离级别 是否只读 异常回滚 异常提交
例如:
<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop> 默认传播行为,和隔离级别
<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly</prop> 只读
<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,+java.lang.ArithmeticException</prop> 有异常扔提交 -->
<bean id="proxyAccountService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="proxyInterfaces" value="cn.lm.tx02.AccountService"></property>
<property name="target" ref="accountServiceImplId"></property>
<property name="transactionManager" ref="txManager"></property>
<property name="transactionAttributes">
<props>
<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop>
</props>
</property>
</bean>
2.3.3.4 测试
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
String xmlPath = "cn/lm/tx02/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
AccountService accountService = (AccountService) applicationContext.getBean("proxyAccountService");
accountService.transfer("jack", "rose", 1000);
}
}
2.3.4 AOP 配置基于xml【掌握】
在spring xml 配置aop 自动生成代理,进行事务的管理
1.配置管理器
2.配置事务详情
3.配置aop
2.3.4.1 Dao
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class AccountDaoImple extends JdbcDaoSupport implements AccountDao {
@Override
public void out(String outer, int money) {
this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,outer);
}
@Override
public void in(String inner, int money) {
this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money,inner);
}
}
2.3.4.2 Service
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String outer, String inner, int money) {
accountDao.out(outer, money);
int i = 1 / 0;
accountDao.in(inner, money);
}
}
2.3.4.3 配置文件
<!-- 创建数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/study"></property>
<property name="user" value="root"></property>
<property name="password" value="10086"></property>
</bean>
<!-- 配置dao -->
<bean id="accountDaoImple" class="cn.lm.tx03_xml.AccountDaoImple">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置service -->
<bean id="accountServiceImplId" class="cn.lm.tx03_xml.AccountServiceImpl">
<property name="accountDao" ref="accountDaoImple"></property>
</bean>
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置 事务详情(事务通知) , 在aop筛选基础上,对ABC三个确定使用什么样的事务。例如:AC读写、B只读 等
<tx:attributes> 用于配置事务详情(属性属性)
<tx:method name=""/> 详情具体配置
propagation 传播行为 , REQUIRED:必须;REQUIRES_NEW:必须是新的
isolation 隔离级别 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<!-- AOP编程,目标类有ABCD(4个连接点),切入点表达式 确定增强的连接器,从而获得切入点:ABC -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.lm.tx03_xml.*.*(..))"/>
</aop:config>
2.3.4.4 测试 import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
String xmlPath = "cn/lm/tx03_xml/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
AccountService accountService = (AccountService) applicationContext.getBean("accountServiceImplId");
accountService.transfer("jack", "rose", 1000);
}
}
2.3.5 AOP配置基于注解【掌握】
1.配置事务管理器,将并事务管理器交予spring
2.在目标类或目标方法添加注解即可 @Transactional
2.3.5.1 Service层
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT)
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String outer, String inner, int money) {
accountDao.out(outer, money);
//int i = 1 / 0;
accountDao.in(inner, money);
}
}
2.3.5.2 spring配置
<!-- 4.1 事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 4.2 将管理器交予spring
* transaction-manager 配置事务管理器
* proxy-target-class
true : 底层强制使用cglib 代理
-->
<tx:annotation-driven transaction-manager="txManager"/>
2.3.5.3 事务详情配置
@Transactional(propagation=Propagation.REQUIRED , isolation = Isolation.DEFAULT)
public class AccountServiceImpl implements AccountService {
3 整合Junit
导入jar包
基本 :4+1
测试:spring-test…jar
1.让Junit通知spring加载配置文件
2.让spring容器自动进行注入
修改测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations=“classpath:applicationContext.xml”)
public class TestApp {
@Autowired //与junit整合,不需要在spring xml配置扫描
private AccountService accountService;
@Test
public void demo01(){
// String xmlPath = “applicationContext.xml”;
// ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
// AccountService accountService = (AccountService) applicationContext.getBean(“accountService”);
accountService.transfer(“jack”, “rose”, 1000);
}
}
4 整合web
0.导入jar包
spring-web.xml
1.tomcat启动加载配置文件
servlet --> init(ServletConfig) --> 2
filter --> init(FilterConfig) --> web.xml注册过滤器自动调用初始化
listener --> ServletContextListener --> servletContext对象监听【】
spring提供监听器 ContextLoaderListener --> web.xml …
如果只配置监听器,默认加载xml位置:/WEB-INF/applicationContext.xml
2.确定配置文件位置,通过系统初始化参数
ServletContext 初始化参数 web.xml
contextConfigLocation
classpath:applicationContext.xml
3.从servletContext作用域 获得spring容器 (了解)
// 从application作用域(ServletContext)获得spring容器
//方式1: 手动从作用域获取
ApplicationContext applicationContext =
(ApplicationContext) this.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
//方式2:通过工具获取
ApplicationContext apppApplicationContext2 =
WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
5 SSH整合
5.1 jar整合
struts:2.3.15.3
hibernate : 3.6.10
spring: 3.2.0
5.1.1 struts
struts-2.3.15.3\apps\struts2-blank\WEB-INF\lib
模板技术 ,一般用于页面静态化
freemarker:扩展名:*.ftl
velocity :扩展名 *.vm
5.1.2 spring
基础:4+1 , beans、core、context、expression , commons-logging (struts已经导入)
AOP:aop联盟、spring aop 、aspect规范、spring aspect
db:jdbc、tx
测试:test
web开发:spring web
驱动:mysql
连接池:c3p0
整合hibernate:spring orm
5.1.3 hibernate
%h%\hibernate3.jar 核心
%h%\lib\required 必须
%h%\lib\jpa jpa规范 (java persistent api 持久api),hibernate注解开发 @Entity @Id 等
整合log4j
导入 log4j…jar (struts已经导入)
整合(过渡):slf4j-log4j12-1.7.5.jar
二级缓存
核心:ehcache-1.5.0.jar
依赖:
backport-util-concurrent-2.1.jar
commons-logging (存在)
5.1.4 整合包
spring整合hibernate: spring orm
struts 整合spring:struts2-spring-plugin-2.3.15.3.jar
删除重复jar包
5.2 spring整合hibernate:有hibernate.cfg.xml
5.2.1 创建表
create table t_user(
id int primary key auto_increment,
username varchar(50),
password varchar(32),
age int
);
5.2.2 PO 类
javabean
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
映射文件
5.2.3 dao层
spring提供 HibernateTemplate 用于操作PO对象,类似Hibernate Session对象。
public class UserDaoImpl implements UserDao {
//需要spring注入模板
private HibernateTemplate hibernateTemplate;
public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
@Override
public void save(User user) {
this.hibernateTemplate.save(user);
}
}
5.2.4 service层
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void register(User user) {
userDao.save(user);
}
}
5.2.5 hibernate.cfg.xml
com.mysql.jdbc.Driver
jdbc:mysql:///ee19_spring_day03
root
1234
<!-- 2 配置方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- 3 sql语句 -->
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<!-- 4 自动生成表(一般没用) -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 5本地线程绑定 -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- 导入映射文件 -->
<mapping resource="com/itheima/domain/User.hbm.xml"/>
</session-factory>
5.2.6 applicationContext.xml
5.2.6.1 添加命名空间
5.2.6.2 加载hibernate配置文件
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
</bean>
<!-- 2创建模板
* 底层使用session,session 有sessionFactory获得
-->
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
5.2.6.3 dao和service
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="hibernateTemplate" ref="hibernateTemplate"></property>
</bean>
<!-- 4 service -->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
5.2.6.4 事务管理
<!-- 5.1 事务管理器 :HibernateTransactionManager -->
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" >
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 5.2 事务详情 ,给ABC进行具体事务设置 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="register"/>
</tx:attributes>
</tx:advice>
<!-- 5.3 AOP编程,ABCD 筛选 ABC -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service..*.*(..))"/>
</aop:config>
5.2.7 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations=“classpath:applicationContext.xml”)
public class TestApp {
@Autowired
private UserService userService;
@Test
public void demo01(){
User user = new User();
user.setUsername("jack");
user.setPassword("1234");
user.setAge(18);
userService.register(user);
}
}
5.3 spring整合hibernate:没有hibernate.cfg.xml 【】
删除hibernate.cfg.xml文件,但需要保存文件内容,将其配置spring中
修改dao层,继承HibernateDaoSupport
5.3.1 修改spring,配置SessionFactory
<!-- 1.2 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///ee19_spring_day03"></property>
<property name="user" value="root"></property>
<property name="password" value="1234"></property>
</bean>
<!-- 1.3配置 LocalSessionFactoryBean,获得SessionFactory
* configLocation确定配置文件位置
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
1)dataSource 数据源
2)hibernateProperties hibernate其他配置项
3) 导入映射文件
mappingLocations ,确定映射文件位置,需要“classpath:” ,支持通配符 【】
<property name="mappingLocations" value="classpath:com/itheima/domain/User.hbm.xml"></property>
<property name="mappingLocations" value="classpath:com/itheima/domain/*.hbm.xml"></property>
mappingResources ,加载执行映射文件,从src下开始 。不支持通配符*
<property name="mappingResources" value="com/itheima/domain/User.hbm.xml"></property>
mappingDirectoryLocations ,加载指定目录下的,所有配置文件
<property name="mappingDirectoryLocations" value="classpath:com/itheima/domain/"></property>
mappingJarLocations , 从jar包中获得映射文件
-->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
</props>
</property>
<property name="mappingLocations" value="classpath:com/itheima/domain/*.hbm.xml"></property>
</bean>
5.3.2 修改dao,使用HibernateDaoSupport
继承HibernateDaoSupport
// 底层需要SessionFactory,自动创建HibernateTemplate模板
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
@Override
public void save(User user) {
this.getHibernateTemplate().save(user);
}
}
spring 删除模板,给dao注入SessionFactory
5.4 struts整合spring:spring创建action
1.编写action类,并将其配置给spring ,spring可以注入service
2.编写struts.xml
3.表单jsp页面
4.web.xml 配置
1.确定配置文件contextConfigLocation
2.配置监听器 ContextLoaderListener
3.配置前端控制器 StrutsPrepareAndExecuteFitler
5.4.1 action类
通用
public class UserAction extends ActionSupport implements ModelDriven {
//1 封装数据
private User user = new User();
@Override
public User getModel() {
return user;
}
//2 service
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
功能
/**
* 注册
* @return
*/
public String register(){
userService.register(user);
return “success”;
}
5.4.2 spring配置
<bean id="userAction" class="com.itheima.web.action.UserAction" scope="prototype">
<property name="userService" ref="userService"></property>
</bean>
5.4.3 struts配置
<package name="default" namespace="/" extends="struts-default">
<!-- 底层自动从spring容器中通过名称获得内容, getBean("userAction") -->
<action name="userAction_*" class="userAction" method="{1}">
<result name="success">/messag.jsp</result>
</action>
</package>
5.4.4 jsp表单
用户名:密码:
年龄:
5.4.5 配置web.xml
contextConfigLocation classpath:applicationContext.xml org.springframework.web.context.ContextLoaderListener struts2 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter struts2 /*5.5 struts整合spring:struts创建action 【】
删除spring action配置
struts
要求:Action类中,必须提供service名称与 spring配置文件一致。(如果名称一样,将自动注入)
分析:
-
struts 配置文件
default.properties ,常量配置文件
struts-default.xml ,默认核心配置文件
struts-plugins.xml ,插件配置文件
struts.xml,自定义核心配置文件
常量的使用,后面配置项,将覆盖前面的。
2.default.properties ,此配置文件中确定 按照【名称】自动注入
/org/apache/struts2/default.properties -
struts-plugins.xml ,struts整合spring
<constant name="struts.objectFactory" value="spring" />
struts的action将有spring创建
总结,之后action有spring创建,并按照名称自动注入
6 要求
1 spring day03回顾
1.1 事务管理
基于xml配置
1.配置事务管理器
jdbc:DataSourceTransactionManager
hibernate:HibernateTransactionManager
2.事务通知(详情、属性)
<tx:advice id="" transaction-manager="">
tx:attributes
<tx:method name=“add*”>
<tx:method name=“update*”>
<tx:method name=“delete*”>
<tx:method name="find* read-only=“true”>
- AOP编程,ABCD–> ABC
aop:config
<aop:advisor advice-ref=“txAdvice” pointcut=“execution(* com….(…))”>
基于注解
xml 配置
1.事务管理器
2.将配置事务管理器交予spring
<tx:annotation-driven transaction-manager="…">
目标类
@Transactional 类 | 方法
1.2 整合Junit
@RunWith(SpringJunit4RnnerClass.class)
@ContextConfiguration(locations=“classpath:…xml”)
@Autowired 注入
@Test 测试
1.3 整合web
web.xml 配置
1.确定xml位置
name:contextConfigLocation
value:classpath:…xml
2.加载xml文件,配置监听器
ContextLoaderListener
1.4 整合
- hibernate po (domain、bean)
2.dao层:
需要HibernateTemplate 相当于session操作PO类 --> 必须提供setter方法,让spring注入,xml配置模板
save() update delete saveOrUpdate find
继承HibernateDaoSupport,需要注入SessionFactory,底层自动创建模板
dao方法中 this.getHibernateTemplate()
3.service 层,spring配置
properties <context:property-placeholder location>
配置数据源:ComboPooledDataSource
配置SessionFactory:LocalSessionFactoryBean
事务管理
4.web层 ,aciton
struts.xml 前面看到一位struts,底层使用spring
默认与spring整合之后,按照【名称】自动注入
spring day04 – spring day07 ,案例(小项目)
day04: SVN 、搭建环境(完成PO类)
day05: 基本功能(struts拦截器、hibernate关联获取)
day06: 复杂操作(查询+分页+条件、ajax级联操作)
day07: 重写–工具 (BaseDao、BaseAction)
2 SVN
2.1 版本控制
2.1.1 什么版本控制
版本控制(Revision Control):是维护工程蓝图的标准做法,能追踪工程蓝图从诞生一直到定案的过程。是一种记录若干文件内容变化,以便将来查阅特定版本修订情况的系统。也是一种软体工程技巧,籍以在开发的过程中,确保由不同人所编辑的同一档案都得到更新。
2.1.2 版本控制软件
CVS(Concurrent Versions System)代表协作版本系统或者并发版本系统,是一种版本控制系统,方便软件的开发和使用者协同工作。
VSS ( Visual Source Safe )只能在windows下,作为 Microsoft Visual Studio 的一名成员,它主要任务就是负责项目文件的管理
Git是用于Linux内核开发的版本控制工具。它采用了分布式版本库的方式,不必服务器端软件支持,使源代码的发布和交流极其方便。Git的速度很快,这对于诸如Linux kernel这样的大项目来说自然很重要。Git最为出色的是它的合并跟踪(merge tracing)能力。
www.github.org
。。。。。。
SVN(Subversion ),是一个开放源代码的版本控制系统,采用了分支管理系统,它的设计目标就是取代CVS。
2.2 SVN特点
统一的版本号。CVS是对每个文件顺序编排版本号,在某一时间各文件的版本号各不相同。而Subversion下,任何一次提交都会对所有文件增加到同一个新版本号,即使是提交并不涉及的文件。所以,各文件在某任意时间的版本号是相同的。版本号相同的文件构成软件的一个版本。
原子提交。一次提交不管是单个还是多个文件,都是作为一个整体提交的。在这当中发生的意外例如传输中断,不会引起数据库的不完整和数据损坏。
重命名、复制、删除文件等动作都保存在版本历史记录当中。
对于二进制文件,使用了节省空间的保存方法。(简单的理解,就是只保存和上一版本不同之处)
目录也有版本历史。整个目录树可以被移动或者复制,操作很简单,而且能够保留全部版本记录。
分支的开销非常小。
优化过的数据库访问,使得一些操作不必访问数据库就可以做到。这样减少了很多不必要的和数据库主机之间的网络流量。
支持元数据(Metadata)管理。每个目录或文件都可以定义属性(Property),它是一些隐藏的键值对,用户可以自定义属性内容,而且属性和文件内容一样在版本控制范围内。
支持FSFS和Berkeley DB两种资料库格式。
不足:只能设置目录的访问权限,无法设置单个文件的访问权限。
2.3 体系结构
2.4 安装
测试安装
检查path系统环境变量
2.5 创建仓库
格式:cmd> svnadmin create 路径
仓库目录结构
注意:创建仓库时,目录必须是空的 ,及新建文件夹
2.6 启动
格式:cmd> svnserve -d -r 仓库的路径
-d后台执行
-r版本库的根目录
启动时,指定“仓库路径”不同,分类:多仓库和单仓库
多仓库【掌握】
启动:svnserve -d -r 仓库父目录 ,表示启动时多仓库
例如:svnserve -d -r G:\repository\svn
访问:svn://localhost:3690/bbs
单仓库
启动:svnserve -d -r 仓库的根 ,表示启动时单仓库
例如:svnserve -d -r G:\repository\svn\bbs
访问:svn://localhost:3690
将操作注册成操作系统的“服务”,开机启动。
1.“运行”,services.msc 打开“服务”
2. 删除“服务”
- 注册“服务”
sc create svn binpath= “D:\java\Subversion\bin\svnserve.exe --service -r G:\repository\svn” displayname= “SVN-Service” start= auto depend= Tcpip
4. 启动或停止“服务”
2.7 操作【掌握 思想】
2.7.1 checkout
格式:svn checkout 服务器地址 下载地址
2.7.2 commit
格式:svn commit 资源
问题1:没有纳入版本控制
使用add子命令 添加到本地版本库
问题2:没有编写日志
采用 -m 参数设置日志信息
问题3:没有权限
修改权限,设置匿名访问
G:\repository\svn\bbs\conf\svnserve.conf
2.7.3 update
格式: svn update
2.8 图形化:TortoiseSVN 安装
安装成功之后,所有的操作都是“右键”
2.9 svn权限
权限需要3个配置文件
svnserve.conf
开启认证
确定账号配置文件位置
确定认证配置文件位置
passwd 账号配置 (一行一个账号,账号用户名和密码组成,使用等号分隔)
authz 认证配置文件
配置组 , 格式:组名 = 用户1 ,用户2,…
认证细节配置
多仓库
[bbs:/] --> 确定仓库名称 。[bbs:/doc] 给bbs仓库的doc目录配置权限
@itheima = rw --> 给itheima组设置权限。
read ('r') ,read-write ('rw') ,or no access ('').
user3 = r --> 给user3 指定权限
*= --> 其他用户没有权限
单仓库
[/] -->当仓库的根 [/doc] 单仓库doc目录
2.10 TortoiseSVN 常见图标
2.11 myeclipse svn 插件
2.11.1 安装插件
如果3个都不能使用,直接换eclipse
安装方式1:直接复制
eclipse插件规范
eclipse 目录
| -- features目录
| -- plugins 目录
方式2:使用link文件
将插件解压到任意目录(不含中文、空格),在myeclipse/dropins目录添加一个link文件
文件名:自定义
文件扩展名:link
文件内容:
path = 插件完整目录,需要指定到eclipse,及可以看到(features 、 plugins)
例如:
path=D:\java\MyEclipse\MyEclipse 10\svn\eclipse
path=D:/java/MyEclipse/MyEclipse 10/svn/eclipse
方式3:在线安装
安装成功标志
2.11.2 操作
2.12 svn目录规范
trunk ,主线,用于存放程序整个进度
branches ,分支,例如:bug修复、特殊功能等
tags,标签(版本),此目录下的内容不能修改
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?