1.mvc架构
model(模型:service、daomapper);view(视图:jsp);controller(控制:servlet)
1.用户发请求 2.servlet接受请求数据,并调用对应的业务逻辑方法 3.业务处理完毕,返回更新后的数据给servlet 4.servlet转向到jsp,jsp渲染页面 5.响应给前端更新后的页面
C:控制器 1.取得表单数据/json数据 2.调用业务逻辑 3.转向指定页面 M:模型 1.业务逻辑 2.保存数据的状态 V:视图 1.显示页面
ioc的实现方式是di
-di依赖注入-依赖注入有属性注入,set方法注入和构造器注入
;@Autowire是属性注入创建
-反射允许在运行时动态的检查类的信息,更灵活的实现对象的实例化和管理初始化
-管理对象之间的依赖关系,将组件的依赖关系描述在配置文件或使用注解销毁
-控制bean的实例化和生命周期Spring 框架为现代基于 java 的企业应用程序提供了一个全面的编程和配置模型——适用于任何类型的部署平台。
ioc要答到依赖注入 & 反射,aop要答到核心业务与周边功能 & 动态代理 要是说有哪些组成,就是切面=切点+通知
例子:使用ioc管理service层和daomapper层的依赖关系,aop去将横切关注点切到业务逻辑中,实现日志等横切功能
自定义切面类
/**
* 自定义切面类
* 切面=通知+切入点
* 是切面就加@aspect
*/
@Component
@Slf4j
@Aspect
public class AutoFillAspect {
// 切入点
// pointCut指的是拦截哪些方法
// com.sky.mapper.*.*(..) *.* :所有的类所有的方法 ..指的是匹配所有参数
// execution(* 这个*没有别的意思
// @annotation(com.sky.annotation.AutoFill) 拦截有这个注释的
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut(){}
// 通知:什么时候拦截
/**
* 自定义前置通知,在通知中,进行公共字段的复制
*/
@Before("autoFillPointCut()") // 自定义切点表达式
public void autoFill(JoinPoint joinPoint){
System.out.println("开始进行公共字段自动填充...");
// 1.获得被拦截的方法对应的数据库操作类型
// 获得signature 强转为方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// signature中得到注解
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
// 得到注解的值
OperationType operationType = autoFill.value();
// 2.得到相应的方法的参数 --> 实体对象
Object[] args = joinPoint.getArgs();
// 保险操作
if (args==null || args.length==0){
return;
}
// 直接得到args,**确保实体类是第一个参数**
Object entity = args[0];
// 3.准备数据
LocalDateTime now = LocalDateTime.now();
Long id = BaseContext.getCurrentId();
// 4.根据不同方法,为对应的属性通过反射进行赋值
if (operationType==OperationType.INSERT){
try {
Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
setCreateTime.invoke(entity, now);
setUpdateTime.invoke(entity, now);
setCreateUser.invoke(entity, id);
setUpdateUser.invoke(entity, id);
} catch (Exception e) {
e.printStackTrace();
}
} else if (operationType==OperationType.UPDATE){
try {
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
setUpdateTime.invoke(entity, now);
setUpdateUser.invoke(entity, id);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
1.ioc实现机制:
2.aop实现机制
有两种动态代理:基于jdk \基于cglib
根据配置加载并实例化相关bean,保存到ioc容器中
依赖注入是一种编码方式,不直接在类中new依赖类的对象,而是在外面完成依赖类的对象创建,通过属性注入、set注入、构造函数注入等完成依赖关系的设置 依赖倒置:高层模块不依赖低层模块,他们共同依赖同一个抽象,抽象不要依赖具体实现的细节,具体实现细节依赖抽象
非核心功能的整合,面向对象思维的一个补充
动态代理
因为Java是单继承的,而代理类又必须继承自Proxy类,所以通过jdk代理的类必须实现接口。
/**
* 自定义切面类
* 切面=通知+切入点
* 是切面就加@aspect
*/
@Component
@Slf4j
@Aspect
public class AutoFillAspect {
// 切入点
// pointCut指的是拦截哪些方法
// com.sky.mapper.*.*(..) *.* :所有的类所有的方法 ..指的是匹配所有参数
// execution(* 这个*没有别的意思
// @annotation(com.sky.annotation.AutoFill) 拦截有这个注释的
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut(){}
// 通知:什么时候拦截
/**
* 自定义前置通知,在通知中,进行公共字段的复制
*/
// 自定义切点表达式/自定义切点的方法
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint){
System.out.println("开始进行公共字段自动填充...");
// 1.获得被拦截的方法对应的数据库操作类型
// 获得signature 强转为方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// signature中得到注解
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
// 得到注解的值
OperationType operationType = autoFill.value();
// 2.得到相应的方法的参数 --> 实体对象
Object[] args = joinPoint.getArgs();
// 保险操作
if (args==null || args.length==0){
return;
}
// 直接得到args,**确保实体类是第一个参数**
Object entity = args[0];
// 3.准备数据
LocalDateTime now = LocalDateTime.now();
Long id = BaseContext.getCurrentId();
// 4.根据不同方法,为对应的属性通过反射进行赋值
if (operationType==OperationType.INSERT){
try {
Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
setCreateTime.invoke(entity, now);
setUpdateTime.invoke(entity, now);
setCreateUser.invoke(entity, id);
setUpdateUser.invoke(entity, id);
} catch (Exception e) {
e.printStackTrace();
}
} else if (operationType==OperationType.UPDATE){
try {
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
setUpdateTime.invoke(entity, now);
setUpdateUser.invoke(entity, id);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
切面=切点+通知
反射的特性/可以做到什么:
用到的地方:
1.ioc:
用反射实现了依赖注入。开发者会写xml配置文件/注解{@autowire}来表明依赖关系,当spring启动时,扫描到这些注解/配置文件,他就会通过反射实例化bean,并完成依赖关系的装配
2.动态代理的实现: 对现有的类方法进行加强,就会用到反射结合动态代理的技术
典例:aop
反射机制促进了松耦合高内聚
spring只解决了通过set方法进行依赖注入且是在单例模式下产生的依赖注入问题
通过三级缓存,核心是一个通过构造函数先实例化,放入缓存,然后再属性注入:
当 A、B 两个类发生循环引用时,在 A 完成实例化后,就使用实例化后的对象放到二级缓存去创建一个对象工厂,并添加到三级缓存中 如果 A 被 AOP 代理,那么,通过这个工厂获取到的就是 A 代理后的对象,如果 A 没有被 AOP 代理,那么,这个工厂获取到的就是 A 实例化的对象。
当 A 进行属性注入时,会去创建 B,同时,B 又依赖了 A,所以,创建 B 的同时又会去调用 getBean(a) 来获取需要的依赖,此时的 getBean(a) 会从缓存中获取,工厂起到一个提前暴露bean的作用,第一步,先获取到三级缓存中的工厂;第二步,调用工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到 B 中。紧接着 B 会走完它的生命周期流程,包括初始化、后置处理器等。当 B 创建完后,会将 B 再注入到 A 中,此时, A 再完成它的整个生命周期。
通过上述流程,可以看出 bean 都是需要先被实例化才可以的,所以,这也就是为什么构造器依赖可能会失败的原因。
例如,Bean A 的构造器依赖 B,而实例化 A 需要先调用 A 的构造函数,发现依赖 B,那么,需要再去初始化 B,但是,B 也依赖 A,不管 B 是通过构造器注入还是 setter 注入,此时,由于 A 没有被实例化,没有放入三级缓存,所以, B 无法被初始化,所以,spring 会直接报错。反之,如果 A 通过 setter 注入的话,那么,则可以通过构造函数先实例化,放入缓存,然后再填充属性,这样的话不管 B 是通过 setter 还是构造器注入 A,都能在缓存中获取到,于是可以初始化。
三级都是map数据结构
一级缓存singletonObjects|单例池:存放已经完全实例化的bean
二级缓存earlySingletonObjects|早期曝光对象:存放初始化完成了,但是可能还没有属性还没有赋值的bean
三级缓存singletonFactories|早期曝光对象工厂:存放的是 ObjectFactory 的匿名内部类实例,调用 ObjectFactory.getObject() 最终会调用 getEarlyBeanReference 方法,该方法可以获取提前暴露的单例 Bean 引用。解决循环依赖的问题。
@autowire:自动装配bean @component:将bean注册到ioc容器中,并实例化 @configuration:means spring的配置类;配置类可以有@bean的方法,用于定义和配置bean,作为全局配置 @bean:当一个方法被@bean以后,spring 会将该方法的返回值作为一个bean,并添加到ioc容器 @repository:数据访问层组件| 同@mapper
不能生效 上一题图片就是说的这个
1.初始化spring容器
2.将配置类的beanDefinition注册到容器中
3.调用refresh方法刷新容器
所谓的生命周期就是:对象从创建开始到最终销毁的整个过程
图片有错误应该initializingBean的调用和初始化同属一个环节初始化:在属性赋值和BeanPostProcessor前置处理之后,容器会调用Bean的初始化方法,这个初始化方法可以是自定义的初始化方法(通过@Bean(init-method)或@PostConstruct注解声明)或者实现了InitializingBean接口的afterPropertiesSet()方法。
如果你还想在初始化前和初始化后添加代码,可以加入“Bean后处理器”;需要编写一个类实现BeanPostproccessor接口,并重写里面的postProcessBeforeInitialization()和postProcessAfterInitialization()方法
aware的接口需要看执行了什么方法决定他在哪里?Aware相关的接口包括:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware ①当Bean实现了BeanNameAware,Spring会将Bean的名字传递给Bean。 ②当Bean实现了BeanClassLoaderAware,Spring会将加载该Bean的类加载器传递给Bean。 ③当Bean实现了BeanFactoryAware,Spring会将Bean工厂对象传递给Bean。
调用initializingBean接口的afterPropertiesSet()
调用disposableBean的destroy()
Bean生命周期流程总结
默认单例,如果是多例,在bean的定义中通过设置scope="prototype"中文名原型
单例bean的生命周期完全交由ioc容器管理;原型的bean就是创建好以后,容器不在管理
单例 | 原型 | request | session | application | websocket
bean对象
初始化前后,实现后处理器接口PostProcessor,重写before() & after()
实现initializingbean接口,实现afterPropertiesSet()
disposableBean接口的destory()
都是会有一个beanDefinitionReader去读取特定的东西,如果是xml就是配置文件,如果是bean就是注解,读完以后解析成beandefinition注册到容器里,方便之后的实例化初始化。
postProcessor:在初始化之前、之后,添加额外的代码
PropertySource:定义不同的属性源数据源
SpringMVC的HandlerInterceptor:用于拦截处理请求,可以在处理请求前、中、后执行特定逻辑,how?
mvcconfig extend MessageConverters/implements mvcconfigurer
拦截器继承
/**
* jwt令牌校验的拦截器
*/
@Component
@Slf4j
public class JwtTokenUserInterceptor implements HandlerInterceptor {
@Autowired
private JwtProperties jwtProperties;
/**
* 校验jwt
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断当前拦截到的是Controller的方法还是其他资源
if (!(handler instanceof HandlerMethod)) {
//当前拦截到的不是动态方法,直接放行
return true;
}
//1、从请求头中获取令牌
String token = request.getHeader(jwtProperties.getUserTokenName());
// System.out.println("jwtProperties.getAdminTokenName() = " + jwtProperties.getAdminTokenName());
// 名字就是token
//2、校验令牌
try {
log.info("jwt校验:{}", token);
Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token); // 解析令牌
Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());
log.info("当前用户id:{}", userId);
// threadlocal当同一个请求(就是同一个线程),可以用它存当前用户的id
BaseContext.setCurrentId(userId);
//3、通过,放行
return true;
} catch (Exception ex) {
//4、不通过,响应401状态码
response.setStatus(401);
return false;
}
}
}
他是model - view - controller的缩写,一种软件设计典范,将业务逻辑model,界面分离,这样组织代码
model(模型:业务bean:service、daomapper;数据承载的bean:pojo、dto、vo);view(视图:jsp);controller(控制:servlet)
流程步骤: 1.用户通过view界面向服务端发请求 2.Controller层接受请求数据,并调用对应的业务逻辑方法Model 3.业务处理完毕,返回更新后的数据给Controller层承上启下的作用 4.controller层找到相应的view界面,jsp渲染页面,响应给前端更新后的页面
1.客户端向服务器发送请求,被dispatcherServlet接收到 2.dispatcherServlet调handlermapping解析url,得到请求资源标识符uri,判断请求对应的handler映射;handlermapping:主要用来解析请求,解析出控制器 3.如果存在,执行:获得handler的所有相关对象(包括他的拦截器),以执行链的形式返回 4.根据handler,选择合适的handleradapter。{handleradapter:HandlerAdapter主要是{不同的调度方法}调度Controller来处理业务逻辑等,相关类有6个。} 5.成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】 6.提取request的数据,执行controller里面的方法 7.Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。 8.拦截器的posthandler() 逆向拦截 9.根据返回的ModelAndView选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图. 10.aftercompletion() 逆向拦截 11.渲染结果返回给前端
请求-->dispatcherServlet-mapping->handler以执行链的形式-handlerAdapter->modelandview-viewresolver->返回前端
handler也是controller
handlermapping是根据url/请求将映射到对应处理器controller确定对应的处理器
handleradapter:负责调度controlller处理业务
1.构造器注入{离开spring常用这个,一旦初始化没法改};2.set方法注入;{前两个是我们常用的没有spring的时候手动注入的方法}3.属性注入{最常用}@autowired适用于三种方式,加在构造器上是构造器注入,创建的时候就给他注入,set方法上是set注入{调用set的时候注入},直接在类上是属性注入,简单!
依赖注入:不会再类里面去创建依赖类的对象;通过以上三种方法将在外部创建好的依赖类注入到类中
注解的底层都是反射
该模式用于封装和管理对象的创建,是一种创建型模式。
1.简单工厂类:SimpleFactory
简单工厂类(SimpleFactory):只包含创建产品的静态方法。 抽象产品父类(Product):简单工厂类中生产的产品接口,声明了产品的抽象方法。 具体产品子类(ProductA 或ProductB extends Product):具体产品的实现
类FruitFactory 就是一个简单的工厂,将创建对象(苹果或香蕉)的工作单独负责下来,使得外界调用统一的方法 createFruit()即可完成不同对象创建(生产苹果或香蕉)。
2.工厂方法模式
是对简单工厂模式的改进,它去掉了简单工厂模式中工厂方法(例如,createFruit()这个方法)的静态属性{静态方法不可以被重写},使得该方法能够被子类继承,将简单工厂模式中在静态工厂方法中集中创建对象的操作转移到各子类中完成,从而减轻了父类方法的负担。
抽象工厂类(AbstractFactory):工厂方法模式的核心,是具体工厂类必须实现的接口或者必须继承的抽象父类。 具体工厂类(ConcreteFactoryA 或者 ConcreteFactoryB):由具体的类来实现,用于创建工厂类的对象,它必须实现抽象工厂的方法。 抽象产品类(Product):具体产品继承的抽象父类或者实现的接口,定义了产品类的方法。 具体产品类(ProductA 或ProductB):具体工厂类产生的对象就是具体产品类的对象。
3.抽象工厂模式
抽象工厂模式又是工厂方法模式的升级版本。它的主要思想:提供了一个创建一系列相关或者相互依赖对象的接口。
它和工厂方法模式的区别:抽象工厂模式有创建多个不同类型的对象的方法,针对的是有多个产品(称为产品族)的创建模式(苹果厂生产苹果、苹果脯;香蕉厂生产香蕉、香蕉干);而工厂方法针对的只是一种产品的创建模式(苹果厂生产苹果;香蕉厂生产香蕉)。抽象工厂模式中的抽象工厂接口里有多个工厂方法。
抽象工厂模式包括以下主要角色:
抽象工厂类(AbstractFactory):模式的核心,是具体工厂类必须实现的接口或者必须继承的抽象父类。 具体工厂类(ConcreteFactoryA 或者 ConcreteFactoryB):由具体的类来实现,用于创建工厂类的对象,它必须实现抽象工厂的方法。 抽象产品(AbstractProductA和AbstractProductB):具体产品继承的父类或者实现的接口,在Java中由抽象类或者接口实现。 具体产品(ProductA或ProductB):具体工厂类产生的对象就是具体产品类的对象。每一个抽象产品都可以有多个具体产品实现类或者继承子类,称为产品族。
简化开发:开箱即用的组件template,数据库用的,使得开发人员专注于业务逻辑的实现
快速启动:内嵌tomcat,无需额外部署
自动装配,快速启动
适配器模式:mvc中用到了对于不同类型的controller适配器模式,handlerAdapter是同一的接口,里面还定义了不同的适配器类
1.约定优于配置:约定了项目结构,约定了bean的命名规范,减少开发者做出的决策,保证团队开发的一致性 2.自动化配置:可以根据项目的环境和依赖,配置应用程序,如果项目引入了mvc依赖,springboot会自动配置一个基本的web应用程序上下文 3.起步依赖:快速搭建框架
中间的service层和manager层可以简单的混为一谈
原理是根据条件化配置和@enableAutoConfiguration注解实现的,通过依赖自动配置应用程序的上下文和功能
介绍理解:
那就是在我们的 HelloService 中通过 @Autowired 注入了一个 RedisTemplate 类,但是我们的代码中并没有写过这个类,也没有使用类似于@RestController,@Service 这样的注解将 RedisTemplate 注入到 Spring IoC 容器中,那为什么我们就可以通过 @Autowired 注解从 IoC 容器中获取到 RedisTemplate 这个类呢?这里就是常说的自动装配的功能了。
其中本质上自动装配的原理很简单,本质上都需要实现一个配置类,只不过这个配置类是官方帮我们创建好了,再加了一些条件类注解,让对应的实例化只发生在类路径存在某些类的时候才会触发。
启动类上面有一个 @SpringBootApplication 注解 -->@EnableAutoConfiguration 注解 --> @Import({AutoConfigurationImportSelector.class}),导入了一个 AutoConfigurationImportSelector 类,该类间接实现了 ImportSelector 接口,实现了一个 String[] selectImports(AnnotationMetadata importingClassMetadata) 方法,这个方法的返回值是一个字符串数组,对应的是一系列主要注入到 Spring IoC 容器中的类名。当在 @Import 中导入一个 ImportSelector 的实现类之后,会把该实现类中返回的 Class 名称都装载到 IoC 容器中。一旦被装载到 IoC 容器中过后,我们在后续就可以通过 @Autowired 来进行使用了。
启动类上面有一个 @SpringBootApplication 注解 -->@EnableAutoConfiguration 注解 --> @Import({AutoConfigurationImportSelector.class}) -->selectImports()返回值是string数组,为一系列主要注入到 Spring IoC 容器中的类名 -->getAutoConfigurationEntry() -->getCandidateConfigurations() --> ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
String location = String.format("META-INF/spring/%s.imports", annotation.getName());
其中的 ImportCandidates.load 方法我们可以看到是通过加载 路径{src/main/resources/meta-inf/spring.factories}String location = String.format("META-INF/spring/%s.imports", annotation.getName()); 对应路径下的 org.springframework.boot.autoconfigure.EnableAutoConfiguration 文件,其中就包含了很多自动装配的配置类,并且这些配置类中就有我们需要的很多bean。在配置类上面有一个 @ConditionalOnClass({RedisOperations.class}) 注解,表示只要在类路径上有 RedisOperations.class 这个类的时候才会进行实例化。这也就是为什么只要我们添加了依赖,就可以自动装配的原因。
1.创建maven项目,添加相关的依赖 2.在main/resources/meta-inf/spring.factories添加一个自定义的配置类,并且创建该类里面有一个bean,有一个配置属性类,加上@configuration的注解和@EnableConfigurationProperties用于启用我们自定义的配置属性类 3.创建配置属性类:加上@ConfigurationProperties,绑定配置文件中的属性 4.在application.yml中配置属性
jdbc是什么:Java DataBase Connectivity(Java语言连接数据库)
不用像jdbc那样打开关闭连接;提供对象字段映射,提供对象关系映射这里其实就是在定义数据库字段和Java属性之间的映射关系!重点:一对多;与spring集成
加载数据库驱动:不一样的数据库的驱动不一样 建立数据库连接连接:url name password 创建statement对象 executeupdate/executequery 处理查询结果 关闭连接
配置mybatis
mybatis:
#mapper配置文件
mapper-locations: classpath:mapper/*.xml
# 简化类型映射,在MyBatis的XML配置文件中,类型映射通常需要指定完整的类名{全限定名},用这个只用类名
type-aliases-package: com.sky.entity
configuration:
#开启驼峰命名!!! 重要
map-underscore-to-camel-case: true
创建实体类 编写mapper接口:创建数据库操作方法 编写sql映射文件{xml}:定义sql语句和映射关系 编写具体sql语句 调用查询方法
"#":防sql注入,会采用预编译的方式 "$":简单的拼接,不能防止sql注入的安全问题
简化crud操作,自动生成mapper接口以及xml映射文件{即编写sql映射文件xml},通用方法的封装:排序、分页查询
适配器 mybatis对jdbc不同数据源的适配{我们在springboot中会配置datasource}
分布式系统是独立计算机的集合,对于用户来说访问分布式系统就像访问一个单个系统
目的是利用更多的机器处理更多的数据{业务模块放在单机放不下}
两台机相互调用的方式http&rpc
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。