深圳的网站建设公司哪家好/seo排名计费系统

深圳的网站建设公司哪家好,seo排名计费系统,久久建材有限公司,佛山做外贸网站案例文章目录 1. Spring2. IOC 控制反转2-1. 通过配置文件定义Bean2-1-1. 通过set方法来注入Bean2-1-2. 通过构造方法来注入Bean2-1-3. 自动装配2-1-4. 集合注入2-1-5. 数据源对象管理(第三方Bean)2-1-6. 在xml配置文件中加载properties文件的数据(context命名空间)2-1-7. 加载容器…

文章目录

  • 1. Spring
  • 2. IOC 控制反转
    • 2-1. 通过配置文件定义Bean
      • 2-1-1. 通过set方法来注入Bean
      • 2-1-2. 通过构造方法来注入Bean
      • 2-1-3. 自动装配
      • 2-1-4. 集合注入
      • 2-1-5. 数据源对象管理(第三方Bean)
      • 2-1-6. 在xml配置文件中加载properties文件的数据(context命名空间)
      • 2-1-7. 加载容器的其他方式
      • 2-1-8. p命名空间
      • 2-1-9. c命名空间
      • 2-1-10. util命名空间
    • 2-2. 通过注解加载Bean
      • 2-2-1. 选择性实例化Bean
      • 2-2-2. 纯注解加载Bean
      • 2-2-3. 自动装配
      • 2-2-4. 简单类型的注入
      • 2-2-5. 管理第三方Bean
    • 2-3. Bean的实例化方式
      • 2-3-1. 通过构造方法来实例化Bean
      • 2-3-2. 通过简单工厂模式实例化
      • 2-3-3. 通过工厂方法模式来实例化
      • 2-3-4. 通过FactoryBean接口实例化
      • 2-3-5. BeanFactory 和 FactoryBean 的区别
      • 2-3-6. FactoryBean 的应用(以自定义Date类型举例)
    • 2-4. Bean的生命周期(5步)
      • 2-4-1. Bean 的生命周期之7步
      • 2-4-2. Bean 的生命周期之10步
      • 2-4-3. Bean的作用域
      • 2-4-4. 自己实例化的对象让spring容器去管理
    • 2-5. Bean 循环依赖问题
      • 2-5-1. set注入 + 单例模式之循环依赖
      • 2-5-2. 构造器注入 + 单例模式之循环依赖
      • 2-5-3. Spring 解决循环依赖的机理
    • 2-6. 自定义 spring 框架
  • 3. Spring之JdbcTemplate
  • 4. Spring 代理模式
    • 4-1. jdk 之动态代理
    • 4-2. cglib 之动态代理
    • 4-3. jdk 与 cglib 动态代理的区别
  • 5. 面向切面编程 AOP
  • 6. Spring 事务
    • 6-1. 事务的传播特性
    • 6-2. 事务的隔离级别
    • 6-3. 事务的超时时间
    • 6-4. 设置事务只读(readOnly)

1. Spring

主要包括两种功能,分别为IOC(Inverse Of Control,意为着控制反转,用于反转创建Bean的控制权。通过使用ioc,可以降低代码的耦合度,耦合度指的是类与类之间的依赖关系,如果耦合度高表明类与类之间的依赖关系越紧密,此时如果修改其中的一些代码,可能会造成其他类出错的情况,对于后期的维护及其不便。使用ioc容器管理的Bean,推荐使用实现接口的实现类)、AOP(Aspect Oriented Programming,意为面向切面编程)。

2. IOC 控制反转

IOC意为控制反转,也就是反转创建Bean的控制权。在这之前我们需要调用一个类下的某个方法时,通常做法是首先对这个类进行实例化,然后再调用其实例化对象的方法。通过IOC,把这个创建类交给一个容器去管理,我们需要用到时,只需要从容器中去拿即可。当然前提是我们需要定义配置文件,当然,随着版本的迭代,后期发展到我们只需要添加一些注解即可。

2-1. 通过配置文件定义Bean

这个前提是需要有spring-context的依赖(我的这个是SpringBoot项目哈),导入这个依赖之后,在resources这个目录下鼠标右键,找到新建xml配置文件就有对应的配置文件格式了。
在这里插入图片描述
具体xml配置文件格式如下:

<?xml version="1.0" encoding="UTF-8"?>
<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="userDao" class="com.lz.demo_spring_24_1.dao.UserDao"/></beans>

