快速入门Spring

复习总结

代码地址

https://github.com/huyang1981115225/AAAA-Spring.git

day01-----------------Spring

1、什么是Spring

开源的、用来简化企业级开发应用的框架

2、Spring的优点

1. 简化开发
2. 解耦
3. 集成其他框架

3、Spring如何创建对象


1. 无参构造器:必须有无参构造器,否则会报错
2. 静态工厂方法:factory-method
3. 实例工厂方法:factory-method、factory-bean

4、Spring的作用域Scope


1.Singleton---构造方法和init都只会执行一次
2.Prototype---构造方法和init执行多次 

5、Spring的生命周期相关的方法

1. init-method--------初始化
2. destroy-method--------销毁:只有作用域为Singleton的bean,销毁方法才会执行

6、Spring的延迟加载(lazy-init)

1. 默认情况下,Spring管理的类的对象都是单例的,并且在加载Spring配置文件的时候就已经创建了bean的对象(非延迟加载)
2. 启动容器后,对于作用域为Singleton的bean,不在创建(当调用getBean()方法时才创建)(延迟加载)

7、Spring的IOC和DI?

1. IOC:Inversion Of Controller    控制反转,对象的依赖关系由容器建立
2. DI:Dependency Injection   依赖注入,容器通过set方法或者构造器来建立对象之间的依赖关系

8、Spring依赖注入的方式

8.1、 set方法注入


8.2、构造器注入


9、向Spring管理的bean中注入值

9.1、 直接注入


9.2、 引用的方式注入


10、Spring表达式 --SpEL

语法类似于 EL:SpEL 使用 #{…} 作为定界符, 为 bean 的属性进行动态赋值

11、Spring的自动装配(autowire)

表现为可以自动注入值,而不需要正在XML中通过set方法或者构造器来注入。
简化了开发,避免了XML配置文件过于臃肿,但是依赖关系表现的不明确。

1. byName:容器会根据属性名去查找对应的bean(即以属性名作为id的bean),找到后,调用对应的set方法来注入,如果找不到对应的bean,则不会注入。
2. byType:容器会根据属性类型去查找对应的bean,找到后,调用对应的set方法来注入,如果找到多个,则会报错。
3. constructor:类似byType,找到之后,调用构造器来注入。

12、Spring的组件扫描(component-scan)

容器启动之后,会扫描指定包及其子包下面的所有类,如果该类前面有一些特定的注解(比如@Component),则容器会将该类纳入容器进行管理(相当于在XML配置文件里有一个bean,该bean的id就是首字母小写后的类名)

@Component:通用注解

@Controller:控制器的注解

@Service:持久层的注解

@Scope(“singleton”):单例

@PostConstruct:init—初始化

@PreDestroy:destroy—销毁



12.1 @Autowired、@Qualifier的组合

使用@Autowired和@Qualifier的组合可以实现自动装配,这种组合不仅仅支持set方式注入,也可以基于构造器方式注入!并且还可以直接应用于需要注入的属性。

1.Autowired自动装配时会按byType去set注入,这样有可能会找到多个类型符合的,加Qualifier来指定被注入bean的id
2.注解属性的优势:简单---利用反射
3.注解方法的优势:可以在方法中添加其他说明代码

12.2 @Resource

13、Spring的AOP-----------------非常重要

13.1 什么是AOP?

面相切面编程

13.2 aop中的一些术语

1. Aspect(切面):切入点+通知
2. Joinpoint(连接点):目标对象中,所有可以增强的方法
3. Pointcut(切入点):目标对象中,已经增强的方法,就是带有通知的连接点,在程序中主要体现为书写切入点表达式
4. Advice(通知)
5. Target(目标对象):被代理的对象
6. Weaving(织入):将通知应用到切入点的过程
7. Proxy(代理):将通知织入到目标对象之后,形成的代理对象

13.3 Spring默认使用JDK动态代理,使用动态代理实现AOP的功能

package cn.tedu.spring.aop.day01;
/**
 * 不使用AOP,使用动态代理实现
 */
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class ArithmeticCalculatorLoggingProxy {

//要代理的对象
private ArithmeticCalculator target;

public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
	super();
	this.target = target;
}

