Spring介绍
Spring
是一个开源框架,Spring
是于2003 年兴起的一个轻量级的Java
开发框架,由Rod Johnson
在其著作Expert One-On-One J2EE Development and Design
中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。Spring
使用基本的JavaBean
来完成以前只可能由EJB
完成的事情。然而,Spring
的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring
中受益
简单来说,Spring
是一个轻量级的控制反转(IoC
)和面向切面(AOP
)的容器框架。
Spring好处
方便解耦,简化开发:
-
Spring
就是一个大工厂,专门负责生成Bean
,可以将所有对象创建和依赖关系维护由Spring
管理
AOP编程的支持:
-
Spring
提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
声明式事务的支持:
- 只需要通过配置就可以完成对事务的管理,而无需手动编程
方便程序的测试:
-
Spring
对Junit4
支持,可以通过注解方便的测试Spring
程序
方便集成各种优秀框架:
Spring
不排斥各种优秀的开源框架
- 其内部提供了对各种优秀框架(如:
Struts
、Hibernate
、MyBatis
、Quartz
等)的支持
降低JavaEE API
的使用难度Spring
:
- 对
JavaEE
开发中一些难用的API
(JDBC
、JavaMail
、远程调webservice
用等),都提供了封装,使这些API
应用难度大大降低
Spring体系结构
Spring
框架是一个分层架构,,它包含一系列的功能要素并被分为大约20个模块。这些模块分为Core Container
、Data Access/Integration
、Web
、AOP(Aspect Oriented Programming)
、Instrumentation
和测试部分,如下图所示:
Spring在项目中的架构
-
web
层:Struts
,SpringMVC
-
dao
层:Hibernate
,mybatis
Spring在项目中的架构
Spring IOC的底层实现原理
Spring IOC的底层实现原理Spring核心jar包
-
spring-core-3.2.2.RELEASE.jar
包含Spring
框架基本的核心工具类,Spring
其它组件要都要使用到这个包里的类,是其它组件的基本核心。 -
spring-beans-3.2.2.RELEASE.jar
所有应用都要用到的,它包含访问配置文件、创建和管理bean
以及进行Inversion of Control(IoC) / Dependency Injection(DI)
操作相关的所有类 -
spring-context-3.2.2.RELEASE.jar
Spring
提供在基础IoC
功能上的扩展服务,此外还提供许多企业级服务的支持,
如邮件服务、任务调度、JNDI定位、EJB集成、远程访问、缓存以及各种视图层框架的封装等。 -
spring-expression-3.2.2.RELEASE.jar
Spring
表达式语言 -
com.springsource.org.apache.commons.logging-1.1.1.jar
第三方的主要用于处理日志
Spring IOC/DI
-
IOC
Inverse of Control
反转控制的概念, 就是将原本在程序中手动创建对象的控制权, 交给Spring
框架管理, 简单的说, 就是将创建对象控制权被翻转到了Spring
框架 -
DI
Dependency Injection
依赖注入的概念, 就是在Spring
创建这个对象的过程中, 将这个对象所依赖的属性注入进去
简单的Spring框架使用
创建一个UserService
接口
public void sayHello();
和它的实现类UserServiceImpl
public void sayHello {
private String name;
public String getName() {
return name;
}
public void setName(name) {
this.name = name;
}
public void sayHello() {
System.out.println("Spring hello" + name)
}
}
- 传统使用
UserService
public void demo() {
UserService userService = new UserServiceImpl();
userService.setName("李四");
userService.sayHello();
}
输出结果
Spring hello李四
- 使用Spring
在resource里创建一个applicationContext.xml
文件, 里面写入
// IOC
<bean id = "userService" class="com.rui.ioc.demo.UserServiceImpl>
<property name = "name" value="李四" />
</bean>
public void demo () {
// 创建Spring工厂, 加载classPath下的配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通过工厂获得类
UserService userService = (UserService) applicationContext.getBean("userService");
userService.sayHello();
}
输出结果
Spring hello李四
-
IOC
=>
使用`Spring`工厂获得类叫做`IOC`, 即将创建对象的权利交给Spring
-
DI
=>
<property name = "name" value="李四" />
叫做依赖注入, 就是在Spring
创建这个对象的过程中, 将这个对象所依赖的属性注入进去
Spring的工厂类
Spring的工厂类-
ClassPathXmlApplicationContext
和BeanFactory
的区别-
BeanFactory
是spring
中比较原始,比较古老的Factory
。因为比较古老,所以BeanFactory
无法支持spring
插件,例如:AOP
、Web
应用等功能。 - 如果使用
ApplicationContext
,如果配置的bean
是singleton
,那么不管你有没有或想不想用它,它都会被实例化。好处是可以预先加载,坏处是浪费内存。 -
BeanFactory
,当使用BeanFactory
实例化对象时,配置的bean
不会马上被实例化,而是等到你使用该bean
的时候(getBean
)才会被实例化。好处是节约内存,坏处是速度比较慢。多用于移动设备的开发。 - 没有特殊要求的情况下,应该使用
ApplicationContext
完成。因为BeanFactory
能完成的事情,ApplicationContext
都能完成,并且提供了更多接近现在开发的功能。
-
-
加载某个位置下的配置文件
public void demo () {
// 创建Spring工厂
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("c:\\applicationContext.xml");
// 通过工厂获得类
UserService userService = (UserService) applicationContext.getBean("userService");
userService.sayHello();
}
Spring的Bean管理方式
-
XML
-
三种实例化Bean的方式
-
使用类构造器实例化(默认无参数)
-
// 对象 public class Bean1 { public Bean1() { System.out.println("Bean1被实例化了..."); } }
// Spring配置文件 <bean id="bean1" class="com.rui.demo.Bean1" />
输出结果为@Test public void demo () { // 创建Spring工厂, 加载classPath下的配置文件 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); Bean1 bean1 = (bean1) applicationContext.getBean("bean1"); }
Bean1被实例化了...
-
使用静态工厂实例化(简单工厂模式)
// 对象 public class Bean2 { public Bean2() { System.out.println("Bean2被实例化了..."); } }
// Bean2的静态工厂 public class Bean2Factory { public static Bean2 createBean2() { System.out.println("Bean2被实例化了..."); return new Bean2(); } }
<bean id="bean2" class="com.rui.demo.Bean2Factory" factory-method="createBean2" />
输出结果为public void demo () { // 创建Spring工厂, 加载classPath下的配置文件 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); Bean2 bean2 = (bean2) applicationContext.getBean("bean2"); }
Bean2被实例化了...
-
使用实例工厂方法实例化(工厂方法模式)
// 对象 public class Bean3 { public Bean3() { System.out.println("Bean3被实例化了..."); } }
// Bean3的实例化工厂方法 public class Bean3Factory { public Bean3 createBean3() { System.out.println("Bean3被实例化了..."); return new Bean3(); } }
<bean id="bean3Factory" class="com.rui.demo.Bean3Factory" /> <bean id="bean3" actory-bean="bean3Factory" factory-method="createBean3" />
输出结果为public void demo () { // 创建Spring工厂, 加载classPath下的配置文件 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); Bean3 bean3 = (bean3) applicationContext.getBean("bean3"); }
Bean3被实例化了...
-
Bean的常用配置
-
id和name
- 一般情况下, 装配一个Bean的时候, 通过一个id属性作为Bean的名称
- id属性在IOC容器里必须是唯一的
- 如果Bean的名称中含有特殊字符, 就需要使用name属性
-
class
- class用于设置一个类的完全路径名称, 主要作用是IOC容器生成类的实例
-
Bean的作用域Scope
类别 说明 singleton
(默认)在 SpringIOC
容器中仅存在一个Bean
实例,Bean
以单例的方式存在prototype
每次调用 getBean()
时都返回一个新的实例request
每次Http请求都会创建一个新的 Bean
, 该作用域仅适用于WebApplicationContext
环境Session
同一个 HTTP Session
共享一个Bean
, 不同的HTTP Session
使用不同的Bean
, 该作用域仅适用于WebApplicationContext
环境 -
-
Spring容器中Bean的生命周期
Spring初始化bean或销毁bean的时候, 有时候需要做一些处理工作, 因此Spring可以在创建和销毁bean的时候调用bean的两个声明周期方法
类别 说明 init-method
初始化方法 destory-method
销毁方法, 仅在 scope=singleton有效
Spring容器中Bean的生命周期// 当bean被载入到容器的时候调用init, 当bean从容器中删除的时候调用destory(仅在`scope=singleton有效) <bean id="xxx" class="xxxx" init-method="init" destory-method="destory" />
-
instantiate bean
对象实例化 -
populate properties
封装对象 - 如果
Bean
实现BeanNameAware
执行setBeanName
- 如果
Bean
实现BeanFactoryAware
或者ApplicationContextAware
设置工厂 - 如果存在类实现
BeanPostProcessor
(后处理Bean
), 执行postProcessBeforeInitialization
- 如果
Bean
实现InitializingBean
执行afterPropertiesSet
- 调用
<bean init-method="init" />
指定初始化init
- 如果存在类实现
BeanPostProcessor
(处理Bean
), 执行postProcessAfterInitiazation
- 执行业务逻辑
-
Spring中beanPostProcessor使用案例
- applicationContext.xml
<bean class="com.rui.ioc.MyBeanPostProcessor" /> <bean id="userDao" class="com.rui.ioc.UserDaoImpl" />
- UserDao
public interface UserDao { public void findAll(); public void save(); public void update(); public void delete(); }
- UserDaoImpl
public class UserDaoImpl implements UserDao { @Override public void findAll() { System.out.println("查"); } @Override public void save() { System.out.println("存"); } @Override public void update() { System.out.println("改"); } @Override public void delete() { System.out.println("删"); } }
- MyBeanPostProcessor
public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException { if ("userDao".equals(beanName)) { Object proxy = java.lang.reflect.Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("save".equals(method.getName())) { System.out.println("权限调用"); return method.invoke(bean, args); } return method.invoke(bean, args); } }); return proxy; } else { return bean; } } }
- 输出
第八步2222 查 权限调用 存 改 删
-
-
Spring的属性注入
- 构造函数注入, 在对象中, 必须实现对应的构造函数参数为设置的属性
<bean id="user" class="com.rui.demo.User"> <contructor-arg name="name" value="1223"> <contructor-arg name="age" value="23"> </bean>
- 属性setter方法注入, 在Spring配置文件中, 通过<property>设置注入的属性, 在对象中必须实现对应的参数的setter方法
<bean id="user" class="com.rui.demo.User"> <property name="name" value="1223"> <property name="age" value="23"> </bean> ``` ``` // User对象中含有一个对象Cat <bean id="user" class="com.rui.demo.User"> <property name="name" value="1223"> <property name="age" value="23"> <property name="cat" ref="cat"> // ref引入别的bean的id </bean> <bean id="cat" class="com.rui.demo.Cat"> <property name="name" value="ketty"> </bean> ```
- P名称空间注入
<!--xmlns:p="http://www.springframework.org/schema/p" 需要引入--> <bean id="userDao" class="com.rui.ioc.UserDaoImpl" p:age="12" p:name="shsj" />
- SpEL注入
<bean id="catogery" class="com.rui.ioc.Category"> <property name="name" value="#{'服装'}" /> </bean> <bean id="productInfo" class="com.rui.ioc.ProductInfo" /> <bean id="productInfo" class="com.rui.ioc.Product"> <property name="name" value="#{'男装'}" /> <!-- <property name="price" value="#{'1999'}" />--> <property name="price" value="#{productInfo.caculatePrice}" /> <property name="category" value="#{catogery}" /> </bean>
- 复杂类型的属性注入(一般用于整合其他框架)
// 数组类型 <property name="arrs"> <list> <value>aaa</value> </list> </property> // list类型 <property name="list"> <list> <value>aaa</value> </list> </property> // 数组类型 <property name="arrs"> <list> <value>aaa</value> </list> </property> // set类型 <property name="set"> <set> <value>aaa</value> </set> </property> // Map类型 <property name="map"> <set> <value>aaa</value> </set> </property> // Map类型 <property name="arrs"> <map> <entry key="aaa" value="bbb" /> </map> </property> // properties类型 <property name="properties"> <props> <prop key="username">root</prop> <prop key="password">12345</prop> </props> </property>
- 构造函数注入, 在对象中, 必须实现对应的构造函数参数为设置的属性
-
Spring的Bean注解方式
-
Bean的管理
在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:component-scan base-package="com.rui.ioc"/>
</beans>
就会自动扫描ioc
下所有的类, 在类上标注@Component/@Service/@Repository/@Controller
1、
@controller
控制器(注入服务)
2、@service
服务(注入dao
)
3、@repository dao
(实现dao
访问)
4、@component
(@component("xxx")
)(把普通pojo
实例化到spring
容器中,相当于配置文件中的<bean id="xxx" class=""/>
)
-
属性的注入-注解方式
- 简单属性注入
@value("coding") private String something; // something输出值为coding
-
@Autowired
进行自动注入-
@Autowired
默认按照类型进行注入- 如果两个相同的
Bean
类型相同, 则按照名称进行注入
- 如果两个相同的
- 通过
@Autowired
的required
属性, 设置一定要找到匹配的Bean
- 使用
@Qualifier
指定注入Bean
的名称
同时使用@Autowired
和@Qualifier
可以指定固定的Bean, 效果和使用@Resource
相同
-
-
@PostConstruct
: 初始化 -
@PreDestroy
: 销毁 -
@Scope("prototype")
: 多例, 默认为单例
- 简单属性注入
Spring AOP
-
什么是AOP
AOP Aspect Oriented Programing 面向切面编程
AOP采取横向抽取机制, 取代了传统纵向继承体系重复性代码(性能监视, 事务管理, 安全检查, 缓存) -
AOP术语
AOP术语 -
JDK动态代理
UserDao
package com.rui.aop;
public interface UserDao {
public void findAll();
public void save();
public void update();
public void delete();
}
UserDaoImpl
package com.rui.aop;
public class UserDaoImpl implements UserDao {
@Override
public void findAll() {
System.out.println("查找用户~");
}
@Override
public void save() {
System.out.println("保存用户~");
}
@Override
public void update() {
System.out.println("更新用户~");
}
@Override
public void delete() {
System.out.println("删除用户~");
}
}
MyJdkProxy
package com.rui.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyJDKProxy implements InvocationHandler {
private UserDao userDao;
public MyJDKProxy(UserDao userDao) {
this.userDao = userDao;
}
public Object createProxy() {
Object proxy = Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao
.getClass().getInterfaces(), this);
return proxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("save".equals(method.getName())) {
System.out.println("保存用户之前的操作");
return method.invoke(userDao, args);
}
return method.invoke(userDao, args);
}
}
aopTest
package com.rui.aop;
import org.junit.Test;
public class aopTest {
@Test
public void demo1() {
UserDao userDao = new UserDaoImpl();
userDao = (UserDao)new MyJDKProxy(userDao).createProxy();
userDao.findAll();
userDao.delete();
userDao.save();
userDao.update();
}
}
输出结果
查找用户~
删除用户~
保存用户之前的操作
保存用户~
更新用户~
-
使用CGLIB生成代理
MyCglibProxy
public class MyCglibProxy implements MethodInterceptor {
private ProductDao productDao;
public MyCglibProxy(ProductDao productDao) {
this.productDao = productDao;
}
public Object createProxy() {
// 1. 创建核心类
Enhancer enhancer = new Enhancer();
// 2. 设置父类
enhancer.setSuperclass(productDao.getClass());
// 3. 设置回调
enhancer.setCallback(this);
// 4.生成代理
Object proxy = enhancer.create();
return proxy;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if ("save".equals(method.getName())) {
System.out.println
("============================权限校验==========================");
return methodProxy.invokeSuper(o, objects);
}
return methodProxy.invokeSuper(o, objects);
}
}
ProductDao
public class ProductDao {
public void findAll() {
System.out.println("查找用户~");
}
public void save() {
System.out.println("保存用户~");
}
public void update() {
System.out.println("更新用户~");
}
public void delete() {
System.out.println("删除用户~");
}
}
Test
@Test
public void demo1() {
ProductDao productDao = new ProductDao();
ProductDao proxy = (ProductDao)new MyCglibProxy(productDao).createProxy();
proxy.findAll();
proxy.delete();
proxy.save();
proxy.update();
}
输出
查找用户~
删除用户~
============================权限校验==========================
保存用户~
更新用户~
代理相关
-
Spring
在运行期, 生成动态代理对象, 不需要特殊的编译器 -
Spring AOP
的底层就是通过JDK
动态代理或CGLib
动态代理技术为目标Bean
执行横向织入- 若目标对象实现了若干接口,
Spring
使用JDK
的java.lang.reflect.Proxy
类代理 - 若目标对象没有实现任何接口,
spring
使用cglib
库生成目标对象的子类
- 若目标对象实现了若干接口,
- 程序中应该优先对接口创建代理, 便于程序解耦维护
- 标记为
final
的方法, 不能被代理, 因为无法覆盖-
JDK
动态代理, 是针对接口生成子类, 接口中方法不能使用final
修饰 -
CGLib
是针对目标类生产子类, 因此类或方法, 不能使用final
的
-
-
Spring
只支持方法连接点, 不提供属性连接点
Spring AOP增强类型
- AOP联盟为通知
Advice
定义了org.aopallinance.aop.Interface.Advice
- Spring按照通知Advice在目标类方法的链接点位置, 可以分为5类
- 前置通知
org.springframework.aop.MethodBeforeAdvice
- 在目标方法执行前实施增强
- 后置通知
org.springframework.aop.AfterReturningAdvice
- 在目标方法执行后实施增强
- 环绕通知
org.springframework.intercept.MethodInterceptor
- 在目标方法执行前后都实施增强
- 异常抛出通知
org.springframework.ThrowsAdvice
- 在方法抛出异常后实施增强
- 引介通知
org.springframework.IntroductionInterceptor
- 在目标类中添加一些新的方法和属性
- 前置通知
Spring AOP切面类型
- Advisor: 代表一般切面, Advice本身就是一个切面, 对目标类所有方法进行拦截
- PointcutAdvisor: 代表具有切点的切面, 可以指定拦截目标类哪些方法
- IntroductionAdvisor: 代表引介切面, 针对引介通知而使用切面
自动创建代理
-
BeanNameAutoProxyCreator
根据Bean名称创建代理 -
DefaultAdvisorAutoProxyCreator
根据Advisor本身包含信息创建代理 -
AnnotationAwareAspectJAutoProxyCreator
基于Bean中的AspectJ注解进行自动代理
AspectJ AOP
- AspectJ是一个基于java语言的AOP框架
- Spring 2.0以后新增了对AspectJ切点表达式的支持
1.Spring开启Aspectj
<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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/bea ns 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">
<!-- 开启aspectj的注解开发-->
<aop:aspectj-autoproxy/>
</beans>
2.@AspectJ提供不同的通知类型
-
@Before
前置通知, 相当于BeforeAdvice
-
@AfterReturning
后置通知, 相当于AfterReturningAdvice
-
@Around
环绕通知, 相当于MethodInterceptor
-
@AfterThrowing
异常跑出通知, 相当于ThrowAdvice
-
@After
最终final通知, 不管是否异常, 该通知都会执行 -
@DeclareParents
引介通知, 相当于IntroductionInterceptor
3. 在通知中通过value属性定义切点
- 通过excution函数, 可以定义切点的方法切入
- 语法
excution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
- eg.
- 匹配所有类public方法 excution(public * *(...))
- 匹配指定包下所有类方法 excution(* com.imooc.dao.*(...)) 不包含子包
- 匹配指定包下所有类方法 excution(* com.imooc.dao..*(...)) 包含子包
- 匹配指定类所有方法 excution(* com.imooc.service.UserService.*(..))
- 匹配实现特定接口所有类方法 excution(* com.imooc.service.dao.GenericDAO+.*(..))
- 匹配所有save开头的方法 excution(* save*(..))
4. @Before/@AfterReturning/@Around/AfterThrowing/After
- 切面类
@Aspect
public class MyAspect {
@Pointcut(value="execution(* com.rui.aspectj.ProductDao.save(..))")
private void save() {}
@Pointcut(value = "execution(* com.rui.aspectj.ProductDao.update(..))")
private void update() {}
@Pointcut(value = "execution(* com.rui.aspectj.ProductDao.delete(..))")
private void delete() {}
@Before(value="save()")
public void before(JoinPoint joinPoint) {
System.out.println("前置通知============" + joinPoint);
}
@AfterReturning(value="update()",
returning = "result")
public void afterReturning(Object result) {
// result为返回值
System.out.println("后置通知============" + result);
}
@Around(value="delete()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前通知=====================");
Object obj = joinPoint.proceed(); // 执行目标方法
System.out.println("环绕后通知=====================");
return obj;
}
@AfterThrowing(value="execution(* com.rui.aspectj.ProductDao.findOne(..))",
throwing = "e")
public void afterThrowing(Throwable e) {
System.out.println("异常抛出通知===========" + e.getMessage());
}
@After(value = "execution(* com.rui.aspectj.ProductDao.findAll(..))")
public void after() {
System.out.println("最终通知!================");
}
}
- Dao
public class ProductDao {
public void save() {
System.out.println("保存商品...");
}
public String update() {
System.out.println("更新商品...");
return "hello world";
}
public void delete() {
System.out.println("删除商品...");
}
public void findOne() {
System.out.println("查找一个商品...");
}
public void findAll() {
System.out.println("查找所有商品...");
}
}
- Spring 配置
<!-- 开启aspectj的注解开发-->
<aop:aspectj-autoproxy/>
<!-- 目标类-->
<bean id="productDao" class="com.rui.aspectj.ProductDao" />
<!--定义切面-->
<bean class="com.rui.aspectj.MyAspect" />
- 测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AspectTest {
@Resource(name = "productDao")
private ProductDao productDao;
@Test
public void demo1() {
productDao.save();
productDao.delete();
productDao.findAll();
productDao.findOne();
productDao.update();
}
}
- output
前置通知============execution(void com.rui.aspectj.ProductDao.save())
保存商品...
环绕前通知=====================
删除商品...
环绕后通知=====================
查找所有商品...
最终通知!================
查找一个商品...
异常抛出通知===========/ by zero
JDBC Template
使用
- Mysql驱动
- Spring组件(Core, beans, context, aop)
- JDBCTemplate(jdbc, tx)
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://local........." />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="datasource" ref="datasource" />
</bean>
具体方法
- update方法
// 对数据进行增删改操作
int update(String sql, Object[] args)
int update(String sql, Object... args)
public void testUpdate() {
String sql = "insert into student(name, sex) values(?,?)";
jdbcTemplate.update(sql, new Object[]{"zhagsan", "man"});
// 或
jdbcTemplate.update(sql, "zhagsan", "man");
}
- batchUpdate方法
// 对数据进行批量增删改操作
int [] batchUpdate(String [] sql)
int [] batchUpdate(String sql, List<Object[]> args)
public void testBatchUpdate() {
String[] sqls = {
"insert into student(name, sex) values ('关羽', '男')",
"insert into student(name, sex) values ('张飞', '男')"
};
jdbcTemplate.batchUpdate(sqls);
}
public void testBatchUpdate() {
String sql = insert into student(name, sex) values (?, ?)";
List<Object []> list = new ArrayList<Object[]>();
list.add(new Object[]{"关羽", "男"});
list.add(new Object[]{"张飞", "男"});
jdbcTemplate.batchUpdate(sql, list);
}
-
查询简单数据项
- 获取一个
T queryForObject(String sql, Class<T> type) T queryForObject(String sql, Object[] args, Class<T> type) T queryForObject(String sql, Class<T> type, Object... args)
public void testQuerySimple() { String sql = "select count(*) from student"; int count = jdbcTemplate.queryForObject(sql, Integer.class); }
- 获取多个
List<T> queryForList(String sql, Class<T> type) List<T> queryForList(String sql, Object[] args, Class<T> type) List<T> queryForList(String sql, Class<T> type, Object... args)
public void testQuerySimple() { String sql = "select name from student where sex=?"; List<String> names = jdbcTemplate.queryForList(sql, String.class, "男"); }
-
查询复杂对象(封装为Map)
- 获取一个
Map queryForMap(String sql) Map queryForMap(String sql, Object[] args) Map queryForMap(String sql, Object... args)
public void testMap() { String sql = "select * from student where id = ?"; Map<String, Object> stu = jdbcTemplate.queryForMap(sql, 1003); }
- 获取多个
List<Map<String, Object>> queryForList(String sql) List<Map<String, Object>>queryForList(String sql, Object[] args) List<Map<String, Object>>queryForList(String sql, Object... args)
public void testMap() { String sql = "select * from student"; Map<String, Object> stu = jdbcTemplate.queryForMap(sql); }
-
查询复杂对象(封装为实体对象)
- RowMapper接口
- 获取一个
T queryForObject(String sql, RowMapper<T> mapper) T queryForObject(String sql, Object[] args, RowMapper<T> mapper) T queryForObject(String sql, RowMapper<T> mapper, Object... arg)
public void testQueryEntity() { String sql = "select * from student where id = ?"; Student stu = jdbcTemplate.queryForObject(sql, new RowMapper<Student>() { public Student mapRow(ResultSet resultSet, int i) throw SQLException { Student stu = new Student(); stu.setId(resultSet.getInt("id")); stu.setName(resultSet.getInt("name")); stu.setSex(resultSet.getInt("Sex")); stu.setBorn(resultSet.getInt("born")); return stu; } }, 1004) }
- 获取多个
List<T> query(String sql, RowMapper<T> mapper) List<T> query(String sql, Object[] args, RowMapper<T> mapper) List<T> query(String sql, RowMapper<T> mapper, Object... arg)
在日常开发中, 匿名类的形式比较冗余, 一般进行封装, 将public void testQueryEntityList() { String sql = "select * from student"; List<Student> stus = jdbcTemplate.query(sql, new RowMapper<Student>() { public Student mapRow(ResultSet resultSet, int i) throw SQLException { Student stu = new Student(); stu.setId(resultSet.getInt("id")); stu.setName(resultSet.getInt("name")); stu.setSex(resultSet.getInt("Sex")); stu.setBorn(resultSet.getInt("born")); return stu; } }) }
RowMapper
单独实现
使用如下:private class StudentRowMapper implements RowMapper<Student> { public Student mapRow(ResultSet resultSet, int i) throw SQLException { Student stu = new Student(); stu.setId(resultSet.getInt("id")); stu.setName(resultSet.getInt("name")); stu.setSex(resultSet.getInt("Sex")); stu.setBorn(resultSet.getInt("born")); return stu; } }
但是在实际开发中, 每个类都要实现一个public void testQueryEntityList() { String sql = "select * from student"; List<Student> stus = jdbcTemplate.query(sql, new StudentRowMapper());
RowMapper
会让开发变得极其繁琐, 一般实现一个通用的工具类来做转换的功能:
- RowMapper接口
-
JDBCTemplate优缺点
- 优点
- 简单
- 灵活
- 缺点
- sql和java代码牵扯的比较杂乱
- 功能不够丰富, 比如分页的功能没有等
- 优点
Spring事务管理
什么是事务
- 事务就是正确执行一系列的操作(或者动作), 使数据库从一种状态转换成另一种状态且保证操作全部成功, 或者全部失败
事务的特点
- 原子性(Atomicity)
- 即不可分割性, 事务要么全部被执行, 要么就全部不执行
- 一致性(Consistency)
- 事务的执行使得数据库从一种正确的状态转换成另一种正确的状态
- 隔离性(Isolation)
- 在事务正确提交之前, 它可能的结果不应该显示给任何其他事务
- 持久性(Durability)
- 事务正确提交以后, 其结果将永久保存在数据库中
Java事务的产生
- 程序操作数据库的需要. 在Java编写的程序或者系统中, 实现ACID的操作
- Java事务实现范围
- 通过jdbc相应方法间接来实现对数据库的增删改查, 把事务转移到Java程序代码中进行控制
- 确保事务, 要么全部执行成功, 要么撤销不执行
- Java事务机制和原理就是确保数据库操作的ACID特性
Java事务实现模式
- Java事务的实现
- 通过Java代码来实现对数据库的事务性操作
- Java事务类型
-
JDBC
事务: 用Connection
对象控制, 包括手动模式和自动模式; - JTA(
Java Transaction API
)事务: 与实现无关, 与协议无关的API; - 容器事务: 应用服务器提供的, 且大多是基于JTA完成(通常基于
JNDI
的, 相当复杂的API
实现)
-
三种事务的差异
- JDBC事务: 控制的局限性在一个数据库连接内, 但是其使用简单
- JTA事务: 功能强大, 可跨越多个数据库或者多个DAO, 使用比较复杂
- 容器事务: 主要指的是J2EE应用服务器提供的事务管理, 局限于EJB
事务接口
-
事务接口架构
事务接口架构
事务读取类型
- 脏读: 事务没提交, 提前读取
- 不可重复读: 多次读取到的数据不一致
- 幻读: 事务不是独立执行时发生的一种非预期现象
事务隔离级别
隔离级别事务传播行为
事务传播行为事务超时
- 事务超时就是事务的一个定时器, 在特定时间内事务如果没有执行完毕, 那么久会自动回滚, 而不是一直等待其结束
事务回滚
- 默认情况下, 事务只有遇到运行期异常才会回滚, 而在遇到检查型异常时不会回滚
- 自定义回滚策略
- 生命失误在遇到特定的检查型异常时像遇到运行期异常那样回滚
- 声明事务遇到特别的异常不回滚, 即使这些异常时运行期异常
Spring事务状态
- 事务接口
- 通过事务管理器获取
TransactionStatus
实例; - 控制事务在回滚或提交的时候需要应用对应的事务状态;
- Spring事务接口
// Spring事务状态接口 // 通过调用PlatformTransactionManager的getTransaction() // 获取事务状态实例 public interface TransactionStatus { boolean isNewTransaction(); // 是否是新的事务 boolean hasSavepoint(); // 是否有恢复点 boolean setRollbackOnly(); // 设置为只回滚 boolean isRollbackOnly(); // 是否为只回滚 boolean isCompleted(); // 是否已完成 }
- 通过事务管理器获取
编程式事务实现方式
事务管理器(PlatformTransactionManager)方式
- 类似应用
JTA UserTransaction API
方式, 但异常处理更简洁; - 核心类为:
Spring
事务管理的三个接口类以及JdbcTemplate
类 - 步骤:
- 获取事务管理器
- 创建事务属性对象
- 获取事务状态对象
- 创建JDBC模板对象
- 业务数据操作处理
模板事务(TransactionTemplate)的方式
- 此为
Spring
官方团队推荐的编程式事务管理方式 - 主要工具为
JdbcTemplate
类
声明式事务管理
声明式事务实现原理
- 基于AOP模式机制, 对方法前后进行拦截
声明式事务管理的配置类型:
- 5种类型: 独立代理; 共享代理, 拦截器, tx拦截器, 全注释(注解方式) (前三类2.0版本以后不推荐使用)
声明式事务管理配置实现方式:
<!-- 引入配置文件-->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="2"/>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<!--配置属性文件-->
<value>classpath:datasource.properties</value>
</list>
</property>
<property name="fileEncoding" value="utf-8"/>
</bean>
<!--配置连接池-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${db.driverClassName}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
<!-- 连接池启动时的初始值 -->
<property name="initialSize" value="${db.initialSize}"/>
<!-- 连接池的最大值 -->
<property name="maxActive" value="${db.maxActive}"/>
<!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
<property name="maxIdle" value="${db.maxIdle}"/>
<!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
<property name="minIdle" value="${db.minIdle}"/>
<!-- 最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制 -->
<property name="maxWait" value="${db.maxWait}"/>
<!--#给出一条简单的sql语句进行验证 -->
<!--<property name="validationQuery" value="select getdate()" />-->
<property name="defaultAutoCommit" value="${db.defaultAutoCommit}"/>
<!-- 回收被遗弃的(一般是忘了释放的)数据库连接到连接池中 -->
<!--<property name="removeAbandoned" value="true" />-->
<!-- 数据库连接过多长时间不用将被视为被遗弃而收回连接池中 -->
<!--<property name="removeAbandonedTimeout" value="120" />-->
<!-- #连接的超时时间,默认为半小时。 -->
<property name="minEvictableIdleTimeMillis" value="${db.minEvictableIdleTimeMillis}"/>
<!--# 失效检查线程运行时间间隔,要小于MySQL默认-->
<property name="timeBetweenEvictionRunsMillis" value="40000"/>
<!--# 检查连接是否有效-->
<property name="testWhileIdle" value="true"/>
<!--# 检查连接有效性的SQL语句-->
<property name="validationQuery" value="SELECT 1 FROM dual"/>
</bean>
<!-- 使用@Transactional进行声明式事务管理需要声明下面这行 -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
<!-- 事务管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
<property name="rollbackOnCommitFailure" value="true"/>
</bean>
网友评论