@Qualifier注解:
IService 接口有 2 个实现类:Service1 和 Service2,这俩上使用了@Component注解,将其注册到 spring 容器。
Service3 中IService service标注有@Autowired注解,需要自动注入IService类型的一个 bean。
Service3 中需要注入 service,此时会按照类型 IServce 从容器中找配置的 bean,找到了 2 个满足的:service1 和 service2,到底注入哪个呢?
spring 不知道如何选择了,此时可以调整一下 Service3 的代码,如下,使用@Qualifier("service2")来限定候选 bean 的名称,此时注入的是service2,这种用法想必很多人都很熟悉,下面再来看另外 2 种。
@Resource(name = "service")==@autowired + @Qualifier("service");但是@Resource注解,还是没搞明白!
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
Spring因为依赖而出现的错误还是很难排查的!
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("aplicationContext.xml");
Se s = context.getBean(Se.class); //获得的是类级别上的bean
Se s = context.getBean("s",Se.class);
注意:标红的地方都是接口,而不是实现类,否则的话就会报错!
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
String s = context.getBean("aaa",String.class);
注意:这个获取的bean是方法层级上的bean,而String代表的是这个方法的返回值类型!
spring的注解有点复杂了!
SpringMVC的视图问题
Spring和SpringBoot所做的最令人惊奇的事情之一就是自动为应用程序提供所有的基础功能,让开发人员专注于应用程序特有的逻辑。
“唯一不变的就是变化”,这句话抓住了软件开发的真谛。
Spring的核心是提供了一个容器(container),通常称为Spring应用上下文(Spring application context),他们会创建和管理应用组件。这些组件也可以称为bean,会在Spring应用的上下文中装配在一起,从而形成一个完整的应用程序。
将bean配置在一起的行为是通过一种基于依赖注入的模式实现的。
- 基于XML的配置
- 基于Java的配置
在历史上,指导Spring应用上下文将bean装配在一起的方式是使用一个或多个XML文件(描述各个组件以及他们与其他组件的关联关系)。
但是,在最近的Spring版本中,基于Java的配置更常见。
相对于基于XML的配置方式,基于Java的配置会带来 多项额外的收益,包括更强的类型安全性以及更好的重构能力。
在Spring技术中,自动配置起源于所谓的自动装配(autowiring)和组件扫描(component scanning)。
SpringBoot是Spring框架的扩展,提供了很多增强生产效率的方法。最为大家所熟知的增强方法就是自动配置(autoconfiguration),SpringBoot能够基于类路径中的条目、环境变量和其他因素合理猜测需要配置的组件爱你并将他们装配在一起。
没有代码,就是自动装配的本质。
SpringXML配置是一种过时的方式。
- @Configuration:这是Spring中最常用的注解之一,它表示一个类是一个配置类。通过@Configuration注解,Spring可以自动扫描和注入所有需要的Bean,从而简化了配置过程。
- @Component:这是Spring中最常用的注解之一,它表示一个类是一个组件类。通过@Component注解,Spring可以将一个类声明为Spring组件,并自动扫描和注入所有需要的Bean。
- @Repository:这是Spring中用于表示一个类是一个数据库访问类的注解。通过@Repository注解,Spring可以将一个类声明为Spring数据库访问组件,并自动扫描和注入所有需要的Bean。
- @Controller:这是Spring中用于表示一个类是一个控制器类的注解。通过@Controller注解,Spring可以将一个类声明为Spring控制器,并自动扫描和注入所有需要的Bean。
- @Autowired:这是Spring中用于自动注入Bean的注解。通过@Autowired注解,Spring可以在需要的地方自动注入所需要的Bean。
- @Value:这是Spring中用于将属性值注入到Bean中的注解。
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
ClassPathXmlApplicationContext()和AnnotationConfigApplicationContext()还是有区别的:
ClassPathXmlApplicationContext()
:这个类用于从类路径下加载XML配置文件,创建并初始化Spring容器。
AnnotationConfigApplicationContext()
:这个类用于基于Java配置(使用注解)创建Spring容器。它不需要XML配置文件,而是直接扫描指定的包或类,找到带有@Configuration、@ComponentScan、@Bean等注解的配置类,然后根据这些配置类创建容器。
使用AnnotationConfigApplicationContext()扫描指定的包时,能够有效防止@autowired注解失效的问题!也就是说,能够扫描到@autowired所自动注入的类。
默认情况下@Autowired(required=true)required=true
表示这个依赖是必须的,如果找不到匹配的 bean,Spring 容器会抛出异常。
IOC容器是Spring最核心的概念和内容。它代替了传统的new 方式初始化对象,通过读取在XML文件中配置的Bean定义,自动创建并管理容器的Bean实例及其生命周期;
JavaBean对象
JavaBean是JCP定义的一种Java类的标准,包括属性、方法事件三方面的规范。规范内容如下:
- 是一个公共作用域的类;定义成公共作用域的类是为了提供给其他类使用。
- 这个类有默认的无参数构造函数;无参构造函数是让框架和容器可以使用反射机制进行实例化。
- 这个类需要被序列化且实现自Serializable接口;Serializable接口是为了可以序列化和反序列化来进行对象的传输或保存到文件中。
- 可能有一系列可读写属性、通过getter或setter方法存取属性值;使用getter和setter方法读写属性,是为了不暴露属性。容器可以通过反射机制来进行属性值的读写。
标准的JavaBean类必须满足一下3个条件:
- JavaBean必须是一个public类
- 该类必须含没有任何参数的构造函数。
- 每个属性都要有getter和setter方法。
BeanDefinition
对于每个JavaBean,都有各自的类名、属性、类型和是否单例等信息。Spring是通过BeanDefinition来管理各种JavaBean及JavaBean相互之间的依赖关系的。BeanDefinition抽象了开发人员对JavaBean的定义,Spring将开发人员定义的JavaBean的数据结构转化为内存中的BeanDefinition数据结构进行维护。
POJO(简单Java对象)
POJO(Plain Old Java Object)具体含义是指没有继承任何类,也没有实现任何接口,不需要尊从框架的定义,更没有被其他框架侵入的Java对象。 一句话,POJO基本上不需要遵循任何规范。
当一个POJO可序列化,有无参构造函数,使用getter和setter方法来访问属性时,它就是一个JavaBean。
组件是为了代码的重用而对代码进行隔离和封装;框架在提供一系列组件的基础上,定义了更高层级的规范和开发方式;容器对不同层级的对象进行存放和管理。
仅以Spring来说,它是一个Java开发的框架,包含了一个IoC类型的Bean管理容器,另外还提供了切面编程(AOP)、数据访问事务管理等组件。
Spring的控制反转(IOC)
IoC(Inversion of Control,控制反转)是一种编程思想,或者说是一种设计模式。
补充:23中设计模式用来实现系统的松耦合,提高代码的可维护性。
IoC模式将耦合从代码中移出去,放入配置中(比如XML文件),容器在启动时依据依赖配置生成依赖对象并注入。使用IoC容器后,代码从内部的耦合转到外部容器,解耦性更高,也更灵活。
Spring IoC容器的实现,会涉及很多Spring底层代码的原理。任何复杂的框架,其底层原理都是很简单的。
(1)实例场景:有ClassA、ClassB两个类,ClassA中的方法methodA()需要调用ClassB中的methodB()方法,传统的方式是在ClassA的方法中通过new ClassB()的方式获取ClassB的实例后调用methodB()方法。
(2)初步改进:使用单例模式或工厂模式获取ClassB的实例。
(3)进一步改进:使用IoC模式,由容器创建和维护ClassB的实例,ClassA需要的时候从容器中获取就可以。这样,创建ClassB的实例的控制权就由程序代码转到容器。
控制权的转移即所谓的反转,依赖对象创建的控制权从应用程序本身转移到外部容器。依赖注入就是控制反转实现的一种策略。
依赖注入(DI)
依赖注入:依赖对象通过注入进行创建,由容器负责组件的创建和注入,这是更为流行的IoC实现策略,根据配置将符合依赖关系的对象传递给需要的对象。属性注入和构造器注入是常见的依赖注入方式。
IoC是一种软件设计思想,DI是这种思想的一种实现。控制反转乃至依赖注入的目的不是提升系统性能,而是提升组件重用性和系统的可维护性。依赖注入使用“反射”等底层技术,根据类名来生成相应的对象,注入依赖项和执行方法。但是与常规方式相比,反射会消耗更多的资源并导致性能的衰减,虽然JVM改良优化后,反射方式与一般方式的性能差距逐渐缩小,但是提升代码的结构性和可维护性的同时,需要牺牲一定的系统性能为代价。
设置值注入
设置值注入使用的是属性的setter方法来注入依赖对象。使用setter方法设置依赖也是传统开发中较为常见的方式,使用Spring容器注入也不需要显式地调用setter方法,仅需要进行配置,由容器完成注入。
Spring核心包及相关依赖包的导入
Spring框架的核心模块包括Beans、Core和Context等,而spring-context是依赖在core和bean包之上的。
Spring容器初始化
Spring IoC容器用来创建和管理类的实例称为Bean。根据Bean的配置,使用Bean工厂(BeanFactory接口实现类的对象)创建和管理Bean实例。除了创建和管理Bean实例,Spring容器最重要的作用是根据配置注入依赖对象。
BeanFactory和ApplicationContext是Spring进行对象管理的两个主要接口。Beanfactory沿袭了传统的对象工厂模式来进行命名,ApplicationContext扩展了BeanFactory,除了基本的对象管理之外,还提供了应用程序所需要的其他上下文信息。
BeanFactory是整个IoC容器最顶层接口,其定义了一个IoC容器的基本规范和基本功能。
Spring可以通过Bean配置id或者Bean类来获取Bean实例。BeanFactory是Spring IoC容器的一个重要接口,字面上看是属于Bean的工厂类,功能类似于设计模式的工厂模式获取类的实例。基于Spring框架的应用在启动时会根据配置创建一个实现BeanFactory接口的类对象,这个对象也就是所谓的容器。
相比于低配版的IoC容器BeanFactory,ApplicationContext是高配版的IoC容器。从ApplicationContext接口的定义可以看出,其在原有的BeanFactory接口上实现了更加复杂的扩展功能。
BeanFactory需要手动注册,而ApplicationContext是自动注册。也就是说,BeanFactory要在代码里写出来才可以被容器识别,而ApplicationContext是直接配置在配置文件即可。
ApplicationContext初始化
作为Bean管理的容器接口,ApplicationContext定义了初始化、配置和组装Bean的方法,由继承接口的类实现。Spring提供了多种ApplicationContext接口的实现方式,使用XML配置的实现类包括ClassPathXmlApplicationContext和FileSystemXmlApplicationContext。
Spring配置文件最简单的就是以applicationContext.xml来命名。
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Bean的配置方式
<bean id="address" class="pojo.Address">
一个Bean元素包含两个基本属性:class和id。class的值是包含包名的全路径类名,这个类的要求很简单,不需要特定规范,也不需要继承任何接口,一般的POJO即可,但是必须是类,不能使用接口。
id属性给这个类的实例一个标识,以方便容器和程序查找使用。如果不设定id的属性和值,则容器默认会以类名的首字母小写作为标识。
默认情况,容器根据每个Bean的配置创建和维护一个单例对象。与单例工厂不同的是,不同的Bean可以使用相同的类,设定不同的id,容器就会维护该类的多个实例。
哪些类需要配置成Bean (不是很理解)
理论上,符合JavaBean规范的类和不符合JavaBean规范的POJO都可以配置成Bean,也就是说,基本上所有的类都可以配置成Bean并交由Spring管控。容器使用反射机制来创建对象和注入依赖,过度地使用对系统的性能会产生不必要的浪费。对于细粒度的域对象,类似于实体类就,没有必要交由容器管理。
- 服务层对象:
- 数据访问对象:
- 框架基础对象:
Spring容器主要是对对象的生命周期和对依赖关系进行管理。从类的特征上看,具备单例特征的类都适合交由容器管理,依赖关系较为复杂或者依赖关系会发生变化的类也适合Spring进行管理。
Spring容器的定义
不直接创建对象,而是由容器创建,并设定一个或多个标识,需要的时候通过Bean名字从容器中获取,这看上去是类似于EJB的服务查找,但是Spring真正的核心是对于依赖的处理,使用配置的方式注入依赖。
以A、B、C三个类为例,B、C都是单例模式的类,A需要调用B、C类的方法,这样A就和B、C形成了依赖,如果B、C交由容器管理,A虽然和容器产生依赖,但是依赖程度降低了。
使用类的名字在XML中配置Bean,并给这个Bean设置一个标识,容器基于配置来创建相应的类的对象,之后就可以通过标识(也可以通过类)从容器中获取该对象,实现所谓的容器对Bean的托管。
依赖注入与方式
依赖关系注入使用配置的方式,而不是固定写在代码里,以实现系统的解耦。构造函数注入和设置值注入是Spring中两种常见的依赖注入方式,在同一个Bean配置中,可以独立使用,也可以同时使用两种方式。Spring官方推荐使用构造器注入方式,而且对于一些第三方的类没有暴露setter方法,就只能使用构造器注入了。
Spring IoC容器综述
Spring框架学习的基本内容就是<bean>的属性和子元素的配置。属性包括Bean的id、class和scope等属性,子元素主要是构造器和属性的依赖注入的配置。掌握了Bean配置及原理,也就掌握了Spring框架的核心。
属性名 | 说明 |
id | Bean标识,默认类名首字母小写 |
class | Bean对应的Java类,一般情况下是必要的,实例工厂方式不需要 |
factory-method | 静态工厂和实例工厂配置使用 |
factory-bean | 实例工厂配置使用 |
scope | 作用域配置,默认为singleton singleton:从spring中获取bean时,每次获取的是同一个bean prototype:从spring中获取bean时,每次获取的是新的bean request:生成的bean存活于request域中,生命周期也与request相同 session:生成的bean存活于session域中,生命周期也与session相同 |
init-method | 初始化方法 |
destroy-method | 销毁方法 |
lazy-init | 懒加载 |
autowire | 自动配置 byName就是通过Bean的属性名称(id或name)自动装配。 byType就是通过Bean的Class类型来自动装配。使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。 |
parent | 定义继承 |
constructor-arg | 构造器依赖注入name、ref和value |
property | 属性依赖注入name、ref和value |
replaced-method | 方法替换 |
Bean配置主要属性和元素
Spring IoC的底层原理实现:Spring IoC容器启动分为两步--创建BeanFactory和实例化Bean。Spring的Bean在内存中的状态就是BeanDefinition,在Bean的创建和依赖注入的过程中,需要根据BeanDefinition的信息来递归地完成依赖注入,从代码中可以看到,这些递归都是以getBean()为入口的,一个递归是在上下文体系中查找需要的Bean和创建Bean的依赖关系,另一个递归是在依赖注入时,通过递归调用容器的getBean方法得到当前的依赖Bean,同时也触发对依赖Bean的创建和注入。在对Bean的属性进行依赖注入时,解析的过程也是一个递归过程,这样根据 依赖关系,一层一层地完成Bean的创建和注入,直到与当前Bean相关的整个依赖链的注入完成。
基于注解和代码的配置
从Spring 2.5开始,在以XML文件作为主要配置的同时,可以将某些配置以注解的方式在代码中直接配置,极大地减少了配置的繁琐度,提高配置的效率,Java开发人员也更容易熟悉和适应。从Spring 3.0开始可以完全脱离XML文件,使用Java代码的方式进行容器和框架的配置。
在Java中,注解与类、接口和枚举是在同一个层次,所以可以像定义接口一样来定义注解。
Java基本注解
通过反射获取的Java类(class)、方法(method)和属性(Field),都提供了获取注解的方法。
注解功能具有使用方便和解耦等特性。
Spring支持的注解类型与开启方式
依赖注解有Spring提供的@Autowired、@Value
@Service注解和@Component注解的区别
Spring注解功能的开启方式
Spring提供或实现的容器配置注解,在默认状况下使用并不会立即生效,需要配置相关的处理类进行处理。Spring提供3种方式开启此类型注解的功能,分别是配置实现注解功能的BeanPostProcessor的Bean、使用<context:annotation-config>标签和配置<context:component-scan>标签。
@Resource--依赖注入注解
@Resource是JSR-250的注解,用来标注系统或容器中资源类型的对象引用包括持久层访问对象资源(DAO)、文件或容器等资源。
@Required--依赖项检查
@Required用于属性的Setter方法上,以检查该属性是否进行了依赖注入。
@Autowired--依赖对象的自动装配 (属性注入)
在XML中通过<constructor-arg>和<property>标签进行构造器注入和属性注入依赖对象是传统的依赖注入方式,但这种方式较为繁琐,特别是对于依赖项特别多的状况,Bean的配置就显得冗杂,容易出错,整个XML文件的维护也比较麻烦。Spring提供了@Autowired注解,直接在代码中进行依赖对象的自动装载,大大简化了配置,提高了开发效率。
- 在构造器中使用@Autowired
- 在方法中使用@Autowired
- 在属性中应用@Autowired: 使用@Autowired自动装配的注解后,Bean在XML中的配置就不需要再处理依赖项的注入了,只需要配置一行代码即可。
假如这个属性是一个接口,那么这个接口的实现类如果被@Component等注解修饰,那么,就会自动将这个属性成功注入!
@Component--组件注解
通过@Autowired注解将依赖注入从XML配置转到Java代码中,更近一步,可以将Bean的定义也放到Java代码中使用注解的方式进行配置。
在类中使用@Component注解,容器可以在启动时进行该类的实例化。@Component是通用的组件注解,在Java Web开发中对应不同的组件层级,Spring在@Component元注解的基础上定义了不同类型的子注解,常用的包括@Controller、@Service、@Repository,分别对应到控制层、服务层和数据访问层的组件。相比@Component,这3个注解提供了对应层级的一些附加功能,比如@Repository提供了持久层处理异常的自动转换。
通过配置<context:component-scan>标签即可开启@Component的注解功能,默认状况下会扫描base-package包下的所有@Component和子注解(@Controller、@Service和@Repository)标签的类进行实例化并注册。如果需要对扫描和注册的类及注解做一些过滤,由两种方式可以做,分别是使用<context:exclude-filter>子标签,以及使用<context:include-filter>子标签和use-filters属性。
警告:扫描每个包会不必要地降低启动过程。
这个@Resource注解真的是无语了,查也查不到到底是哪里出现了问题!
Bean--方法层级的组件注解
@Component及其子注解是使用在类层级的组件注解,也可以在类方法上使用@Bean注解来注册Bean。
@Bean注解的方法需要有非空的返回类型,返回的对象就是注册Bean的对象。该注解只有在其方法对应的类被注册为Bean的状况下才有效(可以通过XML配置或@Component注解配置)。
@Bean较常使用在Java代码配置类中(使用@Configuration注解的类)
@Configuration
在类中使用@Configuration注解Spring的配置类时,@Configuration使用@Component元注解定义的注解,说明@Configuration也是一种组件类型的注解。
@Bean注解的方法不能是private或final,在非@Configuration注解类的@Bean注解方法中不能定义Bean间的依赖关系。
@ComponentScan--组件扫描注解
<context:component-scan base-package="dao"/>是Spring框架中的一个配置,用于自动扫描指定包(在这里是“dao”)下的所有类,并将这些类注册为Spring容器中的bean。这样,我们可以在其他地方通过依赖注入的方式使用这些bean。
XML配置方式中使用<context:component-scan>标签配置自动扫描@Component等注解的组件,base-package属性设置需要扫描的路径。与此类似,在代码配置方式中可以结合@ComponentScan与@Configuration实现此功能,使用属性basePackages设置扫描的路径。
@ComponentScan的属性: value指定扫描的包,includeFilters包含那些过滤,excludeFilters不包含那些过滤,useDefaultFilters默认的过滤规则是开启的,如果我们要自定义的话是要关闭的。其中@Filters是一个过滤器的接口。
元注解
元注解是描述注解的注解。
元注解有5个:
- @Retention
- @Target
- @Documented
- @Inherited
- @Repeatable
@Retention:
用于指定注解的保留范围,也就是描述注解的声明周期。
RUNTIME:注解信息将在运行期也保留,可以通过反射机制读取注解的信息。
一个自定义的注解要想起作用,需要依赖反射机制,通过反射可以取得声明的注解的全部内容。因此,在定义注解时,必须将注解的保留策略设置为RetentionPolicy.RUNTIME 。
@Target:
使用@Target可以限制注解的使用范围,也就是指注解可以修饰哪些JAVA元素。
ElementType的枚举值:
- TYPE:表示该注解可以修饰类、接口、枚举类。
- FIELD:表示该注解可以修饰成员变量(包括枚举常量)。
@Documented:
使用@Documented的注解,会在生成文档时将注解内容也写入API文档中。
Spring AOP
<aop:aspectj-autoproxy/>是Spring AOP(面向切面编程)中的一个配置元素,用于开启基于AspectJ的自动代理。在Spring配置文件中添加这个元素后,Spring会自动为符合条件的Bean生成代理对象,从而实现AOP功能。<aop:aspectj-autoproxy/><bean id="diy" class="log.DiyPointCut"/><aop:config><aop:aspect ref="diy"><aop:pointcut id="point" expression="execution(* service.UserServiceImpl.*(..))"/><aop:before method="before" pointcut-ref="point"/><aop:after method="after" pointcut-ref="point"/></aop:aspect></aop:config>
execution
Factory foodFactory = (Factory) context.getBean("foodFactory");
,那么在执行这行代码之前,Spring容器会先加载配置文件,创建并初始化所有的bean,包括foodFactory这个bean。因此,横切逻辑打印的时机是在Spring容器初始化时,而不是在执行这行代码时。
而如果直接使用new关键字创建foodFactory对象,即第二行代码:Factory foodFactory = new FoodFactory();
,那么在执行这行代码时,不会触发任何横切逻辑的打印。
横切逻辑是指在软件系统中多个模块或组件共同需要的一些功能,例如日志记录、性能监控等。在Spring容器初始化时,可以通过AOP(面向切面编程)来实现横切逻辑的打印。
在Spring中,AOP通过代理模式实现,可以在目标对象的方法执行前、执行后或抛出异常时插入横切逻辑。具体来说,可以通过以下步骤来实现横切逻辑的打印:
- 定义一个切面类,该类包含了需要插入的横切逻辑。例如,可以在切面类中定义一个前置通知方法,在目标方法执行前打印相关信息。
- 在Spring配置文件中配置切面类和目标对象。通过配置元素和元素来定义切面和切点。
- 在元素中配置通知类型和切点表达式。通知类型可以是前置通知、后置通知、异常通知等,切点表达式用于指定哪些方法需要被拦截。
- 在Spring容器初始化时,会根据配置信息生成代理对象。当调用目标对象的方法时,代理对象会根据配置的切点和通知类型来决定是否插入横切逻辑。
通过以上步骤,可以在Spring容器初始化时实现横切逻辑的打印。
<mvc:default-servlet-handler/>是SpringMVC中的一个配置元素,用于配置默认的Servlet处理器。当请求没有匹配到任何Controller时,该处理器会将请求交给默认的Servlet(通常是Tomcat中的JSP Servlet)进行处理。这样可以方便地处理静态资源,如HTML、CSS、JavaScript等。
这种方式是用<context:component-scan base-package="com.zhang.service"/>是注解生效生成的bean!
需要注意的是userMapper的类CustomerOrderService只能是接口,不能是实现类!!!(不知道为什么)