//返回代理对象
public ArithmeticCalculator getLoggingProxy(){
	
	ArithmeticCalculator proxy = null;
	
	// 代理对象由哪一个类加载器加载
	ClassLoader loader = target.getClass().getClassLoader();
	
	//代理对象的类型,即其中有哪些方法
	Class [] interfaces = new Class[]{ArithmeticCalculator.class};
	
	// 当调用代理对象其中的方法时,该执行的代码
	InvocationHandler h = new InvocationHandler() {
		/**
		 * proxy: 代理对象。 一般不使用该对象
		 * method: 正在被调用的方法
		 * args: 调用方法传入的参数
		 */
		@Override
		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			String methodName = method.getName();
			//打印日志
			System.out.println("[before] The method " + methodName + " begins with " + Arrays.asList(args));
			
			//调用目标方法
			Object result = null;
			
			try {
				/**
				 * 反射
				 */
				//前置通知
				result = method.invoke(target, args);
				//返回通知, 可以访问到方法的返回值
			} catch (NullPointerException e) {
				e.printStackTrace();
				//异常通知, 可以访问到方法出现的异常
			}
			
			//后置通知. 因为方法可以能会出异常, 所以访问不到方法的返回值
			
			//打印日志
			System.out.println("[after] The method "+methodName+" ends with " + result);
			
			return result;
		}
	};
	
	/**
	 * loader: 代理对象使用的类加载器。 
	 * interfaces: 指定代理对象的类型. 即代理代理对象中可以有哪些方法. 
	 * h: 当具体调用代理对象的方法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 方法
	 */
	proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
	
	return proxy;
	}
}


public class Test02 {
public static void main(String[] args) {
	
	ArithmeticCalculator target =  new ArithmeticCalculatorImpl();
	ArithmeticCalculator proxy = new ArithmeticCalculatorLoggingProxy(target).getLoggingProxy();
	proxy.add(1, 2);
	proxy.sub(2, 1);
	proxy.mul(5, 1);
	proxy.div(4, 2);
	}
}

13.4 AOP

package cn.tedu.spring.aop.day02;
import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
 * 把这个类声明为一个切面:@Component
 * 1、需要把该类放到IOC容器中@Aspect
 * 2、再声明为一个切面
 * 
 * 注意:在aop.xml中配置	<!-- 使 AspectJ 的注解起作用 -->
 *	                <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
 * 
 * @author HY
 *
 */
@Component
@Aspect
public class LoggingAspect {
/**
 * 1、@Before前置通知
 */
// 声明该方法是一个前置通知:在目标方法开始之前执行

@Before("execution(public int cn.tedu.spring.aop.day02.ArithmeticCalculator.*(int, int))")
public void beforeMethod(JoinPoint joinPoint){
	String methondName = joinPoint.getSignature().getName();
	List<Object> args = Arrays.asList(joinPoint.getArgs());
	System.out.println("The method "+methondName+" begins with "+args);
}

/**
 * 1、@After后置通知
 * @param joinPoint
 */
// 声明该方法是一个后置通知:在目标方法开始之后执行(无论是否发生异常)
// 注意:在后置通知中不能访问目标方法执行的结果。

@After("execution(public int cn.tedu.spring.aop.day02.ArithmeticCalculator.*(int, int))")
public void afterMethod(JoinPoint joinPoint){
	String methondName = joinPoint.getSignature().getName();
	List<Object> args = Arrays.asList(joinPoint.getArgs());
	System.out.println("The method "+methondName+" end with "+args);
	}
}

package cn.tedu.spring.aop.day03;
import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 把这个类声明为一个切面:@Component
 * 1、需要把该类放到IOC容器中@Aspect
 * 2、再声明为一个切面
 * 
 * 注意:在xml中配置	<!-- 使 AspectJ 的注解起作用 -->
 *	                <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
 *
 * 1、bean组件切入点:
 *               @Before("bean(userService)||bean(carService)")
 *               @Before("bean(*Service)")
 * 2、类切入点:
 *               @Before("within(cn.tedu.service.userService)")
 *               @Before("within(cn.tedu.service.*Service)") 
 *               
 * 3、方法切入点:
 *               @Before("execution(public int cn.tedu.spring.aop.day03.ArithmeticCalculator.add(int, int))")
 *               @Before("execution(public int cn.tedu.spring.aop.day03.ArithmeticCalculator.*(..))")  
 * 
 * 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
 * 
 * @author HY
 *
 */