我在这个配置文件里边定义了一个Bean,并且这个Bean的名字为userDao,此时我们使用java代码就可以从这个容器中去获取这个Bean了。当然首先需要先加载到这个配置文件,这里使用 ClassPathXmlApplicationContext去加载,加载完之后可以得到一个ioc容器对象,此时,只需要通过这个ioc容器对象通过getBean即可获取对应的Bean对象。

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.dao.UserDao;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.support.ClassPathXmlApplicationContext;@SpringBootTest(classes = Test2.class)
public class Test2 {@Testpublic void test1(){ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config.xml");UserDao userDao = (UserDao) ctx.getBean("userDao");System.out.println(userDao);}
}

2-1-1. 通过set方法来注入Bean

在一个类下如果需要引入另外一个类的方法,前提是需要对这个类实例化。如果使用ioc,添加set方法即可(还有其他)。

package com.lz.demo_spring_24_1.service;import com.lz.demo_spring_24_1.dao.UserDao;
public class UserService {private UserDao ud;public void setUd(UserDao ud) {this.ud = ud;}public void print(){System.out.println(ud);}
}

对应的配置文件中需要做的配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<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="userDao" class="com.lz.demo_spring_24_1.dao.UserDao"/><bean id="userService" class="com.lz.demo_spring_24_1.service.UserService"><property name="ud" ref="userDao"/></bean>
</beans>

测试代码如下:

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.support.ClassPathXmlApplicationContext;@SpringBootTest(classes = Test2.class)
public class Test2 {@Testpublic void test1(){ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config.xml");UserService us = (UserService) ctx.getBean("userService");System.out.println(us);us.print();}
}

注意:需要提醒一下是,set注入的配置文件Bean的property属性name值是根据对应类的set方法名来的,而不是根据对应Bean的变量名。
在这里插入图片描述
在这里插入图片描述
上述是错误写法,其中name属性值应该修改为abcSdi才对。
另外,关于xml配置文件中的其他一些属性。
name属性用于给这个Bean起别名,多个别名之间用逗号隔开。
在这里插入图片描述
scope属性用于设置对应的Bean是单例,还是原型,默认情况下是单例的
在这里插入图片描述
可以修改为原型的。对于一个简单类型,想在配置文件中注入值,只需要设置其value属性即可。
在这里插入图片描述
如果想了解哪些类型是简单类型,可以去BeanUtils类下找到isSimpleValueType方法,查看对应的源码就可以知道。
在这里插入图片描述
常见的简单类型有八种基本数据类型、字符串、枚举类型、Class类型、日期类型(不过日期类型在配置文件需要写入特定的格式才支持,因此通常情况下会把日期类型当作是简单类型来注入)。

2-1-2. 通过构造方法来注入Bean

需要添加对应的构造方法即可,然后在配置文件中添加对应的构造参数即可。

package com.lz.demo_spring_24_1.service;import com.lz.demo_spring_24_1.dao.UserDao;public class UserService {private UserDao ud;private Integer val;public void setVal(Integer val) {this.val = val;}public void setUd(UserDao ud) {this.ud = ud;}public UserService(){}public UserService(UserDao ud){this.ud = ud;}public UserService(UserDao ud,Integer val){this.ud = ud;this.val = val;}public void print(){System.out.println(ud+" "+val);}
}
<?xml version="1.0" encoding="UTF-8"?>
<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="userDao" class="com.lz.demo_spring_24_1.dao.UserDao"/><bean id="userService" class="com.lz.demo_spring_24_1.service.UserService"><constructor-arg name="ud" ref="userDao"/><constructor-arg name="val" value="1000"/></bean></beans>

上述这样配置存在一个问题,那就是耦合度比较高(因为这是通过构造方法的变量名来进行注入的)比如如果我在类文件里边修改ud为ud1,那么此时就需要在配置文件中做对应的修改。此时可以通过设置类型type属性从而解决这个耦合度问题,如下:
在这里插入图片描述
但是如果构造器方法中存在很多相同类型,上述解决办法就不行了,此时可以通过设置构造器方法中的参数位置index属性来解决。
在这里插入图片描述

2-1-3. 自动装配

在xml配置文件中的配置autowire属性即可,本质依旧是set注入,因此在类文件下依旧需要添加对应的set方法,如下:

<?xml version="1.0" encoding="UTF-8"?>
<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="userDao" class="com.lz.demo_spring_24_1.dao.UserDao"/><bean id="userService" class="com.lz.demo_spring_24_1.service.UserService" autowire="byType"/>
</beans>

属性autowire的值也可以修改为byName,此时根据的是通过Bean的名字进行注入,因此在配置文件中要注入的Bean的id值不能随便取,这里需要额外注意一下。上述是根据Bean的类型进行注入的,只不过在xml配置文件中要注入的Bean只能为一个,否则会报错,因为它不知道到底需要注入哪一个。
对应的java类如下:

package com.lz.demo_spring_24_1.service;import com.lz.demo_spring_24_1.dao.UserDao;
public class UserService {private UserDao ud;public void setUd(UserDao ud) {this.ud = ud;}public void print(){System.out.println(ud+" ");}
}

既然是自动装配,那么装配的那个Bean肯定是需要在配置文件中进行定义的。
在这里插入图片描述

2-1-4. 集合注入

对应java类参考代码如下:

package com.lz.demo_spring_24_1.other;import java.util.*;public class Datas {private int[] arr1;private List<String> list1;private Set<String> set;private Map<String,Object> map;private Properties properties;public void setArr1(int[] arr1) {this.arr1 = arr1;}public void setList1(List<String> list1) {this.list1 = list1;}public void setSet(Set<String> set) {this.set = set;}public void setMap(Map<String, Object> map) {this.map = map;}public void setProperties(Properties properties) {this.properties = properties;}@Overridepublic String toString() {return "Datas{" +"arr1=" + Arrays.toString(arr1) +", list1=" + list1 +", set=" + set +", map=" + map +", properties=" + properties +'}';}
}

xml配置文件中的配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<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="datas" class="com.lz.demo_spring_24_1.other.Datas"><property name="arr1"><array><value>123</value><value>456</value></array></property><property name="list1"><list><value>123</value><value>234</value></list></property><property name="set"><set><value>123</value><value>234</value></set></property><property name="map"><map><entry key="country" value="china"/><entry key="age" value="100"/></map></property><property name="properties"><props><prop key="country">china</prop><prop key="age">100</prop></props></property></bean>
</beans>

如果在数组、List、Set中注入非简单类型,只需要把value标签修改为ref标签,且在ref标签的bean属性中写入对应的Bean的名称即可。
在这里插入图片描述
对于map数据类型,如果key、value值是非简单类型,直接使用key-ref、value-ref即可。
在这里插入图片描述

2-1-5. 数据源对象管理(第三方Bean)

这里以druid数据源为例,需要导入druid的依赖。

<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version>
</dependency>

在对应的xml配置文件中定义的Bean如下(这里使用的是set注入,由于其并没有对应设置对应配置的构造方法,所以使用set注入,而不使用构造器注入):

<?xml version="1.0" encoding="UTF-8"?>
<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="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mytest1"/><property name="username" value="root"/><property name="password" value="root"/></bean></beans>

运行结果:
在这里插入图片描述

2-1-6. 在xml配置文件中加载properties文件的数据(context命名空间)

前提是需要在xml中开启context命名空间,如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd">
</beans>

之后需要在xml配置文件中加载properties文件,最后修改其中Bean的一些配置即可。propertis文件配置如下:

spring.application.name=demo_spring_24_1jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mytest1
jdbc.username=root
jdbc.password=root

此时在xml配置文件中只需要使用${}引入对应的名称即可。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:property-placeholder location="application.properties"/>
<!--    使用context空间加载properties文件--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean>
</beans>

如果想要加载多个properties文件,可以在location属性中用逗号隔开,写上其他properties文件即可。

<context:property-placeholder location="application.properties,application2.properties"/>

上述这种写法并不怎么规范,规范写法应该是这样,如下:

<context:property-placeholder location="classpath:*.properties"/>

如果想不加载系统属性,可以在上面context的属性system-properties-mode设置为NEVER即可。

<context:property-placeholder location="application.properties" system-properties-mode="NEVER"/>

2-1-7. 加载容器的其他方式

上述方式都是通过加载类路径下的配置文件来进行的,其实还可以通过加载绝对路径来进行。
在这里插入图片描述

2-1-8. p命名空间

这种方式本质上是set注入,因此依旧需要在对应的类文件中添加set方法,但是在配置文件中可以简化一些操作而已。(是set注入的一种简化而已)首先,需要对配置文件中添加一点配置,参考如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"></beans>

在这里插入图片描述

package com.lz.demo_spring_24_1.entity;import java.util.Date;public class User {private String name;private Date birthDay;public void setName(String name) {this.name = name;}public void setBirthDay(Date birthDay) {this.birthDay = birthDay;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", birthDay=" + birthDay +'}';}
}

在这里插入图片描述
只需要在配置文件以p:变量名后添加对应的值即可,因为变量birthDay是日期类型,可以使用简单类型,也可以使用非简单类型。
在这里插入图片描述
运行结果:
在这里插入图片描述

2-1-9. c命名空间

本质上是构造方法注入,因此需要添加对应的构造方法。(只是构造器注入的一种简化而已)。和p命名空间一样,都需要对xml配置文件添加一些配置,用以开启c命名空间。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:c="http://www.springframework.org/schema/c"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="date" class="java.util.Date"/><bean id="user" class="com.lz.demo_spring_24_1.entity.User" c:_0="张三" c:birthDay-ref="date"/></beans>

有两种方式可以进行注入,一种是通过参数的位置,另外一种是通过参数名,参考如上,运行结果和p命名空间一样。

2-1-10. util命名空间

和上面两种命名空间一样,需要在xml配置文件中添加util命名空间的配置。配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:util="http://www.springframework.org/schema/util"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
</beans>

现在假设我想要连接数据库,但是可以使用连接池可以用druid、c3p0,它们都需要添加一些连接数据库的配置。
在这里插入图片描述
在这里插入图片描述

并且连接的配置信息都相同,此时可以采用util命名空间(实现配置复用而已),如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:util="http://www.springframework.org/schema/util"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"><util:properties id="pro"><prop key="driver">com.mysql.cj.jdbc.driver</prop><prop key="url">jdbc:mysql://localhost:3306/mytest1</prop><prop key="username">root</prop><prop key="password">root</prop></util:properties><bean id="ds1" class="com.lz.demo_spring_24_1.entity.MyDataSource1"><property name="properties" ref="pro"/></bean><bean id="ds2" class="com.lz.demo_spring_24_1.entity.MyDataSource2"><property name="properties" ref="pro"/></bean>
</beans>

运行结果如下:
在这里插入图片描述

2-2. 通过注解加载Bean

这种方式最初的版本依旧需要写xml配置文件,同时需要在对应的类上加上注解@Component,如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.lz.demo_spring_24_1.dao"/></beans>

只要是在com.lz.demo_spring_24_1.dao这个目录下所有的添加了@Component注解的类都会被容器进行管理。如果需要添加其他包下Bean被spring容器进行管理,可以在上述配置文件的包后用逗号隔开,之后再添加其他包路径即可。当然也可以写两个包的父包即可。

package com.lz.demo_spring_24_1.dao;import org.springframework.stereotype.Component;@Component
public class UserDao {
}

从@Component又衍生出其他三种注解,分别为@Repository(数据层)、@Service(业务层)、@Controller(表现层)。它们的功能都相同,只是为了便于分辨而已。

2-2-1. 选择性实例化Bean

对于一个包下所定义的Bean(添加了对应注解的),如何在xml配置文件中选择性去选择哪些Bean可以被实例化。比如现在我定义了两个Bean,其中一个为a,另一个为b,a上添加了注解@Service,b上添加了注解@Controller,现在使用context命名空间扫描这两个bean的父包。然后进行过滤,只把有注解@Service Bean a添加到spring容器中进行管理,参考代码如下:

package com.lz.demo_spring_24_1.beans;import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;@Service
public class A {public A(){System.out.println("A........");}
}@Controller
class B {public B(){System.out.println("B........");}
}

xml配置文件写法1

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.lz.demo_spring_24_1.beans" use-default-filters="true"><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan></beans>

xml配置文件写法2

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.lz.demo_spring_24_1.beans" use-default-filters="false"><context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/></context:component-scan></beans>

运行结果如下:
在这里插入图片描述

2-2-2. 纯注解加载Bean

这种模式不需要编写xml配置文件,在上述代码不变的基础上,新建一个配置类,当然需要添加@Configuration注解。写这个配置类相当于是代替上面那个xml配置文件。

package com.lz.demo_spring_24_1.config;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration
@ComponentScan(value = "com.lz.demo_spring_24_1.dao")
public class SpringConfig {
}

之前是加载那个配置文件xml,此时如果想要运行成功,需要加载这个配置类。

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.config.SpringConfig;
import com.lz.demo_spring_24_1.dao.UserDao;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;import java.sql.SQLException;@SpringBootTest(classes = Test2.class)
public class Test2 {@Testpublic void test1() {ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);UserDao userDao = (UserDao) ctx.getBean("userDao");System.out.println(userDao);}
}

2-2-3. 自动装配

现在我在数据访问层定义了一个类UserDao,还在业务层定义了一个类UserService,其中需要在UserService中引用UserDao类下的某个方法。为此,需要在UserService中new UserDao,在前面知识中了解到,可以通过set、构造器、自动装配这三种方式注入UserDao类对象,但是,上述讲述的是xml配置文件来进行注入的,现在如何使用配置类来实现上述那种自动装配的效果呢?参考代码如下:

package com.lz.demo_spring_24_1.dao;import org.springframework.stereotype.Repository;@Repository
public class UserDao {
}
package com.lz.demo_spring_24_1.service;import com.lz.demo_spring_24_1.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate UserDao ud;public void print(){System.out.println(ud);}}

