依赖注入
**依赖注入(DI)**是Spring框架核心功能之一,它允许对象间的依赖关系通过配置或代码在运行时动态注入,而不是通过对象内部硬编码。这种方法淘汰了代码间的耦合,增加了灵活性和可重用性。
依赖注入强调的是对象的依赖关系由外部来管理和提供,而不是由对象自身负责创建依赖对象。通过这种方式,可以低沉对象之间的耦合度。即下面的方式也属于依赖注入。
常见的依赖注入方式:
- 构造函数注入:通过类的构造函数将依赖对象转达给该类。在对象实例化时,容器会调用相应的构造函数,并传入依赖对象的实例。
- public class ProductService {
- private final ProductRepository productRepository;
- public ProductService(ProductRepository productRepository) {
- this.productRepository = productRepository;
- }
- // 业务方法
- }
复制代码
- Setter 方法注入:利用类的 setter 方法将依赖对象设置到该类中。容器先创建对象实例,然后调用 setter 方法来注入依赖对象。
- public class UserService {
- private UserRepository userRepository;
- public void setUserRepository(UserRepository userRepository) {
- this.userRepository = userRepository;
- }
- // 业务方法
- }
复制代码 xml依赖注入
- <property>
- <list><value></value></list>
- <map><entry key=' ' value=' '></map>
- <array><value></value></array>
- <set><value></value></set>
- <props><prop key=' '>值</prop></props>
- ---------------Student类------------------
- public class Student {
- private String name;
- private Adress address;
- private String[] books;
- private List<String> hobbies;
- private Map<String,String> card;
- private Set<String> games;
- private String wife;
- private Properties info;
- //getter,setter,无参and全参构造
- }
- ---------------Teacher类------------------
- public class Teacher {
- private String name;
- private int age;
- //getter,setter,无参and全参构造
- }
- ---------------applicationContext.xml------------------
- <bean id="adress" class="com.luxiya.Adress">
- <property name="address" value="空中花园"/>
- </bean>
- <!-- 需要相应的构造函数-->
- <bean id="teacher" class="com.luxiya.Teacher" c:name="luxiya" c:age="18"/>
- <!-- 需要相应的构造函数-->
- <bean id="teacher1" class="com.luxiya.Teacher">
- <constructor-arg name="name" value="luxiya-constructor"/>
- <constructor-arg name="age" value="188"/>
- </bean>
- <!-- 需要空参构造-->
- <bean id="teacher2" class="com.luxiya.Teacher" p:name="zhihuiguan" p:age="19">
- </bean>
- <bean id="student" class="com.luxiya.Student">
- <property name="name" value="luxiya"/>
- <property name="address" ref="adress"/>
- <property name="books">
- <array>
- <value>java</value>
- <value>python</value>
- </array>
- </property>
- <property name="hobbies">
- <list>
- <value>打篮球</value>
- <value>打游戏</value>
- </list>
- </property>
- <property name="card">
- <map>
- <entry key="身份证" value="123456789"/>
- <entry key="银行卡" value="123456789"/>
- </map>
- </property>
- <property name="games">
- <set>
- <value>LOL</value>
- <value>DOTA</value>
- </set>
- </property>
- <property name="wife">
- <null/>
- </property>
- <property name="info">
- <props>
- <prop key="name">luxiya</prop>
- <prop key="age">18</prop>
- </props>
- </property>
- </bean>
- ---------------测试类------------------
- ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- Student student = (Student) context.getBean("student");
- System.out.println(student.toString());
- Teacher teacher = (Teacher) context.getBean("teacher");
- System.out.println(teacher.toString());
- Teacher teacher1 = (Teacher) context.getBean("teacher1");
- System.out.println(teacher1.toString());
- Teacher teacher2 = (Teacher) context.getBean("teacher2");
- System.out.println(teacher2.toString());
复制代码 控制反转
在传统的程序计划中,当一个对象(A)需要使用另一个对象(B)的功能时,通常会在对象 A 内部主动创建对象 B 的实例,即对象 A 对对象 B 的创建和生命周期管理具有控制权。而在控制反转的头脑下,对象 A 所依赖的对象 B 的创建、实例化以及依赖关系的建立,不再由对象 A 自身负责,而是由一个外部的容器(比如 Spring 容器)来负责管理。如许一来,对象 A 得到依赖对象 B 的过程被反转了,控制权从对象 A 转移到了外部容器,这就是 “控制反转” 名称的由来。
IoC 容器负责创建对象、管理对象的生命周期,并在得当的时候将对象注入到需要它们的其他对象中。应用程序中的各个组件(对象)只需要关注自身的业务逻辑,而不需要关心依赖对象的创建和获取过程。容器会根据配置信息(如 XML 配置文件或注解)来确定对象之间的依赖关系,并自动完成对象的实例化和注入。
- XML 配置:在 Spring 早期,XML 是配置 Bean 的主要方式。通过编写 XML 文件,**将 Bean 的定义信息(如 Bean 的类名、依赖关系、属性值等)与 Bean 的实现类分脱离来。**如许做的好处是可以在不修改实现类代码的情况下,修改 Bean 的配置信息,便于对系统举行维护和扩展。但缺点是 XML 文件可能会变得冗长复杂,增加了配置的难度和维护成本。
- 注解配置:随着 Spring 框架的发展,注解配置逐渐流行起来。使用注解(如 @Component、@Service、@Repository、@Autowired 等)可以直接在实现类中定义 Bean 的信息和依赖关系,将 Bean 的定义和实现合二为一。这种方式淘汰了大量的 XML 配置,使得代码更加轻便,开辟服从更高,实现了所谓的 “零配置”(或者说淘汰了显式配置)。
相关
ApplicationContext 接口,工厂的接口,使用该接口可以获取到详细的 Bean 对象。该接口下有两个详细的实现类。
ClassPathXmlApplicationContext,加载类路径下的 Spring 配置文件。(注:ClassPathXmlApplicationContext在读取application.xml文件时,就会自动创建bean实例)
FileSystemXmlApplicationContext,加载当地磁盘下的 Spring 配置文件。
Spring 框架的 Bean管理的配置文件方式
id 属性为每个 Bean 提供了一个唯一的标识符。
class 属性指定了 Bean 对象的全限定类名。在这个例子中,com.example.service.UserService 就是 UserService 类的完整路径,Spring 容器会根据这个路径来创建相应的 Bean 实例。
- singleton(单例):这是 scope 属性的默认值。当 scope 设置为 singleton 时,Spring 容器只会创建该 Bean 的一个实例,而且在整个应用程序的生命周期中都使用这个实例。
- prototype(多例):当 scope 设置为 prototype 时,每次从 Spring 容器中获取该 Bean 时,容器都会创建一个新的实例。
- init - method 和 destroy - method 属性
- init - method:当 Bean 被加载到 Spring 容器时,会调用 init - method 属性指定的方法。
- destroy - method:当 Bean 从 Spring 容器中移除时,会调用 destroy - method 属性指定的方法。
- <!-- 定义一个单例的 UserService Bean -->
- <bean id="userServiceSingleton" class="com.example.service.UserService"
- scope="singleton"
- init-method="init"
- destroy-method="destroy">
- </bean>
- <!-- 定义一个多例的 UserService Bean -->
- <bean id="userServicePrototype" class="com.example.service.UserService"
- scope="prototype">
- </bean>
- -----------------测试------------------
- public class UserService {
- // 初始化方法
- public void init() {
- System.out.println("UserService 初始化");
- }
- // 销毁方法
- public void destroy() {
- System.out.println("UserService 销毁");
- }
- }
复制代码 实例化Bean的三种方式
无参构造器实例化
这是 Spring 中最基本的实例化 Bean 的方式,Spring 容器会调用 Bean 类的无参构造器(假如没有指定其他构造器)或者指定的有参构造器来创建 Bean 对象。
- public class UserService {
- public UserService() {
- System.out.println("UserService实例化");
- }
- }
- -------------application.xml--------------
- <bean id="userService" class="com.luxiya.demo.UserService"/>
- -------------测试类--------------
- ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- UserService userService = (UserService) context.getBean("userService");
复制代码 静态工厂方法实例化
使用一个静态工厂方法来创建 Bean 对象。静态工厂方法是类中的一个静态方法,该方法会返回一个对象实例。Spring 容器会调用这个静态工厂方法来获取 Bean 对象。
- public class StaticFactory {
- public static UserService getUserService()
- {
- System.out.println("静态工厂创建");
- return new UserService();
- }
- }
- -------------application.xml--------------
- <!-- 静态工厂-->
- <bean id="uSstatic" class="com.luxiya.demo.StaticFactory" factory-method="getUserService"/>
- -------------测试类--------------
- ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- UserService userService = (UserService) context.getBean("uSstatic");
复制代码 实例工厂方法实例化
使用一个实例工厂的方法来创建 Bean 对象。起首需要创建一个工厂类的实例,然后调用该实例的方法来创建 Bean 对象。
- -------------application.xml--------------
- <!--动态工厂-->
- <bean id="dFactory" class="com.luxiya.demo.DFactory"/>
- <bean id="useDF" factory-bean="dFactory" factory-method="getUserService"/>
- -------------测试类--------------
- ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- UserService userService = (UserService) context.getBean("useDF");
复制代码 静态和动态对比
- 静态工厂:静态工厂方法相对固定,由于它是类的静态方法,不能根据差异的实例状态举行差异的处理。通常适用于创建一些通用的、不依赖于实例状态的对象。不依赖于工厂类的实例。
- 动态工厂:动态工厂方法可以根据工厂类实例的差异状态来创建差异的 Bean 对象,具有更高的灵活性。例如,工厂类可以有差异的属性值,根据这些属性值在实例工厂方法中创建差异范例或配置的 Bean 对象。依赖于工厂类的实例。
注解
常用注解
@Component 普通的类
@Controller 表现层@Service 业务层
@Repository 长期层
依赖注入常用的注解
@Value 用于注入普通范例(String,int,double 等范例)
@Autowired 默认按范例举行自动装配(引用范例)
@Qualifier 和@Autowired 一起使用,强制使用名称注入
@Resource Java 提供的注解,也被支持。使用 name 属性,按名称注入
对象生命周期(作用范围)注解
@Scope 生命周期注解,取值 singleton(默认值,单实例)和 prototype(多
例)
初始化方法和销毁方法注解(相识)
@PostConstruct 相当于 init-method
@PreDestroy 相当于 destroy-method
- @Service
- public class TeacherService {
- public TeacherService() {
- System.out.println("TeacherService初始化");
- }
- }
- -----------application.xml----------
- <!--开启注解扫描-->
- <context:component-scan base-package="com.luxiya.demo"/>
- -----------------测试类--------------
- ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- TeacherService teacherService = (TeacherService) context.getBean("teacherService");
复制代码 纯注解
纯注解的方式是微服务架构开辟的主要方式,所以也黑白常的紧张。纯注解的目标是替换掉全部的配置文件。但是需要编写配置类。
常用的注解总结
@Configuration 声明是配置类
@ComponentScan 扫描详细包结构的。也可以扫描多个包@ComponentScan(value ={“com.qcbyjy.demo4”,“com.qcbyjy.demo3”})
@Import 注解 Spring 的配置文件可以分成多个配置的,编写多个配置类。用于导入其他配置类
@Bean 注解 只能写在方法上,表明使用此方法创建一个对象,对象创建完成保存到 IOC 容器中
- ------------demo包下的配置类--------------
- @Configuration
- @ComponentScan(basePackages = "com.luxiya.demo")
- @Import(SpringConfig2.class)
- public class SpringCofig {
- @Bean(name="dataSource")
- public DataSource createDataSource(){
- DruidDataSource dataSource = new DruidDataSource();
- dataSource.setDriverClassName("com.mysql.jdbc.Driver");
- dataSource.setUrl("jdbc:mysql:///spring_db");
- dataSource.setUsername("root");
- dataSource.setPassword("root");
- return dataSource;
- }
- }
- -------------------基础测试--------------
- ApplicationContext context = new AnnotationConfigApplicationContext(SpringCofig.class);
- Student student = (Student) context.getBean("student");
- -------------demo2包下的配置类----------------
- @Configuration
- @ComponentScan(basePackages = "com.luxiya.demo2")
- public class SpringConfig2 {
- }
- //demo2包下
- @Data
- @Component
- public class Course {
- @Value("java")
- private String name;
- public Course()
- {
- System.out.println("Course初始化");
- }
- }
- //demo包下
- @Component
- @Data
- public class Student {
- @Value("luxiya")
- private String name;
- public Student() {
- System.out.println("Student实例化");
- }
- }
- ------------测试@Import注解-------------
- ApplicationContext context = new AnnotationConfigApplicationContext(SpringCofig.class);
- Course course = (Course) context.getBean("course");
-
- ------------测试@Bean注解-------------
- ApplicationContext context = new AnnotationConfigApplicationContext(SpringCofig.class);
- // Student student = (Student) context.getBean("student");
- // Course course = (Course) context.getBean("course");
- // 从应用上下文中获取 DataSource 对象
- DataSource dataSource = context.getBean("dataSource", DataSource.class);
- // 验证 DataSource 对象是否为 DruidDataSource 类型
- if (dataSource instanceof DruidDataSource) {
- DruidDataSource druidDataSource = (DruidDataSource) dataSource;
- System.out.println("Druid DataSource 配置信息:");
- System.out.println("Driver Class Name: " + druidDataSource.getDriverClassName());
- System.out.println("URL: " + druidDataSource.getUrl());
- System.out.println("Username: " + druidDataSource.getUsername());
- System.out.println("Password: " + druidDataSource.getPassword());
- }
复制代码 别的问题
为什么p 定名空间方式需要无参构造
缘故原由:在 Spring 中,当你使用属性注入(如你给出的 p 定名空间方式)时,Spring 容器在创建 Bean 实例的过程中,默认情况下会尝试使用无参构造函数来实例化对象,之后再通过 setter 方法举行属性注入。
Spring 容器在创建 Bean 实例时,主要有以下几个步调:
- 实例化对象:Spring 需要先创建 Bean 对应的 Java 对象实例,一般情况下,它会优先尝试使用无参构造函数来创建对象。这是由于无参构造函数可以简单直接地创建对象,不需要额外的参数信息。
- 属性注入:在对象实例创建完成后,Spring 会根据配置(如 p 定名空间指定的属性值)调用对象的 setter 方法将属性值注入到对象中。
所以,当你使用 p 定名空间举行属性注入时,Spring 起首会尝试调用无参构造函数来创建 Teacher 对象,若类中没有无参构造函数,就会抛出 No default constructor found 如许的异常。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |