Inversion of Control(控制反转),是一种实现对象解耦的设计思想
,将对象交给 Spring 中 IoC 容器管理,在其他类中不直接 new 对象,而是通过将对象传递到当前类的方式来实现解耦的,而非让程序主动去创建依赖对象,控制对象外部资源的获取 ==> 控制Dependency Injection,依赖注入,是一种具体的技术实现,在IOC容器运行期间,动态地将某个依赖对象注入到当前对象
Spring框架中广泛使用了不同类型的设计模式,如下:
生产产品:⼀个工厂最核⼼的功能就是⽣产产品。Spring不用Bean自行实例化,而是统一
交给 Spring通过反射实现
库存产品:工厂的生产管理通过工厂模式来实现,工厂⼀般都是有库房的,用来存取产品,毕竟生产的产品不能⽴马就运走。Spring容器中存的就是对象,不能每次来取对象,都得现场来反射创建对象,需要把创建出的对象存起来
订单处理:工厂通过订单来提供产品,经过处理,指导工厂的出货
在 Spring中,也有这样的订单,即bean 的定义和依赖关系,可以是 xml,也可以是注解的形式
如下,我们来简单地来实现一个mini版的Spring IOC:
userDao:com.example.interviewStudy.ioc.UserDao
hobby:com.example.interviewStudy.ioc.Hobby
package com.example.interviewStudy.ioc;
public class UserDao {
public Object getUserInfo(){
System.out.println("Spring AOP mini implementing....");
return UserDao.class.getName();
}
}
package com.example.interviewStudy.ioc;
public class Hobby {
public void play(){
System.out.println("play basketball...");
}
}
import lombok.Builder;
import lombok.Data;
@Builder
@Data
public class BeanDefinition {
private String beanName;
private Class beanClass;
}
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
/**
* 资源加载器,⽤来完成配置⽂件中配置加载
*/
public class ResourceLoader {
public static Map<String, BeanDefinition> getResource() {
Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>(16);
Properties properties = new Properties();
try {
// 1.指定从配置文件中读取配置
InputStream resourceStream = ResourceLoader.class.getResourceAsStream("/beans.properties");
properties.load(resourceStream);
Iterator<String> iterator = properties.stringPropertyNames().iterator();
// 2.依次遍历beans.properties中的配置,并将其存储到容器中
while (iterator.hasNext()) {
String key = iterator.next();
String className = properties.getProperty(key);
Class<?> clazz = Class.forName(className);
BeanDefinition beanDefinition = BeanDefinition.builder()
.beanName(key)
.beanClass(clazz)
.build();
// 将bean记录到容器中
beanDefinitionMap.put(key,beanDefinition);
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
// 3.返回容器对象
return beanDefinitionMap;
}
}
import java.util.HashMap;
import java.util.Map;
public class BeanRegister {
// 单例Bean缓存
private Map<String,Object> singletonMap = new HashMap<>(32);
public Object getSingletonBean(String beanName){
return singletonMap.get(beanName);
}
public void registerSingletonBean(String beanName,Object bean){
if (singletonMap.containsKey(beanName)){
return;
}
singletonMap.put(beanName,bean);
}
}
import java.util.HashMap;
import java.util.Map;
public class BeanFactory {
private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<>();
private BeanRegister beanRegister;
public BeanFactory(){
// 创建Bean的注册器
beanRegister = new BeanRegister();
// 加载资源
this.beanDefinitionMap = ResourceLoader.getResource();
}
/**
* 先从缓存中获取bean,缓存没有存储bean,则创建bean
*/
public Object getBean(String beanName){
Object bean = beanRegister.getSingletonBean(beanName);
if (bean != null){
System.out.println("current bean is exist,getting bean from the cache");
return bean;
}
return createBean(beanDefinitionMap.get(beanName));
}
private Object createBean(BeanDefinition beanDefinition) {
System.out.println("bean is creating...");
try {
Object bean = beanDefinition.getBeanClass().newInstance();
beanRegister.registerSingletonBean(beanDefinition.getBeanName(), bean);
return bean;
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
@Test
public static void test() {
BeanFactory beanFactory = new BeanFactory();
UserDao userDao1 = (UserDao) beanFactory.getBean("userDao");
Object info1 = userDao1.getUserInfo();
System.out.println(info1);
UserDao userDao2 = (UserDao) beanFactory.getBean("userDao");
Object info2 = userDao2.getUserInfo();
System.out.println(info2);
Hobby hobby = (Hobby) beanFactory.getBean("hobby");
System.out.println(hobby);
Hobby hobby1 = (Hobby) beanFactory.getBean("hobby");
System.out.println(hobby1);
}
由于时间+篇幅的限制,当前demo比较简陋,没有面向接口、没有解耦、边界检查、异常处理,扩展性、健壮性都有很大不足,但基本存取bea的功能已然实现
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
public class PersonBean implements InitializingBean, BeanFactoryAware, BeanNameAware, DisposableBean {
// 身份证号
private String perNo;
// 姓名
private String pname;
public String getPerNo() {
return perNo;
}
public void setPerNo(String perNo) {
this.perNo = perNo;
}
public String getPname() {
return pname;
}
public PersonBean(){
System.out.println("1.调用构造方法,创建Bean ==> 我出生了");
}
public void setPname(String pname) {
this.pname = pname;
System.out.println("2.为属性赋值,我的名字是: " + pname);
}
@Override
public void setBeanName(String pname) {
System.out.println("3.调⽤BeanNameAware#setBeanName方法:" + pname + "要上学了..");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("4.调⽤BeanFactoryAware # setBeanFactory方法:选好学校了\"");
}
// 5.实现BeanPostProcessor接口,通过postProcessBeforeInitialization方法完成 初始化的前置操作
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("6.实现InitializingBean # afterPropertiesSet方法,入学登记了..");
}
public void init(){
System.out.println("7.自定义init方法,努力上学中...");
}
// 8.实现BeanPostProcessor接口,通过 postProcessAfterInitialization 方法完成 初始化的后置操作
@Override
public void destroy() throws Exception {
System.out.println("9.实现DisposableBean # destroy方法,平淡的圆满谢幕了!");
}
public void destroyMethod(){
System.out.println("10.自定义destroy方法,累了困了,让我在土里躺一会儿...");
}
public void work(){
System.out.println("使用/管理Bean中, 我要努力工作,只有对社会没用的人才放假");
}
}
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("5.BeanPostProcessor.postProcessBeforeInitialization方法:到学校报名啦");
System.out.println("bean: " + bean.toString());
System.out.println("beanName: " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("8.BeanPostProcessor#postProcessAfterInitialization方法:终于毕业,拿到毕业证啦!");
return bean;
}
}
<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 name="myBeanPostProcessor" class="com.example.interviewStudy.bean.MyBeanPostProcessor"/>
<bean name="personBean" class="com.example.interviewStudy.bean.PersonBean"
init-method="init" destroy-method="destroyMethod">
<property name="perNo" value="42607619450872234"/>
<property name="pname" value="张晓静"/>
bean>
beans>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestLifeCycle {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
PersonBean personBean = (PersonBean) context.getBean("personBean");
personBean.work();
((ClassPathXmlApplicationContext)context).destroy();
}
}
Bean 创建过程见AbstractBeanFactory # doGetBean方法,在该方法中可见
Bean 的实例化,赋值、初始化的过程,Bean的销毁,可见ConfigurableApplicationContext # close()
byName
:根据Bean的名称自动匹配,假设Person有⼀个名为 car 的属性,如果容器中刚好有⼀个名为 car 的 bean,Spring 就会⾃动将其装配给Person的 car 属性byType
:根据Bean的类型自动匹配,假设Person有⼀个 Car 类型的属性,如果容器中刚好有⼀个Car 类型的 Bean,Spring 就会⾃动将其装配给Person这个属性constructor
:与byType类似,只不过是针对构造函数而言的,如果Person有⼀个构造autodetect
:根据 Bean 的自省机制决定采用byType 还是 constructor进行自动装配,如果Bean 提供了默认的构造函数,则采用byType,否则采⽤ constructorSpring中的Bean支持五种作用域:
public class UserHolder {
private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();
public static void saveUser(UserDTO user){
tl.set(user);
}
public static UserDTO getUser(){
return tl.get();
}
public static void removeUser(){
tl.remove();
}
}
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
Spring缓存实现见DefaultSingletonBeanRegistry类:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** Maximum number of suppressed exceptions to preserve. */
private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 略
}
发现A存在但不完整,就将A放入二级缓存中,同时删除掉三级缓存中的A
,如此,B就完成了实例化到初始化的过程,B被放入到一级缓存中@Aspect
注解声明当前类为切面,一般要在切面定义切入点和通知// 以自定义注解 @CustomLog为切点
@Pointcut("@annotation(com.example.interviewStudy.annotation.CustomLog)")
public void logPcut() {
}
AOP有五种通知的方式:
当有多个切面的情况下,可以通过 @Order指定先后顺序,数字越小,优先级越高
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.7.8version>
<relativePath/>
parent>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
import java.lang.annotation.*;
@Target({ElementType.METHOD}) // 指定注解使用在方法上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomLog {
String info();
}
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
@Aspect // 标识当前类为切面
@Component
public class CustomLogAspect {
// getLogger(Class> clazz)
public static final Logger logger = LoggerFactory.getLogger(CustomLogAspect.class);
// 以自定义注解 @CustomLog为切点
@Pointcut("@annotation(com.example.interviewStudy.annotation.CustomLog)")
public void logPcut() {
}
// 前置通知: 在切点之前织入
@Before("logPcut()")
public void doBefore(JoinPoint joinPoint) throws JsonProcessingException {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
logger.info("========== 开始打印请求参数 ===========");
logger.info("URL: {}", request.getRequestURL().toString());
logger.info("HTTP Method: {}", request.getMethod());
logger.info("Controller的全路径 和 执行方法: {} , {}方法", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
logger.info("请求入参:{}", new ObjectMapper().writeValueAsString(joinPoint.getArgs()));
}
// 后置通知,在切入点之后织入
@After("logPcut()")
public void doAfter() {
logger.info("======== 请求日志输出完毕 ========");
}
/**
* 环绕通知: ProceedingJoinPoint对象调用proceed方法,实现 原本目标方法的调用
* ProceedingJoinPoint 只支持环绕通知,如果其他通知也采用ProceedingJoinPoint作为连接点,就会出现异常
* ==> Caused by: java.lang.IllegalArgumentException: ProceedingJoinPoint is only supported for around advice
* */
@Around("logPcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
logger.info("请求结果: {}", new ObjectMapper().writeValueAsString(result));
logger.info("请求处理耗时: {} ms", (end - start));
return result;
}
}
@RestController
@RequestMapping("/aop")
public class CustomAspectController {
@GetMapping("/hello")
@CustomLog(info = "hello,使用AOP实现请求日志输出")
public String hello(String uname) {
return "Hello,welcome to studing AOP, your name is " + uname;
}
}
我们来看⼀个常见的⼩场景,客服中转,解决⽤户问题:
代码实现:
public interface ISolver {
public String solve();
}
public class ProblemSolver implements ISolver{
@Override
public String solve() {
System.out.println("ProblemSolver,solve方法 ==> 问题正在解决中...");
return "OKK";
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
// 维护一个目标对象
private Object target;
public ProxyFactory(Object target){
this.target = target;
}
// 为目标对象生成代理对象
public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("请描述您的问题:");
// 通过反射机制 调用目标对象方法
Object result = method.invoke(target, args);
System.out.println("问题已经得到解决!!");
// 返回 目标对象方法的返回值
return result;
}
});
}
}
public class Client {
public static void main(String[] args) {
ISolver developer = new ProblemSolver();
// 创建代理对象实例
ISolver instance = (ISolver) new ProxyFactory(developer).getProxyInstance();
// 代理对象调用目标对象方法,得到目标方法的返回值并输出
String res = instance.solve();
System.out.println(res);
}
}
public class CglibSolver {
public String solve(){
System.out.println("Testing implement proxy by cglib");
return "CglibSolver ==> solve方法";
}
}
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class ProxyFactory implements MethodInterceptor, Callback {
private Object target;
public ProxyFactory(Object target){
this.target = target;
}
public Object getProxyInstance(){
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(target.getClass());
// 设置回调函数,用于监听当前事件
enhancer.setCallback(this);
// 创建子类对象代理
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("请问有什么可以帮到您?");
// 调用目标对象的方法
Object result = method.invoke(target, args);
System.out.println("问题得到解决啦哈!");
return result;
}
}
public class CgClient {
public static void main(String[] args) {
CglibSolver solver = new CglibSolver();
// 创建代理对象
CglibSolver proxy = (CglibSolver) new ProxyFactory(solver).getProxyInstance();
// 通过代理对象实例调用目标对象方法
String result = proxy.solve();
System.out.println("result : " + result);
}
}
Spring AOP属于运行时增强,主要具有如下特点:
AspectJ是功能强大的AOP框架,属于编译时增强
,可以单独使用,也可以整合到其他框架中,是AOP编程的完全解决方案
AspectJ属于静态织入,通过修改代码来实现,在实际运行之前就完成了织入,生成的类没有额外运行时开销,可织入时机如下:
A、编译期织入(Compile-time weaving):如 A类使用AspectJ添加了某属性,B类引用了A类,该场景就需要编译期进行织入,否则没法编译B类
B、编译后织入(Post-compile weaving):在已生成了字节码/ class文件,或已经打包成jar包后,该情况需要增强,就需要使用到编译后织入
C、类加载后织入(Load-time weaving):在加载类时进行织入
Spring支持编程式事务
和声明式事务
管理两种方式:
1)编程式事务管理:使用TransactionTemplate,需要显示地执行事务
2)声明式事务管理:建立在AOP之上,其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,即在目标方法开始之前启动事务,在执行完目标方法之后根据执行情况 进行提交或回滚事务
TransactionDefinition.PROPAGATION_SUPPORTS
:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行TransactionDefinition.PROPAGATION_NOT_SUPPORTED
:以非事务方式运行,如果当前存在事务,则将当前事务挂起TransactionDefinition.PROPAGATION_NEVER
:以非事务方式运行,如果当前存在事务,则抛出异常// 希望自定义的异常可以进行回滚
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = MyException.class)
如果在目标方法中抛出的异常为rollbackFor指定的异常子类,事务同样回滚
org.springframework.transaction.UnexpectedRollbackException:Transaction rolled back because i t has been marked a s rollback - only
未完待续…
SpringMVC虽然整体流程复杂,但大部分的组件不需要开发人员创建和管理,只需要通过配置文件的方式完成配置即可,真正需要开发人员处理的只有Handler(Controller)、View、Model
@GetMapping("/user")
@ResponseBody
public User user() {
return new User(1,"张三");
}
加入@ResponseBody注解后,整体流程和ModelAndView大致相同,只是在细节上有所不同:
1)客户端向服务端发送请求,该请求会先到达前端控制器DispatcherServlet
2)DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器,由此得知,该请求应由哪个Controller来处理
3)DispatcherServlet 调用HandlerAdapter处理器适配器,告知处理器适配器应该要去执行哪个Controller
4)Controller被封装成了 ServletInvocableHandlerMethod,HandlerAdapter处理器适配器去执行invokeAndHandle方法,完成对Controller的请求处理
5)HandlerAdapter执行完对Controller的请求,会调用HandlerMethodReturnValueHandler去处理返回值,主要的过程如下:
A、调用RequestResponseBodyMethodProcessor,创建ServletServerHttp实例(该实例是Spring对原生ServerHttpResponse的封装)
B、使用HttpMessageConverter的write方法,将返回值写入ServletServerHttpResponse的OutputStream 输出流中
C、在写入的过程中,使用JsonGenerator(默认使用Jackson)对返回值进行Json序列化
6)执行完请求后,返回的ModelAndView为null,ServletServerHttpResponse中也写入响应,所以不用关心View的处理
约定大于配置
的思想来实现,相比Spring的优势如下:@SpringBootApplication
注解是一个复合注解,包含@EnableAutoConfigurationAutoConfigurationImportSelector
类实现的package org.springframework.boot.autoconfigure;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.support.SpringFactoriesLoader;
/**
* @see ConditionalOnBean
* @see ConditionalOnMissingBean
* @see ConditionalOnClass
* @see AutoConfigureAfter
* @see SpringBootApplication
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class) // 加载自动装配类
public @interface EnableAutoConfiguration {
/**
* 当自动配置生效时,可用于重写环境属性
*/
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* 排除那些没有被使用的、具体的自动配置类
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* 排除那些没有被使用的、具体的自动配置类的类名
* @return the class names to exclude
*/
String[] excludeName() default {};
}
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
/**
* @return the auto-configurations that should be imported
* 返回需要导入的自动装配
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 1.获取到注解的属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 2.获取需要自动装配的所有配置类 ==》读取META-INF/spring.factories,读取自动配置类的路径
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 3.1、移除重复的配置
configurations = removeDuplicates(configurations);
// 3.2、处理需要排除的配置
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}