我们只需要在UserService类中使用注解@AutoWired这种注入方式即可,当然也可以使用注解@Resource。它们两者的区别是前者首先是根据类型去对应包下去查找是否存在UserDao这个Bean,如果没有,再通过Bean名去查找,如果两者都没有找到或者出现歧义,最终会报错;而后者正好相反。@Resource注解可以通过name属性指定Bean名,如果name值没有指定,那么会把变量名当作Bean名来使用。。(也就是说如果此时变量名和对应的Bean名不一致,此时再根据类型来进行装配,如果没有找到,会报错。。。)

如果所注入的Bean有多个(比如有多个类都实现了某个接口,而且注入的Bean类型使用了泛型),此时可以在注解@AutoWired下添加注解@Qualifier,并在@Qualifier内写上对应的Bean名,当然这个Bean名需要在对应的类上写上才行。如下,有一个接口UserDao,它有一个方法printUserInfo,它有两个实现类UserDaoImpl1、UserDaoImpl2,这个类上都添加了注解@Repository。然后有一个类UserService,添加了注解@Service,在这个类下需要UserDao的依赖,这里直接使用泛型。如下:

package com.lz.demo_spring_24_1.beans;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate UserDao ud;public void printUserInfo(){ud.printUserInfo();}
}

此时运行结果如下:
在这里插入图片描述
因为UserDao有两个实现类,此时不知道使用哪个,因此报错,此时就可以添加注解@Qualifier添加对应的Bean名(不设置对应Bean名,Bean名默认为类名首字母小写。。),如下:
在这里插入图片描述
当然也可以直接使用@Resource注解,写上对应的Bean名。
在这里插入图片描述
另外,注解@AutoWired也可以放在set方法上及构造方法上,如果所注入的Bean只有一个,@AutoWired可以省略,但是需要添加对应的构造方法,需要注意的是如果存在默认无参构造方法,这样是不行的,如下:
在这里插入图片描述

2-2-4. 简单类型的注入

通过使用注解@Value可以注入简单类型,如下:

package com.lz.demo_spring_24_1.dao;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;@Repository
public class UserDao {@Value("lize")private String name;@Overridepublic String toString() {return "UserDao{" +"name='" + name + '\'' +'}';}
}

不过,上述这样写基本上没有任何意义,直接在name后面添加等于号并写上对应的值不也是一样吗?@Value真正意义在于可以注入配置文件properties中的变量,如下:
首先和上面xml配置一样,首先需要在配置类上指明所对应的配置文件,需要用到注解@PropertySource。
在这里插入图片描述
使用的话只需要在@Value注解内用${}指明引用哪个变量的值。
在这里插入图片描述
运行结果如下:
在这里插入图片描述
另外@Value还可以使用在set方法上,以及构造方法上的对应参数上。。

2-2-5. 管理第三方Bean

首先需要定义一个配置类,在这个配置类下定义对应Bean的方法。参考代码如下:

package com.lz.demo_spring_24_1.config;import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;@Configuration
public class SpringConfig2 {@Beanpublic DataSource dataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName("com.mysql.jdbc.driver");ds.setUrl("jdbc:mysql://localhost:3306/mytest1");ds.setUsername("root");ds.setPassword("root");return ds;}
}

