<!doctype html>Spring框架html
Spring框架第一章 spring的使用spring管理bean的做用域(scope)初始化和销毁如何作到对象初始化的时候作某件事情?init例子destroy例子代理类,返回代理类的实例小常识BeanFactory与ApplicationContext的不一样spring-core,spring-beans,spring-web各个依赖里面大体有哪些功能?配置bean的时候,其lazy-init是什么意思?理解prototype做用域时,为何销毁方法不生效?第二章 依赖注入了解控制反转构造函数注入与属性注入注入的类型命名空间父子beanautowireautowire的理解第三章 与其它框架整合与dbutil+druid整合与mybatis+druid整合什么是 MyBatis-Spring?第一种(有mybatis-config文件)第二方法(没有mybatis配置文件)使用spring-web第四章 aopaop的理解案例案例讲解aop的几个术语:通知类型获取目标方法的参数aop排序切点表达式proxy-target-class的用法aop的通知器(advisor)aop代理实现通知器第五章 spring事务管理器事务管理局部事物 vs. 全局事务事务管理器案例事务与mybatis整合java
spring是现现在最流行的框架之一,有人说没学过spring框架那么就没有资格说本身学过java。建立一个简单的spring,须要哪些条件。node
一、添加一个spring依赖,固然若是是maven项目的话只须要添加仓库地址便可,好比:mysql
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
二、建立一个spring.xml配置文件(元数据),在此文件经过一个个的bean来告诉spring管理哪些类,这个类必须是能够实例化的,因此不能是接口,抽象类等。jquery
若是你不肯定spring.xml配置文件基础格式是什么样的,没关系的,咱们能够经过idea的快捷键来建立一个spring.xml的配置,作法以下:git
xxxxxxxxxx
选中文件夹右键 ————> new ------>XML Configuration File ------->spring Config
若是你的idea没有这个功能,你也别慌,你能够把下面的配置代码复制粘贴到你的配置文件里:github
xxxxxxxxxx
<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的xml文件通常命名为 applicationContext.xmlweb
三、在xml里面写上你要管理的类,好比我有一个叫UserInfo的类,那么我在spring的xml就应该这样配置:ajax
xxxxxxxxxx
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置以下-->
<bean id="userInfo" class="com.entity.UserInfo"/>
</beans>
四、建立一个类,在类方法里面建立一个ApplicationContext对象,通常是它子类的是实例(ClassPathXmlApplicationContext),实例化对象的时候传递配置文件名。spring
五、获取管理的对象,用getBean(id:配置文件bean元素的id)获取这个id管理的bean。
用法以下:
xxxxxxxxxx
public class Main{
public static void main(String[] args){
//获取ApplicationContext对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//经过getBean()方法获取对象
UserInfo userInfo = context.getBean("userInfo",UserInfo.class);
}
}
六、哪些类应该被spring管理
dao、service、部分工具类能够被spring管理,可是servlet是必定不能被spring管理的。实体类,通常不被spring管理,从技术的角度来看是能够被spring管理的。
做用域就是:你的脸有多大,你说的话就有多大的效果。
在spring中做用域其实指的就是被spring管理的bean的存活时间。
spring的做用域有4种:
一、prototype(原型):每次getBean的时候都会从新建立一个新的对象。
二、singleton(单例):此做用域类型的bean,在spring容器启动时已经被建立,每次getBean的时候,直接从容器中获取便可。
三、requet(请求):request与session做用域都是在web环境下才有效,被spring管理的bean它的生存期就是在一个完整的请求周期里面。
四、session(会话):就是bean的做用范围是一个完整的session,不一样人有不一样的会话,互相不干扰。
默认状况下,被spring管理的bean的做用域是singleton,用的最多的就是一、2。singleton做用域的bean,其实例是在容器启动时建立,之后就再也不建立。prototype做用的bean是在每次getBean的时候都反复建立bean的实例出来。
如何建立一个原型的bean:
xxxxxxxxxx
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置以下-->
<bean id="userInfo" class="com.entity.UserInfo" scope="prototype"/>
</beans>
若是没有spring框架,写代码的一个规范是构造函数里面不要写很复杂的代码,若是是这种状况,建议是把这些代码抽取到一个独立的方法中去,这个方法通常叫init这种名字。按照之前的写法就必须在构造函数中调用这个init方法。
有了spring以后,就能够直接设置一下就会让init被执行,init执行就是在对象建立出来后自动获得执行。
下面写一个spring管理bean初始化的例子:
好比有一个UserInfo类:
xxxxxxxxxx
public class UserInfo{
public void init(){
System.out.pringln("我是初始化方法");
}
}
在spring的xml文件里就须要对这个init方法的调用。能够两个地方设置
第一种是在这个被spring管理的bean里设置,好比:
xxxxxxxxxx
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置以下-->
<bean id="userInfo" class="com.entity.UserInfo" init-method="init"/>
</beans>
这个配置只在当前bean生效。
还有一种是在当前spring的xml管理的bean生效,是在beans里面配置一个default-init-method="init":
xxxxxxxxxx
<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"
default-init-method="init">
<!--配置以下-->
<bean id="userInfo" class="com.entity.UserInfo"/>
</beans>
若是做用域是singleton,init只会执行一次,prototype的话就会反复调用。
设置了spring的xml配置后,那么应该怎么用呢?
xxxxxxxxxx
public class Main{
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserInfo userInfo = context.getBean("userInfo");
}
}
这样它就自动调用了UserInfo类里面的init方法
销毁方法(destroy)跟init差很少的用法。它是在ApplicationContext对象关闭生效的。
好比:
xxxxxxxxxx
public class UserInfo{
public void init(){
System.out.pringln("我是初始化方法");
}
public void destroy(){
System.out.pringln("我是销毁的方法");
}
}
在spring的xml配置跟init同样也是有两种配法:
xxxxxxxxxx
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置以下-->
<bean id="userInfo" class="com.entity.UserInfo" init-method="init" destroy-method="destroy"/>
</beans>
还有一种整个spring配置文件管理的bean生效的配置
xxxxxxxxxx
<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"
default-init-method="init" default-destroy-method="destroy">
<!--配置以下-->
<bean id="userInfo" class="com.entity.UserInfo"/>
</beans>
用法就跟init不同了。它须要关闭ApplicationContext对象的时候才会调用destroy的方法。可是ApplicationContext对象里面是没有close方法的,可是它子类有。因此关闭的时候就须要类型强转。
xxxxxxxxxx
public class Main{
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserInfo userInfo = context.getBean("userInfo");
//执行下面这句话的时候调用销毁的方法
((ConfigurableApplicationContext)context).close();
}
}
注意:若是全局设置后,spring管理的全部bean的初始化与销毁都不须要在进行设置,若是某一个bean也设置了init-method或者destroy-method就会覆盖全局的设置。
除了上面的方法外还能够实现接口进行初始化和销毁。
实习的接口有两个:一个是初始化的接口InitializingBean,和销毁的接口DisposableBean
实现这两个接口的方法:
afterPropertiesSet():这个方法名取名叫:"在属性设置完毕以后",其意思就是此类中setter方法被调用后才会调用这个初始化的方法。
destroy():销毁时运行。
当调用一个类的方法的时候,返回另外一个类的实例,这种就是叫作代理类。
举个例子:
当有个A类,它里面有个update方法:
xxxxxxxxxx
public class A{
public void update(){
System.out.println("我是A类的update方法");
}
}
下面建立一个A类的代理类B类,有个方法返回一个A的实例:
xxxxxxxxxx
public class B{
public void update(){
System.out.println("我是A类的update方法");
}
public static A createA(){
return new A();
}
}
在配置文件里配置以下:
xxxxxxxxxx
<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"
default-init-method="init" default-destroy-method="destroy">
<!--配置以下-->
<bean id="b" class="com.entity.B" factory-method="createA"/>
</beans>
上面这个配置方法createA方法必须是静态的,若是不是静态的,那么应该先建立一个B的实例出来,而后调用它的方法,好比:
xxxxxxxxxx
<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"
default-init-method="init" default-destroy-method="destroy">
<!--配置以下-->
<bean id="b" class="com.entity.B"/>
<bean id="createA" factory-bean="b" factory-method="createA" />
</beans>
那么在用的时候:
xxxxxxxxxx
public class Main{
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
A a = context.getBean("createA");
}
}
它就成功的返回了A的实例出来。
若是不想太依赖spring框架,那么能够实现接口来实现代理方法:
public class MyFactoryBean implements FactoryBean<A> {
/**
* 这个方法用来建立一个对象
* @return
* @throws Exception
*/
public A getObject() throws Exception {
return new A();
}
/**
* 这个方法是用来代表此工厂Bean建立出来的对象的class
* @return
*/
public Class<?> getObjectType() {
return A.class;
}
/**
* 这个方法代表此工厂Bean建立出来的对象,在spinrg管理下的做用域
* true表示是singleton
* @return
*/
public boolean isSingleton() {
return true;
}
}
上面这个配置方式返回的是A这个类的实例,缘由是由于MyFactoryBean实现了FactoryBean这个接口,若是没有这个实现,那么返回的就是MyFactoryBean这个实例自己。
这两个类是spring框架中2个表明性的容器类型(接口)功能上BeanFactory主要有获取实例的功能(getBean)
ApplicationContext在BeanFactory基础上进行了功能的加强,好比,读取消息(作国际化),发布事件,从新加载资源
spring-core:提供spring框架的一些核心的基础功能,好比动态代理的建立,读取文件等
spring-beans:提供spring框架的核心的bean管理,ioc,aop等能力
spring-web:spring框架与servlet技术整合使用
lazy-init:延迟初始化的意思。 spring容器启动时,默认是立刻实例化scope为singleton对象,这种作法有一个缺点,就是若是spring容器管理的bean过大,就会致使性能低,因此就提供了一个选项lazy-init,设置为true的时候,容器启动的时候,这个设置的bean不会当即实例化,而会等到第一次调用getBean获取实例对象是才实例化。
这个lazy-init属性对scope为prototype的bean无效.
由于prototye就是每次getBean的时候临时建立一个新的对象 ,此对象不会被spring所引用,因此就没法对其生命周期进行管理, 因此就不会有销毁方法被调用。
singleton的bean,因为在容器启动时会建立对象出来,并且 会被spring所引用,管理,因此当spring关闭时,会调用其销毁方法
IOC:inverse of control 控制反转
DI:dependency injection 依赖注入
ioc==DI ,网上有些人认为ioc跟DI不是一回事。
假设有两个类UserService、UserDao,不反转的状况,意思是:
UserService service = new UserService();
service.setDao(new UserDao);
由于上面的UserService 类须要UserDao,给其添加UserDao实例的过程是由本身写代码控制,就是一切都在自己的掌控中,这种状况就叫作不反转。
反转的就是:
context.getBean("UserService",UserService.class);
上面的代码执行完毕以后,UserService已经帮其设置了UserDao。
依赖注入:全部被spring管理的bean,当它依赖一个被其它spring管理的bean的时候,spring负责把其注入进来。
spring管理的依赖注入的形式有三种:
一、构造函数的注入。
二、属性注入(setter)。
三、接口注入(不推荐使用)。
有个类:
xxxxxxxxxx
public class DbConfig {
private String url;
private String username;
private String password;
private String driverClassname;
public DbConfig(String url, String username, String password) {
this.url = url;
this.username = username;
this.password = password;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDriverClassname() {
return driverClassname;
}
public void setDriverClassname(String driverClassname) {
this.driverClassname = driverClassname;
}
public String toString() {
return "DbConfig{" +
"url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
", driverClassname='" + driverClassname + '\'' +
'}';
}
}
上面这个类,既有属性又有构造函数
xxxxxxxxxx
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 下面的配置顺序与构造函数是一一对应,因此再也不须要额外的设置-->
<!--<bean id="dbConfig" class="inject.basic.DbConfig">
<constructor-arg value="url.."/>
<constructor-arg value="root"/>
<constructor-arg value="pwd"/>
<property name="driverClassname" value="driver"/>
</bean>-->
<!--
当下面的构造设置顺序与想复制的类的构造函数顺序不一致时
不能仅仅只是设置一个value,还须要额外的信息告诉
spring,谁赋值给谁
-->
<bean id="dbConfig" class="inject.basic.DbConfig" >
<!--原本这个root应该是赋值给url,但由于加了name=username
因此就把root 赋值给了构造函数的第二个参数
-->
<constructor-arg value="root" name="username" />
<constructor-arg value="pwd" name="password"/>
<constructor-arg value="url.." name="url"/>
<property name="driverClassname" value="driver"/>
</bean>
<!--<bean id="dbConfig" class="inject.basic.DbConfig">
<constructor-arg value="root" index="1" />
<constructor-arg value="pwd" index="2"/>
<constructor-arg value="url.." index="0"/>
<property name="driverClassname" value="driver"/>
</bean>-->
<!--配置时,通常是java类型对应一样名字的配置元素
好比List集合,你就用list来配置,Set集合就用set元素配置
集合类型与配置元素类型都会起做用:意思是Set集合类型的元素里面是不能重复
若是你用List元素配置了重复的内容,但Set集合仍然不会有重复
若是你是List集合,但用set元素来配置,就可让此List集合内部没有重复
-->
<bean id="addr" class="inject.basic.Address">
<property name="province" value="gd"></property>
<property name="city" value="zhuhai"/>
</bean>
<bean id="userInfo" class="inject.basic.UserInfo">
<property name="name" value="aaaa"/>
<!-- <property name="address" ref="addr"/>-->
<!--下面的配置方式叫作inner bean(内部bean)
只是给address属性使用,没法经过getBean方式获得这个对象
-->
<property name="address" >
<bean class="inject.basic.Address" />
</property>
<property name="hobbyList" >
<list>
<value>football</value>
<value>basketball</value>
<value>basketball</value>
</list>
</property>
<property name="xueLi">
<set>
<value>chu zhong</value>
<value>gao zhong</value>
<value>gao zhong</value>
</set>
</property>
<property name="xueFen">
<map>
<entry key="yuwen" value="95"/>
<entry key="shuxue" value="100"/>
</map>
</property>
<!--若是用props配置,值只能是字符串类型-->
<property name="properties">
<props>
<prop key="javaT">111</prop>
<prop key="netT">yongguang</prop>
</props>
</property>
<property name="addressList">
<list>
<ref bean="addr"/>
<bean class="inject.basic.Address">
<property name="city" value="ganzhou"/>
<property name="province" value="jiangxi"/>
</bean>
</list>
</property>
</bean>
</beans>
因此说,构造函数的模糊性解决办法有三个:
一、根据name来匹配
二、根据index来匹配
三、根据type来匹配
你们都知道,在java里面是有不少类型的。那么在配置的时候应该怎么来配置:
一、普通类型:能够是用value来匹配:
xxxxxxxxxx
<property name="name" value="aaaa"/>
二、对象类型。若是是本身定义的类,那么可使用ref或者使用内部bean的方式。
xxxxxxxxxx
<property name="address" ref="b"/>
xxxxxxxxxx
<property name="address">
<bean class="com.entity.Address"></bean>
</property>
三、list属性
xxxxxxxxxx
<property name="list">
<list>
<value>aaa</value>
</list>
</property>
四、map
xxxxxxxxxx
<property name="list">
<map>
<entry key="yuwen" value="95"/>
<entry key="shuxue" value="100"/>
</map>
</property>
五、set
xxxxxxxxxx
<property name="list">
<set>
<value>bbb</value>
</set>
</property>
set跟list的区别。list的值能够重复,set不能够重复
五、properties
xxxxxxxxxx
<!--若是用props配置,值只能是字符串类型-->
<property name="properties">
<props>
<prop key="javaT">111</prop>
<prop key="netT">yongguang</prop>
</props>
</property>
ref:references的缩写,引用的意思,它的值通常是另外一个被spring管理的bean的id。
六、若是类型是String的类型,它想赋值为null。那么直接设置为”“是表示空字符串,而不是null。
xxxxxxxxxx
<property name="name">
<null></null>
</property>
namespace:命名空间
在spring里命名空间有两个:分别是
p:表明着属性的意思
c:表明着构造函数的意思
命名空间主要是让咱们少写一些代码。
用法以下:
x
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<!--须要引入这两个地址-->
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource"
class="inject.namespace.MyDataSource" p:username="sa" c:url="url--" >
<property name="password" >
<null></null>
</property>
</bean>
<bean id="factory" class="inject.namespace.MySqlFactory"
p:dataSource-ref="dataSource"
c:_0-ref="dataSource">
</bean>
</beans>
引入了
xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
就能够在配置里用p跟c了。
在子bean里面设置了parent属性,只是表名在xml配置中会有继承配置的关系,并不要求实际的java类有继承关系。
当一个bean设置abstract为true的时候,意思就是告诉spring不要实例化这个bean出来跟实际的java类是不是抽象无关。通常状况若是一个bean的主要的做用是给子bean继承用,其class能够不设置。
好比:
xxxxxxxxxx
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--在子bean里面设置了parent属性,只是代表在xml配置中会有继承配置的关系
并不要求实际的java类有继承关系
当一个bean设置abstract为true的时候,意思就是告诉spring不要实例化这个bean出来
跟实际的java类是不是抽象无关
通常状况若是一个bean的主要的做用是给子bean继承用,其class能够不设置
-->
<bean id="myParent" abstract="true">
<property name="p1" value="p1"/>
<property name="p2" value="p2"/>
</bean>
<bean id="childOne"
class="inject.parentchild.ChildOne" parent="myParent">
<property name="childOne" value="childOne"/>
</bean>
</beans>
xxxxxxxxxx
关于自动装配(autowire)的理解
1.启动spring容器
2.扫描xml文件.
3.spring就知道全部被它管理的bean
4.每一个bean,spring经过反射是否是都知道
其类型,以及构造函数,以及全部的属性(property)
5.由于bean的做用域是singleton,因此spring要建立出
对象,建立对象包含实例化和初始化
5.1 实例化就是调用构造函数,若是构造函数有依赖
,spring就会尝试解决掉这个依赖是什么东西
5.2 初始化:全部的属性,spring都尝试帮你注入值进来
6.spring尝试给某个bean全部依赖的东西,帮你注入进来
7.若是你配置了自动装配,那么spring就帮你找一个合适的
8.如何找呢? 须要依据自动装配类型的设置,autowire的值
8.1 byType:由于你的Service类依赖的类型是UserDao
spring又知道全部被它管理的UserDao bean配置只有一个
因此就自动帮你注入这一个.
若是找到多个:报错
8.2 byName:找到service类里面有一个属性名叫dao
找全部被管理bean的名字为dao的,就帮你注入.
若是找到的bean id值为dao的类型不符合要求,也会报错
若是有多个bean符合自动装配
能够经过在全部符合条件的被装配bean上进行设置来解决
1.在想被注入的bean上设置primary=true,就表示用这个
2. 在不想被注入的bean上设置autowire-candidate=false
还能够在beans这个根元素上,配置default-autowire-type来设定
一个全局的自动装配类型,这样就不须要在每个bean上进行设置了
beans上的default-autowire-candidates =设置做为候选baen的名字模式
多个之间用逗号分隔,好比*dao这个名字,意思就是以dao结尾的bean 的名字
xml配置:
xxxxxxxxxx
<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"
default-autowire-candidates="*dao" default-autowire="byType">
<bean id="dao" class="autowire.UserDaoImpl" ></bean>
<bean id="userDao2" class="autowire.UserDaoImpl2" ></bean>
<bean id="userService"
class="autowire.UserServiceImpl"
>
</bean>
</beans>
在 default-autowire-candidates="*dao" 能够写多个值。
既然是和dbutil、druid整合,那么它们的依赖确定要导入项目里来。
在这里dbutil是apache下的dbutil,你能够在中央仓库找这个依赖。若是你使用的是maven框架写项目的话就加入以下配置:
xxxxxxxxxx
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.7</version>
</dependency>
druid是阿里的druid。一样若是你要这个jar包,你也能够去中央仓库去找。你使用的是maven框架写项目的话就在配置里加入:
xxxxxxxxxx
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.19</version>
</dependency>
弄好依赖就开始写项目了。咱们最后的效果是在浏览器里显示数据,简单来讲就是使用spring+dbutils+druid整合写一个简单的web项目。
一开始确定要写好项目结构,这里我就默认建立好了。
实体类:Employee
xxxxxxxxxx
public class Employee {
private Integer id;
private String username;
private BigDecimal salary;
private Integer gender;
private Integer did;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public BigDecimal getSalary() {
return salary;
}
public void setSalary(BigDecimal salary) {
this.salary = salary;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Integer getDid() {
return did;
}
public void setDid(Integer did) {
this.did = did;
}
public String toString() {
return "Employee{" +
"id=" + id +
", username='" + username + '\'' +
", salary=" + salary +
", gender=" + gender +
", did=" + did +
'}';
}
}
dao层:EmployeeDao
xxxxxxxxxx
public class EmployeeDaoImpl {
private QueryRunner queryRunner ;
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
public Employee getById() {
String sql = "select id,username from employee where id = 1";
BeanHandler<Employee> handler = new BeanHandler<>(Employee.class);
Employee employee = null;
try {
employee = queryRunner.query(sql,handler);
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("query failed...");
}
return employee;
}
}
service层:EmployeeService
xxxxxxxxxx
public class EmployeeServiceImpl {
private EmployeeDao dao;
public void setDao(EmployeeDao dao) {
this.dao = dao;
}
public Employee getById() {
return dao.getById();
}
}
web层(放置servlet、jsp文件的):EmployeeServlet类 、index.jsp
xxxxxxxxxx
"/index") (
public class EmployeeController extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmployeeService service = context.getBean("employeeService", EmployeeService.class);
Employee employee = service.getById();
req.setAttribute("emp", employee);
req.getRequestDispatcher("index.jsp").forward(req, resp);
}
}
若是使用这种方法的话,若是servlet多了,就会建立不少个spring容器对象,那么相应的也会致使被spring容器管理的bean不具有单例的状况,有多少个spring容器,单例的bean就会有多少个,全部spring容器应该只须要一个就能够了。
解决方法:
使用监听器,确保spring容器与web共存亡,且只建立一次。
这里我就建立一个listener包,用来存放监听器:
xxxxxxxxxx
public class ApplicationContextInstantiateListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext();
//获取在web.xml配置的context-param标签的值,也就是动态的实现调用spring配置文件
String configFille = servletContext.getInitParameter("configFile");
ApplicationContext context = new ClassPathXmlApplicationContext(configFille);
servletContext.setAttribute("context",context);
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
监听器的配置在WEB-INF/web.xml
xxxxxxxxxx
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>configFile</param-name>
<param-value>applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>com.spring.ApplicationContextInstantiateListener</listener-class>
</listener>
</web-app>
用的时候能够经过实现ApplicationContextAware接口的类来实现调用
xxxxxxxxxx
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getApplicationContext(){
return context;
}
public static <T> T getBean(String name,Class<T> clz){
return context.getBean(name,clz);
}
}
当调用一次ApplicationContext对象时,那么spring就会自动帮这个类注入一个ApplicationContext对象。固然这个类应该被spring管理。
用法:
xxxxxxxxxx
"/index") (
public class EmployeeServlet extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
EmployeeService service = ApplicationContextHolder.getBean("employeeService", EmployeeService.class);
Employee employee = service.getById();
req.setAttribute("emp", employee);
req.getRequestDispatcher("index.jsp").forward(req, resp);
}
}
配置文件:beans.xml
xxxxxxxxxx
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="password" value="root"></property>
<property name="username" value="root"/>
<property name="url" value="jdbc:mysql://localhost:3306/demo"/>
</bean>
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg ref="dataSource"></constructor-arg>
<constructor-arg value="true"/>
</bean>
<bean id="employeeDao" class="com.dao.impl.EmployeeDaoImpl">
<property name="queryRunner" ref="queryRunner"/>
</bean>
<bean id="employeeService" class="com.service.impl.EmployeeServiceImpl">
<property name="dao" ref="employeeDao"/>
</bean>
<!---->
<bean class="com.web.ApplicationContextHolder"/>
</beans>
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎全部的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
它所在的依赖配置:
xxxxxxxxxx
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
由于是mybatis与spring的整合,因此须要用到mybatis-spring依赖,
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将容许 MyBatis 参与到 Spring 的事务管理之中,建立映射器 mapper 和 SqlSession
并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的 DataAccessException
。最终,能够作到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。
xxxxxxxxxx
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
不是有了mybatis-spring就不用spring的依赖了,还须要用到spring的依赖:
spring-jdbc
xxxxxxxxxx
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
spring-context
xxxxxxxxxx
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
下面看下怎么去实现一个web项目
不使用mybatis配置文件与mapper映射文件,使用注解的方式:
跟上面的dbutils+spring+druid代码类似,惟一不一样的就是dao层的代码与applicationContext的配置:
dao层,只须要一个接口,跟mybatis的作法同样。还记得mybatis是用SqlSession对象的getMapper(Class clz)方法获取一个代理对象,接口就不须要有具体的实现类。
xxxxxxxxxx
public interface EmployeeDao {
"select id,username from employee where id=1") (
Employee getById();
}
由于须要用到SqlSession对象的getMapper()方法获取一个代理类,因此就开始就须要一个SqlSessionFactory对象。applicationContext里面就要怎么代理这个对象。若是咱们本身去实现的话就十分麻烦,庆幸mybatis-spring帮咱们作了这件事。
xxxxxxxxxx
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
有了SqlSessionFactory对象就能够建立一个dao层的代理类:
xxxxxxxxxx
<bean id="employeeDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!--注入要建立实现类的mapper接口就能够获得class信息-->
<property name="mapperInterface" value="com.dao.EmployeeDao"/>
<!--注入了SqlSessionFactory就能够获得SqlSession-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
最终applicationContext.xml的效果是:
xxxxxxxxxx
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="password" value="root"></property>
<property name="username" value="root"/>
<property name="url" value="jdbc:mysql://localhost:3306/demo"/>
</bean>
<!--
这个配置是一个FactoryBean,用来建立SqlSessionFactory
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--
这个配置用来给某一个特定的Mapper接口(dao)建立出它的实现类
sqlSession.getMapper(XxxDao.class);
若是项目中有多个Mapper接口,就须要多个MapperFactoryBean的配置
-->
<bean id="employeeDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!--注入要建立实现类的mapper接口就能够获得class信息-->
<property name="mapperInterface" value="com.dao.EmployeeDao"/>
<!--注入了SqlSessionFactory就能够获得SqlSession-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean id="employeeService" class="com.service.impl.EmployeeServiceImpl">
<property name="dao" ref="employeeDao"/>
</bean>
<!--配置此bean是由于此bean实现了ApplicationContextAware
被spring管理后会自动往此bean里面注入spring容器对象
-->
<bean class="com.spring.ApplicationContextHolder"/>
</beans>
这个方法如今都废弃不用了,由于若是有多个dao的话,就要有多个MapperFactoryBean配置。
下面说两种比较常见的整合方式:
保留mybatis配置文件与mapper映射文件。
mybatis配置文件:
xxxxxxxxxx
<configuration>
<mappers>
<mapper resource="employeeMapper.xml"/>
</mappers>
</configuration>
若是须要插件就要在mybatis配置文件里面配置:
xxxxxxxxxx
<configuration>
<!--这是是否使用分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="supportMethodsArguments" value="true"/>
</plugin>
</plugins>
<mappers>
<mapper resource="employeeMapper.xml"/>
</mappers>
</configuration>
至于mapper文件是用来写sql语句的:
xxxxxxxxxx
<mapper namespace="com.dao.EmployeeDao">
<select id="getById" resultType="com.entity.Employee">
select id,username from employee where id = 1
</select>
</mapper>
这样的配置的好处:遵循开闭原则。在dao层这里配置注解的方式,若是须要修改sql语句那么就须要修改代码。
applicationContext配置:
xxxxxxxxxx
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
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://mybatis.org/schema/mybatis-spring
http://mybatis.org/schema/mybatis-spring.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="password" value="root"></property>
<property name="username" value="root"/>
<property name="url" value="jdbc:mysql://localhost:3306/demo"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<mybatis:scan base-package="com.dao"/>
<bean id="employeeService" class="com.service.impl.EmployeeServiceImpl" autowire="byType"/>
</beans>
这里sqlSessionFactory里面是能够配置mybatis配置文件的,因此咱们能够给他一个资源名字。而后它就会自动去找这个mybatis配置文件。
上面提到的须要多个MapperFactoryBean配置,咱们能够用:
<mybatis:scan base-package="com.dao">
若是dao层有多个dao类,它都会代理出来准备你去用它。
那么问题来了,EmployeeService须要的dao,那么咱们怎么知道须要注入哪一个dao类。这里咱们就可使用自动注入的机制了:
若是咱们想不要mybatis的配置文件,那么咱们也是能够作到的,那么咱们就须要在spring的配置文件里配置mybatis所须要的配置:
xxxxxxxxxx
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
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://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/com.demo"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:employeeMapper.xml"/>
<property name="configuration">
<!--这个是配置sql日志的-->
<bean class="org.apache.ibatis.session.Configuration">
<property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"></property>
</bean>
</property>
<!--配置插件-->
<property name="plugins">
<list>
<!--分页的插件-->
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<value>
supportMethodsArguments=true
resonable=true
</value>
</property>
</bean>
</list>
</property>
</bean>
<mybatis:scan base-package="com.dao"/>
<bean id="employeeService" class="com.service.EmployeeService" autowire="byType"/>
</beans>
sqlSessionFactoryBean类下有不少属性,其中就有配置mybatis所须要的配置:
mapperLocations,经过这个属性能够告诉spring咱们用的是哪一个映射文件。咱们就能够把mybatis的配置丢掉了。
mybatis-config文件解析以后mybatis是用Configuration类型来表明(入口对象),因此咱们就能够配置是否使用各类mybatis的配置。
plugins:配置插件。
对了,说下以前咱们作过一个web项目,用到过sprng容器根据tomcat启动而建立且只建立一次的知识。
那时是实现了一个接口ApplicationContextAware接口,其实这个功能已经有人帮咱们作了,咱们只须要拿来用就行了。怎么拿呢,咱们能够添加一个spring-web的依赖。
xxxxxxxxxx
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
而后在web.xml配置监听器:
xxxxxxxxxx
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--
这个上下文参数的配置是给ContextLoaderListener监听器使用的
用来告诉监听器在建立spring容器对象时元数据文件的路径
若是不配置,默认就是在WEB-INF目录下找applicationContext.xml文件
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
tomcat一启动时,它就会加载spring容器。而后把applicationContext对象注入到WebApplicationContextUtil里,而后咱们就能够用它:
xxxxxxxxxx
public class EmployeeServlet extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(req.getServletContext());
EmployeeService service = context.getBean("employeeService", EmployeeService.class);
Employee employee = service.getById();
req.setAttribute("emp", employee);
req.getRequestDispatcher("index.jsp").forward(req, resp);
}
}
在这里我写个与pageHelper整合的web层的代码:
servlet:
xxxxxxxxxx
"/index") (
public class EmployeeServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher("WEB-INF/index.jsp").forward(req,resp);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
int pageNum = Integer.parseInt(req.getParameter("pageNum"));
int pageSize = Integer.parseInt(req.getParameter("pageSize"));
EmployeeService service = new EmployeeService();
List<Employee> emps = service.getEmpsByPageHelper(pageNum, pageSize);
PageInfo<Employee> pageInfo = new PageInfo<Employee>(emps,3);
Gson gson = new Gson();
String json = gson.toJson(pageInfo);
resp.getWriter().print(json);
}
}
jsp:
xxxxxxxxxx
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2019/10/14
Time: 9:55
To change this template use File | Settings | File Templates.
--%>
<%page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="${pageContext.request.contextPath}/jquery/jquery-3.4.1.js"></script>
</head>
<body>
<table>
<thead>
<tr>
<th>编号</th>
<th>姓名</th>
<th>工资</th>
<th>性别</th>
<th>部门编号</th>
</tr>
</thead>
<tbody class="tab-body">
</tbody>
</table>
<br>
<div id="data">
</div>
<script>
$.ajax({
url: "${pageContext.request.contextPath}/index",
method: "post",
data: {
pageNum: 1,
pageSize: 3
},
dataType: "json"
}).done(function (res) {
resultReduction(res)
c()
//事件
function c() {
$(".pageClick").click(function (e) {
var pageNum = $(this).val()
$.ajax({
url: "${pageContext.request.contextPath}/index",
method: "post",
data: {
pageNum: pageNum,
pageSize: 3
},
dataType: "json"
}).done(function (res) {
resultReduction(res)
c()
})
})
}
function resultReduction(res) {
$(".tab-body").html("")
$("#data").html("")
res.list.forEach(function (emp) {
var sql = "<tr><td>" + emp.id + "</td><td>" + emp.username + "</td><td>" +
emp.salary + "</td><td>" + emp.gender + "</td><td>" + emp.did + "</td></tr>";
$(".tab-body").append(sql);
})
var sql = "<button class='pageClick' value ='1'>首页</button>" +
"<button class='pageClick' value ='" + res.prePage + "'>上一页</button>" +
"<button class='pageClick' value ='" + res.nextPage + "'>下一页</button>" +
"<button class='pageClick' value ='" + res.pages + "'>尾页</button>";
res.navigatepageNums.forEach(function (num) {
sql += "<br><button class='pageClick' value ='" + num + "'>"+num+"</button>";
})
$("#data").append(sql);
}
})
</script>
</body>
</html>
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,经过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP能够对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度下降,提升程序的可重用性,同时提升了开发的效率。
在使用spring的aop以前应该导入一个依赖:
xxxxxxxxxx
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
aspectjweaver是spring的切入点表达式须要用的包。
下面经过一个案例实现aop的运用,主要的目标是在执行业务层的时候输出日志:
业务层EmployeeImpl类的代码:
xxxxxxxxxx
package com;
public class EmployeeServiceImpl {
public final void insert(){
System.out.println("开始插入(insert)-----");
}
public void update(){
System.out.println("开始更新(update)-----");
}
}
日志类的代码:
xxxxxxxxxx
public class LogImpl {
public void beforeXXX(){
System.out.println("开始执行*********");
}
}
applicationContext.xml的配置:
xxxxxxxxxx
<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 https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="log" class="com.LogImpl"/>
<bean id="employeeService" class="com.EmployeeServiceImpl"/>
<aop:config>
<!--这是切面类的配置-->
<aop:aspect id="logAspect" ref="log">
<!--这是切点表达式的配置-->
<aop:pointcut id="myPointcut" expression="execution(* com.*.*())"/>
<!--before的通知,主要用来肯定当前这个切面类的什么方法在何时切入到
切点表达式指向的方法里面.
-->
<aop:before method="beforeXXX" pointcut-ref="myPointcut" />
</aop:aspect>
</aop:config>
</beans>
而后就可使用:
xxxxxxxxxx
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmployeeServiceImpl service = context.getBean("employeeService", EmployeeServiceImpl.class);
service.insert();
service.update();
}
}
输出:
xxxxxxxxxx
开始执行*********
开始插入(insert)-----
开始执行*********
开始更新(update)-----
Aspect:切面 pointcut:切点
xxxxxxxxxx
1.切面(aspect):案例中指的就是LogImpl这个类
2.切点(pointcut):它也称为切点表达式,目的是描述符合条件的方法
3.目标(target):就是案例中的EmployeeServiceImpl对象
4.链接点(join point):就是目标对象中的insert,update方法
5.通知(advice):就是切面类中的beforeXXX这个方法
6.aop代理(aopProxy):spring aop的实现就是靠代理来作到的,默认利用jdk代理和cglib代理 来实现
7.织入(weaving):是个动词,表示把切面类的通知与目标对象链接点糅合在一块儿的过程就叫织入
上面说到before是一种通知类型,那么相对应的还有其余通知类型:
xxxxxxxxxx
1.before:前置通知,在链接点方法以前执行,并且不能控制链接点是否执行
2.after:后置通知也叫最终通知,意思就是链接点方法只要执行(无论是否有错误),它都会获得执行。它是在目标方法执行完执行。
3.after-return:返回通知,链接点正常执行(不报错)时才会执行这个通知.
4.throwing:异常通知:链接点方法抛出异常时才会获得执行.这个通知不能处理异常只能获得异常信息.异常通知若是想把目标方法抛出的异常传递给通知方法只须要在异常通知的throwing属性设置的值等于通知方法的参数名就能够.
当异常通知方法没有异常类型做为参数时,潜台词就是目标方法抛出任何异常,通知都会获得执行
当异常通知方法"有"异常类型做为参数是,潜台词是只有目标方法抛出的异常是参数指定类型的异常或是子类型时,此通知方法才会获得执行
5.around通知:环绕通知,环绕通知是最强的通知类型,它能够彻底取代上面的4种
也能够进行异常的捕获处理,也能够组织目标方法执行
这五种是比较常见的通知类型,其实大部分在项目里比较经常使用的就只有环绕通知。
通常来讲,目标方法是有一些是有参数的,若是须要获取这些参数的值那么应该怎么作呢?
咱们先来讲说环绕通知怎么获取目标方法参数:
好比:
主要实现功能:有个类,方法里面有参数,而后在日志类里获取其参数。
EmployeeServiceImpl:
xxxxxxxxxx
public class EmployeeServiceImpl {
public void update(){
System.out.println("update*****");
}
public void insert(){
System.out.println("insert***");
}
public int add(int a,int b){
return a+b;
}
}
这个方法有返回值,也有参数。
而后日志类:
xxxxxxxxxx
public class LogImpl {
public Object aroundParam(ProceedingJoinPoint joinPoint){
System.out.println("before");
//获取参数的集合
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
System.out.println("arg = " + arg);
}
Object result = null;
try {
//获取返回值
result = joinPoint.proceed();
} catch (Throwable throwable) {
System.out.println("throw......");
}
System.out.println("after");
return result;
}
}
applicationContext.xml:
xxxxxxxxxx
<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 https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="log" class="com.demo.LogImpl"/>
<bean id="employeeService" class="com.demo.EmployeeServiceImpl"/>
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(* com.demo.*.*(..))"/>
<aop:aspect id="logAspect" ref="log">
<aop:around method="aroundParam" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
</beans>
使用:
xxxxxxxxxx
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmployeeServiceImpl employeeService = context.getBean("employeeService", EmployeeServiceImpl.class);
employeeService.add(5,6);
}
}
结论:若是是环绕通知,那么就能够经过ProceedingJoinpoint里面的getArgs()来获取全部参数。同时若是有返回值的话,能够定义一个变量存储,而后result = joinPoint.proceed();。
若是是其余方法想要获取目标方法的参数:
xxxxxxxxxx
1. 切点表达式中的args与execution都叫切点指示器(PointCut Designer)
2.args是用来表示查找有多少个参数,args(x),表示找到只有一个参数
3.args里面的名字至关于一个变量名,在调用目标方法时,依据调用目标方法
传递的数据,给这个变量名赋值
4.通知执行的时候,须要参数,那么这些参数的值就经过从变量里面找,默认就是找同名的
因此在不设置arg-names的时候,就会找args()表达式里面与通知方法同名的数据
5.可是4步的作法会致使切点表达式与通知方法的形参名耦合一块儿,因此spring提供了一个灵活的机制
就是在配置通知时,利用arg-names指定"别名"
6.args-names指定了别名后就以别名为准,那么就会致使args表达式里面就须要与args-names对应起来
总而言之: 想让通知方法获取到传递给目标方法的参数须要这么作:
1. args表达式里面的名字与通知方法的名字与个数匹配便可,此时不须要配置通知的arg-names
配置代码以下:
<aop:pointcut id="myPointcut1" expression="execution(* com.service.EmployeeServiceImpl.*(..)) and args(m,n)"/>
<aop:before method="before2" pointcut-ref="myPointcut1" />
2. args表达式里面的名字任意,此时就须要在通知方法的arg-names配置得与args表达式名字同样
此时这二者配置的名字不须要与通知方法的形参名同样.
<aop:pointcut id="myPointcut1" expression="execution(* com.service.EmployeeServiceImpl.*(..)) and args(x1,y1)"/>
<aop:before method="before2" pointcut-ref="myPointcut1" arg-names="x1,y1"/>
3.因为全部的通知方法均可以添加JoinPoint类型做为第一个参数,而这个类型
是能够很方便的获得参数,你也能够采用这种方式,这样就不须要上面的2种方法来达成一样的目的
配置文件:
xxxxxxxxxx
<aop:config>
<aop:pointcut id="myPointcut1" expression="execution(* com.EmployeeServiceImpl.*(..)) and args(x1,y1)"/>
<aop:aspect ref="paramAspect" order="3">
<aop:before method="before2" pointcut-ref="myPointcut1" arg-names="x1,y1"/>
<!-- <aop:around method="aroundParam" pointcut-ref="myPointcut1"/>-->
</aop:aspect>
</aop:config>
xxxxxxxxxx
在同一个切面中,配置的同类型通知,其顺序官方没有明确的说明就不能认为以配置的顺序为准,可是若是是在不一样的切面中配置,能够经过order属性来准确的控制其执行顺序数字小的先执行.
好比:
xxxxxxxxxx
<aop:config>
<aop:pointcut id="myPointcut1" expression="execution(* com.service.EmployeeServiceImpl.*(..))"/>
<aop:aspect ref="logImpl" order="3">
<aop:around method="aroundAdvice" pointcut-ref="myPointcut1"/>
</aop:aspect>
<aop:aspect ref="logImpl" order="4">
<aop:around method="aroundAdvice2" pointcut-ref="myPointcut1"/>
</aop:aspect>
</aop:config>
上面这个案例确定是order="3"先执行,
spring aop只针对方法进行aop代理,不想aspectj联盟搞的aop实现 这个aop联盟的实现功能比spring aop要强大,好比能够针对字段进行切面编程.
1.切点指示器(PCD:PointCutDesigner):指示器能够理解为一种描述找到方法的方式.spring支持的切点指示器有以下几个:
xxxxxxxxxx
1.1 execution:用来匹配链接点方法的,用的最多的一个指示器
1.2 within:英文的意思是:在某某以内,通常就是指的是在"某些"类以内 within(com.service.*)就是指com.service包下的全部类的方法进行aop代理
1.3 this:指的就是动态代理生成的对象,这种指示器通常表示某个动态代理 对象是某个类型,好比this(com.service.EmployeeService),就表示 动态代理对象是EmployeeService的实现类
1.4 target:指的是被代理的对象.指定的是"某个"特定的目标对象
1.5 args:此指示器是在方法的参数层面来描述,好比args(integer,String)就表示 全部有2个参数,而且类型分别是integer,string的方法
1.6 @target:就表示目标类型上有特定注解修饰的目标对象 @target(com.MyFirstAnnotation),就会找全部被spring 管理的bean上面有MyFirstAnnotation注解的目标类
1.7 @args:与arg相似,只不过是代表参数上有特定注解的
1.8 @Within:与within相似,只不过是代表类上有特定的注解修饰
1.9 @annotation:指的是链接点方法上有特定注解
1.10 bean:这个指示器不是aop联盟的标准,是spring本身提供的 指示特定bean的名字的指示器
二、 指示器的逻辑运算符
而且:and(&&)
或者:or(||)
非:not(!)
三、 execution指示器不一样的指示器,表达式的写法的模式多是不同的额, 它的编写格式: execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?) 上面格式的含义以下:
xxxxxxxxxx
3.1 ?:表示有或者没有 ,没有问号就表示必须写
3.2 modifiers-pattern:修饰符模式
ret-type-pattern:返回类型模式
declaring-type-pattern:声明类型模式
name-pattern:指的就是方法的名字模式
param-pattern:指的就是方法参数的模式
throws-pattern:指的是方法抛出的异常的模式
其中三个模式必不可少:返回类型,方法名,方法参数
通配符: *:表示任意名字 ..:任意段的任意字符
常见的例子:
xxxxxxxxxx
1. execution(public * *(..)):找到全部的公有方法
2. execution(* get*(..)):找到以get开头的方法
3. public * com.service.emp.EmployeeService+.*(..))
+ 表示接口的全部方法以及此接口实现类本身的方法都会被aop代理
4. public * com.service..*.*(..)) 表示com.service包以及子孙包下
5. within( com.service..*) 表示com.service包以及子孙包下的全部类,写法与execution指示器是不一样的,不须要写返回类型
6. target( com.service.emp.impl.EmployeeServiceImpl) 给EmployeeServiceImpl进行aop代理,不能使用通配符.表达式中指定父类型或者接口时是能够的.
7. this(com.service.emp.impl.EmployeeServiceImpl):给EmployeeServiceImpl类进行aop代理.写法与target相似.意思是:若是能代理成功,那么生成的代理类是表达式里面设定的类型的实例
8. bean(emp)表示给emp这个bean进行aop代理
若是有个接口叫EmployeeService,其有个实现类EmployeeServiceImpl,若是在aop那里代理的是实现类。而后获取这个类的时候用的是接口来接受的,这样是依赖倒转设计原则里面所说的。可是若是你在调用其方法是错误的。
缘由是:默认状况下,EmployeeServiceImpl被aop代理的时候生成的是一个代理类。因此代理类是不能够向上转型成为EmployeeService的。若是你想要生成EmployeeServiceImpl的子类的话,就要设置:
xxxxxxxxxx
<aop:config proxy-target-class="true">
...
</aop:config>
这样设置就是叫aop代理的时候使用jdk的方式代理,生成的是EmployeeServiceImpl的子类。然而这个子类是能够向上转型成为EmployeeService的。
Advice:通知,表明一种注入方式,好比前置,后置。
Advisor:通知器或者统治者.有某个或某些特定通知类型的切面类
特定通知是靠此类实现某些接口来表示的.
spring有以下接口来代表不一样的通知类型:
xxxxxxxxxx
MethodBeforeAdvice:前置通知
AfterReturningAdvice:返回通知
ThrowsAdvice:异常通知
MethodInterceptor:环绕通知
注意:没有最终通知(after通知)
实现这些接口就是告诉spring,我这个实现类里的哪一个方法是什么样的通知。
这四种接口里,除了异常通知接口是空接口外,其它都是有方法的。
可是咱们若是实现了异常接口,那么咱们就须要本身写一个方法,否则是会保错的。
异常接口的方法的签名必须是:
xxxxxxxxxx
1.返回类型是void
2.方法名是afterThrowing
3.方法的参数能够是
3.1 Method method, Object[] args, Object target
3.2 或者Method method, Object[] args, Object target,异常类
可是,我试了下,可能第三个方法参数不能为第一个。
下面咱们经过一个案例了解一下:
业务层:EmployeeServiceImpl
xxxxxxxxxx
public class EmployeeServiceImpl implements EmployeeService {
public int add(Integer x, Integer y) {
// throw new RuntimeException("failed----"); //异常通知测试
return x + y;
}
}
通知器类:MyAdvisor
xxxxxxxxxx
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;
import java.lang.reflect.Method;
public class MyAdvisor implements MethodBeforeAdvice, AfterReturningAdvice, MethodInterceptor, ThrowsAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("before----");
}
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("-----debug: returnValue = " + returnValue);
}
public Object invoke(MethodInvocation invocation) throws Throwable {
//这行代码至关于改变了传递给目标对象方法的参数值.
invocation.getArguments()[0] = (Integer) invocation.getArguments()[0] + 100;
System.out.println("around before");
Object result = invocation.proceed();
System.out.println("around after");
return result;
}
//异常通知方法
public void afterThrowing(Method method, Object[] args, Object target, RuntimeException re) throws Throwable {
System.out.println("throw----" + re.getMessage());
}
}
由于咱们告诉了spring,我这些方法是什么方法。因此就不须要在applicationContext.xml里面在声明我调用的是什么方法了,<aop:before />。咱们只须要告诉spring,我用的是哪一个通知器就行,固然通知器也是须要被spring所代理。
applicationContext.xml:
xxxxxxxxxx
<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 https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--通知器被spring管理-->
<bean id="myAdvisor" class="com.advisor.MyAdvisor"/>
<bean id="emp" class="com.dao.EmployeeDaoImpl"/>
<aop:config>
<!--配置切点表达式-->
<aop:pointcut id="myPointcut"
expression="execution(* com.service..*.*(..))"/>
<!--告诉spring。我用的是哪一个通知器-->
<aop:advisor advice-ref="myAdvisor"
pointcut-ref="myPointcut"/>
</aop:config>
</beans>
这样当你用的方法符合切点表达式的时候,通知器里面的方法只要符合通知类型且须要通知,那么都会执行。
Main:
xxxxxxxxxx
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmployeeService employeeService = context.getBean("emp", EmployeeService.class);
int result = employeeService.add(5, 6);
System.out.println(result);
}
}
这个我也不怎么懂,原理是什么。可是仍是写个案例吧。其主要功能就是代理目标对象,而后设置下,就能作到aop代理这个目标方法。
配置文件:applicationContext.xml:
xxxxxxxxxx
<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 https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--
关于这方面的详细能够在官方文档中搜索ProxyFactoryBean去了解
主要在官方文档的spring aop api这一节中
-->
<bean id="myAdvisor" class="com.advisor.MyAdvisor"/>
<bean id="emp" class="com.dao.EmployeeDaoImpl"/>
<bean id="empFactory" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="emp"></property>
<property name="interfaces">
<list>
<value>com.service.EmployeeService</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>myAdvisor</value>
</list>
</property>
</bean>
</beans>
main:
xxxxxxxxxx
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext_factoryBean.xml");
EmployeeService employeeService = context.getBean("empFactory", EmployeeService.class);
int result = employeeService.add(5, 6);
System.out.println(result);
}
}
有兴趣去官网看吧。
一个数据库事务是一个被视为单一的工做单元的操做序列。这些操做应该要么完整地执行,要么彻底不执行。事务管理是一个重要组成部分,RDBMS 面向企业应用程序,以确保数据完整性和一致性。事务的概念能够描述为具备如下四个关键属性说成是 ACID:
一个真正的 RDBMS 数据库系统将为每一个事务保证全部的四个属性。使用 SQL 发布到数据库中的事务的简单视图以下:
Spring 框架在不一样的底层事务管理 APIs 的顶部提供了一个抽象层。Spring 的事务支持旨在经过添加事务能力到 POJOs 来提供给 EJB 事务一个选择方案。Spring 支持编程式和声明式事务管理。EJBs 须要一个应用程序服务器,但 Spring 事务管理能够在不须要应用程序服务器的状况下实现。
局部事务是特定于一个单一的事务资源,如一个 JDBC 链接,而全局事务能够跨多个事务资源事务,如在一个分布式系统中的事务。
局部事务管理在一个集中的计算环境中是有用的,该计算环境中应用程序组件和资源位于一个单位点,而事务管理只涉及到一个运行在一个单一机器中的本地数据管理器。局部事务更容易实现。
全局事务管理须要在分布式计算环境中,全部的资源都分布在多个系统中。在这种状况下事务管理须要同时在局部和全局范围内进行。分布式或全局事务跨多个系统执行,它的执行须要全局事务管理系统和全部相关系统的局部数据管理人员之间的协调。
spring事务写法要点:
xxxxxxxxxx
1.配置一个DataSource
2.配置事务管理器,用上dataSource
3.配置一个事务通知tx:advice
3.1 对某些方法进行事务相关属性配置,好比超时(timeout),事务隔离级别 ,事务传播方面的配置,只读配置 3.2 必定要记得关联事务管理器,默认名字是transactionManager
4.配置aopconfig,肯定对哪些业务类的方法进行事务处理
***事务的处理是针对业务类,不是dao***
事务管理器:
主要用来管理物理链接,事务提交,回滚等功能有了事务配置,对咱们的dao里面用的链接相关的信息就有了要求:
1.由于这个事务管理器是针对DataSource,因此咱们的dao必须用"同一个"dataSource
2.DataSource获取方法必须是Spring提供的方式
下面经过案例来讲明spring事务管理器
主要实现功能:两个表删除数据,实现事务功能——若是一个表的列删除失败有异常,则另外一个表的列也不会删除
这就是回滚机制。
dao层:由于有两张表,因此有两个dao类
DeptDaoImpl:
xxxxxxxxxx
public class DeptDaoImpl extends BaseDao {
public void deleteById(int id) throws Exception {
String sql = "delete from dept where id =?";
//int i = 5/0; //这个注释是会抛异常的
//jdbcTemplate是spring-jdbc里的方法。其主要功能是用来操做数据库的
jdbcTemplate.update(sql, id);
}
}
EmployeeDaoImpl:
xxxxxxxxxx
public class EmployeeDaoImpl extends BaseDao {
public void deleteByDeptId(int id){
String sql = "delete from employee where deptid =?";
jdbcTemplate.update(sql, id);
}
}
上面两个类都继承了父类,该父类是个抽象类。主要是两个子类的重复代码的封装:
xxxxxxxxxx
public abstract class BaseDao {
protected JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
而后是业务层的代码:
xxxxxxxxxx
public class DeptServiceImpl {
private DeptDaoImpl deptDao;
private EmployeeDaoImpl employeeDao;
public void setDeptDao(DeptDaoImpl deptDao) {
this.deptDao = deptDao;
}
public void setEmployeeDao(EmployeeDaoImpl employeeDao) {
this.employeeDao = employeeDao;
}
public void deleteWholeDeptById(int id) throws Exception {
employeeDao.deleteByDeptId(id);
deptDao.deleteById(id);
}
}
主要的applicationContext.xml配置:
xxxxxxxxxx
<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.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/demo"/>
<property name="password" value="root"/>
<property name="username" value="root"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="baseDao" abstract="true">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="deptDao" class="com.dao.DeptDaoImpl" parent="baseDao">
</bean>
<bean id="empDao" class="com.dao.EmployeeDaoImpl" parent="baseDao">
</bean>
<bean id="deptService" class="com.service.impl.DeptServiceImpl">
<property name="employeeDao" ref="empDao"/>
<property name="deptDao" ref="deptDao"/>
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--
tx:advice的事务管理器设置:
若是你配置的事务管理器的名字就叫作transactionManager,
那么transaction-manager就能够不用设置
-->
<tx:advice id="txAdvisor" transaction-manager="txManager">
<tx:attributes>
<!--查询操做-->
<tx:method name="get*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED" rollback-for="com.dao.MyCheckEx"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serivceTx" expression="execution(* com.service..*.*(..))"/>
<aop:advisor advice-ref="txAdvisor" pointcut-ref="serivceTx"/>
</aop:config>
</beans>
能够配置多个<tx:method />,通常的配置,查询操做用只读事务,会优化性能它也支持通配符*默认状况下,spring会对运行时异常产生回滚,检查异常不回滚若是想针对检查异常也回滚,那么就须要配置rollback-for
mybatis这种持久层框架,其全部数据库操做的异常都是运行时异常因此method的rollback-for保留默认便可,不须要额外配置
注意:tx:attributes是必须配置,若是不配置,整个就运行在非事务环境下
xxxxxxxxxx
///////////////////////******************源码解析*******************/////////////////
tx:attributes是必须配置的,不然在xml这种配置状况下就没有事务相关的配置信息,
spring并不提供事务相关属性的默认值,因此会致使方法运行在非事务环境下
研究方法:点击tx:advice跳转到xsd文件,会看到TransactionInterceptor,在此类的invoke方法中能够看到
调用了invokeWithinTransaction方法,在此方法中能够看到获取TransactionAttribute信息时,考虑到了方法.若是没有
相关的事务信息配置,就不会建立调用事务管理器的getTransaction方法,就不会有事务.
1.有配置tx:attributes时会用到NameMatchTransactionAttributeSource,没有配置时会用到AbstractFallbackTransactionAttributeSource
2.方法调用流:invoke()->invokeWithinTransaction()->createTransactionIfNecessary()
///////////////////////******************源码解析*******************/////////////////
Main:
xxxxxxxxxx
public class Main {
public static void main(String[] args) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
DeptServiceImpl deptService = context.getBean("deptService", DeptServiceImpl.class);
try {
deptService.deleteWholeDeptById(22);
} catch (Throwable throwable) {
System.out.println("-----debug: throwable.getClass().getName() = " + throwable.getClass().getName());
}
}
}
注意:整合时,事务管理器用的dataSource必须与sqlSessionFactory同样(与mybatis整合时);
其实这个整合和mybatis与spring的整合差很少。只是添加了事务而已。可是他们不会相互影响。
applicationContext.xml:
xxxxxxxxxx
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="url" value="jdbc:mysql://localhost:3306/demo"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="mapperLocations" value="classpath*:*Mapper.xml"/>
</bean>
<mybatis:scan base-package="com.dao" />
<bean id="manager" class="com.service.XXXManager" autowire="byType"></bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice id="txAdvisor">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="servicePointcut"
expression="execution(* com.service..*.*(..))"/>
<aop:advisor advice-ref="txAdvisor" pointcut-ref="servicePointcut"/>
</aop:config>
</beans>
其余都是没什么变化的,具体的能够去看上面的mybatis与spring的整合。