@Order(2)
@Component
@Aspect
public class LoggingAspect {

/**
 * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. 
 * 使用 @Pointcut 来声明切入点表达式. 
 * 后面的其他通知直接使用方法名来引用当前的切入点表达式. 
 */
@Pointcut("execution(public int cn.tedu.spring.aop.day03.ArithmeticCalculator.*(int, int))")
public void declareJointPointExpression(){}

/**
 * 在 com.atguigu.spring.aop.ArithmeticCalculator 接口的每一个实现类的每一个方法开始之前执行一段代码
 */
@Before("execution(public int cn.tedu.spring.aop.day03.ArithmeticCalculator.*(int, int))")
public void beforeMethod(JoinPoint joinPoint){
	String methodName = joinPoint.getSignature().getName();
	Object [] args = joinPoint.getArgs();
	
	System.out.println("[Before] The method " + methodName + " begins with " + Arrays.asList(args));
}

/**
 * 2、@After后置通知:
 * 					声明该方法是一个后置通知:在目标方法开始之后执行(无论是否发生异常)
 *                  注意:在后置通知中不能访问目标方法执行的结果
 */
//	@After("execution(public int cn.tedu.spring.aop.day03.ArithmeticCalculator.add(int, int))")
@After("execution(public int cn.tedu.spring.aop.day03.ArithmeticCalculator.*(int, int))")
public void afterMethod(JoinPoint joinPoint){
	String methondName = joinPoint.getSignature().getName();
	List<Object> args = Arrays.asList(joinPoint.getArgs());
	System.out.println("[After] The method "+methondName+" end with "+args);
}

/**
 * 3、@AfterReturning返回通知:
 * 返回通知中可以访问目标方法执行的结果(没有异常的情况下执行)
 */
//	@AfterReturning(value="execution(public int cn.tedu.spring.aop.day03.ArithmeticCalculator.add(int, int))",returning="result")
@AfterReturning(value="execution(public int cn.tedu.spring.aop.day03.ArithmeticCalculator.*(..))",returning="result")
public void afterReturningMethod(JoinPoint joinPoint,Object result){
	String methondName = joinPoint.getSignature().getName();
	List<Object> args = Arrays.asList(joinPoint.getArgs());
	System.out.println("[AfterReturning] The method "+methondName+" end with "+result);
}

/**
 * 4、@AfterThrowing异常通知:
 * 							异常通知(有异常的情况下执行)。
 */
//	@AfterThrowing(value="execution(public int cn.tedu.spring.aop.day03.ArithmeticCalculator.add(int, int))",throwing="ex")
@AfterThrowing(value="execution(public int cn.tedu.spring.aop.day03.ArithmeticCalculator.*(..))",throwing="ex")
public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
	String methondName = joinPoint.getSignature().getName();
	List<Object> args = Arrays.asList(joinPoint.getArgs());
	System.out.println("[AfterThrowing] The method "+methondName+" occurs exception :  "+ex);
}

/**
 * 5、@Around环绕通知:
 *                   1、必须有参数ProceedingJoinPoint,可以决定是否执行目标方法。
 *                   2、必须有返回值 Object
 *                   3、必须抛出异常 Throwable
 *                   
 */
//	@Around(value="execution(public int cn.tedu.spring.aop.day03.ArithmeticCalculator.add(int, int))")
@Around("declareJointPointExpression()")
public Object aroundMethod(ProceedingJoinPoint jp){
	System.out.println("开始进入环绕通知...");
	Object result  = null;
	String methodName = jp.getSignature().getName();
	try {
		//前置通知
		System.out.println("The method "+methodName+" begins with "+Arrays.asList(jp.getArgs()));
		
		// 执行目标方法
		result = jp.proceed();
		
		//返回通知
		System.out.println("The method "+methodName+" end with "+result);
	} catch (Throwable e) {
		
		//异常通知
		System.out.println("The method "+methodName+" occurs exception:  "+e);
		
	}
	//后置通知
	System.out.println("The method "+methodName+" ends ");
	System.out.println("环绕通知结束。");
	return result;
	}
}

13.5 AOP的切面优先级

package cn.tedu.spring.aop.day03;

/**
 * 演示切面优先级:
 * 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
 * 
 * 重用切点表达式:
 * 
 * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. 
 * 使用 @Pointcut 来声明切入点表达式. 
 * 后面的其他通知直接使用方法名来引用当前的切入点表达式. 
 *               	@Pointcut("execution(public int cn.tedu.spring.aop.day03.ArithmeticCalculator.*(int, int))")
                public void declareJointPointExpression(){}
                
                用的时候直接
                           @Before("cn.tedu.spring.aop.day03.LoggingAspect.declareJointPointExpression()")
                           @Around("declareJointPointExpression()")

 */
import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Order(1)
@Aspect
@Component
public class VlidationAspect {

@Before("cn.tedu.spring.aop.day03.LoggingAspect.declareJointPointExpression()")
public void validateArgs(JoinPoint joinPoint){
	System.out.println("-->validate:" + Arrays.asList(joinPoint.getArgs()));
	}
}

13.6 AOP----XML配置版

14、Spring的事务

14.1 声明式事务

1. 配置事务管理器 
2. 加入tx命名空间
3. 启用事务注解
4. 在对应方法上加@Transactional注解

