框架和中间件
Spring Boot
AutoConfiguration 自动配置类,启动核心
导入的类是注解实现的关键
Spring
依赖对象的获得被反转了
例如,要获得一个Person对象,朴素的方法是取new 一个Person对象
而在控制反转,把创建对象的权利交给了别人,别人给你一个对象,直接拿来用就可以了(不是所有的对象都不用自己创建了,而是核心的,能够复用的对象由别人创建)
在编程中,有很多对象需要共用,之间有耦合、依赖关系,如果自己去维护,可能会出现问题,有可能业务发生变化的时候,会很麻烦;如果有了依赖注入,交给别人去管理,可以帮我们解决
框架的开发者,用BeanFcatory,本质上是工厂,用来生产bean的
框架的使用者,用ApplicationContext
装载bean,bean未必来源于磁盘上,jar包,也可能来源于内存,网络,数据库,动态代理创建bean
bean的获取需要有人来完成,resourceLoader加载resource,从哪里加载resource接口有不同的实现,resourceLoader从不同的渠道把所要的bean信息加载到内存中,然后把信息读到以后,封装到beanDefinition里,于是就得到了抽象的bean
可以清晰的看到这里接口里面的方法,是判断资源的类型,是对资源访问的定义,可见,它代表的是一类资源
ResourceLoader用来加载resource
用这两个接口就可以把资源加载到内存里
BeanDefinition是对所有bean的一个抽象
BeanDefinition抽象的实现AbstractBeanDefiniton
构造器,初始化bean的过程
初始化完以后,由BeanDefinitionRegistry注册到容器里,其实就是通过容器的方法,装到HashMap里
存放和删除的方法:
刚刚是bean初始化和放到容器里面
但是bean之间还有依赖关系,依赖注入是重中之重
从左到右调用
BeanFactory和FactoryBean有什么关系?在这里体现
缓存中没有,从当前工厂找,找不到,从父亲里找
mdb就是beanDefinition
FactoryBean不是简单的bean,它能修饰别的bean,能产生别的bean
例子:
首先一个普通的类AlphaObject,没有任何注解,启动的时候当然是不会放到spring容器里,
package com.nowcoder.seckill;
public class AlphaObject {
private String info;
public AlphaObject(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "AlphaObject{" +
"info='" + info + '\'' +
'}';
}
}
实现了FactoryBean接口,实际要产生的对象是AlphaObject这种类型的
现在有了这个注解,实现了这个接口,spring不会把AlphaObjectFactoryBean装到容器里,而是把接口所产生的对象装到容器里,装的是第一个方法返回的对象
所以FactoryBean的作用是装载一个特殊的对象,而不是这个对象本身
但是为什么不直接在那个类上加注解,放到容器里呢。这是因为有时候那个类可能不是自己写的,不能改,所以写这样一个类,由这个类来实现装到容器里;或者AOP底层是由动态代理实现的,如果动态代理现生成一个对象,可以用这种方式来处理
FactoryBean是为了装载比较复杂的,不好创建的目标bean
package com.nowcoder.seckill;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;
@Component("alphaObject")
public class AlphaObjectFactoryBean implements FactoryBean<AlphaObject> {
@Override
public AlphaObject getObject() throws Exception {
System.out.println("初始化AlphaObject...");
return new AlphaObject("alpha");
}
@Override
public Class<?> getObjectType() {
return AlphaObject.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public String toString() {
return "AlphaObjectFactoryBean{}";
}
}
测试类
package com.nowcoder.seckill;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@SpringBootTest
public class AlphaObjectFactoryBeanTest implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Autowired
private AlphaObject alphaObject;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Test
public void test1() {
// alphaObject由AlphaObjectFactoryBean产生
System.out.println(alphaObject);
// 通过name取到的是目标Bean
System.out.println(applicationContext.getBean("alphaObject"));
// 通过&name取到的是FactoryBean本身
System.out.println(applicationContext.getBean("&alphaObject"));
}
}
例如,要在很多方法开始的时候记录日志,我们可以把这个分散的关注点分离出来,封装一下,不在每一个地方分散的调用了
切面就是分散的关注点
被代理的对象:就是分散的对象,有共同需求的分散的对象
连接点:被拦截对象的方法
切面:解决问题的类,在统一的类中编程,这个位置就叫做切面
无接口的bean用CGLIB动态代理
例子:
package example;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyDemo {
public static void main(String[] args) {
Vehicle v = new Car();
//创建一个代理对象,传入对象的类加载器和接口,还有这个对象
Vehicle p = (Vehicle) Proxy.newProxyInstance(
v.getClass().getClassLoader(), v.getClass().getInterfaces(), new VehicleProxy(v));
p.run(100);
}
}
interface Vehicle {
void run(int speed);
}
class Car implements Vehicle {
@Override
public void run(int speed) {
System.out.println("The car is running at " + speed + " km/h.");
}
}
class VehicleProxy implements InvocationHandler {
private Object target;
public VehicleProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 代理对象
System.out.println(proxy.getClass().getName());
// 代理方法
System.out.println(method.getName());
// 方法参数
System.out.println(args == null ? null : args[0]);
System.out.println("before ... ");
Object obj = method.invoke(target, args);
System.out.println("after ...");
return obj;
}
}
输出:
Spring MVC
Spring最终要执行的是Controller,执行的过程中会受到拦截器的拦截Interceptor
拦截器和Controller有什么关系,Controller由谁来调用,涉及到哪些组件呢
中间的DispathcherServlet是分发请求的,所有请求进来提交给DispathcherServlet,根据请求的路径url把请求分发给不同Controller的不同方法,是SpringMVC的核心
这个过程有4步:
第一步,遍历HandlerMapping,里面封装了URL和控制器的映射关系。SpringMVC也有自动配置类,自动配置类触发带有Controller注解的扫描,扫描到以后就会存到这里map里面。请求来了,就去找和当前路径匹配的HandlerMapping,里面不止有方法,还有拦截器
然后封装成一个chain,就知道先调谁后调谁
第二步:根据信息,实例化HandlerAdapter,也就是Controller,在控制器前后,可能有拦截器拦截,在这之前,调用拦截器的preHandler方法。调用完成以后,返回ModelAndView对象,里面封装了数据和模板路径
第三步:现在知道了视图在哪里,模板在哪里,数据是什么,然后调用ViewResolver,把信息给它,装到模板里去,然后调用view返回给客户端
第四步:在finally里面,调用afterComletion()方法
事务