后端:Spring(IOC、AOP)
文章目录
- 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
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 {
}
当然测试代码的加载容器需要修改一下:
相关文章:
后端:Spring(IOC、AOP)
文章目录 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. 加载容器…...
基于SpringBoot的诊所管理系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…...
基于GA遗传优化的最优阈值计算认知异构网络(CHN)能量检测算法matlab仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2022a 3.部分核心程序 (完整版代码包含详细中文注释和操作步骤视频)…...
【技术分享】如何利用rdesktop实现Linux远程Windows桌面高效办公
文章目录 前言1. Windows 开启远程桌面2. Linux安装rdesktop工具3. Win安装Cpolar工具4. 配置远程桌面地址5. 远程桌面连接测试6. 设置固定远程地址7. 固定地址连接测试 前言 随着技术的飞速发展,我们有了越来越多的方法来实现远程办公。今天我要给大家介绍一个特别…...
PDFelement 特别版
Wondershare PDFelement Pro 是一款非常强大的PDF编辑软件,它允许用户轻松地编辑、转换、创建和管理PDF文件。这个中文特别版的软件具有许多令人印象深刻的功能,PDFelement Pro 提供了丰富的编辑功能,可以帮助用户直接在PDF文件中添加、删除、…...
【江协STM32】10-2/3 MPU6050简介、软件I2C读写MPU6050
1. MPU6050简介 MPU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等需要检测自身姿态的场景3轴加速度计(Accelerometerÿ…...
【首发 1day】WordPress Crypto 插件存在前台任意用户登录漏洞(CVE-2024-9989)
漏洞描述 WordPress 的 Crypto 插件在 2.15 及以下版本(包括 2.15)中容易受到身份验证绕过攻击。这是由于对 ‘crypto_connect_ajax_process’ 函数中 ‘crypto_connect_ajax_process::log_in’ 函数的任意方法调用有限。这使得未经身份验证的攻击者可以以站点上的任何现有…...
c语言-----常识问题
1.VS的C4996错误 由于微软在VS2013中不建议再使用C的传统库函数scanf,strcpy,sprintf等,所以直接使用这些库函数会提示C4996错误: VS建议采用带_s的函数,如scanf_s、strcpy_s,但这些并不是标准C函数。 要想继续使用此函数&…...
MIUI显示/隐藏5G开关的方法,信号弱时开启手机Wifi通话方法
5G网速虽快,手机功耗也大。 1.取消MIUI强制的5G,手动设置4G的方法! 【小米澎湃OS, Xiaomi HyperOS显示/隐藏5G开关的方法】 1.1.小米MIUI系统升级后,被强制连5G,手动设置开关被隐藏,如下图: 1…...
超简单,使用Kube-Vip实现K8s高可用VIP详细教程
具体步骤如下: 以下步骤在其中一个 master 上操作即可, 1、参数配置 export VIP192.168.0.110 export INTERFACEens33 export KVVERSIONv0.8.7VIP 是虚拟IP地址,和主机同一个网段,且未被占用。INTERFACE 是你当前主机的网络接口…...
中国数字化发展的问题与机会
橙蜂智能公司致力于提供先进的人工智能和物联网解决方案,帮助企业优化运营并实现技术潜能。公司主要服务包括AI数字人、AI翻译、埃域知识库、大模型服务等。其核心价值观为创新、客户至上、质量、合作和可持续发展。 橙蜂智农的智慧农业产品涵盖了多方面的功能,如智能化推荐、…...
为什么ip属地一会河南一会江苏
在使用互联网的过程中,许多用户可能会遇到这样一个问题:自己的IP属地一会儿显示为河南,一会儿又变成了江苏。这种现象可能会让人感到困惑,甚至产生疑虑,担心自己的网络活动是否受到了某种影响。为了解答这一疑问&#…...
【机器学习】神经网络(BP算法)含具体计算过程
目录 神经元的“激活函数” 多层前馈网络结构编辑 BP(BackPropagation:误差逆传播算法) BP算法推导 手动计算BP神经网络的权值来实现学习 前向传播(正向运算)的过程 隐藏层输入: 隐藏层输出: 输出层输入: 输出层输出: …...
【HarmonyOS NEXT】鸿蒙应用点9图的处理(draw9patch)
【HarmonyOS NEXT】鸿蒙应用点9图的处理(draw9patch) 一、前言: 首先在鸿蒙中是不支持安卓 .9图的图片直接使用。只有类似拉伸的处理方案,鸿蒙提供的Image组件有与点九图相同功能的API设置。 可以通过设置resizable属性来设置R…...
Swift语言的网络编程
Swift语言的网络编程探秘 随着移动互联网的迅猛发展,网络编程已经成为开发者必备的核心技能之一。尤其在iOS开发领域,Swift语言作为Apple官方推荐的编程语言,以其简洁的语法和强大的功能受到了广泛的关注。本文将深入探讨Swift语言的网络编程…...
江科大STM32入门——UART通信笔记总结
wx:嵌入式工程师成长日记 1、简介 简单双向串口通信有两根通信线(发送端TX和接收端RX)TX与RX要交叉连接当只需单向的数据传输时,可以只接一根通信线当电平标准不一致时,需要加电平转换芯片 传输模式:全双工;时钟&…...
2. 使用springboot做一个音乐播放器软件项目【框架搭建与配置文件】
上一章文章 我们做了 音乐播放器这个项目的 前期规划 项目需求, 环境安装 和 springboot框架的 搭建与配置。如果有小伙伴没看过 第一章文章 可以去看一下 https://blog.csdn.net/Drug_/article/details/144994317 今天这篇文章 我们来 主要分享一些 我们在开发中…...
历代iPhone运行内存大小和电池容量信息
系列设备名称充电端口标配充电线PD快充无线充电 (W)标配充电器电池容量 (mAh)发布时间RAM运存iPhone 16iPhone 16 Pro MaxUSB Type-CUSB-C to USB-C支持25无47472024/9/108GB LPDDR5XiPhone 16 ProUSB Type-CUSB-C to USB-C支持25无35772024/9/108GB LPDDR5XiPhone 16 PlusUSB …...
(STM32笔记)十二、DMA的基础知识与用法 第三部分
我用的是正点的STM32F103来进行学习,板子和教程是野火的指南者。 之后的这个系列笔记开头未标明的话,用的也是这个板子和教程。 DMA的基础知识与用法 三、DMA程序验证1、DMA 存储器到存储器模式实验(1)DMA结构体解释(2…...
ThinkPHP 8高效构建Web应用-获取请求对象
【图书介绍】《ThinkPHP 8高效构建Web应用》-CSDN博客 《2025新书 ThinkPHP 8高效构建Web应用 编程与应用开发丛书 夏磊 清华大学出版社教材书籍 9787302678236 ThinkPHP 8高效构建Web应用》【摘要 书评 试读】- 京东图书 使用VS Code开发ThinkPHP项目-CSDN博客 编程与应用开…...
深入解析 Python 2 与 Python 3 的差异与演进
Python 2 和 Python 3 是 Python 编程语言的两个主要版本。Python 3 于 2008 年发布,旨在解决 Python 2 中的一些设计缺陷,并引入了许多新特性。虽然 Python 2 在很长一段时间内仍然被广泛使用,但自 2020 年 1 月 1 日起,Python 2…...
57. Three.js案例-创建一个带有聚光灯和旋转立方体的3D场景
57. Three.js案例-创建一个带有聚光灯和旋转立方体的3D场景 实现效果 该案例实现了使用Three.js创建一个带有聚光灯和旋转立方体的3D场景。 知识点 WebGLRenderer(WebGL渲染器) THREE.WebGLRenderer 是 Three.js 中用于将场景渲染为 WebGL 内容的核…...
移动端可互动轮播图
首先通过事件监听获得到初始滑动位置,并关闭掉轮播图的自动轮播定时器 //设置事件代理 $(".slider").on("touchstart", function (e) {// 当滑动触发的时候关闭定时器clearInterval(time);// 开始时的pxstartX e.touches[0].clientX; }); 然…...
深入讲解 Docker 及实践
Docker 是现代化应用开发、测试和生产环境部署中不可或缺的工具。它能够为开发人员提供与生产环境一致的开发环境,同时支持高效的容器化部署、资源隔离、容器编排等高级功能。尤其在微服务架构和云原生应用中,Docker 更是提供了简化的流程和高效的可扩展…...
科大讯飞前端面试题及参考答案( 上)
前端有用到哪些数据结构? 在前端开发中,会运用到多种数据结构,以下是一些常见的类型及其应用场景。 数组(Array) 数组是一种有序的元素集合,可以存放不同类型的数据(在 JavaScript 等前端常用语言中)。比如在构建一个网页的列表展示时,像新闻列表、商品列表等,我们可…...
本地导入封装的模块 在docker内报错ImportError
本地封装了一个login方法 在写testcase的时候去复用这个方法 但是进入docker运行的时候一直报上面的错误 目录 出现的原因: 解决方法: 1. 根据docker的路径写绝对路径 2. 用sys 加入path到code 作用: 好处: 出现的原因…...
Java-日志技术大全
一:目录 1.jul的使用 2.log4j的使用 3.logback的使用 4.log4j2的使用 二:jul使用 jul是JDK自带的日志技术,不需要导入其他依赖,默认的级别为info 1.关键组件: (1).Logger:记录器 (2).Handler&…...
ARP-Batch-Retargeting 部署实战
git 地址: https://github.com/Shimingyi/ARP-Batch-Retargeting bpy安装: pypi上搜索 bpy bpy 4.3.0,4.2.0版本报错: Traceback (most recent call last):File "E:\project\jijia_4d\retarget\ARP-Batch-Retargeting-…...
二分查找题目:寻找峰值 II
文章目录 题目标题和出处难度题目描述要求示例数据范围 解法思路和算法证明代码复杂度分析 题目 标题和出处 标题:寻找峰值 II 出处:1901. 寻找峰值 II 难度 7 级 题目描述 要求 一个二维网格中的峰值元素是指其值严格大于相邻值(左、…...
调和级数不为整数的证明
文章目录 1. 问题引入2. 证明2.1 引理12.2 引理22.3 引理3:2.4 核心证明: 3. 参考 1. 问题引入 s ( n ) 1 1 2 1 3 ⋯ 1 n , n ∈ N ∗ , n ≥ 2 s(n) 1\frac{1}{2}\frac{1}{3}\cdots\frac{1}{n}, \quad \\n \in N^*, n \ge2 s(n)12131⋯n1,…...
Redis 源码分析-内部数据结构 dict
Redis 源码分析-内部数据结构 dict 在上一篇 Redis 数据库源码分析 提到了 Redis 其实用了全局的 hash 表来存储所有的键值对,即下方图示的 dict,dict 中有两个数组,其中 ht[1] 只在 rehash 时候才真正用到,平时都是指向 null&am…...
git相关操作笔记
git相关操作笔记 1. git init git init 是一个 Git 命令,用于初始化一个新的 Git 仓库。执行该命令后,Git 会在当前目录创建一个 .git 子目录,这是 Git 用来存储所有版本控制信息的地方。 使用方法如下: (1ÿ…...
STM32小实验2
定时器实验 TIM介绍 TIM(Timer)定时器 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中断…...
Oracle Dataguard(主库为双节点集群)配置详解(2):备库安装 Oracle 软件
Oracle Dataguard(主库为双节点集群)配置详解(2):备库安装 Oracle 软件 目录 Oracle Dataguard(主库为双节点集群)配置详解(2):备库安装 Oracle 软件一、Orac…...
基于 Pod 和 Service 注解的服务发现
基于 Pod 和 Service 注解的服务发现 背景 很多应用会为 Pod 或 Service 打上一些注解用于 Prometheus 的服务发现,如 prometheus.io/scrape: "true",这种注解并不是 Prometheus 官方支持的,而是社区的习惯性用法,要使…...
操作系统之文件的逻辑结构
目录 无结构文件(流式文件) 有结构文件(记录式文件) 分类: 顺序文件 特点: 存储方式: 逻辑结构: 优缺点: 索引文件 目的: 结构: 特点…...
网络分析与监控:阿里云拨测方案解密
作者:俞嵩(榆松) 随着互联网的蓬勃发展,网络和服务的稳定性已成为社会秩序中不可或缺的一部分。一旦网络和服务发生故障,其带来的后果将波及整个社会、企业和民众的生活质量,造成难以估量的损失。 2020 年 12 月: Ak…...
vue实现虚拟列表滚动
<template> <div class"cont"> //box 视图区域Y轴滚动 滚动的是box盒子 滚动条显示的也是因为box<div class"box">//itemBox。 一个空白的盒子 计算高度为所有数据的高度 固定每一条数据高度为50px<div class"itemBox" :st…...
服务器/电脑与代码仓gitlab/github免密连接
git config --global user.name "xxxx" git config --global user.email "xxxxxx163.com" #使用注册GitHub的邮箱 生成对应邮箱的密码对 ssh-keygen -t rsa -b 4096 -C "xxxxxx163.com" 把公钥id_rsa.pub拷贝到github中 Setting----->…...
用户界面软件03
一种标准的满足不同的非功能性需求的技术是对子系统进行不同的考虑……但是一个用户 界面要求有大量的域层面的信息,以符合比较高的人机工程标准,所以,这些分开的子系统还是 紧密地耦合在一起的。 一个软件架构师的标准反应是将不同的非功能…...
年会抽奖Html
在这里插入图片描述 <!-- <video id"backgroundMusic" src"file:///D:/background.mp3" loop autoplay></video> --> <divstyle"width: 290px; height: 580px; margin-left: 20px; margin-top: 20px; background: url(D:/nianhu…...
(一)Ubuntu20.04版本的ROS环境配置与基本概述
前言 ROS不需要在特定的环境下进行安装,不管你是Ubuntu的什么版本或者还是虚拟机都可以按照教程进行安装。 1.安装ROS 一键安装ros及ros2 wget http://fishros.com/install -O fishros && . fishros 按照指示安装你想要的ros。 ros和ros2是可以兼容的…...
深入分析线程安全问题的本质
深入分析线程安全问题的本质 1. 并发编程背后的性能博弈2. 什么是线程安全问题?3. 源头之一:原子性问题3.1. 原子性问题示例3.2. 原子性问题分析3.3. 如何解决原子性问题? 4. 源头之二:可见性问题4.1. 为什么会有可见性问题&#…...
58. Three.js案例-创建一个带有红蓝配置的半球光源的场景
58. Three.js案例-创建一个带有红蓝配置的半球光源的场景 实现效果 本案例展示了如何使用Three.js创建一个带有红蓝配置的半球光源的场景,并在其中添加一个旋转的球体。通过设置不同的光照参数,可以观察到球体表面材质的变化。 知识点 WebGLRenderer …...
插入实体自增主键太长,mybatis-plaus自增主键
1、问题 spring-boot整合mybtais执行insert语句时,主键id为长文本数据。 2、分析问题 1)数据库主键是否自增 2)数据库主键的种子值设置的多少 3、解决问题 1)数据库主键设置的时自增 3)种子值是1 所以排查是数据库的问题 4、继…...
【利用 Unity + Mirror 网络框架、Node.js 后端和 MySQL 数据库】
要实现一个简单的1v1战斗小游戏,利用 Unity Mirror 网络框架、Node.js 后端和 MySQL 数据库,我们可以将其分为几个主要部分:客户端(Unity)、服务器(Node.js)和数据库(MySQL…...
https原理
一、基本概念 1、https概念 https(全称: Hypertext Transfer Protocol Secure,超文本传输安全协议),是以安全为目标的http通道,简单讲是http的安全版。 2、为啥说http协议不安全呢? 我们用h…...
如何处理京东商品详情接口返回的JSON数据中的缺失值?
1.在 Python 中处理缺失值 使用if - else语句进行检查和处理 假设通过requests库获取了接口返回的 JSON 数据,并使用json模块进行解析,存储在data变量中。 import json import requestsurl "YOUR_API_URL" response requests.get(url) dat…...
window对象
bom dom部分学完了,来看看bom吧~ bom是整个浏览器,本质上bom与dom是包含的关系,window是里面最大的对象 调用的方法默认对象是window,一般都会省略前面的window 创建的全局变量也是属于window的,当然window也可以省…...
(五)ROS通信编程——参数服务器
前言 参数服务器在ROS中主要用于实现不同节点之间的数据共享(P2P)。参数服务器相当于是独立于所有节点的一个公共容器,可以将数据存储在该容器中,被不同的节点调用,当然不同的节点也可以往其中存储数据,关…...