但是上述方式存在一个问题,就是如果我想修改上述一些配置信息,此时还需要找到这个类,然后再进行修改,为此,我们可以把上述信息放到配置文件properties中去,需要用到时只需要通过@Value注解注入即可。

2-3. Bean的实例化方式

这部分有一些内容和前面有重复,但是这里相当于是总结了吧!

2-3-1. 通过构造方法来实例化Bean

在xml配置文件中定义对应的Bean,然后直接通过容器.getBean方法来获取对应的Bean。默认情况下,是调用Bean的无参构造方法。。

2-3-2. 通过简单工厂模式实例化

本质上依旧是通过构造方法来实例化Bean,参考代码如下:
工厂类

 package com.lz.demo_spring_24_1.entity.factory;public class Factory1 {public static User getUser(){return new User();}}

xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<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="user" class="com.lz.demo_spring_24_1.entity.factory.Factory1" factory-method="getUser"/></beans>

运行结果:
在这里插入图片描述

2-3-3. 通过工厂方法模式来实例化

本质上依旧是通过构造方法来实例化Bean,参考代码如下:
工厂类

package com.lz.demo_spring_24_1.entity.factory;public class UserFactory {public User get(){return new User();}
}

xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<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="user2Factory" class="com.lz.demo_spring_24_1.entity.factory.UserFactory"/><bean id="user2" factory-bean="user2Factory" factory-method="get"/>
</beans>

运行结果:
在这里插入图片描述

2-3-4. 通过FactoryBean接口实例化

可以说是第三种方式的一种简化。参考代码如下:
实现了FactoryBean接口的类

package com.lz.demo_spring_24_1.entity.factory;import org.springframework.beans.factory.FactoryBean;public class UserFactoryBean implements FactoryBean<User> {@Overridepublic boolean isSingleton() {
//        return FactoryBean.super.isSingleton();return true;}// 默认是单例的@Overridepublic User getObject() throws Exception {return new User();}@Overridepublic Class<?> getObjectType() {return null;}
}

xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<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="user3" class="com.lz.demo_spring_24_1.entity.factory.UserFactoryBean"/>
</beans>

运行结果:
在这里插入图片描述

2-3-5. BeanFactory 和 FactoryBean 的区别

BeanFactory 是spring ioc容器的最顶层对象,意为 “Bean工厂”,负责创建Bean对象。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
FactoryBean 是一个Bean,是一个能够辅助 spring 实例化其他Bean对象的一个Bean。在spring中,Bean可以分为两类,一种为普通Bean,另外一种是工厂Bean。

2-3-6. FactoryBean 的应用(以自定义Date类型举例)

从前面可以知道,Date这种类型既可以当作是简单类型,也可以当作非简单类型。当作简单类型时,在xml配置文件中定义时,需要输入特定格式,否则会报错。而 FactoryBean 是Spring中一种用于辅助实例化其他Bean的Bean,为此,可以使用 FactoryBean 的形式,使在xml文件定义Date 类型的Bean支持自定义格式输入,参考代码如下:
继承了FactoryBean 接口的类

package com.lz.demo_spring_24_1.entity.factory;import org.springframework.beans.factory.FactoryBean;import java.text.SimpleDateFormat;
import java.util.Date;public class DateFactoryBean implements FactoryBean<Date> {private String date_str;public DateFactoryBean(String date_str) {this.date_str = date_str;}@Overridepublic boolean isSingleton() {return true;}@Overridepublic Date getObject() throws Exception {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");// 自定义输入的日期格式return sdf.parse(date_str);}@Overridepublic Class<?> getObjectType() {return null;}
}

xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<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="date" class="com.lz.demo_spring_24_1.entity.factory.DateFactoryBean"><constructor-arg name="date_str" value="2021-01-01"/></bean></beans>

运行结果:
在这里插入图片描述

2-4. Bean的生命周期(5步)

就是Bean从被创建到销毁的过程。Bean的生命周期可以被划分为5个过程,分别是实例化Bean、Bean属性赋值、初始化Bean、使用Bean、销毁Bean

  • 实例化Bean,调用无参数构造方法;
  • Bean属性赋值,调用set方法;
  • 初始化Bean,调用 Bean 的 init方法,需要自己编写代码,并进行配置;
  • 使用Bean;
  • 销毁Bean,调用 Bean 的destory方法,需要自己编写代码,并进行配置;

Bean的声明周期代码演示如下:

package com.lz.demo_spring_24_1.entity;// Bean 的生命周期
public class User3 {private String name;public User3() {System.out.println("1. 实例化Bean。。。");}public void setName(String name) {this.name = name;System.out.println("2. Bean 参数赋值...");}// 初始化Beanpublic void init(){System.out.println("3. 初始化Bean。。。");}// 销毁 Beanpublic void destory(){System.out.println("5. 销毁Bean。。。");}
}
<?xml version="1.0" encoding="UTF-8"?>
<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="user" class="com.lz.demo_spring_24_1.entity.User3" init-method="init" destroy-method="destory"><property name="name" value="张三"/></bean></beans>
package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.entity.User3;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test2025_1 {@Testpublic void test3(){ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-2025-3.xml");User3 user3 = ctx.getBean("user", User3.class);System.out.println("4. 使用Bean"+user3);// 使用Beanctx.close();// 关闭容器,只能applicationContext实现类才有close方法。。。}
}

运行结果:
在这里插入图片描述

2-4-1. Bean 的生命周期之7步

在上面说到Bean的生命周期只有5步,7步的说法是在前面5步的基础上添加了2步,就是在5步的第4步 初始化Bean 前面加上 执行”Bean后处理器“的before方法,在后面加上 执行”Bean后处理器“的fater方法。。。
需要在前面基本上添加一个类,这个类需要实现BeanPostProcessor接口。。。,并重写其下面的2个方法。

package com.lz.demo_spring_24_1.entity.interfaces;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;public class LogBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("执行 Bean后处理器 的before方法");return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("执行 Bean后处理器 的after方法");return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);}
}

另外需要在xml配置文件中配置这个Bean。
在这里插入图片描述
之后就可以看到运行结果了。。。
在这里插入图片描述
需要注意的是,上述添加这个Bean对所有的Bean都会生效。。。也就是说 当前容器对象 getBean 之后获取到Bean都会执行上述两个函数。。。

2-4-2. Bean 的生命周期之10步

在前面7步的基础之上,再额外添加3步,添加的位置分别为:

  • 在 执行”Bean后处理器“的before方法 前面添加 检查 Bean 是否实现了Aware的相关接口,并设置相关依赖;
  • 在 执行”Bean后处理器“的before方法 后面添加 检查 Bean 是否实现了InitializingBean接口,并调用接口方法;
  • 在使用Bean 之后添加了 检查 Bean 是否实现了DispossableBean接口,并调用接口方法。

其中Aware的相关接口有:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware。

  • 当Bean实现了BeanNameAware,spring会将Bean的名字传递给Bean;
  • 当Bean实现了BeanClassLoaderAware,spring会将加载该Bean的类加载器传递给Bean;
  • 当Bean实现了BeanFactoryAware,spring会将Bean工厂对象传递给Bean。

