工具
- 建议直接下载最新版包含sts的eclipse,可以去https://spring.io/tools
使用
导入相关包
增加配置文件
- 在eclipse中直接new一个spring bean configuration file;
配置bean
注入方式
- 构造器注入/set注入
public class HelloWorld {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public HelloWorld(String age) {
super();
this.age = age;
}
@Override
public String toString() {
return "HelloWorld [name=" + name + ", age=" + age + "]";
}
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld bean = context.getBean("helloWorld",HelloWorld.class);
System.out.println(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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="helloWorld" class="com.huzd.study01.HelloWorld">
<constructor-arg name="age" value="StringConstructor"></constructor-arg>
<property name="name" value="String"></property>
</bean>
</beans>
- 注意:set注入,name对应bean的属性,value就是属性值;
- 注意:构造器注入,name对应参数名,value对应参数值,对应参数列表:参数个数,类别,顺序,其中参数个数是由construct标签的个数决定,参数类别可以由type属性决定,参数顺序可以由index来标记或者由name属性对应参数名称;
- 注入中有特殊标记,例如“>”这种尖括号,可以使用<![CDATA[标记]]>
直接使用会报错
使用CDATA标签即可
引用其他bean
- 在标签中使用ref属性即可
- 使用内部bean,注意内部bean可以不要id,只能内部使用,不能外部引用;
特殊赋值
- 赋值null
- 级联赋值
外部bean级联
内部bean级联
- 注意:由内部bean的级联可以,级联的方式是由属性名引用赋值,而并非使用外部或者内部bean的id名称赋值;
集合赋值
- list赋值(可以内部bean,也可以引用)
<bean id="person" class="com.huzd.study01.Person">
<property name="cars">
<list>
<bean class="com.huzd.study01.Car">
<property name="speed" value="100"></property>
</bean>
<bean class="com.huzd.study01.Car">
<property name="speed" value="200"></property>
</bean>
<bean class="com.huzd.study01.Car">
<property name="speed" value="300"></property>
</bean>
</list>
</property>
</bean>
- map赋值
<bean id="car1" class="com.huzd.study01.Car">
<property name="speed" value="100"></property>
</bean>
<bean id="car2" class="com.huzd.study01.Car">
<property name="speed" value="200"></property>
</bean>
<bean id="car3" class="com.huzd.study01.Car">
<property name="speed" value="300"></property>
</bean>
<bean id="person" class="com.huzd.study01.Person">
<property name="carMap">
<map>
<entry key="car1" value-ref="car1" ></entry>
<entry key="car2" value-ref="car2" ></entry>
<entry key="car3" value-ref="car3" ></entry>
</map>
</property>
</bean>
- properties赋值
<bean id="dataSource" class="com.huzd.study01.DataSource">
<property name="properties">
<props>
<prop key="user">root</prop>
<prop key="password">123456</prop>
<prop key="url">jdbc</prop>
<prop key="driver">driverclass</prop>
</props>
</property>
</bean>
- 配置单独的集合bean
<util:list id="cars">
<ref bean="car1"/>
<ref bean="car2"/>
<ref bean="car3"/>
</util:list>
<bean id="person" class="com.huzd.study01.Person">
<property name="cars" ref="cars"></property>
</bean>
- 利用p命名空间简化配置
<bean id="person1" class="com.huzd.study01.Person" p:name="huzd" p:age="111" p:cars-ref="cars"></bean>
自动装配
- 在bean的标签中指定autowire属性,spring中id跟name区别,前提是必须生成set方法;
- byName:根据bean的名称来自动装配,由于id跟name都只能唯一,故此方法获取bean唯一
- byType:根据bean的class类型来进行自动装配
继承
- 使用parent让子bean来继承父bean,并且在子bean中还可以覆盖父bean属性
<bean id="person" class="com.huzd.study02.autowire.Person" p:name="huzd" ></bean>
<bean id="car" class="com.huzd.study02.autowire.Car" p:speed="1000"></bean>
<bean id="person2" parent="person" p:name="huzd1"></bean>
- 在父bean中增加abstract属性为true可以让父bean只用来被继承而不能被实例化,若一个bean的class属性没有被指定,则该bean只能是抽象bean
依赖
- 使用depends-on属性来指定依赖bean(用逗号分隔多个bean)
<bean id="person" class="com.huzd.study02.autowire.Person" p:name="huzd" depends-on="car" ></bean>
<bean id="car" class="com.huzd.study02.autowire.Car" p:speed="1000"></bean>
- 假设car这个bean不存在则启动报错,避免赋空值
作用域
- 通过配置scope来配置
- singleton(默认):单例,在初始化spring容器的时候就已经初始化了bean,以后所有的请求都返回相同的bean对象;
- prototype:在使用的时候才会创建bean,并且每次使用都会创建新bean,相当于new一个bean
配置外部属性文件
- 在spring配置文件中引入context命名空间,并配置location为外部配置文件,然后在使用中使用${}来获取即可;
bean.properties
name=huzd
applicationContext.xml
<context:property-placeholder location="classpath:bean.properties"/>
<bean id="person" class="com.huzd.study02.autowire.Person" p:name="${name}"></bean>
SpEL
- 显示常量:#{‘常量值’},主要括号中需要用单引号或者双引号引起来
<bean id="person" class="com.huzd.study02.autowire.Person" p:name="#{'huzd'}"></bean>
- 显示静态类的方法或者属性:#{T(方法全类名).method()}
<bean id="car1" class="com.huzd.study02.autowire.Car" p:speed="#{T(java.lang.Math).random()}"></bean>
- 显示其他bean或者其他bean属性
<bean id="person" class="com.huzd.study02.autowire.Person" p:name="#{'huzd'}" p:car="#{car2}"></bean>
<bean id="car2" class="com.huzd.study02.autowire.Car" p:speed="#{car1.speed}"></bean>
- 使用运算符
<bean id="car3" class="com.huzd.study02.autowire.Car" p:speed="#{300}"></bean>
<bean id="person" class="com.huzd.study02.autowire.Person" p:name="#{'huzd'}" p:car="#{car3}" p:info="#{car3.speed > 30 ? '大人物':'普通小伙计'}"></bean>
bean的生命周期
- 调用bean的构造器或者工厂方法创建bean实例对象
- 调用bean的set方法根据配置设置属性值
- 如果实现了BeanNameAware,BeanFactoryAware等接口,则注入相应的属性
- 调用BeanPostProcessor的postProcessBeforeInitialization方法
- 调用InitializingBean的afterPropertiesSet方法
- 调用自定义的init方法
- 调用BeanPostProcessor的postProcessAfterInitialization方法
- bean准备就绪,可以使用
- 调用DispostbleBean的destory方法
- 调用自定义 destroy方法
bean的后置处理器
- 实现beanPostProcessor接口
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("before");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("after");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
- 在配置文件中配置
<bean class="com.huzd.study02.spel.MyBeanPostProcessor"></bean>
- 最终效果:可以在init前后处理bean
- 注意:
- 由于BeanPostProcessor是对所有bean都会生效,所以在方法内部需要用beanName来判断具体哪个bean再处理
- 在配置文件中如果配置多个实现了BeanPostProcessor接口的类,则会相应的执行多次方法;
获取bean
通过类全类名基于反射获取
- 即直接在spring配置文件中配置class属性获取bean
利用静态工厂方法来获取bean
- 配置静态工厂类
public class StaticCarFactory {
private static Map<String,Car> cars = new HashMap<String, Car>();
static {
cars.put("car", new Car(1000));
}
public static Car getCar(String name) {
return cars.get(name);
}
}
- 配置文件:class指向工厂类,配置的是bean实例,factory-method指向静态工厂方法,如果有参数通过constructor-arg来传入;
<bean id="car1"
class="com.huzd.study03.factory.StaticCarFactory"
factory-method="getCar">
<constructor-arg value="car"></constructor-arg>
</bean>
利用实例工厂来创建bean
- 创建实例工厂
public class InstanceFactory {
private Map<String, Car> cars = null;
public InstanceFactory() {
cars = new HashMap<String, Car>();
cars.put("car", new Car(2000));
}
public Car getCar(String name) {
return cars.get(name);
}
}
- 配置文件:首先需要配置工厂bean实例,然后配置car的实例,配置factory-bean属性指向工厂bean,factory-method指向获取bean的方法,有参数依旧由constructor-arg传入
<bean id="carFactory" class="com.huzd.study03.factory.InstanceFactory"></bean>
<bean id="car2" factory-bean="carFactory" factory-method="getCar">
<constructor-arg name="name" value="car"></constructor-arg>
</bean>
通过FactoryBean
- 配置类实现FactoryBean接口
public class CarFactoryBean implements FactoryBean<Car> {
@Override
public Car getObject() throws Exception {
return new Car(3000);
}
@Override
public Class<?> getObjectType() {
return Car.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
- 配置文件直接配置car实例bean并且class指向FactoryBean即可,上面两种都需要指定方法,这个接口配置之后,默认调用实现类的getObject获取car实例
<bean id="car3" class="com.huzd.study03.factory.CarFactoryBean"></bean>
注解
基本使用
- 基本的4个注解
- Component:组件
- Repository:一般用来标记持久层
- Service:一般用来标记服务层
- Controller:一般用来标记表现层
- 配置完成之后需要在配置文件中配置包扫描:base-package用来指定要扫描哪些包,resource-pattern用来匹配特定包或者类
<context:component-scan
base-package="com.huzd.study03.annotation"
resource-pattern="dao/*.class" ></context:component-scan>
-
通过默认类名第一个字母小写的方式获取或者使用注解的value属性配置
如下:配置repository的UserDaoImpl类可以通过userDaoImpl获取
@Repository public class UserDaoImpl implements UserDao { @Override public void add() { System.out.println("save"); } }
public class Test { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext3.xml"); UserDaoImpl userDaoImpl = context.getBean("userDaoImpl",UserDaoImpl.class); userDaoImpl.add(); } }
-
或者通过使用value属性来配置
@Repository(value = "userDao") public class UserDaoImpl implements UserDao { @Override public void add() { System.out.println("save"); } }
public class Test { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext3.xml"); UserDaoImpl userDaoImpl = context.getBean("userDao",UserDaoImpl.class); userDaoImpl.add(); } }
-
在
context:component-scan
内部配置子标签context:exclude-filter
- annotation类型排除:表示排除所有以Repository这个注解的bean
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
- annotation类型包含:表示只包含Repository这个注解的bean,需要注意的是,需要在父标签
context:component
中配置use-default-filters="false"
,可以理解成,默认配置为true是扫描用的默认过滤器扫描包下所有文件,配置context:exclude-filter
标签会从所有文件中过滤掉某些文件(交集),但是context:include-filter
会扫描某些文件(并集),最终得到的跟还是所有文件都扫描,所以需要关掉默认过滤器才有效果;
<context:component-scan base-package="com.huzd.study03.annotation" resource-pattern="dao/*.class" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/> </context:component-scan>
- 接口类型排除:设置type为assignable,expression为某一接口类,则通过该接口实现的类都排除
<context:exclude-filter type="assignable" expression="com.huzd.study03.annotation.dao.UserDao"/>
- 接口类型包括:设置type为assignable,expression为某一接口类,则通过改接口实现的类都可以获取到
<context:component-scan base-package="com.huzd.study03.annotation" resource-pattern="dao/*.class" use-default-filters="false"> <context:include-filter type="assignable" expression="com.huzd.study03.annotation.dao.UserDao"/> </context:component-scan>
自动装配
- 在构造器,字段,方法上面都可以使用@Autowired来注入相应值(构造器跟方法注入的是会注入相同参数名称类型的bean)
- 默认情况下Autowired如果找不到装配的类会报错,可以摄者required属性为false不报错(为null)
- 当有过个兼容类型的bean是,自动装配会报错,一种解决方案是将需要注入的类设置成跟注入的属性名称一致(autowire会先byType再byName,直到找到唯一一个),另外一种方式是使用@Qualifier属性将bean微调注入
UserServiceImpl类
@Service
public class UserServiceImpl implements UserService {
UserDao UserDao;
@Override
public void add() {
UserDao.add();
}
}
UserServiceImpl2
@Service
public class UserServiceImpl2 implements UserService {
UserDao UserDao;
@Override
public void add() {
UserDao.add();
}
}
直接装配时,先byType会找到两个userService接口的类,再byName还是会找到userServiceImpl跟userServiceImpl2两个类(默认以类名第一个字母小写作为id),这样的话会报错,可以使用Qualifier注解将userServiceImpl微调成userService注入
@Controller
public class UserController {
@Autowired
@Qualifier("userServiceImpl")
UserService userService;
public void execute() {
userService.add();
}
}
使用在方法中就是参数前微调
@Controller
public class UserController {
UserService userService;
@Autowired
public void setUserService(@Qualifier("userServiceImpl") UserService userService) {
this.userService = userService;
}
public void execute() {
userService.add();
}
}
- @Autowired也可以应用在数组,集合,map上面,放在数组上面就是匹配所有数组类型的类注入,放在集合上面就是匹配所有的集合类型的类注入,放在map上面,若map的键为String,则是以bean的id为key,bean本身作为value注入;
泛型依赖注入
- 一个泛型父类引用另外一个泛型父类,那么子类也会自动将同一个泛型对应依赖,但是如果有多个类型匹配则会报错;
示例
父类service泛型类引用BaseRepository泛型父类
public class BaseService<T> {
@Autowired
protected BaseRepository<T> repository;
public void add() {
System.out.println("add");
System.out.println(repository);
}
}
父类BaseRepository泛型类
public class BaseRepository<T> {
}
具体UserService 类继承BaseService并设置泛型为User
@Service
public class UserService extends BaseService<User> {
}
具体UserRepository 继承BaseRepository并设置泛型为User,此时UserService 就自动依赖上了UserRepository
@Repository
public class UserRepository extends BaseRepository<User> {
}
若还有另外一个也是设置User泛型,则会报错;
@Repository
public class UserRepository2 extends BaseRepository<User>{
}
spring AOP
AOP术语
- 切面(aspect):需要插入的对象,例如日志切面;
- 通知(advice):切面必须要完成的工作;
- 目标(target):被通知的对象
- 代理(proxy):向目标对象应用通知之后创建的对象
- 连接点(joinPoint):程序执行的某个特定位置,由两个要素决定,方法跟方位(方法执行之前,之后,异常后等等)
- 切点(pointCut):切面与业务逻辑相交的点,对应连接点根据方位不同,一个切点对应多个连接点
基于注解
- 在配置文件中增加配置
<!-- 让aspectJ注解起作用 自动生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- UserService中引用Userdao
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void save(User u) {
System.out.println("save user");
userDao.add(u);
}
public void add(User u) {
System.out.println("add User....");
}
}
@Repository
public class UserDaoImpl implements UserDao {
@Override
public void add(User u) {
System.out.println("add User....");
}
}
- 新建切面,并设置before注解表示在某个方法之前执行,由org.aspectj.lang.JoinPoint可以获取方法信息
@Aspect
@Component
public class LoggingAspect {
@Before("execution(public void com.huzd.study01.aop.UserService.*(User))")
public void beforeAdd(JoinPoint joinpoint) {
String methodName = joinpoint.getSignature().getName();
Object[] args = joinpoint.getArgs();
System.out.println("methodName:"+methodName);
System.out.println("args:"+Arrays.asList(args));
System.out.println("before add...");
}
}
对于六种术语的解释
- 切面:在AspectJ注解中,切面是一个带有@Aspect注解的java类
- 通知:AspectJ支持五种类型的通知:
- @Before:前置通知,在方法执行之前执行
- @After:后置通知,在方法执行之后执行
- @AfterReturning:返回通知,在方法返回结果之后执行
- @AfterThrowing:异常通知,在方法返回异常之后执行
- @Around:环绕通知,围绕着方法执行
- 目标:例如上面UserService类
- 代理:AOP容器生成的代理对象(在配置文件以标签声明)
- 连接点:切面中的方法
- 切点:目标中的方法
对于五种通知类型的解释
Before 前置通知
在UserService的save方法执行之前执行beforeAdd方法
@Before("execution(public void com.huzd.study01.aop.UserService.save(User))")
public void beforeAdd(JoinPoint joinpoint) {
String methodName = joinpoint.getSignature().getName();
Object[] args = joinpoint.getArgs();
System.out.println("methodName:"+methodName);
System.out.println("args:"+Arrays.asList(args));
System.out.println("before add...");
}
After 后置通知
在UserService的save方法执行之后执行afterAdd方法
@Aspect
@Component
public class LoggingAspect {
@After("execution(public void com.huzd.study01.aop.UserService.save(User))")
public void afterAdd(JoinPoint joinpoint) {
String methodName = joinpoint.getSignature().getName();
Object[] args = joinpoint.getArgs();
System.out.println("methodName:"+methodName);
System.out.println("args:"+Arrays.asList(args));
System.out.println("after add...");
}
}
AfterThrowing 异常通知
在UserService的save方法抛出异常之后执行,需要注意的是,如果异常被捕获,则不会执行;
@Aspect
@Component
public class LoggingAspect {
@AfterThrowing("execution(public void com.huzd.study01.aop.UserService.save(User))")
public void beforeAdd(JoinPoint joinpoint) {
String methodName = joinpoint.getSignature().getName();
Object[] args = joinpoint.getArgs();
System.out.println("methodName:"+methodName);
System.out.println("args:"+Arrays.asList(args));
System.out.println("after add...");
}
}
AfterReturning返回通知
无论连接点是正常返回还是异常返回,后置通知都会执行,如果指向让正常返回才执行,可以用返回通知代替后置通知,在注解中通过设置returning = "result"还可以获取返回值
@Aspect
@Component
public class LoggingAspect {
//@AfterThrowing("execution(public void com.huzd.study01.aop.UserService.save(User))")
//@After("execution(public void com.huzd.study01.aop.UserService.save(User))")
@AfterReturning(value = "execution(public String com.huzd.study01.aop.UserService.save(User))",returning = "result")
public void afterAdd(JoinPoint joinpoint,Object result) {
String methodName = joinpoint.getSignature().getName();
Object[] args = joinpoint.getArgs();
System.out.println("methodName:"+methodName);
System.out.println("args:"+Arrays.asList(args));
System.out.println("after add...");
System.out.println("result:"+result);
}
}
Around 环绕通知
其实就是类似动态代理,连接点必须有ProceedingJoinPoint 参数,并且需要调用proeed方法
@Around(value = "execution(public String com.huzd.study01.aop.UserService.save(User))")
public Object around(ProceedingJoinPoint pjd) throws Throwable {
Object proceed = null;
try {
System.out.println("前置通知");
proceed = pjd.proceed();
System.out.println(1/0);
System.out.println("返回通知");
} catch (Exception e) {
System.out.println("异常通知");
}
System.out.println("后置通知");
return proceed;
}
几种通知的比较
因为AOP是利用的动态代理来实现,因此几种通知相当于在invoke中如下位置:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
Object result = null;
try {
//前置通知
result = method.invoke(userDao, args);
//返回通知
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
//异常通知
}
//后置通知
return result;
}
AspectJ表达式
execution(* *.*.*(. .)
)
第一个*
表示任意修饰符跟任意返回值
第二个*
表示任意包
第三个*
表示任意类
第四个*
表示任意方法
最后括号表示方法,中间的..
表示任意参数
- 指向com.huzd.study01.aop这个包下的UserService这个类的具有public修饰符并且返回值为void,有一个User类型参数的save方法
execution(public void com.huzd.study01.aop.UserService.save(User))
- 指向com.huzd.study01.aop这个包下的UserService这个类的具有public修饰符并且返回值为void,有任意参数的save方法
execution(public void com.huzd.study01.aop.UserService.save(..))
- 指向com.huzd.study01.aop这个包下的UserService这个类的具有public修饰符并且返回值为void,有任意参数的任意方法
execution(public void com.huzd.study01.aop.UserService.*(..))
- 指向com.huzd.study01.aop这个包下的任意类的具有public修饰符并且返回值为void,有任意参数的任意方法
execution(public void com.huzd.study01.aop.*.*(..))
- 指向com.huzd.study01.aop这个包下的任意类的具有任意修饰符并且返回值任意类型,有任意参数的任意方法
execution(* com.huzd.study01.aop.*.*(..))
- 指向任意包下的任意类的具有任意修饰符并且返回值任意类型,有任意参数的任意方法
execution(* *.*.*(..))
- 指向任意包下的任意类的具有任意修饰符并且返回值任意类型,第一个参数为Int类型,后面为任意类型或数量参数的任意方法
execution(* *.*.*(Int,..))
- 符合前面表达式或者符合后面表达式的方法(&&,!同理)
execution(* *.*.*(..)) || execution(* *.*.*(..))
切面优先级
在class上实现Ordered接口或者使用@Order注解,getOrder()返回值或者是@Order的值越小则优先级越高;
重用切点
在切面中声明通知方法时,一个切点表达式可能会出现在多个通知中,使用@PointCut来将一个切点声明为普通方法,该方法通常是空的,因为将切点的定义与逻辑混淆是不合理的;
将切点的定义定义成pointCut普通方法,方法可以由pointCut属性指定也可以由value属性指定,如果切点定义不在本类中,需要指定包名跟类名,例如com.huzd.study01.aop.LoggingAspect.getPointCut()
@Aspect
@Component
public class LoggingAspect {
//@AfterThrowing("execution(public void com.huzd.study01.aop.UserService.save(User))")
//@After("execution(public void com.huzd.study01.aop.UserService.save(User))")
@AfterReturning( pointcut = "com.huzd.study01.aop.LoggingAspect.getPointCut()",returning = "result")
public void afterAdd(JoinPoint joinpoint,Object result) {
String methodName = joinpoint.getSignature().getName();
Object[] args = joinpoint.getArgs();
System.out.println("methodName:"+methodName);
System.out.println("args:"+Arrays.asList(args));
System.out.println("after add...");
System.out.println("result:"+result);
}
//@Around("getPointCut()")
public Object around(ProceedingJoinPoint pjd) throws Throwable {
Object proceed = null;
try {
System.out.println("前置通知");
proceed = pjd.proceed();
System.out.println(1/0);
System.out.println("返回通知");
} catch (Exception e) {
System.out.println("异常通知");
}
System.out.println("后置通知");
return proceed;
}
@Pointcut(value = "execution(public String com.huzd.study01.aop.UserService.save(User))")
public void getPointCut() {}
}
基于配置文件
<?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"
xmlns:p="http://www.springframework.org/schema/p"
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-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 配置服务类,拥有userDao引用 -->
<bean id="userService" class="com.huzd.study01.aop.UserService" autowire="byName"></bean>
<!-- 配置userDao类 -->
<bean id="userDao" class="com.huzd.study01.aop.UserDaoImpl"></bean>
<!-- 配置切面bean -->
<bean id="logAspect" class="com.huzd.study01.aop.LoggingAspect"></bean>
<!-- 配置aop -->
<aop:config>
<!-- 配置切点表达式 -->
<aop:pointcut expression="execution(* com.huzd.study01.aop.*.*(..))" id="testOperation"/>
<!-- 配置切面,并指向切面bean -->
<aop:aspect id="loggingAspect" ref="logAspect" >
<!-- 配置通知 -->
<aop:before method="afterAdd" pointcut-ref="testOperation" />
</aop:aspect>
</aop:config>
</beans>
Spring事务
声明式事务有两种,基于xml跟基于注解
基于xml
- 首先构建数据源dataSource
- 然后声明事务管理器
- 声明事务通知
- 声明aop配置
基于注解
- 声明事务管理器
- 注解扫描包
- 使事务注解生效<tx:annotation-driven/>
- 然后再在需要添加事务的方法上增加@Transactional
注意
由于springAOP是基于代理的方法,所以只能增强公共方法,因此只有公共方法才能通过springAOP进行事务管理
事务的传播行为
基于注解:@Transactional(propagation=XXX)
基于xml:
- required(默认):如果外层方法有事务,那么内层方法直接使用外层方法的事务,因此,只要整个外层事务中有一个方法调用失败,那么整个外层事务都回滚了;
- required_new:外层方法有一个事务,内层方法自己独立开启事务,因内层方法事务互不影响,外层方法中若有一个事务回滚,只对其自己的事务生效;
事务的隔离级别
基于注解
基于xml
异常回滚
默认情况下,只有未检查异常会导致事务回滚,而受检查的不会,因此受检查异常需要自己捕获并重新抛出运行时异常,也可以使用rollbackFor或者noRollbackFor属性来定义
rollbackFor:遇到异常回滚
noRollbackFor:遇到异常不会滚;
超时跟只读
- 超时:事务在强制回滚之前,事务的占用时间;这样可以防止事务长时间占用资源
- 只读:事务只是读取数据不会修改事务,帮助数据库引擎优化事务
整合hibernate
整合struts2
在整合成web工程的时候,spring配置文件需要有web容器加载,因此需要在web.xml中配置
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:mybatis-spring/spring-config.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>