14.2 Spring事务的传播行为(@Transactional(propagation=Propagation.REQUIRES_NEW))

当事务方法被另一个事务方法调用时,必须指定事务应该如何传播 ,Spring定义了7种传播行为

1. ☆ required:如果有事务在运行,当前方法就在这个事务内运行,否则启动一个新事务 ,并在这个自己的新事务内运行。
2. ☆ requires_new:开启自己的事务,并在自己的事务中运行,如果有事务正在运行,则将它挂起。

3. supports:如果有事务正在运行,当前方法就在这个事务内运行,如果没有就算了。
4. not_supported:当前的方法不支持事务,如果有事务正在运行,则将它挂起。
5. mandatory(强制的):当前的方法必须运行在事务内部,如果没有正在运行的事务,则抛出异常。
6. never:当前方法不能运行在事务内部,如果有正在运行的事务,则抛出异常。
7. nested(嵌套):如果有事务正在运行,当前的方法就应该在事物的嵌套事务中运行,如果没有,则启动一个新的事务并在自己的事务内运行。

package cn.tedu.spring.tx;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service("bookShopService")
public class BookShopServiceImpl implements BookShopService {

@Autowired
private BookShopDao bookShopDao;

//添加事务注解
//1.使用 propagation 指定事务的传播行为, 即当前的事务方法被另外一个事务方法调用时
//如何使用事务, 默认取值为 REQUIRED, 即使用调用方法的事务
//REQUIRES_NEW: 启动一个自己的事务, 调用的事务方法的事务被挂起.

//2.使用 isolation 指定事务的隔离级别, 最常用的取值为 READ_COMMITTED

//3.默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚. 也可以通过对应的
//属性进行设置. 通常情况下去默认值即可. 

//4.使用 readOnly 指定事务是否为只读. 表示这个事务只读取数据但不更新数据, 
//这样可以帮助数据库引擎优化事务. 若真的事一个只读取数据库值的方法, 应设置 readOnly=true

//5.使用 timeout 指定强制回滚之前事务可以占用的时间.  
//	@Transactional(propagation=Propagation.REQUIRES_NEW,
//			isolation=Isolation.READ_COMMITTED,
//			noRollbackFor={UserAccountException.class})
@Transactional(propagation=Propagation.REQUIRES_NEW,
		isolation=Isolation.READ_COMMITTED,
		readOnly=false,
		
		timeout=3)
/**
 * 如果不加事务注解,余额不扣,但是,库存会减!!!,不符合要求!!!
 */
//	@Transactional(propagation=Propagation.REQUIRES_NEW)   
@Override
public void purchase(String username, String isbn) {
	try {
		Thread.sleep(5000);
	} catch (InterruptedException e) {}
	
	//1. 获取书的单价
	int price = bookShopDao.findBookPriceByIsbn(isbn);
	System.out.println("书的单价为:  "+price);
	
	//2. 更新书的库存
	bookShopDao.updateBookStock(isbn);
	
	//3. 更新用户余额
	bookShopDao.updateUserAccount(username, price);
	}
}

14.3 谈一谈ACID

1. a:atomicity:原子性---整个事务中的所有操作,要么全部完成,要么全部不完成
2. c:consistent:一致性---事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少,例如银行转账
3. i:isolation:隔离性---当两个或多个事务并发执行时,为了避免数据的安全性,将事务隔离开来
4. d:durable:持久性---事务对数据库所作的更改便持久的保存在数据库之中

14.4 并发事务带来的问题

1. 更新丢失(针对覆盖更新):当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,最后更新覆盖了其他事务的更新。

2. ☆脏读(针对修改但未提交):一个事务正在对一条记录做修改,在这个事务完成并提交数据之前,另一个事务读取到了这些“脏数据”,如果事务一回滚了,违背了一致性原则。

3. ☆不可重复读(针对修改和删除):一个事务在读取某些数据的某个时间,再次读取以前读过的数据,去发现两次读过的数据已经发生了变化,或者某些记录已经被删除了。

4. ☆幻读(针对新增):一个事务按相同的查询条件重复读取以前查过的数据,却发现其他事务插入了满足其查询条件的新数据。 

14.5 事物的隔离级别 @Transactional(propagation=Propagation.REQUIRES_NEW,isolation=Isolation.READ_COMMITTED)

						脏读        不可重复读       幻读	
未提交读                 √           √               √
已提交读                 ×           √               √
可重复读                 ×           ×               √
可序列化                 ×           ×               ×        

14.6 事务—XML配置版

猜你喜欢

转载自blog.csdn.net/qq_41606459/article/details/86506608