总结而言:如果测试生命周期10步,需要让对应的类实现5个接口,分别为BeanNameAware、BeanClassLoaderAware、BeanFactoryAware、InitializingBean、DispossableBean。

演示代码如下:

package com.lz.demo_spring_24_1.entity;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;// Bean 的生命周期
public class User3 implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {private String name;public User3() {System.out.println("1. 实例化Bean。。。");}public void setName(String name) {this.name = name;System.out.println("2. Bean 参数赋值...");}// 初始化Beanpublic void init(){System.out.println("4. 初始化Bean。。。");}// 销毁 Beanpublic void destory1(){System.out.println("7. 销毁Bean。。。");}@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {System.out.println("Bean的类加载器:"+classLoader);}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("Bean的 工厂对象是:"+beanFactory);}@Overridepublic void setBeanName(String s) {System.out.println("Bean的名字是:"+s);}@Overridepublic void destroy() throws Exception {System.out.println("实现了DisposableBean接口。。。");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("实现了InitializingBean接口。。。");}
}
<?xml version="1.0" encoding="UTF-8"?>
<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 class="com.lz.demo_spring_24_1.entity.interfaces.LogBeanPostProcessor"/><bean id="user" class="com.lz.demo_spring_24_1.entity.User3" init-method="init" destroy-method="destory1"><property name="name" value="张三"/></bean></beans>

运行结果:
在这里插入图片描述

2-4-3. Bean的作用域

spring 容器只对 单例的 Bean进行完整的生命周期管理。如果是原型的Bean,spring容器只负责将Bean初始化完毕,等客户端一旦获取到该Bean之后,spring容器就不在管理该对象的生命周期了。如果需要测试的话,只需要在xml配置文件的Bean添加属性scope,并设置值为原型,运行结果如下:
在这里插入图片描述

2-4-4. 自己实例化的对象让spring容器去管理

直接复制类代码,导包让软件去导入。。。

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.entity.User3;
import com.lz.demo_spring_24_1.entity.User4;
import com.lz.demo_spring_24_1.entity.factory.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Date;public class Test2025_1 {@Testpublic void test4(){User4 user4 = new User4();System.out.println(user4);DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();beanFactory.registerSingleton("user",user4);User4 user1 = beanFactory.getBean(User4.class);System.out.println(user1);}
}

运行结果:
在这里插入图片描述

2-5. Bean 循环依赖问题

其实就是在一个Bean a中需要Bean b的依赖,而在Bean b中又需要Bean a的依赖。

2-5-1. set注入 + 单例模式之循环依赖

比如如下代码:

package com.lz.demo_spring_24_1.entity.xunhuan;public class UserA {private String name;private UserB userB;public void setName(String name) {this.name = name;}public void setUserB(UserB userB) {this.userB = userB;}@Overridepublic String toString() {return "UserA{" +"name='" + name + '\'' +", userB=" + userB.getName() +'}';}public String getName() {return name;}
}
package com.lz.demo_spring_24_1.entity.xunhuan;public class UserB {private String name;private UserA userA;public void setName(String name) {this.name = name;}public void setUserA(UserA userA) {this.userA = userA;}@Overridepublic String toString() {return "UserB{" +"name='" + name + '\'' +", userA=" + userA.getName() +'}';}public String getName() {return name;}
}

xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<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="usera" class="com.lz.demo_spring_24_1.entity.xunhuan.UserA"><property name="name" value="张三"/><property name="userB" ref="userb"/></bean><bean id="userb" class="com.lz.demo_spring_24_1.entity.xunhuan.UserB"><property name="name" value="李四"/><property name="userA" ref="usera"/></bean>
</beans>

运行结果:
在这里插入图片描述
如果上述代码UserA的toString方法中参数直接是UserB,并且在UserB的toString方法中参数直接是UserA。此时的结果会报错,因为输出UserA这个对象时,实际上调用的是重写toString方法,而在toString方法中又会输出UserB,而在UserB的toString方法下又有UserA,此时会陷入si循环。。最终导致内存溢出从而导致报错。
上面是 单例模式 + set注入(原型模式下不可以) 的模式下的运行结果,Spring容器在加载的时候,进行实例化,只要任意一个Bean实例化后,马上进行“曝光”,不等属性赋值;Bean被“曝光”之后,再进行属性赋值。(在spring中为什么可以解决循环依赖的问题。。。)需要注意的是,在spring中只有当两个Bean都是原型下,才会出现异常,但是如果其中有一个是单例的,就不会出现异常。。

2-5-2. 构造器注入 + 单例模式之循环依赖

这种方式是存在问题,因为这是直接在Bean a构造方法里面给属性赋值,但是其中一个参数Bean b还没有进行实例化,而Bean b里边又有一个参数Bean a也没有进行实例化。参考代码如下:

package com.lz.demo_spring_24_1.entity.xunhuan;public class UserA {private String name;private UserB userB;public UserA(String name, UserB userB) {this.name = name;this.userB = userB;}/*public void setName(String name) {this.name = name;}public void setUserB(UserB userB) {this.userB = userB;}*/@Overridepublic String toString() {return "UserA{" +"name='" + name + '\'' +", userB=" + userB.getName() +'}';}public String getName() {return name;}
}
package com.lz.demo_spring_24_1.entity.xunhuan;public class UserB {private String name;private UserA userA;public UserB(String name, UserA userA) {this.name = name;this.userA = userA;}/*public void setName(String name) {this.name = name;}public void setUserA(UserA userA) {this.userA = userA;}*/@Overridepublic String toString() {return "UserB{" +"name='" + name + '\'' +", userA=" + userA.getName() +'}';}public String getName() {return name;}
}

在这里插入图片描述
也就是说 构造器注入 + 单例模式 这种方式下spring是无法解决循环依赖问题的。

2-5-3. Spring 解决循环依赖的机理

set注入+单例模式下为什么能解决循环依赖问题?
根本原因在于:这种方式可以将 实例化Bean 和 给Bean属性赋值 这两个动作分开去完成。实例化Bean的时候,调用无参构造方法来完成,**此刻可以先不给属性赋值,可以提前将该Bean对象“曝光”给外界。**给Bean属性赋值的时候,调用setter方法来完成。两个过程是完全分开去完成的,并且两个过程不要求在同一个时间点上完成。

2-6. 自定义 spring 框架

这里的spring框架只有基本ioc功能,且还是通过配置文件的形式。。参考代码如下:
myspring核心代码

