Spring
1. Spring概述
????Spring是應用full-stack輕量級框架,以IoC(Inverse Of Control : 控制反轉(zhuǎn))和AOP(Aspect Oriented Programming : 面向切面編程)為內(nèi)核,提供了表現(xiàn)層SpringMVC和持久層Spring JDBC以及業(yè)務層事務管理等眾多的企業(yè)級應用技術,還能整合開源世界中眾多的第三方框架和類庫。
2. Spring的優(yōu)勢
- 方便解耦,簡化開發(fā)
- 通過Spring提供的IoC容器,可以將對象間的依賴關系交由Spring進行控制,編碼硬編碼所造成的過度程序耦合。
- AOP編程支持
- 通過Spring的AOP功能,方便進行面向切面編程,許多不容易用傳榮OOP實現(xiàn)的功能可以通過AOP輕松實現(xiàn)。
- 聲明式事務的支持
- 可以簡化在事務管理方面的代碼,通過聲明的方式進行靈活的事務管理,提高開發(fā)效率和質(zhì)量
- 方便程序的測試
- 可以用非容器依賴的編程方式進行幾乎所有的測試工作,測試的難度被降低了。
- 方便集成各種優(yōu)秀框架
- Spring提供了對各種郵箱框架的直接支持。
- 降低JavaEE API的使用難度
- Spring對JavaEE API中的代碼(JDBC、JavaMail、遠程調(diào)用等)進行了封裝,降低了使用API的難度。
3. Spring的IoC應用
1. 在Maven中導入Spring的坐標
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
2. Spring的主配置文件:applicationContext.xml
- 主配置的約束頭信息
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
- 簡單的Spring配置文件
<!-- 配置service
id : 唯一標識
class : 指定類的全類名
-->
<bean id="accountService" class="com.hsh.study.service.impl.AccountServiceImpl"></bean>
<!-- 配置dao -->
<bean id="accountDao" class="com.hsh.study.dao.impl.AccountDaoImpl"></bean>
- 常見屬性標簽
- bean標簽:用于配置對象讓Spring來創(chuàng)建。默認情況下Spring調(diào)用類中的無參構造函數(shù),沒有無參構造函數(shù)則不能創(chuàng)建成功
- 屬性:
- id:給對象在容器總提供一個唯一標識。用于獲取對象。
- class:指定類的全限定類名。用于反射創(chuàng)建對象,默認情況下調(diào)用無參構造函數(shù)
- scope:指定對象的作用范圍。
- singleton:默認值,單例對象
- prototype:多例的
- request:在WEB中,Spring創(chuàng)建一個Bean對象,將對象存入request域中。
- session:在WEB中,Spring創(chuàng)建一個Bean對象,將對象存入session域中。
- global session:在WEB中,應用在Porlet環(huán)境,如果沒有Porlet環(huán)境那么globalSession相當于Session。
- init-method:指定類中的初始化方法名稱。
- destory-method:指定類中銷毀方法的名稱
- factory-method:指定生產(chǎn)對象的靜態(tài)方法(使用靜態(tài)工廠初始化Bean時)
- factory-bean:用于指定實例工廠bean的id(使用實例工廠初始化Bean時)
- factory-method:用于指定實例工廠中創(chuàng)建對象的方法(使用實例工廠初始化Bean時)
- 屬性:
- bean標簽:用于配置對象讓Spring來創(chuàng)建。默認情況下Spring調(diào)用類中的無參構造函數(shù),沒有無參構造函數(shù)則不能創(chuàng)建成功
3. Spring的執(zhí)行方法
- 使用ApplicaionContext接口獲取Spring的容器:
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); - 根據(jù)配置文件獲取對象:
AccountService accountService = ac.getBean("accountService",AccountService.class); - 使用對象:
accountService.saveAccount();
ApplicationContext和BeanFactory的區(qū)別:
- BeanFactory是Spring容器中的頂層接口。
- ApplicationContext是BeanFactory的子接口。
- BeanFactory和ApplicationContext創(chuàng)建對象的時間點不同;
- BeanFactory:什么時候使用什么時候創(chuàng)建對象
- ApplicationContext:只要讀取完配置文件,默認情況下就會創(chuàng)建對象。
4. Spring的依賴注入
- 依賴注入概念:在編寫程序時,通過IoC(控制反轉(zhuǎn))將對象的創(chuàng)建交給了Spring,但是代碼中不可能出現(xiàn)沒有依賴的情況。IoC解耦只是降低了代碼間的依賴關系,但不會完全消除。
- 構造函數(shù)的注入(通過類中的構造函數(shù)給成員變量完成賦值的操作)
- 編寫需要進行賦值的實體類
public class User { private String username; private Integer age; private Date birthday; public User(String username, Integer age, Date birthday) { this.username = username; this.age = age; this.birthday = birthday; } @Override public String toString() { return "User{" + "username='" + username + '\'' + ", age=" + age + ", birthday=" + birthday + '}'; } }- 用配置文件通過構造函數(shù)對屬性進行賦值
<!-- 通過構造器進行屬性注入 --> <bean id="user" class="com.hsh.study.domain.User"> <!-- 通過<constructor-arg>標簽對構造函數(shù)中的屬性進行賦值 index:指定參數(shù)在構造函數(shù)參數(shù)列表的索引位置 type:指定參數(shù)在構造函數(shù)中的數(shù)據(jù)類型 name:指定參數(shù)在構造函數(shù)中的名稱 ==--- 上面用于指定給誰賦值,下面指定賦什么值 ---== value:能賦值的類型為基本數(shù)據(jù)類型和String類型 ref:能賦值的類型是其他bean類型,必須通過配置文件配置bean --> <constructor-arg name="username" value="張三"></constructor-arg> <constructor-arg name="age" value="18"></constructor-arg> <constructor-arg name="birthday" ref="now"></constructor-arg> </bean> <!-- 引入日期 --> <bean id="now" class="java.util.Date"></bean> - set方法的注入(常用)
- 編寫需要進行賦值的實體類
public class User { private String username; private Integer age; private Date birthday; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } @Override public String toString() { return "User{" + "username='" + username + '\'' + ", age=" + age + ", birthday=" + birthday + '}'; } }- 用配置文件通過構造函數(shù)對屬性進行賦值
<!-- 通過set方法注入 --> <bean id="user" class="com.hsh.study.domain.User"> <!-- 通過<property>標簽對屬性進行賦值 name:找類中set方法后面的部分 ref:給屬性賦值是其他bean類型 value:給屬性賦值的是基本數(shù)據(jù)類型或者string類型 --> <property name="username" value="李四"></property> <property name="age" value="16"></property> <property name="birthday" ref="now"></property> </bean> <!-- 引入日期 --> <bean id="now" class="java.util.Date"></bean> - 使用p名稱空間注入(本質(zhì)還是調(diào)用set方法)
- 編寫需要進行賦值的實體類
public class User { private String username; private Integer age; private Date birthday; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } @Override public String toString() { return "User{" + "username='" + username + '\'' + ", age=" + age + ", birthday=" + birthday + '}'; } }- 用配置文件通過構造函數(shù)對屬性進行賦值
<!-- 導入p標簽的約束文件 --> xmlns:p="http://www.springframework.org/schema/p" <!-- 通過p標簽進行注入 --> <bean id="user" class="com.hsh.study.domain.User" p:username="王五" p:age="18" p:birthday-ref="now" ></bean> <!-- 引入日期 --> <bean id="now" class="java.util.Date" ></bean> - 注入集合屬性
- 編寫需要進行賦值的實體類
public class Collection { private String[] myStrs; private List<String> myList; private Set<String> mySet; private Map<String,String> myMap; private Properties myProps; public void setMyStrs(String[] myStrs) { this.myStrs = myStrs; } public void setMyList(List<String> myList) { this.myList = myList; } public void setMySet(Set<String> mySet) { this.mySet = mySet; } public void setMyMap(Map<String, String> myMap) { this.myMap = myMap; } public void setMyProps(Properties myProps) { this.myProps = myProps; } public String[] getMyStrs() { return myStrs; } public List<String> getMyList() { return myList; } public Set<String> getMySet() { return mySet; } public Map<String, String> getMyMap() { return myMap; } public Properties getMyProps() { return myProps; } }- 用配置文件通過構造函數(shù)對屬性進行賦值
<!-- 集合的注入 --> <bean id="collection" class="com.hsh.study.domain.Collection"> <!-- 注入集合數(shù)據(jù) List結(jié)構:array、List、Set Map結(jié)構:map、properties、entry --> <!-- 數(shù)組注入 --> <property name="myStrs"> <list> <value>AAA</value> <value>BBB</value> <value>CCC</value> </list> </property> <!-- list集合注入 --> <property name="myList"> <array> <value>AAA</value> <value>CCC</value> <value>BBB</value> </array> </property> <!-- set集合注入 --> <property name="mySet"> <set> <value>BBB</value> <value>AAA</value> <value>CCC</value> </set> </property> <!-- map集合注入 --> <property name="myMap"> <map> <entry key="111" value="AAA"></entry> <entry key="222" value="BBB"></entry> <entry key="333" value="CCC"></entry> </map> </property> <!-- Props注入 --> <property name="myProps"> <props> <prop key="333">AAA</prop> <prop key="222">CCC</prop> <prop key="111">BBB</prop> </props> </property> </bean>
5. Spring基于注解的使用
- 在Maven中導入Spring的坐標
- 配置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: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"> </bean>- 在配置文件中開啟對注解的支持
<?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"> <!-- 告知spring創(chuàng)建容器時要掃描的包 --> <context:component-scan base-package="com.hsh.study"></context:component-scan> </beans> - Spring的常用注解
- 創(chuàng)建對象的:<bean id="" class="" ></bean>
-
@Component()
- 將資源交由Spring進行管理,相當于在xml中配置<bean>標簽
- 默認屬性為value,value即是hean的id,如果不指定value屬性則默認id為當前類名,首字母小寫
-
@Controller @Service @Repository
- 這三個注釋的作用與@Component的作用一致,只不過提供了更加明確的語義化
- @Controller:一般用于表現(xiàn)層
- @Service:一般用于業(yè)務層
- @Repository:一般用于持久層
- 這三個注釋的作用與@Component的作用一致,只不過提供了更加明確的語義化
-
@Component()
- 用于注入數(shù)據(jù)的:<property name="" ref="" /> <property name="" value="" />
-
@Autowired
- 自動按照類型注入。當使用注解注入屬性時,可以不用聲明set方法。只能注入其他bean類型。當有多個類型匹配時,使用要注入的對象變量名稱作為bean的id。
-
@Qualifier
- 在自動按照類型的基礎上,再按照Bean的id注入。在給字段注入時不能獨立使用,必須和@Autowired一起使用;但是給方法參數(shù)注入時可以獨立使用。
- value:指定bean的id
-
@Resource
- 直接按照Bean的id進行注入。也是只能注入其他Bean類型
- name:指定bean的id
-
@Value
- 注入基本數(shù)據(jù)類型和String類型的數(shù)據(jù)
- value:用于指定值
-
@Autowired
- 用于改變作用范圍的:<bean id="" class="" ==scope=""==> </bean>
-
@Scope
- 指定bean的作用范圍
- value:指定范圍的值(==singleton==、==prototype==、request、session、globalsession)
-
@Scope
- 和生命周期相關的:<bean id="" class="" ==init-method=""== ==destory-method=""== />
-
@PostConstruct
- 用于指定初始化方法
-
@PreDestroy
- 用于指定銷毀方法
-
@PostConstruct
- 用于純注解配置的注解
-
@Configuration
- 作用:用于指定當前類是一個spring的配置類,當創(chuàng)建容器時會從該類上加載注解。獲取容器時需要使用AnnotationApplicationContext(有@Configuration注解的類.class)
- value:用于指定配置類的字節(jié)碼
- 注:當配置類作為AnnotationConfigApplicationContext對象創(chuàng)建的參數(shù)時,該注解可以不寫。
-
@ComponentScan
- 作用:用于指定Spring再初始化容器時要掃描的包。作用和在spring的xml配置文件中的<context:component-scan base-package="com.hsh.study" />的作用相同
- basePackages:用于指定要掃描的包,和注解中的value屬性作用一致。
-
@Bean
- 作用:該注解只能寫在方法上,表明使用此方法創(chuàng)建一個對象并放入Spring的容器中
- name:給當前的@Bean注解方法創(chuàng)建的對象指定一個名稱
-
@PropertySource
- 作用:用于加載.properties文件中的配置。例如當配置數(shù)據(jù)源時,可以把連接數(shù)據(jù)庫的信息寫道properties配置文件中,使用注解指定配置文件的位置。
- value[]:用于指定properties文件位置。如果是在類路徑下,需要寫上==classpath:==
-
@Import
- 作用:用于導入其他配置類,在引入其他配置時可以不用再寫@Configuration注解。
- value[]:用于指定其他配置類的字節(jié)碼
-
@Configuration
- 創(chuàng)建對象的:<bean id="" class="" ></bean>
- Spring使用XML和使用注解配置的優(yōu)劣
- 使用注解的優(yōu)勢:配置簡單,維護方便,適用于Bean的是實現(xiàn)類由用戶自己開發(fā)
- 使用XML的優(yōu)勢:修改時不需要改源碼,不涉及重新編譯和部署。適用于Bean來自第三方,使用其他的Bean。
6. Spring中使用純注解配置
- 創(chuàng)建一個主配置類
//@Configuration
@ComponentScan("com.hsh.study")
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
}
- 創(chuàng)建JDBC配置類
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 創(chuàng)建QueryRunner對象
* @param dataSource
* @return
*/
@Bean(name = "runner")
@Scope("singleton")
public QueryRunner createQueryRunner(@Qualifier("ds1") DataSource dataSource){
return new QueryRunner(dataSource);
}
/**
* 創(chuàng)建數(shù)據(jù)庫連接對象
* @return
*/
@Bean("ds1")
public DataSource createDataSource(){
try{
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
return ds;
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
- 使用注釋完成創(chuàng)建對象和注入
//1. 業(yè)務層
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public List<Account> findAll() {
return accountDao.findAll();
}
}
//2. 持久層
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private QueryRunner runner;
@Override
public List<Account> findAll() {
try {
return runner.query("select * from account",new BeanListHandler<Account>(Account.class));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
- 測試
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
// 基于注解創(chuàng)建applicationContext
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
List<Account> accounts = accountService.findAll();
for (Account account1 : accounts){
System.out.println(account1);
}
4. Spring整合Junit
1. 導入整合junit的jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
2. 使用注解@RunWith代替原有運行器
@RunWith(SpringJUnit4ClassRunner.class)
public class JdbcTest2 {
@Test
public void test() {
}
}
3. 使用@ContextConfiguration指定spring配置文件位置
- @ContextConfiguration屬性:
- locations屬性:用于指定配置文件的位置。位于類路徑下需使用classpath:表示。
- calsses屬性:用于指定注解的類。當不使用xml時,需要用此屬性指定注解類的位置。
- 應用
@RunWith(SpringJUnit4ClassRunner.class)
// 使用XML配置文件時
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
// 使用注解時
@ContextConfiguration(classes = {config.SpringConfiguration.class})
public class JdbcTest2 {
@Test
public void test() {
}
}
4. 使用@Autowired給變量注入數(shù)據(jù)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {config.SpringConfiguration.class})
public class JdbcTest2 {
@Autowired
private AccountService accountService;
@Test
public void test() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
accountService = applicationContext.getBean("accountService", AccountService.class);
List<Account> accounts = accountService.findAll();
for (Account account1 : accounts){
System.out.println(account1);
}
}
}
5. Spring的AOP
1. 概念:
AOP(Aspect Oriented Programming)面向切面編程。利用AOP對業(yè)務邏輯的各個部分進行隔離,從而降低業(yè)務邏輯各部分之間的耦合度
2. AOP的作用及優(yōu)勢
- 作用:在程序運行期間,在不修改源碼的基礎上對已有方法進行增強。
- 優(yōu)勢:①減少重復代碼②提高開發(fā)效率③維護方便
3. AOP中相關的術語
- JoinPoint(連接點):連接點指的是哪些被攔截到的點。在spring中,這些點指的是方法,因為spring只支持方法類型的連接點。
- Pointcut(切入點):切入點指的是對哪些連接點進行攔截的定義。
- Advice(通知/增強):通知是指攔截到JoinPoint后所做的事情便是通知。
- 通知的類型:前置通知,后置通知,異常通知,最終通知和環(huán)繞通知。
- Introduction(引介):引介是一種特殊的通知在不修改類代碼的前提下,Introduction可以在運行期間為類動態(tài)的添加一些方法或者Field。
- Target(目標對象):代理的目標對象。
- Weaving(織入):指把增強應用到目標對象來創(chuàng)建新的代理對象的過程。
- Proxy(代理):一個類被AOP織入增強后,就產(chǎn)生了一個結(jié)果代理類。
- Aspect(切面):是切入點和通知(引介)的結(jié)合。
3. AOP的使用
- 在Maven中引入Spring的AOP坐標
<!-- AOP坐標 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
- 在spring的配置文件中導入約束
- spring-AOP的約束頭信息
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns: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"> </beans>- spring-AOP的配置
- execution表達式
- 在AOP中使用execution表達式首先要導入表達式的坐標
<!-- AOP使用execution表達式坐標 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency>- execution表達式的使用方法
- 普通的表達式:權限修飾符 返回值類型 包名.包名...類名.方法名(參數(shù)列表)
- 標準表達式寫法:
public void com.hsh.study.service.impl.AccountServiceImpl.findAll() - 權限修飾符可以省略,返回值類型使用通配符代替:
* com.hsh.study.service.impl.AccountServiceImpl.findAll() - 包名使用通配符代替,幾個類幾個通配符:
* *.*.*.*.*.AccountServiceImpl.findAll() - 包名可以使用..表示當前包及其子包:
* *..AccountServiceImpl.findAll() - 方法名和類名都可以使用通配符:
* *..*.*() - 參數(shù)列表:
- 基本數(shù)據(jù)類型直接寫名稱:int
- 引用數(shù)據(jù)類型寫包名.類名的方式 java.lang.String
- 使用..表示有無參數(shù)均可,有參數(shù)可以為任意類型
- 全通配寫法:
execution(* *..*.*(..))
- AOP的具體配置
- 基本配置
<!-- 首先導入對應的bean --> <bean id="accountService" class="..."></bean> <!-- 導入配置方法的bean --> <bean id="logger" class="..." ></bean> <!-- 配置aop --> <aop:config> <!-- 配置切入點表達式<aop:pointcut /> id:指定表達式唯一標識 expression:指定表達式內(nèi)容 此標簽寫在<aop:aspect>內(nèi)部表示當前切面可用 此標簽寫在<aop:aspect>外部表示所有切面可用 --> <aop:pointcut id="pt1" expression="execution(* *..*.*(..))"/> <!-- 配置切面 --> <aop:aspect id="logAdvice" ref="transactionManager"> <!-- 配置前置通知" --> <aop:before method="beforePringLog" pointcut-ref="pt1" /> <!-- 配置后置通知 --> <aop:after-returning method="afterReturningPringLog" pointcut-ref="pt1" /> <!-- 配置異常通知 --> <aop:after-throwing method="afterThrowingPringLog" pointcut-ref="pt1" /> <!-- 配置最終通知 --> <aop:after method="afterPringLog" pointcut-ref="pt1" /> <!-- 配置環(huán)繞通知 --> <!-- <aop:around method="aroundPrintLog" pointcut-ref="pt1" />--> </aop:aspect> </aop:config>- 環(huán)繞通知配置
- 類中
public Object aroundPrintLog(ProceedingJoinPoint pjp){ Object resultValue = null; try{ // 得到方法執(zhí)行所需的參數(shù) Object[] args = pjp.getArgs(); // 執(zhí)行前置通知 System.out.println("前置通知"); // 執(zhí)行切入點方法 resultValue = pjp.proceed(args); // 執(zhí)行后置通知 System.out.println("后置通知"); return resultValue; }catch (Throwable e){ System.out.println("異常通知"); throw new RuntimeException(e); }finally { System.out.println("最終通知"); }
* 配置文件中
<bean id="accountService" class="..."></bean>
<bean id="logger" class="..." ></bean>
<aop:config>
<aop:pointcut id="pt1" expression="execution(* ...*(..))"/>
<aop:aspect id="logAdvice" ref="transactionManager">
</aop:aspect>
</aop:config>
```
- execution表達式
- 基于注解的Spring-AOP配置
- 在配置文件中導入AOP相關的約束頭信息
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 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/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"> </bean>- 指定Spring注解所需掃描的包,并開啟對注解AOP的支持
<!-- 需要掃描的包 --> <context:component-scan base-package="com.hsh.study"></context:component-scan> <!-- 開啟對注解AOP的支持 --> <aop:aspectj-autoproxy/>- 針對各實體類進行注解配置
- 在通知類中使用注解進行通知
- 在通知類上使用@Aspect注釋表明這是一個切面類
@Component("logger") @Aspect public class Logger { }- 切入點表達式注解
- @Aspect
- 作用:指定切入點表達式
- value:指定表達式的內(nèi)容
- @Aspect
- 針對AOP的四種狀態(tài)對應的注釋
- @Before
- 作用:將當前方法看作前置通知
- value:用于指定切入點表達式,還可以指定切入點表達式的引用
- @After-Returning
- 作用:將當前方法看作后置通知
- value:用于指定切入點表達式,還可以指定切入點表達式的引用
- @AfterThrowing
- 作用:將當前方法看作異常通知
- value:同上
- @After
- 作用:將當前方法看作最終通知
- value:同上
- @Around
- 作用:將當前方法看作環(huán)繞通知
- value:同上
@Around("pt1()") //引入切入點時括號要加上
- @Before
- 使用純注解配置時
- 在配置類上使用注解:@EnableAspectJAutoProxy即可
6. Spring中的JdbcTemplate
1. 概念
JdbcTemplate是Spring框架中提供的一個對象,是對原始JDBC API的簡單封裝。其中提供了許多的操作模板類。
- 操作關系數(shù)據(jù)庫:JdbcTemplate、HibernateTemplate
- 操作nosql數(shù)據(jù):RedisTemplate
- 操作消息隊列:JmsTemplate
2. 基于Spring的JdbcTemplate所必須的jar包或坐標
<!-- JdbcTemplate坐標 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 事務控制坐標 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
3. Spring-JdbcTemplate的使用
1. 編寫Spring的配置文件
- 導入spring-xml的約束頭信息
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
- 配置相應的數(shù)據(jù)源
<!-- 使用c3p0數(shù)據(jù)庫連接池時 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?useSSL=false" />
<property name="user" value="root" />
<property name="password" value="123" />
</bean>
<!-- 使用Spring內(nèi)置數(shù)據(jù)源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClass}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 使用外部配置文件的Spring內(nèi)置數(shù)據(jù)源時 -->
<!-- 引入外部配置文件 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties"></property>
</bean>
<!-- 使用注解引入外部文件時 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
2. 使用JdbcTemplate實現(xiàn)增刪改查操作
- 在配置文件中配置JdbcTemplate
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
- 可以直接使用JdbcTemplate實現(xiàn)增刪改查操作
3. 在Dao中使用JdbcTemplate
- 不通過繼承JdbcDaoSupport來簡化獲取JdbcTemplate的操作(用于xml配置和注解配置中)
- 在Dao實現(xiàn)類中添加jdbcTemplate屬性
private JdbcTemplate jdbcTemplate; // 添加注入 public void setJdbcTemplate(JdbcTemplate jdbcTemplate){ this.jdbcTemplate = jdbcTemplate; }- 在配置文件中添加Dao實現(xiàn)類的bean
<bean id="accountDao" class="...AccountDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate" /> </bean>- 使用jdbcTemplate屬性執(zhí)行數(shù)據(jù)庫操作
- 通過繼承JdbcDaoSupport來簡化獲取JdbcTemplate的操作(僅限于xml配置中)
- 在Dao實體類中繼承JdbcDaoSupport,不用添加jdbcTemplate屬性
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { }- 在配置文件中添加Dao實體類的Bean
<bean id="accountDao" class="..AccountDaoImpl"> <!-- 可以配置JdbcTemplate也可以配置DataSource 配置一個即可--> <!-- 配置JdbcTempldate --> <property name="jdbcTemplate" ref="jdbcTemplate" /> <!-- 配置DataSource --> <property name="dataSource" ref="dataSource" /> </bean>- 調(diào)用父類的方法super.getJdbcTemplate()來執(zhí)行相應的數(shù)據(jù)庫操作
7. Spring中的事務控制
-
Spring中關于事務控制的API
1. PlatformTransactionManager(Spring中的事務管理器)
PlatformTransactionManager接口提供了事務操作的方法,其中包含3各具體的操作:
- 獲取事務狀態(tài)信息
TransactionStatus getTransaction(TransactionDefinition definition) - 提交事務
void commit(TransactionStatus status) - 回滾事務
void rollback(TransactionStatus status)
真正使用的是PlatformTransactionManager接口的實體類: - org.springframework.jdbc.datasource.==DataSourceTransactionManager== 使用Spring JDBC或iBatis進行持久化數(shù)據(jù)時使用
- org.springframework.orm.hibernate5.==HibernateTransactionManager== 使用Hibernate版本進行持久化數(shù)據(jù)時使用
2. TransactionDefinition
TransactionDefinition是事務的信息定義對象,方法如下:
- 獲取事務對象名稱:
String getName() - 獲取事務隔離級別:
int getIsolationLevel()- 默認級別,歸屬以下某一種:ISOLATION_DEFAULT
- 可以讀取未提交數(shù)據(jù):ISOLATION_READ_UNCOMMITTED
- 只能讀取已提交數(shù)據(jù),解決臟讀問題(Oracle默認級別):ISOLATION_READ_COMMITTED
- 是否讀取其他事務提交修改后的數(shù)據(jù),解決不可重復讀問題(MySql默認級別):ISOLATION_REPEATABLE_READ
- 是否讀取其他事務提交添加后的數(shù)據(jù),解決幻影讀的問題:ISOLATION_SERIALIZABLE
- 獲取事務傳播途徑:
int getPropagationBehavior()- REQUIRED:如果當前沒有事務,就新建一個事務,如果已經(jīng)存在一個事務中,加入到該事務中。(默認值)
- SUPPORTS:支持當前事務,如果當前沒有事務,就通過非事務方式執(zhí)行
- MANDATORY:使用當前的事務,如果當前沒有事務則拋異常
- REQUERS_NEW:新建事務,如果當前在事務中,則將當前事務掛起
- NOT_SUPPORTED:以非事務的方式執(zhí)行操作,如果當前存在事務,則將當前事務掛起
- NEVER:以非事務的方式運行,如果當前存在事務,拋出異常
- NESTED:如果當前存在事務,則在嵌套事務內(nèi)執(zhí)行。如果當前沒有事務,則執(zhí)行REQUIRED類似的操作
- 獲取事務超時時間:
int getTimeout()- 默認值為-1,沒有超時限制。如果有則以秒為單位進行設置
- 獲取事務是否只讀:
boolean isReadOnly()- 只讀事務:查詢
- 讀寫型事務:增刪改
3. TransactionStatus
TransactionStatus接口提供某個時間點上事務對象的狀態(tài)信息,其中有6個具體的操作
- 刷新事務:
void flush() - 獲取是否存在存儲點:
boolean hasSavepoint() - 獲取事務是否完成:
boolean isCompleted() - 獲取事務是否為新的事務:
boolean isNewTransaction() - 獲取事務是否回滾:
boolean isRollbackOnly() - 設置事務回滾:
void setRollbackOnly()
-
Spring中事務控制的具體操作
1. 導入Spring事務控制的相關坐標
<!-- JDBC相關坐標 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 事務控制相關坐標 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
2. 在配置文件中導入aop和tx約束
<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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
</beans>
3. 配置Bean、數(shù)據(jù)源、事務控制、AOP
<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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
<!-- 配置service -->
<bean id="accountService" class="com.hsh.study.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置DAO -->
<bean id="accountDao" class="com.hsh.study.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置數(shù)據(jù)源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.classDriver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 配置數(shù)據(jù)庫配置文件 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties" />
</bean>
<!-- 配置事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 導入數(shù)據(jù)源 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 事務的配置 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
<!-- 配置事務的屬性 -->
<tx:attributes>
<tx:method name="*" read-only="false" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<!-- AOP的配置 -->
<aop:config>
<!-- 配置切入點表達式 -->
<aop:pointcut id="pt1" expression="execution(* com.hsh.study.service.impl.*.*(..))"/>
<!-- 配置AOP -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>
</beans>
-
Spring中事務控制的具體操作(使用注解開發(fā))
- 主注解類的配置
// 聲明式配置類
@Configuration
// 告訴Spring要掃描的包
@ComponentScan("com.hsh.study")
// 導入的另一個配置類
@Import(JdbcConfig.class)
// 配置文件的聲明
@PropertySource("classpath:jdbc.properties")
// 開啟注解AOP支持
@EnableAspectJAutoProxy
// 開啟注解事務支持
@EnableTransactionManagement
public class SpringConfig {
}
- Jdbc注解類的配置
public class JdbcConfig {
// @Value:表示引入配置文件中的值
@Value("${jdbc.classDriver}")
private String classDriver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
// 聲明jdbcTemplate
@Bean("jdbcTemplate")
public JdbcTemplate createJdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
/**
* 創(chuàng)建事務管理器
* Bean注解不配值時,默認類名首字母小寫如DataSourceTransactionManager變成dataSourceTransactionManager
*/
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
// 聲明數(shù)據(jù)源
@Bean("dataSource")
public DataSource createDataSource(){
try {
DriverManagerDataSource manager = new DriverManagerDataSource();
manager.setDriverClassName(classDriver);
manager.setUrl(url);
manager.setUsername(username);
manager.setPassword(password);
return manager;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
- 業(yè)務層實現(xiàn)
@Service("accountService")
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
@Transactional(readOnly = false,propagation = Propagation.REQUIRED)
public void transfer(String sourceName, String targetName, Double money) {
// 獲取兩個用戶
Account source = accountDao.findByName(sourceName);
Account target = accountDao.findByName(targetName);
// 執(zhí)行轉(zhuǎn)賬操作
source.setMoney(source.getMoney()-money);
target.setMoney(target.getMoney()+money);
// 執(zhí)行更新操作
accountDao.update(source);
accountDao.update(target);
}
- 持久層實現(xiàn)
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Account findById(Integer accountId) {
List<Account> accounts = jdbcTemplate.query("select * from account where id = ?",
new BeanPropertyRowMapper<Account>(Account.class), accountId);
return accounts.isEmpty()?null:accounts.get(0);
}
@Override
public Account findByName(String accountName) {
List<Account> accounts = jdbcTemplate.query("select * from account where name = ?",
new BeanPropertyRowMapper<Account>(Account.class), accountName);
if (accounts.isEmpty()){
return null;
}
if (accounts.size() > 1){
throw new RuntimeException("結(jié)果集不唯一");
}
return accounts.get(0);
}
@Override
public void update(Account account) {
jdbcTemplate.update("update account set money = ? where id = ?",
account.getMoney(),account.getId());
}
}