Spring
IOC
Bean
- Bean的种类
普通bean:Spring直接创建实例并返回
工厂bean:具有工厂生成对象的能力,通过getObject()方法生成对象。
- Bean的生命周期
- Bean的生命周期分为Bean的定义、Bean的初始化、Bean的生存期、Bean的销毁4个部分。
Bean的定义:Spring通过配置扫描进行资源定位,定位到资源后对资源进行解析并将定义的信息进行保存,然后将Bean定义存入IOC容器中。
Bean的初始化:创建Bean的实例对象,进行依赖注入。依赖注入后为Bean实例设置属性,将Bean实例传递给后置处理器postProcessBeforeInitialization方法,调用Bean的初始化方法(通过@PostConstruct标注方法),将Bean实例传递给后置处理器postProcessAfterInitialization方法。
Bean的生存期
Bean的销毁:调用销毁方法(通过@PreDestroy定义)销毁Bean。
- bean的作用域
singleton:单例类型,在创建容器时就会创建该bean对象,每次获取时返回的都是同一个对象。(默认值)
prototype:每次获取时都返回一个新的实例。
request:每次http请求都会创建一个bean
session:同一个http session共享一个bean,不同的session用不同的bean
application:全局作用域,应用在整个web范围的实例。
globalSession:在全局HTTP Session中,一个Bean定义对应一个实例。实践过程中基本不使用。
AOP
AOP术语
- 连接点(join point):被拦截的对象,即方法。
- 切点(point cut):提供使切面适配一个连接点的功能,它能够向Spring描述那些方法需要启用AOP编程。
- 通知(advice):按照约定流程下的方法分为前置通知、后置通知、环绕通知、事后返回通知、异常通知。它会根据约定织入流程中。
- 目标对象(target):被代理对象。
- 引入(introduction):引入新的类和其方法用于加强现有Bean对象。
- 织入(weaving):通过动态代理,对与切点匹配的连接点进行拦截,并按照约定将各类通知织入流程中。
- 切面(aspect):定义切点、各类通知和引入的内容,用于增强Bean对象或将对应方法织入流程。
动态代理实现
定义拦截器接口:
package com.example.aopdemo.interceptor;
import com.example.aopdemo.invoke.Invocation;
import java.lang.reflect.InvocationTargetException;
/**
* 拦截器接口
*/
public interface Interceptor {
// 事前方法
public boolean before();
// 事后方法
public void after();
/**
* 取代原有事件的方法
* @param invocation 回调原有事件的参数
* @return 原有事件返回对象
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException;
// 是否返回方法。事件没有发生异常执行
public void afterReturning();
// 事后异常方法。事件发生异常后执行
public void afterThrowing();
// 是否使用around方法取代原有方法
boolean useAround();
}
实现拦截器:
package com.example.aopdemo.interceptor;
import com.example.aopdemo.invoke.Invocation;
import java.lang.reflect.InvocationTargetException;
public class MyInterceptor implements Interceptor{
// 事前方法
@Override
public void before() {
System.out.println("befor ......");
}
// 事后方法
@Override
public void after() {
System.out.println("after ......");
}
// 取代原有事件的方法
@Override
public Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
System.out.println("around befor ......");
Object obj = invocation.proceed();
System.out.println("around after ......");
return obj;
}
// 事后返回方法
@Override
public void afterReturning() {
System.out.println("afterReturning ......");
}
// 事后异常方法
@Override
public void afterThrowing() {
System.out.println("afterThrowing ......");
}
// 是否使用around方法取代原有方法
@Override
public boolean useAround() {
return true;
}
}
Invocation类:
package com.example.aopdemo.invoke;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Invocation {
private Object[] params;
private Method method;
private Object target;
public Invocation(Object target, Method method, Object[] params) {
this.target = target;
this.method = method;
this.params = params;
}
// 通过反射调用原方法
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, params);
}
}
代理:
package com.example.aopdemo.proxy;
import com.example.aopdemo.interceptor.Interceptor;
import com.example.aopdemo.invoke.Invocation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyBean implements InvocationHandler {
private Object target = null;
private Interceptor interceptor = null;
/**
* 绑定代理对象
* @param target 被代理对象
* @param interceptor 拦截器
* @return 代理对象
*/
public static Object getProxyBean(Object target, Interceptor interceptor) {
ProxyBean proxyBean = new ProxyBean();
// 保存被代理对象
proxyBean.target = target;
// 保存拦截器
proxyBean.interceptor = interceptor;
// 生成代理对象
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), proxyBean);
return proxy;
}
/**
* 处理代理对象方法
* @param proxy 代理对象
* @param method 方法
* @param args 运行参数
* @return 方法调用结果
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 异常标识
boolean exceptionFlag = false;
Invocation invocation = new Invocation(target, method, args);
Object retObj = null;
// 执行事前方法
this.interceptor.before();
try {
if(this.interceptor.useAround()) {
// 如果需要使用around方法覆盖原方法,则执行around方法
retObj = this.interceptor.around(invocation);
} else {
// 否则执行原方法
retObj = method.invoke(target, args);
}
} catch (Exception e) {
// 如果发生异常则进行标记
exceptionFlag = true;
}
// 执行事后方法
this.interceptor.after();
if(exceptionFlag) {
// 如果发生了异常则执行事后异常方法
this.interceptor.afterThrowing();
} else {
// 没发生异常则执行事后返回方法,并返回方法调用结果。
this.interceptor.afterReturning();
return retObj;
}
return null;
}
}
编写service接口和impl实现类,通过以下代码测试:
HelloService helloService = new HelloServiceImpl();
HelloService proxy = (HelloService) ProxyBean.getProxyBean(helloService, new MyInterceptor());
proxy.sayHello(new User("Bob", "111", 99));
得到结果:
befor ......
around befor ......
User(name=Bob, id=111, age=99)hello!
around after ......
after ......
afterReturning ......
环绕通知
切面:
package com.example.aopdemo.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
/**
* 切面
*/
@Aspect
public class MyAspect {
// 切点
@Pointcut("execution(* com.example.aopdemo.service.impl.UserServiceImpl.printUser(..))")
public void pointCut() {
}
@Before("pointCut()")
public void before() {
System.out.println("before ...");
}
@After("pointCut()")
public void after() {
System.out.println("after ...");
}
@Around("pointCut()")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("around before ...");
// 回调原方法
jp.proceed();
System.out.println("around after ...");
}
@AfterReturning("pointCut()")
public void afterReturning() {
System.out.println("afterReturning ...");
}
@AfterThrowing("pointCut()")
public void afterThrowing() {
System.out.println("afterThrowing ...");
}
}
Controller类实现:
package com.example.aopdemo.controller;
import com.example.aopdemo.pojo.User;
import com.example.aopdemo.service.HelloService;
import com.example.aopdemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/print")
@ResponseBody
public User printUser(String id, String name, int age) {
User user = new User();
user.setId(id);
user.setName(name);
user.setAge(age);
userService.printUser(user);
return user;
}
}
在Application类中定义切面:
// 定义切面
@Bean(name = "myAspect")
public MyAspect initMyAspect() {
return new MyAspect();
}
启动服务器,在浏览器输入http://localhost:8080/user/print?id=111&name=Bob&age=66,得到
{"name":"Bob","id":"111","age":66}
控制台打印:
around before ...
before ...
User(name=Bob, id=111, age=66)
afterReturning ...
after ...
around after ...
全局异常处理
全局异常处理类可以处理Controller抛出的所有异常,在其他曾遇到异常不需要处理,抛出到controller层即可。
package com.example.demo.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.sql.SQLIntegrityConstraintViolationException;
/**
* 全局异常处理
*/
@ControllerAdvice
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
/**
* 异常处理方法
* @return
*/
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public String exceptionHandler(SQLIntegrityConstraintViolationException e) {
log.error(e.getMessage());
//违反唯一约束
if(e.getMessage().contains("Duplicate entry")) {
String[] split = e.getMessage().split(" ");
String msg = split[2] + " 已存在";
return msg;
}
return "未知错误";
}
}
Spring事务
事务的特性(ACID)
- 原子性(Atomicity):一个事物的所有操作,要么全都完成,要么都不完成。如果在事务的执行过程中发生错误,则回滚到事务开始前的状态。
- 一致性(Consistency):在事务开始前和结束以后,数据库的完整性没有被破坏,即写入的资料必须完全符合所有预设约束、触发器、级联回滚等。
- 隔离性(Isolation):防止多个事务并发执行时由于交叉执行而导致数据的不一致。
- 持久性(Durability):事务结束后,对数据的修改是永久的。
Spring事务管理
- 使用@Transactional注解对类或者方法进行事务管理
- 当 @Transactional 注解的方法在类以外被调用的时候,Spring 事务管理才生效。(由于@Transactional 的工作机制是基于 AOP 实现的)
事务传播行为
- TransactionDefinition.PROPAGATION_REQUIRED(@Transactional注解默认事务传播行为):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,就在嵌套事务内执行;如果当前没有事务,则创建一个新的事务。
- TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。