package com.lz.demo_spring_24_1.myspring.utils;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class MyBeanFactory {private Map<String,Object> beanMap = new HashMap<>();// 用来存储bean的哈希表// 在构造方法这里的读取xml配置文件的数据,// 然后通过反射机制进行实例化对象,之后把实例化后的对象存储到哈希表中进行存储public MyBeanFactory(String configPath) {try{SAXReader reader = new SAXReader();InputStream stream = ClassLoader.getSystemClassLoader().getResourceAsStream(configPath);Document document = reader.read(stream);List<Node> nodes = document.selectNodes("//bean");// 获取所有的bean标签for (Node node : nodes) {Element ele = (Element) node;// 转换成Element类型String id = ele.attributeValue("id");String clazz = ele.attributeValue("class");// bean 的名称,class 字符串Class<?> aClass = Class.forName(clazz);Constructor<?> constructor = aClass.getDeclaredConstructor();Object o = constructor.newInstance();beanMap.put(id,o);// 通过反射对bean进行无参实例化}setBeanField(nodes,beanMap);}catch (Exception e){e.printStackTrace();}}/*** 获取bean的方法* beanName : Bean的名称* */public Object getBean(String beanName){return beanMap.get(beanName);}// 给对象属性赋值// 相当于set注入private void setBeanField(List<Node> nodes,Map<String,Object> beanMap){for (Node node : nodes) {try{Element ele = (Element) node;String id = ele.attributeValue("id");String clazz = ele.attributeValue("class");// bean 的名称,class 字符串Class<?> aClass = Class.forName(clazz);List<Element> properties = ele.elements();// 所有的属性标签properties.forEach(property->{try{String name = property.attributeValue("name");String value = property.attributeValue("value");String ref = property.attributeValue("ref");String setName = "set" + name.substring(0,1).toUpperCase() + name.substring(1);Field field = aClass.getDeclaredField(name);
//                        field.setAccessible(true);Class<?> type1 = field.getType();Method setMethod = aClass.getDeclaredMethod(setName, type1);Object v = value;if(value != null){// 这个变量是私有的// 简单类型String typeSimpleName = type1.getSimpleName();// 属性类型名switch (typeSimpleName){case "byte":v = Byte.parseByte(value);break;case "short":v = Short.parseShort(value);break;case "int":v = Integer.parseInt(value);break;case "long":v = Long.parseLong(value);break;case "boolean":v = Boolean.parseBoolean(value);break;case "float":v = Float.parseFloat(value);break;case "double":v = Double.parseDouble(value);break;case "char":v = value.charAt(0);break;case "Byte":v = Byte.valueOf(value);break;case "Short":v = Short.valueOf(value);break;case "Integer":v = Integer.valueOf(value);break;case "Long":v = Long.valueOf(value);break;case "Boolean":v = Boolean.valueOf(value);break;case "Float":v = Float.valueOf(value);break;case "Double":v = Double.valueOf(value);break;case "Character":v = Character.valueOf(value.charAt(0));break;}setMethod.invoke(beanMap.get(id),v);}if(ref != null){// 非简单类型setMethod.invoke(beanMap.get(id),beanMap.get(ref));}}catch (Exception e){e.printStackTrace();}});}catch (Exception e){e.printStackTrace();}}}
}

测试类

package com.lz.demo_spring_24_1.myspring;public class User {private String name;private Integer age;private User2 user2;public void setAge(Integer age) {this.age = age;}public void setName(String name) {this.name = name;}public void setUser2(User2 user2) {this.user2 = user2;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +", user2=" + user2 +'}';}
}
package com.lz.demo_spring_24_1.myspring;public class User2 {
}

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans><bean id="user" class="com.lz.demo_spring_24_1.myspring.User"><property name="name" value="张三"/><property name="age" value="20"/><property name="user2" ref="user2"/></bean><bean id="user2" class="com.lz.demo_spring_24_1.myspring.User2"/></beans>

运行结果:
在这里插入图片描述
需要注意的是,因为需要解析xml文件数据,需要导入对应依赖,如下:

<!--        用于解析xml文件的包--><dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3</version></dependency><dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.2.0</version></dependency>

3. Spring之JdbcTemplate

JdbcTemplate是Spring提供的一个jdbc模板类,是对jdbc的封装。当然,现在大多数用的都是MyBatis等。首先需要导入的依赖为:

        <!-- MySQL JDBC 驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.46</version></dependency>
<!--        spring-jdbc--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId></dependency>

另外还需要spring-context的依赖哈。因为我这边使用的mysql 数据库版本为5.xxx的版本,因此使用mysql的驱动为5.xxx,在对应的xml配置文件中的配置为:

<?xml version="1.0" encoding="UTF-8"?>
<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="myDataSource" class="com.lz.demo_spring_24_1.jdbcTemplate.MyDataSource"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mytest1?characterEncoding=utf8"/><property name="username" value="root"/><property name="password" value="root"/></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="myDataSource"/></bean></beans>

