Spring介紹
Spring
是一個(gè)開(kāi)源框架锅减,Spring
是于2003 年興起的一個(gè)輕量級(jí)的Java
開(kāi)發(fā)框架,由Rod Johnson
在其著作Expert One-On-One J2EE Development and Design
中闡述的部分理念和原型衍生而來(lái)炫七。它是為了解決企業(yè)應(yīng)用開(kāi)發(fā)的復(fù)雜性而創(chuàng)建的。Spring
使用基本的JavaBean
來(lái)完成以前只可能由EJB
完成的事情苟呐。然而汪厨,Spring
的用途不僅限于服務(wù)器端的開(kāi)發(fā)低斋。從簡(jiǎn)單性蜂厅、可測(cè)試性和松耦合的角度而言,任何Java應(yīng)用都可以從Spring
中受益
簡(jiǎn)單來(lái)說(shuō)膊畴,Spring
是一個(gè)輕量級(jí)的控制反轉(zhuǎn)(IoC
)和面向切面(AOP
)的容器框架掘猿。
Spring好處
方便解耦,簡(jiǎn)化開(kāi)發(fā):
-
Spring
就是一個(gè)大工廠唇跨,專門負(fù)責(zé)生成Bean
稠通,可以將所有對(duì)象創(chuàng)建和依賴關(guān)系維護(hù)由Spring
管理
AOP編程的支持:
-
Spring
提供面向切面編程,可以方便的實(shí)現(xiàn)對(duì)程序進(jìn)行權(quán)限攔截买猖、運(yùn)行監(jiān)控等功能
聲明式事務(wù)的支持:
- 只需要通過(guò)配置就可以完成對(duì)事務(wù)的管理改橘,而無(wú)需手動(dòng)編程
方便程序的測(cè)試:
-
Spring
對(duì)Junit4
支持,可以通過(guò)注解方便的測(cè)試Spring
程序
方便集成各種優(yōu)秀框架:
Spring
不排斥各種優(yōu)秀的開(kāi)源框架
- 其內(nèi)部提供了對(duì)各種優(yōu)秀框架(如:
Struts
玉控、Hibernate
飞主、MyBatis
、Quartz
等)的支持
降低JavaEE API
的使用難度Spring
:
- 對(duì)
JavaEE
開(kāi)發(fā)中一些難用的API
(JDBC
奸远、JavaMail
既棺、遠(yuǎn)程調(diào)webservice
用等),都提供了封裝懒叛,使這些API
應(yīng)用難度大大降低
Spring體系結(jié)構(gòu)
Spring
框架是一個(gè)分層架構(gòu),,它包含一系列的功能要素并被分為大約20個(gè)模塊。這些模塊分為Core Container
耽梅、Data Access/Integration
薛窥、Web
、AOP(Aspect Oriented Programming)
、Instrumentation
和測(cè)試部分,如下圖所示:
Spring在項(xiàng)目中的架構(gòu)
-
web
層:Struts
,SpringMVC
-
dao
層:Hibernate
,mybatis
Spring在項(xiàng)目中的架構(gòu)
Spring IOC的底層實(shí)現(xiàn)原理
Spring核心jar包
spring-core-3.2.2.RELEASE.jar
包含Spring
框架基本的核心工具類诅迷,Spring
其它組件要都要使用到這個(gè)包里的類,是其它組件的基本核心佩番。spring-beans-3.2.2.RELEASE.jar
所有應(yīng)用都要用到的,它包含訪問(wèn)配置文件罢杉、創(chuàng)建和管理bean
以及進(jìn)行Inversion of Control(IoC) / Dependency Injection(DI)
操作相關(guān)的所有類spring-context-3.2.2.RELEASE.jar
Spring
提供在基礎(chǔ)IoC
功能上的擴(kuò)展服務(wù)趟畏,此外還提供許多企業(yè)級(jí)服務(wù)的支持,
如郵件服務(wù)、任務(wù)調(diào)度滩租、JNDI定位赋秀、EJB集成、遠(yuǎn)程訪問(wèn)律想、緩存以及各種視圖層框架的封裝等猎莲。spring-expression-3.2.2.RELEASE.jar
Spring
表達(dá)式語(yǔ)言com.springsource.org.apache.commons.logging-1.1.1.jar
第三方的主要用于處理日志
Spring IOC/DI
-
IOC
Inverse of Control
反轉(zhuǎn)控制的概念, 就是將原本在程序中手動(dòng)創(chuàng)建對(duì)象的控制權(quán), 交給Spring
框架管理, 簡(jiǎn)單的說(shuō), 就是將創(chuàng)建對(duì)象控制權(quán)被翻轉(zhuǎn)到了Spring
框架 -
DI
Dependency Injection
依賴注入的概念, 就是在Spring
創(chuàng)建這個(gè)對(duì)象的過(guò)程中, 將這個(gè)對(duì)象所依賴的屬性注入進(jìn)去
簡(jiǎn)單的Spring框架使用
創(chuàng)建一個(gè)UserService
接口
public void sayHello();
和它的實(shí)現(xiàn)類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)
}
}
- 傳統(tǒng)使用
UserService
public void demo() {
UserService userService = new UserServiceImpl();
userService.setName("李四");
userService.sayHello();
}
輸出結(jié)果
Spring hello李四
- 使用Spring
在resource里創(chuàng)建一個(gè)applicationContext.xml
文件, 里面寫入
// IOC
<bean id = "userService" class="com.rui.ioc.demo.UserServiceImpl>
<property name = "name" value="李四" />
</bean>
public void demo () {
// 創(chuàng)建Spring工廠, 加載classPath下的配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通過(guò)工廠獲得類
UserService userService = (UserService) applicationContext.getBean("userService");
userService.sayHello();
}
輸出結(jié)果
Spring hello李四
-
IOC
=>
使用`Spring`工廠獲得類叫做`IOC`, 即將創(chuàng)建對(duì)象的權(quán)利交給Spring
-
DI
=>
<property name = "name" value="李四" />
叫做依賴注入, 就是在Spring
創(chuàng)建這個(gè)對(duì)象的過(guò)程中, 將這個(gè)對(duì)象所依賴的屬性注入進(jìn)去
Spring的工廠類
-
ClassPathXmlApplicationContext
和BeanFactory
的區(qū)別-
BeanFactory
是spring
中比較原始,比較古老的Factory
技即。因?yàn)楸容^古老著洼,所以BeanFactory
無(wú)法支持spring
插件,例如:AOP
而叼、Web
應(yīng)用等功能身笤。 - 如果使用
ApplicationContext
,如果配置的bean
是singleton
葵陵,那么不管你有沒(méi)有或想不想用它展鸡,它都會(huì)被實(shí)例化。好處是可以預(yù)先加載埃难,壞處是浪費(fèi)內(nèi)存莹弊。 -
BeanFactory
,當(dāng)使用BeanFactory
實(shí)例化對(duì)象時(shí)涡尘,配置的bean
不會(huì)馬上被實(shí)例化忍弛,而是等到你使用該bean
的時(shí)候(getBean
)才會(huì)被實(shí)例化。好處是節(jié)約內(nèi)存考抄,壞處是速度比較慢细疚。多用于移動(dòng)設(shè)備的開(kāi)發(fā)。 - 沒(méi)有特殊要求的情況下川梅,應(yīng)該使用
ApplicationContext
完成疯兼。因?yàn)?code>BeanFactory能完成的事情,ApplicationContext
都能完成贫途,并且提供了更多接近現(xiàn)在開(kāi)發(fā)的功能吧彪。
-
-
加載某個(gè)位置下的配置文件
public void demo () {
// 創(chuàng)建Spring工廠
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("c:\\applicationContext.xml");
// 通過(guò)工廠獲得類
UserService userService = (UserService) applicationContext.getBean("userService");
userService.sayHello();
}
Spring的Bean管理方式
-
XML
-
三種實(shí)例化Bean的方式
-
使用類構(gòu)造器實(shí)例化(默認(rèn)無(wú)參數(shù))
-
// 對(duì)象 public class Bean1 { public Bean1() { System.out.println("Bean1被實(shí)例化了..."); } }
// Spring配置文件 <bean id="bean1" class="com.rui.demo.Bean1" />
輸出結(jié)果為@Test public void demo () { // 創(chuàng)建Spring工廠, 加載classPath下的配置文件 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); Bean1 bean1 = (bean1) applicationContext.getBean("bean1"); }
Bean1被實(shí)例化了...
-
使用靜態(tài)工廠實(shí)例化(簡(jiǎn)單工廠模式)
// 對(duì)象 public class Bean2 { public Bean2() { System.out.println("Bean2被實(shí)例化了..."); } }
// Bean2的靜態(tài)工廠 public class Bean2Factory { public static Bean2 createBean2() { System.out.println("Bean2被實(shí)例化了..."); return new Bean2(); } }
<bean id="bean2" class="com.rui.demo.Bean2Factory" factory-method="createBean2" />
輸出結(jié)果為public void demo () { // 創(chuàng)建Spring工廠, 加載classPath下的配置文件 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); Bean2 bean2 = (bean2) applicationContext.getBean("bean2"); }
Bean2被實(shí)例化了...
-
使用實(shí)例工廠方法實(shí)例化(工廠方法模式)
// 對(duì)象 public class Bean3 { public Bean3() { System.out.println("Bean3被實(shí)例化了..."); } }
// Bean3的實(shí)例化工廠方法 public class Bean3Factory { public Bean3 createBean3() { System.out.println("Bean3被實(shí)例化了..."); return new Bean3(); } }
<bean id="bean3Factory" class="com.rui.demo.Bean3Factory" /> <bean id="bean3" actory-bean="bean3Factory" factory-method="createBean3" />
輸出結(jié)果為public void demo () { // 創(chuàng)建Spring工廠, 加載classPath下的配置文件 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); Bean3 bean3 = (bean3) applicationContext.getBean("bean3"); }
Bean3被實(shí)例化了...
-
Bean的常用配置
-
id和name
- 一般情況下, 裝配一個(gè)Bean的時(shí)候, 通過(guò)一個(gè)id屬性作為Bean的名稱
- id屬性在IOC容器里必須是唯一的
- 如果Bean的名稱中含有特殊字符, 就需要使用name屬性
-
class
- class用于設(shè)置一個(gè)類的完全路徑名稱, 主要作用是IOC容器生成類的實(shí)例
-
Bean的作用域Scope
類別 說(shuō)明 singleton
(默認(rèn))在 SpringIOC
容器中僅存在一個(gè)Bean
實(shí)例,Bean
以單例的方式存在prototype
每次調(diào)用 getBean()
時(shí)都返回一個(gè)新的實(shí)例request
每次Http請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的 Bean
, 該作用域僅適用于WebApplicationContext
環(huán)境Session
同一個(gè) HTTP Session
共享一個(gè)Bean
, 不同的HTTP Session
使用不同的Bean
, 該作用域僅適用于WebApplicationContext
環(huán)境 -
-
Spring容器中Bean的生命周期
Spring初始化bean或銷毀bean的時(shí)候, 有時(shí)候需要做一些處理工作, 因此Spring可以在創(chuàng)建和銷毀bean的時(shí)候調(diào)用bean的兩個(gè)聲明周期方法
類別 說(shuō)明 init-method
初始化方法 destory-method
銷毀方法, 僅在 scope=singleton有效
// 當(dāng)bean被載入到容器的時(shí)候調(diào)用init, 當(dāng)bean從容器中刪除的時(shí)候調(diào)用destory(僅在`scope=singleton有效) <bean id="xxx" class="xxxx" init-method="init" destory-method="destory" />
Spring容器中Bean的生命周期-
instantiate bean
對(duì)象實(shí)例化 -
populate properties
封裝對(duì)象 - 如果
Bean
實(shí)現(xiàn)BeanNameAware
執(zhí)行setBeanName
- 如果
Bean
實(shí)現(xiàn)BeanFactoryAware
或者ApplicationContextAware
設(shè)置工廠 - 如果存在類實(shí)現(xiàn)
BeanPostProcessor
(后處理Bean
), 執(zhí)行postProcessBeforeInitialization
- 如果
Bean
實(shí)現(xiàn)InitializingBean
執(zhí)行afterPropertiesSet
- 調(diào)用
<bean init-method="init" />
指定初始化init
- 如果存在類實(shí)現(xiàn)
BeanPostProcessor
(處理Bean
), 執(zhí)行postProcessAfterInitiazation
- 執(zhí)行業(yè)務(wù)邏輯
-
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("權(quán)限調(diào)用"); return method.invoke(bean, args); } return method.invoke(bean, args); } }); return proxy; } else { return bean; } } }
- 輸出
第八步2222 查 權(quán)限調(diào)用 存 改 刪
-
-
Spring的屬性注入
- 構(gòu)造函數(shù)注入, 在對(duì)象中, 必須實(shí)現(xiàn)對(duì)應(yīng)的構(gòu)造函數(shù)參數(shù)為設(shè)置的屬性
<bean id="user" class="com.rui.demo.User"> <contructor-arg name="name" value="1223"> <contructor-arg name="age" value="23"> </bean>
- 屬性setter方法注入, 在Spring配置文件中, 通過(guò)<property>設(shè)置注入的屬性, 在對(duì)象中必須實(shí)現(xiàn)對(duì)應(yīng)的參數(shù)的setter方法
<bean id="user" class="com.rui.demo.User"> <property name="name" value="1223"> <property name="age" value="23"> </bean> ``` ``` // User對(duì)象中含有一個(gè)對(duì)象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>
- 復(fù)雜類型的屬性注入(一般用于整合其他框架)
// 數(shù)組類型 <property name="arrs"> <list> <value>aaa</value> </list> </property> // list類型 <property name="list"> <list> <value>aaa</value> </list> </property> // 數(shù)組類型 <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>
- 構(gòu)造函數(shù)注入, 在對(duì)象中, 必須實(shí)現(xiàn)對(duì)應(yīng)的構(gòu)造函數(shù)參數(shù)為設(shè)置的屬性
-
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>
就會(huì)自動(dòng)掃描ioc
下所有的類, 在類上標(biāo)注@Component/@Service/@Repository/@Controller
1、
@controller
控制器(注入服務(wù))
2丢早、@service
服務(wù)(注入dao
)
3姨裸、@repository dao
(實(shí)現(xiàn)dao
訪問(wèn))
4秧倾、@component
(@component("xxx")
)(把普通pojo
實(shí)例化到spring
容器中,相當(dāng)于配置文件中的<bean id="xxx" class=""/>
)
-
屬性的注入-注解方式
- 簡(jiǎn)單屬性注入
@value("coding") private String something; // something輸出值為coding
-
@Autowired
進(jìn)行自動(dòng)注入-
@Autowired
默認(rèn)按照類型進(jìn)行注入- 如果兩個(gè)相同的
Bean
類型相同, 則按照名稱進(jìn)行注入
- 如果兩個(gè)相同的
- 通過(guò)
@Autowired
的required
屬性, 設(shè)置一定要找到匹配的Bean
- 使用
@Qualifier
指定注入Bean
的名稱
同時(shí)使用@Autowired
和@Qualifier
可以指定固定的Bean, 效果和使用@Resource
相同
-
-
@PostConstruct
: 初始化 -
@PreDestroy
: 銷毀 -
@Scope("prototype")
: 多例, 默認(rèn)為單例
- 簡(jiǎn)單屬性注入
Spring AOP
-
什么是AOP
AOP Aspect Oriented Programing 面向切面編程
AOP采取橫向抽取機(jī)制, 取代了傳統(tǒng)縱向繼承體系重復(fù)性代碼(性能監(jiān)視, 事務(wù)管理, 安全檢查, 緩存) -
AOP術(shù)語(yǔ)
AOP術(shù)語(yǔ) -
JDK動(dòng)態(tài)代理
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("查找用戶~(yú)");
}
@Override
public void save() {
System.out.println("保存用戶~(yú)");
}
@Override
public void update() {
System.out.println("更新用戶~(yú)");
}
@Override
public void delete() {
System.out.println("刪除用戶~(yú)");
}
}
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();
}
}
輸出結(jié)果
查找用戶~(yú)
刪除用戶~(yú)
保存用戶之前的操作
保存用戶~(yú)
更新用戶~(yú)
-
使用CGLIB生成代理
MyCglibProxy
public class MyCglibProxy implements MethodInterceptor {
private ProductDao productDao;
public MyCglibProxy(ProductDao productDao) {
this.productDao = productDao;
}
public Object createProxy() {
// 1. 創(chuàng)建核心類
Enhancer enhancer = new Enhancer();
// 2. 設(shè)置父類
enhancer.setSuperclass(productDao.getClass());
// 3. 設(shè)置回調(diào)
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
("============================權(quán)限校驗(yàn)==========================");
return methodProxy.invokeSuper(o, objects);
}
return methodProxy.invokeSuper(o, objects);
}
}
ProductDao
public class ProductDao {
public void findAll() {
System.out.println("查找用戶~(yú)");
}
public void save() {
System.out.println("保存用戶~(yú)");
}
public void update() {
System.out.println("更新用戶~(yú)");
}
public void delete() {
System.out.println("刪除用戶~(yú)");
}
}
Test
@Test
public void demo1() {
ProductDao productDao = new ProductDao();
ProductDao proxy = (ProductDao)new MyCglibProxy(productDao).createProxy();
proxy.findAll();
proxy.delete();
proxy.save();
proxy.update();
}
輸出
查找用戶~(yú)
刪除用戶~(yú)
============================權(quán)限校驗(yàn)==========================
保存用戶~(yú)
更新用戶~(yú)
代理相關(guān)
-
Spring
在運(yùn)行期, 生成動(dòng)態(tài)代理對(duì)象, 不需要特殊的編譯器 -
Spring AOP
的底層就是通過(guò)JDK
動(dòng)態(tài)代理或CGLib
動(dòng)態(tài)代理技術(shù)為目標(biāo)Bean
執(zhí)行橫向織入- 若目標(biāo)對(duì)象實(shí)現(xiàn)了若干接口,
Spring
使用JDK
的java.lang.reflect.Proxy
類代理 - 若目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)任何接口,
spring
使用cglib
庫(kù)生成目標(biāo)對(duì)象的子類
- 若目標(biāo)對(duì)象實(shí)現(xiàn)了若干接口,
- 程序中應(yīng)該優(yōu)先對(duì)接口創(chuàng)建代理, 便于程序解耦維護(hù)
- 標(biāo)記為
final
的方法, 不能被代理, 因?yàn)闊o(wú)法覆蓋-
JDK
動(dòng)態(tài)代理, 是針對(duì)接口生成子類, 接口中方法不能使用final
修飾 -
CGLib
是針對(duì)目標(biāo)類生產(chǎn)子類, 因此類或方法, 不能使用final
的
-
-
Spring
只支持方法連接點(diǎn), 不提供屬性連接點(diǎn)
Spring AOP增強(qiáng)類型
- AOP聯(lián)盟為通知
Advice
定義了org.aopallinance.aop.Interface.Advice
- Spring按照通知Advice在目標(biāo)類方法的鏈接點(diǎn)位置, 可以分為5類
- 前置通知
org.springframework.aop.MethodBeforeAdvice
- 在目標(biāo)方法執(zhí)行前實(shí)施增強(qiáng)
- 后置通知
org.springframework.aop.AfterReturningAdvice
- 在目標(biāo)方法執(zhí)行后實(shí)施增強(qiáng)
- 環(huán)繞通知
org.springframework.intercept.MethodInterceptor
- 在目標(biāo)方法執(zhí)行前后都實(shí)施增強(qiáng)
- 異常拋出通知
org.springframework.ThrowsAdvice
- 在方法拋出異常后實(shí)施增強(qiáng)
- 引介通知
org.springframework.IntroductionInterceptor
- 在目標(biāo)類中添加一些新的方法和屬性
- 前置通知
Spring AOP切面類型
- Advisor: 代表一般切面, Advice本身就是一個(gè)切面, 對(duì)目標(biāo)類所有方法進(jìn)行攔截
- PointcutAdvisor: 代表具有切點(diǎn)的切面, 可以指定攔截目標(biāo)類哪些方法
- IntroductionAdvisor: 代表引介切面, 針對(duì)引介通知而使用切面
自動(dòng)創(chuàng)建代理
-
BeanNameAutoProxyCreator
根據(jù)Bean名稱創(chuàng)建代理 -
DefaultAdvisorAutoProxyCreator
根據(jù)Advisor本身包含信息創(chuàng)建代理 -
AnnotationAwareAspectJAutoProxyCreator
基于Bean中的AspectJ注解進(jìn)行自動(dòng)代理
AspectJ AOP
- AspectJ是一個(gè)基于java語(yǔ)言的AOP框架
- Spring 2.0以后新增了對(duì)AspectJ切點(diǎn)表達(dá)式的支持
1.Spring開(kāi)啟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">
<!-- 開(kāi)啟aspectj的注解開(kāi)發(fā)-->
<aop:aspectj-autoproxy/>
</beans>
2.@AspectJ提供不同的通知類型
-
@Before
前置通知, 相當(dāng)于BeforeAdvice
-
@AfterReturning
后置通知, 相當(dāng)于AfterReturningAdvice
-
@Around
環(huán)繞通知, 相當(dāng)于MethodInterceptor
-
@AfterThrowing
異常跑出通知, 相當(dāng)于ThrowAdvice
-
@After
最終final通知, 不管是否異常, 該通知都會(huì)執(zhí)行 -
@DeclareParents
引介通知, 相當(dāng)于IntroductionInterceptor
3. 在通知中通過(guò)value屬性定義切點(diǎn)
- 通過(guò)excution函數(shù), 可以定義切點(diǎn)的方法切入
- 語(yǔ)法
excution(<訪問(wèn)修飾符>?<返回類型><方法名>(<參數(shù)>)<異常>)
- eg.
- 匹配所有類public方法 excution(public * *(...))
- 匹配指定包下所有類方法 excution(* com.imooc.dao.*(...)) 不包含子包
- 匹配指定包下所有類方法 excution(* com.imooc.dao..*(...)) 包含子包
- 匹配指定類所有方法 excution(* com.imooc.service.UserService.*(..))
- 匹配實(shí)現(xiàn)特定接口所有類方法 excution(* com.imooc.service.dao.GenericDAO+.*(..))
- 匹配所有save開(kāi)頭的方法 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("環(huán)繞前通知=====================");
Object obj = joinPoint.proceed(); // 執(zhí)行目標(biāo)方法
System.out.println("環(huán)繞后通知=====================");
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("查找一個(gè)商品...");
}
public void findAll() {
System.out.println("查找所有商品...");
}
}
- Spring 配置
<!-- 開(kāi)啟aspectj的注解開(kāi)發(fā)-->
<aop:aspectj-autoproxy/>
<!-- 目標(biāo)類-->
<bean id="productDao" class="com.rui.aspectj.ProductDao" />
<!--定義切面-->
<bean class="com.rui.aspectj.MyAspect" />
- 測(cè)試代碼
@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())
保存商品...
環(huán)繞前通知=====================
刪除商品...
環(huán)繞后通知=====================
查找所有商品...
最終通知!================
查找一個(gè)商品...
異常拋出通知===========/ by zero
JDBC Template
使用
- Mysql驅(qū)動(dòng)
- 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方法
// 對(duì)數(shù)據(jù)進(jìn)行增刪改操作
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方法
// 對(duì)數(shù)據(jù)進(jìn)行批量增刪改操作
int [] batchUpdate(String [] sql)
int [] batchUpdate(String sql, List<Object[]> args)
public void testBatchUpdate() {
String[] sqls = {
"insert into student(name, sex) values ('關(guān)羽', '男')",
"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[]{"關(guān)羽", "男"});
list.add(new Object[]{"張飛", "男"});
jdbcTemplate.batchUpdate(sql, list);
}
-
查詢簡(jiǎn)單數(shù)據(jù)項(xiàng)
- 獲取一個(gè)
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); }
- 獲取多個(gè)
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, "男"); }
-
查詢復(fù)雜對(duì)象(封裝為Map)
- 獲取一個(gè)
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); }
- 獲取多個(gè)
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); }
-
查詢復(fù)雜對(duì)象(封裝為實(shí)體對(duì)象)
- RowMapper接口
- 獲取一個(gè)
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) }
- 獲取多個(gè)
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)
在日常開(kāi)發(fā)中, 匿名類的形式比較冗余, 一般進(jìn)行封裝, 將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
單獨(dú)實(shí)現(xiàn)
使用如下: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; } }
但是在實(shí)際開(kāi)發(fā)中, 每個(gè)類都要實(shí)現(xiàn)一個(gè)public void testQueryEntityList() { String sql = "select * from student"; List<Student> stus = jdbcTemplate.query(sql, new StudentRowMapper());
RowMapper
會(huì)讓開(kāi)發(fā)變得極其繁瑣, 一般實(shí)現(xiàn)一個(gè)通用的工具類來(lái)做轉(zhuǎn)換的功能:
- RowMapper接口
-
JDBCTemplate優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn)
- 簡(jiǎn)單
- 靈活
- 缺點(diǎn)
- sql和java代碼牽扯的比較雜亂
- 功能不夠豐富, 比如分頁(yè)的功能沒(méi)有等
- 優(yōu)點(diǎn)
Spring事務(wù)管理
什么是事務(wù)
- 事務(wù)就是正確執(zhí)行一系列的操作(或者動(dòng)作), 使數(shù)據(jù)庫(kù)從一種狀態(tài)轉(zhuǎn)換成另一種狀態(tài)且保證操作全部成功, 或者全部失敗
事務(wù)的特點(diǎn)
- 原子性(Atomicity)
- 即不可分割性, 事務(wù)要么全部被執(zhí)行, 要么就全部不執(zhí)行
- 一致性(Consistency)
- 事務(wù)的執(zhí)行使得數(shù)據(jù)庫(kù)從一種正確的狀態(tài)轉(zhuǎn)換成另一種正確的狀態(tài)
- 隔離性(Isolation)
- 在事務(wù)正確提交之前, 它可能的結(jié)果不應(yīng)該顯示給任何其他事務(wù)
- 持久性(Durability)
- 事務(wù)正確提交以后, 其結(jié)果將永久保存在數(shù)據(jù)庫(kù)中
Java事務(wù)的產(chǎn)生
- 程序操作數(shù)據(jù)庫(kù)的需要. 在Java編寫的程序或者系統(tǒng)中, 實(shí)現(xiàn)ACID的操作
- Java事務(wù)實(shí)現(xiàn)范圍
- 通過(guò)jdbc相應(yīng)方法間接來(lái)實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)的增刪改查, 把事務(wù)轉(zhuǎn)移到Java程序代碼中進(jìn)行控制
- 確保事務(wù), 要么全部執(zhí)行成功, 要么撤銷不執(zhí)行
- Java事務(wù)機(jī)制和原理就是確保數(shù)據(jù)庫(kù)操作的ACID特性
Java事務(wù)實(shí)現(xiàn)模式
- Java事務(wù)的實(shí)現(xiàn)
- 通過(guò)Java代碼來(lái)實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)的事務(wù)性操作
- Java事務(wù)類型
-
JDBC
事務(wù): 用Connection
對(duì)象控制, 包括手動(dòng)模式和自動(dòng)模式; - JTA(
Java Transaction API
)事務(wù): 與實(shí)現(xiàn)無(wú)關(guān), 與協(xié)議無(wú)關(guān)的API; - 容器事務(wù): 應(yīng)用服務(wù)器提供的, 且大多是基于JTA完成(通晨酰基于
JNDI
的, 相當(dāng)復(fù)雜的API
實(shí)現(xiàn))
-
三種事務(wù)的差異
- JDBC事務(wù): 控制的局限性在一個(gè)數(shù)據(jù)庫(kù)連接內(nèi), 但是其使用簡(jiǎn)單
- JTA事務(wù): 功能強(qiáng)大, 可跨越多個(gè)數(shù)據(jù)庫(kù)或者多個(gè)DAO, 使用比較復(fù)雜
- 容器事務(wù): 主要指的是J2EE應(yīng)用服務(wù)器提供的事務(wù)管理, 局限于EJB
事務(wù)接口
-
事務(wù)接口架構(gòu)
事務(wù)接口架構(gòu)
事務(wù)讀取類型
- 臟讀: 事務(wù)沒(méi)提交, 提前讀取
- 不可重復(fù)讀: 多次讀取到的數(shù)據(jù)不一致
- 幻讀: 事務(wù)不是獨(dú)立執(zhí)行時(shí)發(fā)生的一種非預(yù)期現(xiàn)象
事務(wù)隔離級(jí)別
事務(wù)傳播行為
事務(wù)超時(shí)
- 事務(wù)超時(shí)就是事務(wù)的一個(gè)定時(shí)器, 在特定時(shí)間內(nèi)事務(wù)如果沒(méi)有執(zhí)行完畢, 那么久會(huì)自動(dòng)回滾, 而不是一直等待其結(jié)束
事務(wù)回滾
- 默認(rèn)情況下, 事務(wù)只有遇到運(yùn)行期異常才會(huì)回滾, 而在遇到檢查型異常時(shí)不會(huì)回滾
- 自定義回滾策略
- 生命失誤在遇到特定的檢查型異常時(shí)像遇到運(yùn)行期異常那樣回滾
- 聲明事務(wù)遇到特別的異常不回滾, 即使這些異常時(shí)運(yùn)行期異常
Spring事務(wù)狀態(tài)
- 事務(wù)接口
- 通過(guò)事務(wù)管理器獲取
TransactionStatus
實(shí)例; - 控制事務(wù)在回滾或提交的時(shí)候需要應(yīng)用對(duì)應(yīng)的事務(wù)狀態(tài);
- Spring事務(wù)接口
// Spring事務(wù)狀態(tài)接口 // 通過(guò)調(diào)用PlatformTransactionManager的getTransaction() // 獲取事務(wù)狀態(tài)實(shí)例 public interface TransactionStatus { boolean isNewTransaction(); // 是否是新的事務(wù) boolean hasSavepoint(); // 是否有恢復(fù)點(diǎn) boolean setRollbackOnly(); // 設(shè)置為只回滾 boolean isRollbackOnly(); // 是否為只回滾 boolean isCompleted(); // 是否已完成 }
- 通過(guò)事務(wù)管理器獲取
編程式事務(wù)實(shí)現(xiàn)方式
事務(wù)管理器(PlatformTransactionManager)方式
- 類似應(yīng)用
JTA UserTransaction API
方式, 但異常處理更簡(jiǎn)潔; - 核心類為:
Spring
事務(wù)管理的三個(gè)接口類以及JdbcTemplate
類 - 步驟:
- 獲取事務(wù)管理器
- 創(chuàng)建事務(wù)屬性對(duì)象
- 獲取事務(wù)狀態(tài)對(duì)象
- 創(chuàng)建JDBC模板對(duì)象
- 業(yè)務(wù)數(shù)據(jù)操作處理
模板事務(wù)(TransactionTemplate)的方式
- 此為
Spring
官方團(tuán)隊(duì)推薦的編程式事務(wù)管理方式 - 主要工具為
JdbcTemplate
類
聲明式事務(wù)管理
聲明式事務(wù)實(shí)現(xiàn)原理
- 基于AOP模式機(jī)制, 對(duì)方法前后進(jìn)行攔截
聲明式事務(wù)管理的配置類型:
- 5種類型: 獨(dú)立代理; 共享代理, 攔截器, tx攔截器, 全注釋(注解方式) (前三類2.0版本以后不推薦使用)
聲明式事務(wù)管理配置實(shí)現(xiàn)方式:
<!-- 引入配置文件-->
<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}"/>
<!-- 連接池啟動(dòng)時(shí)的初始值 -->
<property name="initialSize" value="${db.initialSize}"/>
<!-- 連接池的最大值 -->
<property name="maxActive" value="${db.maxActive}"/>
<!-- 最大空閑值.當(dāng)經(jīng)過(guò)一個(gè)高峰時(shí)間后那先,連接池可以慢慢將已經(jīng)用不到的連接慢慢釋放一部分,一直減少到maxIdle為止 -->
<property name="maxIdle" value="${db.maxIdle}"/>
<!-- 最小空閑值.當(dāng)空閑的連接數(shù)少于閥值時(shí)赡艰,連接池就會(huì)預(yù)申請(qǐng)去一些連接售淡,以免洪峰來(lái)時(shí)來(lái)不及申請(qǐng) -->
<property name="minIdle" value="${db.minIdle}"/>
<!-- 最大建立連接等待時(shí)間。如果超過(guò)此時(shí)間將接到異常慷垮。設(shè)為-1表示無(wú)限制 -->
<property name="maxWait" value="${db.maxWait}"/>
<!--#給出一條簡(jiǎn)單的sql語(yǔ)句進(jìn)行驗(yàn)證 -->
<!--<property name="validationQuery" value="select getdate()" />-->
<property name="defaultAutoCommit" value="${db.defaultAutoCommit}"/>
<!-- 回收被遺棄的(一般是忘了釋放的)數(shù)據(jù)庫(kù)連接到連接池中 -->
<!--<property name="removeAbandoned" value="true" />-->
<!-- 數(shù)據(jù)庫(kù)連接過(guò)多長(zhǎng)時(shí)間不用將被視為被遺棄而收回連接池中 -->
<!--<property name="removeAbandonedTimeout" value="120" />-->
<!-- #連接的超時(shí)時(shí)間揖闸,默認(rèn)為半小時(shí)。 -->
<property name="minEvictableIdleTimeMillis" value="${db.minEvictableIdleTimeMillis}"/>
<!--# 失效檢查線程運(yùn)行時(shí)間間隔换帜,要小于MySQL默認(rèn)-->
<property name="timeBetweenEvictionRunsMillis" value="40000"/>
<!--# 檢查連接是否有效-->
<property name="testWhileIdle" value="true"/>
<!--# 檢查連接有效性的SQL語(yǔ)句-->
<property name="validationQuery" value="SELECT 1 FROM dual"/>
</bean>
<!-- 使用@Transactional進(jìn)行聲明式事務(wù)管理需要聲明下面這行 -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
<!-- 事務(wù)管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
<property name="rollbackOnCommitFailure" value="true"/>
</bean>