框架是半成品软件,由它生成通用的程序,需要做的是补充配置信息,指导框架。由配置文件、注解两种方法实现。两者各有优缺点。配置文件可以统一配置,清晰明了;但代码量比较大。注解更简单便捷但信息不一目了然。
Spring集成形框架,浅集成,更复杂。 1.0
SpringMVC属于Spring的web部分,是以Spring为基础二次开发形成的框架
SpingBoot是对Spring 的扩展,不仅仅是浅集成,深入集成,更加简便。 2.0
Spring Cloud是在SpringBoot基础上扩展的,完全针对于分布式、微服务的一套框架。 3.0
class QueryController{
UserService userService;
BookService bookservice;
QueryController() {
//需要程序自己实例化对象。
userService = new UserService;
userService.setUserDao(new UserDao());
bookservice = new BookService;
bookservice.setBookDao(new BookDao());
}
public static void main(Strings[] args) {
QueryController queryController = new QueryController();
}
}
@Controller
class QueryController{
@Autowired
UserService userService;
@Autowired
BookService bookservice;
}
一个最简单的动态代理实现如下:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
if (method.getName().equals("morning")) {
System.out.println("Good morning, " + args[0]);
}
return null;
}
};
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(), // 传入ClassLoader
new Class[] { Hello.class }, // 传入要实现的接口
handler); // 传入处理调用方法的InvocationHandler
hello.morning("Bob");
}
}
interface Hello {
void morning(String name);
}
传统的Java应用中,bean的生命周期: 很简单,使用Java关键字 new 进行Bean 的实例化 → 然后该Bean 就能够使用了。 → 一旦bean不再被使用,则由Java自动进行垃圾回收。
Spring管理Bean的生命周期: 复杂多了,正确理解Bean 的生命周期非常重要,因为Spring对Bean的管理可扩展性非常强,下面展示了一个Bean的构造过程
如上图所示,Bean 的生命周期还是比较复杂的,下面来对上图每一个步骤做文字描述:
Spring启动,查找并加载需要被Spring管理的bean,进行Bean的实例化【beanfactory创建对象】
Bean实例化后对将Bean的引入和值注入到Bean的属性中【依赖注入di,属性注入】
如果Bean实现了BeanNameAware接口的话,Spring将Bean的Id传递给setBeanName()方法【名字方法注入】
如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入【工厂方法注入】
如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来。【上下文(对象创建时所带配置信息的指令等)】
如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法。
如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。
如果bean使用init-method声明了初始化方法,该方法也会被调用
如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。
此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。【单例模式,保证对象使用,但该对象使用的生命周期被拉长了,长期占用内存】
三级缓存
https://www.cnblogs.com/semi-sub/p/13548479.html
https://baijiahao.baidu.com/s?id=1711380208642133437&wfr=spider&for=pc
只要两个缓存确实可以做到解决循环依赖的问题,但是有一个前提这个bean没被AOP进行切面代理,如果这个bean被AOP进行了切面代理,那么只使用两个缓存是无法解决问题
注解逐渐替代XML配置文件
maven——简化jar的引入
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>5.3.4version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.4version>
dependency>
spring-beans——创建对象
spring-context——读上下文——读取配置文件
一般类 ——new对象
public class SomeBean {}
//调用
{
SomeBean someBean = new SomeBean();
}
【任意类都是对象,且类不能由new,不能创建对象。必须由Spring来创建对象】——单例,唯一对象
=>bean factory
<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 id="someBean" class="com.jd.javaconfig.SomeBean">bean>
beans>
对比
SomeBean someBean = new SomeBean();
public class MyTest {
@Test
public void test1(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
//读配置文件
SomeBean sb = (SomeBean)ctx.getBean("someBean");
//getBean获得对象
System.out.println(sb);
//使用
}
}
(ClassPath☞类的根路径——src文件夹下)
src源文件,真正可以用的是编译后出现的class下的
类
public class SomeBean {}
public class OtherBean {}
=>bean factory
// 作为Spring的主配置文件
@Configuration
public class AppConfig {
//@Bean标签表示让Spring托管bean
@Bean
public SomeBean someBean(){
return new SomeBean();
}
@Bean
public OtherBean otherBean(){
return new OtherBean();
}
}
AppConfig这个类是一个配置文件,不是XML配置文件——工厂模式
@Test
public void test() {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
//读取配置信息
SomeBean sb = ctx.getBean(SomeBean.class);
OtherBean ob = ctx.getBean(OtherBean.class);
//getBean()获取对象
System.out.println(sb);
System.out.println(ob);
//使用
}
这个是基于AnnotationConfigApplicationContext来配置的。(Annotation注解,Config配置)
到这里我们就已经学完了两个重要的标签 @Configuration和@Bean, @Configuration标签表示这个类可被Spring识别的配置对象的类,只有有这个标记的标签的类才能使用 @Bean标签作用于对应的方法上面 @Bean(destroyMethod = "destory", initMethod = "init")也可以通过这样的写法来配置bean的销毁方法和初始化方法类
@Component
public class SomeBean {}
@Component
public class OtherBean {}
Spring注解创建Bean
//@Configuration标签表示这个类可被Spring识别的配置对象的类,这有有这个标记的标签的类才能使用@Bean标签作用于对应的方法上面
// @ComponentScan:开启组件自动扫描;默认情况下,它会扫描当前类所在的包及其子包中的所有标签对象加载到Spring容器
@Configuration
@ComponentScan(basePackages="com.jd.scan")
public class AppConfig {}
测试类
public class MyTest {
@Test
public void test() {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
SomeBean sb = ctx.getBean(SomeBean.class);
OtherBean ob = ctx.getBean(OtherBean.class);
System.out.println(sb);
System.out.println(ob);
}
}
@ComponentScan:开启组件自动扫描; 默认情况下,它会扫描当前类所在的包及其子包中的所有标签对象加载到Spring容器
控制对象与对象的关系,注入——> 设置属性
例子:打印机
ink 、paper是打印机printer的两个组件
用不同注入方式——都是为了给ink和paper这两个属性赋值
Printer类
public Printer(Ink ink, Paper paper, String brand) {
this.brand = brand;
this.ink = ink;
this.paper = paper;
}
application.xml
<bean id="a4Paper" class="org.lanqiao.print.A4Paper">bean>
<bean id="blackInk" class="org.lanqiao.print.BlackInk">bean>
<bean id="printer" class="org.lanqiao.print.Printer">
<constructor-arg name="ink" ref="blackInk">constructor-arg>
<constructor-arg name="paper" ref="a4Paper">constructor-arg>
<constructor-arg name="brand" value="惠普">constructor-arg>
bean>
构造器注解注入使用注解@Autowired(对非值构造方法使用@Autowired注解)
可能有循环注入问题(类似于死锁)。 抛出BeanCurrentlyInCreationException异常表示循环依赖 → 根本原因:Spring解决循环依赖依靠的是Bean的“中间态”这个概念,而这个中间态指的是已经实例化,但还没初始化的状态。而构造器是完成实例化的东东,所以构造器的循环依赖无法解决~~~一定要提供setter方法,才可以实现setter注入。
<bean id="printer" class="org.lanqiao.print.Printer">
<constructor-arg name="brand" value="佳能"/>
<property name="ink" ref="blackInk"/>
<property name="paper" ref="a4Paper"/>
bean>
接口注入有点复杂,被注入对象如果想要IOC容器为其注入依赖对象,就必须实现某个接口,这个接口提供一个方法,用来为被注入对象注入依赖对象,IOC容器通过接口方法将依赖对象注入到被注入对象中去。相对于前两种注入方式,接口注入比繁琐和死板,被注入对象就必须专声明和实现另外的接口
@Resource(name="a4Paper")
private Paper paper;
由于@Autowired、@Qualifier、@Resource三者自动装配只能针对于注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。因此有了@Value这个注解,@Value专门用来服务基本类型和String类型。
注意:@Value无法作用于构造方法。
SSM仅有singleton,所有数据绑在方法的参数里
<bean id="printer" class="org.lanqiao.printer.Printer">
<property name="ink" ref="blackInk"/>
<property name="paper" ref="b5"/>
bean>
<bean id="colorInk" class="org.lanqiao.printer.ColorInk"/>
<bean id="blackInk" class="org.lanqiao.printer.BlackInk"/>
<bean id="a4" class="org.lanqiao.printer.A4Paper"/>
<bean id="b5" class="org.lanqiao.printer.B5Paper"/>
<bean id="printer" class="org.lanqiao.printer.Printer" autowire="byName"/>
<bean id="colorInk" class="org.lanqiao.printer.ColorInk"/>
<bean id="ink" class="org.lanqiao.printer.BlackInk"/>
<bean id="a4" class="org.lanqiao.printer.A4Paper"/>
<bean id="paper" class="org.lanqiao.printer.B5Paper"/>
<bean id="printer" class="org.lanqiao.printer.Printer" autowire="byType"/>
<bean id="blackInk" class="org.lanqiao.printer.BlackInk"/>
<bean id="b5Paper" class="org.lanqiao.printer.B5Paper"/>
<bean id="printer" class="org.lanqiao.printer.Printer" autowire="constructor"/>
<bean id="blackInk" class="org.lanqiao.printer.BlackInk"/>
<bean id="b5" class="org.lanqiao.printer.B5Paper"/>
XML 配置里的 Bean 自动装配的缺点:
1、在 Bean 配置文件里设置 autowire 属性进行自动装配将会装配 Bean 的所有属性,然而,,若只希望装配个别属性时, autowire 属性就不够灵活了。
2、autowire 属性要么根据类型自动装配, 要么根据名称自动装配, 不能两者兼而有之。
3、一般情况下,在实际的项目中很少使用自动装配功能,因为和自动装配功能所带来的好处比起来,明确清晰的配置文档更有说服力一些。