往数据库中插入一条数据,如下:

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.jdbcTemplate.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;import java.util.List;public class MyJdbcTemplate {@Testpublic void test(){ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-jdbcTemplate.xml");JdbcTemplate jdbcTemplate = ctx.getBean("jdbcTemplate",JdbcTemplate.class);String sql = "insert into user values(?,?)";jdbcTemplate.update(sql,2,"王五");}
}

插入是可以成功的。
如果想查询数据,并且查询出的数据字段都需要映射到对应实体类上对应变量上去,可以使用如下代码:

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.jdbcTemplate.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;import java.util.List;public class MyJdbcTemplate {@Testpublic void test(){ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-jdbcTemplate.xml");JdbcTemplate jdbcTemplate = ctx.getBean("jdbcTemplate",JdbcTemplate.class);String sql = "select * from user";List<User> users = jdbcTemplate.query(sql,new BeanPropertyRowMapper<>(User.class));for (User user : users) {System.out.println(user);}}
}

如果想要更换成其他的DataSource,只需要在xml配置文件中修改对应DataSource的配置即可,class属性值修改为druid的,driver、url、username、password这四个属性名可能有所不同。。

4. Spring 代理模式

关于Spring aop的功能实现本质上就是动态代理,参考文章链接为:Spring AOP原理–动态代理。。。关于上述文章的静态代理,这里有更加详细的参考,代理类和被代理类都需要实现公共的接口,如下:
公共的接口类

package com.lz.demo_spring_24_1.proxy;
// 这是一个接口
public interface IUser {void play();// 方法 play
}

被代理的类

package com.lz.demo_spring_24_1.proxy.impl;import com.lz.demo_spring_24_1.proxy.IUser;public class IUserImpl implements IUser {@Overridepublic void play() {System.out.println("学习编程技术。。。");}
}

代理类

package com.lz.demo_spring_24_1.proxy;// 代理类
public class UserProxy implements IUser{// 这里应用泛型,可以降低代码的耦合度private IUser iUser = null;// 通过构造方法来把对应变量赋值public UserProxy(IUser iUser) {this.iUser = iUser;}@Overridepublic void play() {System.out.println("这里可以做一些前置操作。。。");iUser.play();System.out.println("这里可以做一些后置操作。。。");}
}

测试运行

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.proxy.IUser;
import com.lz.demo_spring_24_1.proxy.UserProxy;
import com.lz.demo_spring_24_1.proxy.impl.IUserImpl;
import org.junit.jupiter.api.Test;public class ProxyTest {@Testpublic void test(){IUser iUser = new IUserImpl();IUser userProxy = new UserProxy(iUser);userProxy.play();}
}

在这里插入图片描述
但是上述代理存在一个很大的问题,那就是接口下面的方法如果很多的话,并且在代理类上上的每个方法都需要增强,那么被代理类就需要写很多可能较为重复的增强代码;而且每个被代理类的都需要编写对应的代理类。因此,有了动态代理。。。

4-1. jdk 之动态代理

使用动态代理,代理类可以不用编写了,但是接口必须要有。。在上述代码基础之上进行操作。。。接口类和被代理类和上面一样。。jdk动态代理不需要额外添加依赖。

Proxy.newProxyInstance(arg1,arg2,arg3)

通过上述代码实现一个代理对象,其中参数分别表示的意思为:

  • arg1:被代理类的类加载器;
  • arg2:被代理类实现的接口;
  • arg3:最为关键,实现InvocationHandler的对象a,且对象a传入参数为被代理的那个对象(这样才能实现增强代码);这个只需要写一个即可,就可以解决上述静态代理存在的那两个问题。

实现InvocationHandler接口的类

package com.lz.demo_spring_24_1.proxy.jdkProxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class MyHandler implements InvocationHandler {private Object target;public MyHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// method 为被代理类方法// args 方法参数// 反射。。System.out.println("这里做一些前置操作。。。");Object ans = method.invoke(target,args);System.out.println("这里做一些后置操作。。。");return ans;}
}

运行代码:

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.proxy.IUser;
import com.lz.demo_spring_24_1.proxy.UserProxy;
import com.lz.demo_spring_24_1.proxy.impl.IUserImpl;
import com.lz.demo_spring_24_1.proxy.jdkProxy.MyHandler;
import org.junit.jupiter.api.Test;import java.lang.reflect.Proxy;public class ProxyTest {@Testpublic void test2(){IUser iUser = new IUserImpl();IUser iUserProxy = (IUser) Proxy.newProxyInstance(iUser.getClass().getClassLoader(),iUser.getClass().getInterfaces(),new MyHandler(iUser));iUserProxy.play();}
}

运行结果和上述一致。。。

4-2. cglib 之动态代理

如果是maven项目,需要额外导入cglib的依赖才行。参考代码如下:

package com.lz.demo_spring_24_1.proxy.cglibProxy;import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;public class MyCallback implements MethodInterceptor {@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("这里可以做一些前置操作。。。");Object ans = methodProxy.invokeSuper(target,args);System.out.println("这里可以做一些后置操作。。。");return ans;}
}
package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.proxy.cglibProxy.MyCallback;
import com.lz.demo_spring_24_1.proxy.impl.IUserImpl;
import org.junit.jupiter.api.Test;
import org.springframework.cglib.proxy.Enhancer;public class ProxyTest {@Testpublic void test3(){Enhancer enhancer = new Enhancer();// 设置被代理的类enhancer.setSuperclass(IUserImpl.class);enhancer.setCallback(new MyCallback());IUserImpl iUser = (IUserImpl) enhancer.create();// 代理的类iUser.play();}
}

运行结果和上面一致。。

4-3. jdk 与 cglib 动态代理的区别

在这里插入图片描述
参考链接在这:jdk 与 cglib 动态代理的区别

5. 面向切面编程 AOP

详细请看这篇博文:Aop 面向切面编程

Spring 的AOP底层实现本质是动态代理,jdk、cglib动态代理两者都有,Spring在这两种动态代理中可以根据实际应用场景实现切换,如果是代理接口,会默认使用jdk动态代理;如果要代理某个类,这个类没有实现接口,那么就会切换到cglib。当然,也可以通过配置强制让Spring来使用两者动态代理中的一种。

在这里插入图片描述
首先需要导入aspect的依赖

 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

之后编写xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><context:component-scan base-package="com.lz.demo_spring_24_1.proxy.aspect"/>
<!--    自动扫描--><aop:aspectj-autoproxy/>
<!--    让 @Aspect 起作用-->
</beans>

编写Aspect的切面类

package com.lz.demo_spring_24_1.proxy.aspect;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Aspect
@Component
public class MyAspect {@Before("execution(* com.lz.demo_spring_24_1.proxy.aspect.UserService.*(..))")public void fun1(){System.out.println("前置通知。。。");}}

测试代码

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.proxy.aspect.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class MyAspectTest {@Testpublic void test(){ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-2025-aspect.xml");UserService userService = ctx.getBean("userService",UserService.class);userService.selectAll();}
}

运行结果:
在这里插入图片描述
在这里可以打印一下UserService的Class值,可以发现它是属于cglib动态代理生成的。
在这里插入图片描述
因为UserService类并不是通过实现某某接口的。
如果想纯注解实现上述效果,只需要把上述xml配置文件用一个配置类来代替即可,配置类参考如下:

package com.lz.demo_spring_24_1.proxy.aspect;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@ComponentScan(value = "com.lz.demo_spring_24_1.proxy.aspect")
@EnableAspectJAutoProxy
public class Config {
}

当然测试代码的加载容器需要修改一下:
在这里插入图片描述

6. Spring 事务

具体详细请看这篇文章:Spring 事务。在一个业务中,可能涉及到多条sql数据的执行,这些sql数据要么全部执行成功,要么全部执行失败,为此,应用到了事务 Transaction。
事务的四个处理过程,包括开启事务、执行核心业务代码、提交事务、回滚事务。事务的四个特性为:原子性、一致性、隔离性、持久性,也就是常说的ACID,其中原子性表示事务不可以再分;一致性表示事务要么同时成功,要么同时失败;隔离性表示事务和事务之间互不干扰;持久性表示一旦事务提交,它对数据库的修改就是永久性的,即使系统发生故障,数据也不会丢失。

6-1. 事务的传播特性

关于事务的传播特性,总共有7种,下述只是给出常见的四种。
在这里插入图片描述
测试结果:如果外部事物存在,并且内部事务也存在,且两个事务的传播行为都为REQUIRED。此时内部事务有抛异常的代码,在外部事务里边进行了try/catch捕获,事务会进行回滚。
如果上述内部事务为REQUIRES_NEW,外部事务不变,此时外部事务会正常执行,内部事务会进行回滚。

6-2. 事务的隔离级别

事务的隔离级别包括读未提交、读已提交、可以重复读、串行化。在上面那篇文章里边只介绍了后3种,因为通过设置后面3种隔离级别,可以解决对应的问题,比如脏读、不可重复读、幻读。
实际测试:关于脏读,如果其中一个事务a执行查询操作,另外一个事务b执行插入操作。如果事务a设置的隔离级别为读未提交,b事务没有设置隔离级别(数据库是MYSQL,也就是隔离级别为读已提交)。事务b先执行,但是没有结束;事务a后执行并已结束,此时事务a读取到数据是脏数据,也就是脏读。如果事务a设置的隔离级别为读已提交,依旧按照上述执行流程来,此时事务a的运行结果会报错。

6-3. 事务的超时时间

如果事务设置了超时时间a,那么表示超过a秒如果该事务种所有的DML语句还没有执行完毕的话,最终结果会选择回滚事务的超时时间指的事务开始到最后一条DML语句执行完的时间(只要不超过这个时间,就不会进行回滚操作)。如果最后一条DML语句后面还有很多业务逻辑,这些业务逻辑执行的时间不计入超时时间。

6-4. 设置事务只读(readOnly)

之所以设置事务为只读,是为了提高select语句的执行效率(这里启动了Spring的优化策略)。在这种事务下,只能执行查询操作,执行插入、删除、修改操作都会报错。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/66939.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从CentOS到龙蜥:企业级Linux迁移实践记录(龙蜥开局)

引言&#xff1a; 在我们之前的文章中&#xff0c;我们详细探讨了从CentOS迁移到龙蜥操作系统的基本过程和考虑因素。今天&#xff0c;我们将继续这个系列&#xff0c;重点关注龙蜥系统的实际应用——特别是常用软件的安装和配置。 龙蜥操作系统&#xff08;OpenAnolis&#…

一.项目课题 <基于TCP的文件传输协议实现>

客户端代码 需要cJSON.c文件和cJSON.h文件 在这里插入代码片#include "myheadth.h" #include "myfun.h"#define TIME 10 int sockfd; void heartbeat(int signum) {cJSON* root cJSON_CreateObject();cJSON_AddStringToObject(root,"request"…

Type-C双屏显示器方案

在数字化时代&#xff0c;高效的信息处理和视觉体验已成为我们日常生活和工作的关键需求。随着科技的进步&#xff0c;一款结合了便携性和高效视觉输出的设备——双屏便携屏&#xff0c;逐渐崭露头角&#xff0c;成为追求高效工作和娱乐体验人群的新宠。本文将深入探讨双屏便携…

计算机视觉算法实战——车道线检测

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​​​​​​ ​​​​​​​​​​​​ ​​​​​ 车道线检测是计算机视觉领域的一个重要研究方向&#xff0c;尤其在自动驾驶和高级驾驶辅助…

【微服务】面试 3、 服务监控 SkyWalking

微服务监控的原因 问题定位&#xff1a;在微服务架构中&#xff0c;客户端&#xff08;如 PC 端、APP 端、小程序等&#xff09;请求后台服务需经过网关再路由到各个微服务&#xff0c;服务间可能存在多链路调用。当某一微服务挂掉时&#xff0c;在复杂的调用链路中难以迅速确定…

【MySQL数据库】基础总结

目录 前言 一、概述 二、 SQL 1. SQL通用语法 2. SQL分类 3. DDL 3.1 数据库操作 3.2 表操作 4. DML 5. DQL 5.1 基础查询 5.2 条件查询 5.3 聚合函数 5.4 分组查询 5.5 排序查询 5.6 分页查询 6. DCL 6.1 管理用户 6.2 权限控制 三、数据类型 1. 数值类…

aws(学习笔记第二十三课) step functions进行开发(lambda函数调用)

aws(学习笔记第二十三课) 开发step functions状态机的应用程序 学习内容&#xff1a; step functions状态机的概念开发简单的step functions状态机 1. step functions状态机概念 官方说明文档和实例程序 AWS的官方给出了学习的链接和实例程序。使用SAM创建step functions 借…

【Docker】入门教程

目录 一、Docker的安装 二、Docker的命令 Docker命令实验 1.下载镜像 2.启动容器 3.修改页面 4.保存镜像 5.分享社区 三、Docker存储 1.目录挂载 2.卷映射 四、Docker网络 1.容器间相互访问 2.Redis主从同步集群 3.启动MySQL 五、Docker Compose 1.命令式安装 …

算法练习7——拦截导弹的系统数量求解

题目描述 某国为了防御敌国的导弹袭击&#xff0c;发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷&#xff1a;虽然它的第一发炮弹能够到达任意的高度&#xff0c;但是以后每一发炮弹都不能高于前一发的高度。 假设某天雷达捕捉到敌国的导弹来袭。由于该系统还在试用…

如何使用高性能内存数据库Redis

一、详细介绍 1.1、Redis概述 Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的、内存中的数据结构存储系统&#xff0c;它可以用作数据库、缓存和消息中间件。Redis支持多种类型的数据结构&#xff0c;如字符串&#xff08;strings&#xff09;、哈希&am…

【Linux系列】`find / -name cacert.pem` 文件搜索

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

UE材质Fab Megascans

2025年Bridge里已经不能直接导入资产了&#xff0c;显示GET IT ON FAB 只能在Fab中导入资产&#xff0c; 纹理打包技术从RMA改成了ORM O&#xff1a;AO 环境光遮蔽 R&#xff1a;Roughness 粗糙度 M&#xff1a;Metallic 金属度 在Fab中找到材质&#xff0c;点击Add to P…

【NP-hard问题】NP与NP-hard问题通俗解释

最近在研究NP-hard问题&#xff0c;讲一下自己的对于NP与NP-hard问题的通俗解释 一、NP-Hard 问题是什么意思&#xff1f; 什么是 NP&#xff1f; NP 问题可以理解为「检查答案很容易&#xff0c;但找到答案很难」。 举个例子&#xff1a; 假设你在一个迷宫里&#xff0c;…

ollama教程(window系统)

前言 在《本地大模型工具哪家强&#xff1f;对比Ollama、LocalLLM、LM Studio》一文中对比了三个常用的大模型聚合工具优缺点&#xff0c;本文将详细介绍在window操作系统下ollama的安装和使用。要在 Windows 上安装并使用 Ollama&#xff0c;需要依赖 NVIDIA 显卡&#xff0c…

[论文阅读] (35)TIFS24 MEGR-APT:基于攻击表示学习的高效内存APT猎杀系统

《娜璋带你读论文》系列主要是督促自己阅读优秀论文及听取学术讲座&#xff0c;并分享给大家&#xff0c;希望您喜欢。由于作者的英文水平和学术能力不高&#xff0c;需要不断提升&#xff0c;所以还请大家批评指正&#xff0c;非常欢迎大家给我留言评论&#xff0c;学术路上期…

半导体数据分析: 玩转WM-811K Wafermap 数据集(三) AI 机器学习

前面我们已经通过两篇文章&#xff0c;一起熟悉了WM-811K Wafermap 数据集&#xff0c;并对其中的一些数据进行了调用&#xff0c;生成了一些统计信息和图片。今天我们接着继续往前走。 半导体数据分析&#xff1a; 玩转WM-811K Wafermap 数据集&#xff08;二&#xff09; AI…

BGP 泄露

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 目录 1. BGP 是什么&#xff1f; 2. 什么是 BGP 泄露&#xff1f; 3. 今天发生了什么&#xff1f; 4. 正常和被劫持状态下的路由示意图 5. 受影响区域 6. 责任在谁&#xff1f; 7. 有办法避免这…

wireshark排除私接小路由

1.wireshark打开&#xff0c;发现了可疑地址&#xff0c;合法的地址段DHCP是192.168.100.0段的&#xff0c;打开后查看发现可疑地址段&#xff0c;分别是&#xff0c;192.168.0.1 192.168.1.174 192.168.1.1。查找到它对应的MAC地址。 ip.src192.168.1.1 2.通过show fdb p…

使用 CompletableFuture 实现异步编程

在现代 Java 开发中&#xff0c;异步编程是一项重要技能。而 CompletableFuture 是从 Java 8 开始提供的一个功能强大的工具&#xff0c;用于简化异步任务的编写和组合。本文将详细介绍 CompletableFuture 的基本使用和一些常见的应用场景。 1. 为什么选择 CompletableFuture&…

AWS云计算概览(自用留存,整理中)

目录 一、云概念概览 &#xff08;1&#xff09;云计算简介 &#xff08;2&#xff09;云计算6大优势 &#xff08;3&#xff09;web服务 &#xff08;4&#xff09;AWS云采用框架&#xff08;AWS CAF&#xff09; 二、云经济学 & 账单 &#xff08;1&#xff09;定…