Spring01
spring框架
spring是轻量级的容器框架
spring framework
1、Spring核心学习内容 IOC、AOp, jdbcTemplate,声明式事务
2、IOC:控制反转,孚以管理部8号对象
3.AOP:切面编程4.JDBCTemplate:是spring提供一套访问数据库的技术,应用性强,相对好理解5.声明式事务:基于ioc/aop实现事务管理,理解有需要小伙伴花时间6.IOC, AOP 是重点同时难点
重要概念
1.Spring可以整合其他的框架(老韩解读: Spring是管理框架的框架)
2.Spring有两个核心的概念: IOC 和 AOP
3.IOC [Inversion Of Control反转控制]。传统的开发模式[JdbcUtils/反射]程序---->环境//程序读取环境配置,然后自己创建对象
1.spring根据配置文件xml注解,创建对象,并放入容器中,并且可以完成对象之间的依赖.
spring可以整合其他框架,是管理其他框架的框架
spring中有两个核心概念:ioc控制反转 aop 切面编程文件
在传统开发中:程序读取环境配置信息{可能会用到new,或者反射}然后自己创建对象
在ioc开发模式:容器中创建好对象---->被程序使用
容器创建对象的方法:1.xml配置文件,2.注解
1.spring根据配置文件或者配置文件创建对象,并放入到容器中;并完成对象之间的依赖
2.当需要某个对象实例的时候,可以直接从容器中获取
3.编写人员可以更加关注对象完成相应的业务
4.DI{dependency injection}依赖注入
5.Spring最大的价值,通过配置提供给程序使用
实例
package com.Test;import com.bean.Monster;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/9 13:56**/
public class MonsterTest {@Testpublic void getMonster() {//创建容器ApplicationContext//该容器和容器配置文件关联//建议用接口接收,ApplicationContext ioc =new ClassPathXmlApplicationContext("beans.xml");//拿到容器是为拿到容器关联配置文件中的Java对象//可以通过getbean获取对应的对象,通过id,默认返回的类型是object,运行类型是monster/*这里的运行类型可以改为monter,因为getclass方法获取到该对象的运行类型为monterObject monster01 = ioc.getBean("monster01");*/Monster monster01 = (Monster) ioc.getBean("monster01");//完成向下转型之后可以分别获取属性//输出System.out.println("monster01="+ monster01 + monster01.getClass());System.out.println(monster01.getName()+monster01.getSkill()+monster01.getMonsterid());System.out.println("ok");//可以在获取的时候直接指定class类型Monster monster2 = ioc.getBean("monster01", Monster.class);System.out.println(monster2.getName()+monster2.getSkill()+monster2.getMonsterid());}
}
配置文件编写
<?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">
<!-- 配置monster对象Javabean-->
<!-- 在beans中可以配置多个bean--><!--bean表示一个对象-->
<!-- class创建Java对象类的全路径-->
<!-- Spring 底层使用反射创建的-->
<!-- id表示Java对象在spring容器中的id,通过id可获得对象 id是唯一的-->
<!-- <property name="name" value="牛哈"/>用于给对象属性赋值,如果不改就是默认值--><bean class="com.bean.Monster" id="monster01"><property name="monsterid" value="100"/><property name="name" value="牛哈"/><property name="skill" value="叫"/></bean>
<!-- 完成Java对象的创建--></beans>
package com.bean;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/9 13:26**/
public class Monster {private Integer monsterid;private String name;private String skill;public Monster(Integer monsterid, String name, String skill) {this.monsterid = monsterid;this.name = name;this.skill = skill;}public Monster() {}
//无参构造器一定要写,因为反射要用public Integer getMonsterid() {return monsterid;}public void setMonsterid(Integer monsterid) {this.monsterid = monsterid;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSkill() {return skill;}public void setSkill(String skill) {this.skill = skill;}}
classpathxmlapplicationcontext 为什么可以读取到beans.xml
public void classPath() {File file = new File(this.getClass().getResource("/").getPath());System.out.println(file);//结果 /*D:\javaprogram\Spring\out\production\Spring*///读取路径是out目录}
debug时会发现:beandefinitionmap会保存类的信息,但是不创建对象
当需要对象时,通过反射创建对象时用;
IOC容器
底层机制
当代码执行到
ApplicationContext ioc =new ClassPathXmlApplicationContext("beans.xml");
时,容器已经创建完毕

在beanfactory下的beandefinitionmap中就会保存配置文件中的信息,通过集合的方式 大小512

图一
ConcurrentHashMap类型时集合,存bean节点配置信息
在beanDefinitionMap中有属性table
table为数组 类型为ConcurrentHashMap$node
因为是数组类型,可以存放很多bean对象信息,就是bean.xml配置文件
初始化为512, 会自动扩容
图二
通过hash算法monster01对象信息就保存在index = 217 位置
保存是以 ConcurrentHashMap$node类型
key值就是beans.xml文件中的id值
value 就是monster01对象的[属性/属性值/是否为懒加载]
属性值保存在propertyValueList,就是记录monster01对象的属性值
beandefinitionmap只保存了类信息,并不保存对象


类的对象用concurrenthashmap$node的类型保存,类型为基于泛型的,
因为保存类的对象时,会默认为单例所以存在这个位置
在beanfactory中的另一个属性singletonObjects
类型为concurrentHashMap
其中有个属性table 类型是concurrenthashmap$node
如果在beans.xml文件中配置的对象时单例的 就是初始化放在里边
当我们getbean(“id”)时,首先会在底层查beandifinitionmap中按照id 名字查询是为单例,如果为单例会在singletonobject[类似单例池的作用]查找并返回,若不是单例会通过动态反射机制创建一个对象放进去.
为了方便我们快速查找定位,直接存入id[就是在beans.xml文件中配置的bean的名称]值,
如果先查看自己定义了多少对象,通过beandefinitionnames方法查找
//查看容器注入了那些bean对象 输出bean的idString[] beanDefinitionNames = ioc.getBeanDefinitionNames();for(String beanDefinitionName : beanDefinitionNames) {System.out.println(beanDefinitionName);}
原理模拟
手动开发一个简单的spring基于XML配置的程序
第一步引入jar包{dom4j}
xml文件用的是上边的deans.xml
类的代码
package com.applicationcontext;import com.bean.Monster;
import com.sun.org.apache.bcel.internal.generic.NEW;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.io.File;
import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;/*** @Author: zs* @Description: 实现spring的一个简单容器* 实现bean.xml文件的解析,并生成容器* @DateTime: 2024/9/10 12:09**/
public class ApplicationContext {private ConcurrentHashMap<String, Object> singletonobject =new ConcurrentHashMap<>();//构造器//接收一个容器的配置文件,该文件默认在srcpublic ApplicationContext(String iocBeanXmlFile) throws DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException {//1, 得到类加载路径"String path = this.getClass().getResource("/").getPath();// 创建saxreader,它用于读取 XML 文件并将其转换为 Java 对象。
// 这个类是基于 SAX (Simple API for XML) 解析器的,
// 它允许你以流的方式读取 XML 文件,而不需要将整个文件加载到内存中,SAXReader saxReader = new SAXReader();//得到document对象Document document = saxReader.read(new File(path + iocBeanXmlFile));//得到rootdocumentElement root = document.getRootElement();//获取bean元素Element bean = (Element) root.elements("bean").get(0);//获取id,属性,属性值,类的全路径String id = bean.attributeValue("id");String classPath = bean.attributeValue("class");List<Element> property = bean.elements("property");;//遍历Integer monsterId =Integer.parseInt(property.get(0).attributeValue("value"));String name = property.get(1).attributeValue("value");String skill = property.get(2).attributeValue("value");// 使用反射创建对象Class<?> aclass = Class.forName(classPath);//这里o对象就是monster,Monster o =(Monster) aclass.newInstance();//给对象转化类型,赋值//1.反射赋值
// Method[] declaredMethods = aclass.getDeclaredMethods();
// for (Method declaredMethod : declaredMethods ) {
// declaredMethod.invoke();
// }//2.直接赋值o.setMonsterid(monsterId);o.setName(name);o.setSkill(skill);//将创建的对象放入容器中singletonobject.put(id, o);}public Object getBean(String id) {//获取容器中的对象return singletonobject.get(id);}}
测试类的编写
package com.applicationcontext;import com.bean.Monster;
import org.dom4j.DocumentException;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/10 12:18**/
public class ApplicationContextTest {public static void main(String[] args) throws DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException {ApplicationContext ioc = new ApplicationContext("beans.xml");Monster monster01 = (Monster)ioc.getBean("monster01");System.out.println(monster01);System.out.println(monster01.getClass());System.out.println(monster01.getName());System.out.println("ok");}
}
Spring管理bean-IOC
bean的配置方式1.基于xml文件配置的方式
2.基于注解
bean的管理:1.创建bean对象
2.注入属性
基于xml形式
1.通过类型获取
<!-- 配置monster 通过类型获取--><bean class="com.bean.Monster"><!--1.当我们给某个bean对象设置属性时底层使用setter方法完成的--><property name="monsterid" value="100"/><property name="name" value="牛哈"/><property name="skill" value="叫"/></bean>
ApplicationContext ioc =new ClassPathXmlApplicationContext("beans.xml");//直接传入class类型Monster bean = ioc.getBean(Monster.class);System.out.println("bean" + bean);}
要求类型获取bean,要求容器IOC中的同一个类的bean只能有一个,否则会抛出异常NouUniqueBeanDefinitionException
应用场景:在一个线程中只需要一个对象实例的情况(单例)
2.通过构造器配置bean
<!-- 配置monster对象,通过构造器--><bean id="monster03" class="com.bean.Monster">
<!-- index索引表示构造器的第几个参数 从零开始计算-->
<!-- 除了index 还可以通过name / type 来指定 类的构造器,不能有完全相同类型顺序的构造器
--><constructor-arg value="200" index="0"/><constructor-arg value="猴" index="1"/><constructor-arg value="变" index="2"/></bean>
@Testpublic void setBeanByConstructor(){ApplicationContext ioc =new ClassPathXmlApplicationContext("beans.xml");Monster monster03 = ioc.getBean("monster03",Monster.class);System.out.println("monster03=" + monster03);}
通过P名称空间配置
//通过p名称空间来设置属性
@Test
public void setBeanByCP(){ApplicationContext ioc =new ClassPathXmlApplicationContext("beans.xml");Monster monster06 = ioc.getBean("monster06", Monster.class);System.out.println(monster06);
}
<!-- 通过P名称空间配置-->
<!-- 在编写代码时.p会报错,p空间没绑定-->
<!-- 解决方法:光标放在爆红的地方,按alt+int 有时需要多试几次--><bean id="monster06" class="com.bean.Monster"p:monsterid="500"p:name="红孩儿"p:skill="玩火"/>
xmlns:p="http://www.springframework.org/schema/p"
引用其他bean
测试文件
@Testpublic void setBeanByRef(){ApplicationContext ioc =new ClassPathXmlApplicationContext("beans.xml");MemberServiceImpl memberService = ioc.getBean("memberService", MemberServiceImpl.class);memberService.add();}
DaoImpl
public class MenberDAOImpl {//构造器.....public MenberDAOImpl() {System.out.println("MenberDAOImpl 构造器");}
// 完成添加public void add(){System.out.println("MemberDaoImpl add()方法");}
}
ServiceImpl
public class MemberServiceImpl {private MenberDAOImpl menberDao;public MenberDAOImpl getMenberDao() {return menberDao;}public void setMenberDao(MenberDAOImpl menberDao) {this.menberDao = menberDao;}public void add(){System.out.println("service方法被调用");menberDao.add();}
}
配置文件
<!-- 配置memberdaoimpl对象--><bean class="com.dao.MenberDAOImpl" id="memberDAO"/>
<!-- 配置menmberserviceimpl对象-->
<!-- ref="memdao" 表示 memberserviceimpl对象的属性memberDao引用的对象是id = memberDao的对象-->
<!-- 体现出spring容器的依赖注入--><bean class="com.service.MemberServiceImpl" id="memberService"><property name="menberDao" ref="memberDAO"/></bean>
引用内部bean对象
<!-- 配置memberserviceimpl-使用内部bean--><bean class="com.service.MemberServiceImpl" id="memberService2"><property name="memberDao" ><bean class="com.dao.MemberDAOImpl"/></property></bean>
引用集合/数组类型的值赋值
list
<!-- 配置master对象--><bean class="com.bean.Master" id="master"><property name="name" value="老头"/>
<!-- 给list属性赋值--><property name="monsterList" ><list><ref bean="monster06"/><ref bean="monster03"/></list></property></bean>
<!-- 配置master对象--><bean class="com.bean.Master" id="master"><property name="name" value="老头"/>
<!-- 给list属性赋值--><property name="monsterList" ><list>
<!-- 引用的方式--><ref bean="monster06"/><ref bean="monster03"/>
<!-- 内部--><bean class="com.bean.Monster"><property name="name" value="老鼠"/><property name="monsterid" value="100"/><property name="skill" value="toushi"/></bean></list></property>
<!-- 给map属性赋值--><property name="monsterMap"><map><entry><key><value>monster03</value></key><ref bean="monster03"/></entry><entry><key><value>monster06</value></key><ref bean="monster06"/></entry></map></property><property name="monsterSet"><set><ref bean="monster03"/><ref bean="monster06"/><bean class="com.bean.Monster"><property name="name" value="男"/><property name="monsterid" value="1000"/><property name="skill" value="吃"/></bean></set></property>
<!-- array标签中的使用value 还是bean,ref 需要根据实际情况--><property name="monsterName"><array><value>怪</value><value>女</value></array></property><property name="pros"><props><prop key="username">root</prop><prop key="password">123456</prop><prop key="ip">127.0.0.1</prop></props></property></bean>
使用util名称空间创建list
<!-- 配置bookstore对象--><bean class="com.bean.BookStore" id="bookStore"><property name="bookList" ref="myBookList"/>
<!-- <list>-->
<!-- <value>三国演义</value>-->
<!-- <value>水浒传</value>-->
<!-- <value>你好</value>-->
<!-- </list>-->
<!-- </property>--></bean>
<!-- 定义一个utillist 并指定一个id 可以达到复用的效果--><util:list id="myBookList"><value>三国演义2</value><value>水浒传2</value><value>你好2</value></util:list>
xmlns:util="http://www.springframework.org/schema/util"
级联属性赋值
在编程中,级联属性赋值通常指的是在对象之间设置属性值时,将一个对象的属性值赋给另一个对象的对应属性。这种操作在面向对象编程中很常见,尤其是在处理具有复杂关系的对象时。级联赋值可以简化代码,避免重复编写相同的赋值语句。
以Java为例,假设我们有两个类 Person 和 Address,其中 Person 类包含一个 Address 类型的属性:
public class Address {private String street;private String city;private String zipCode;// 构造器、getter和setter省略
}public class Person {private String name;private Address address;// 构造器、getter和setter省略
}
如果我们想要级联地为 Person 对象的 address 属性赋值,可以这样做:
public class Main {public static void main(String[] args) {Address address = new Address();address.setStreet("123 Main St");address.setCity("Anytown");address.setZipCode("12345");Person person = new Person();person.setName("John Doe");person.setAddress(address); // 级联赋值// 现在person对象有了一个包含地址信息的address属性}
}
emp类创建
package com.bean;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/10 17:01**/
public class Emp
{private String name;private Dept dept;public Emp() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public Dept getDept() {return dept;}public void setDept(Dept dept) {this.dept = dept;}@Overridepublic String toString() {return "Emp{" +"name='" + name + '\'' +", dept='" + dept + '\'' +'}';}
}
dept类创建
package com.bean;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/10 17:00**/
public class Dept {private String name;public Dept() {}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Dept{" +"name='" + name + '\'' +'}';}
}
级联配置文件
<!-- 配置dept--><bean class="com.bean.Dept" id="dept"/>
<!-- 配置emp--><bean class="com.bean.Emp" id="emp"><property name="name" value="jj"/><property name="dept" ref="dept"/>
<!-- 这我希望给dept的name属性赋值--><property name="dept.name" value="开发部门"/></bean>
通过静态工厂获取对象
创建一个静态工厂类
package com.factory;import com.bean.Monster;import java.util.HashMap;
import java.util.Map;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/10 17:22**/
public class MyStaticFactory {private static Map<String, Monster> monsterMap;//使用static代码块进行初始化//static代码块特点:随着类的加载而加载,并且只加载一次static {monsterMap = new HashMap<>();monsterMap.put("monster01", new Monster(1000,"你啦","跑"));monsterMap.put("monster02",new Monster(100000,"你","走走走"));}//提供一个方法,返回monster对象public static Monster getMonster(String key) {return monsterMap.get(key);}
}
配置文件
<!-- 配置一个monster对象-->
<!-- 通过静态工厂类获取/配置bean-->
<!-- class 是静态工厂的全路径-->
<!-- foctory-mehtod 表示是静态工厂用哪个方法返回对象-->
<!-- constructor-arg value 返回静态工厂的那个对象--><bean id="my_monster01" class="com.factory.MyStaticFactory"factory-method="getMonster"><constructor-arg value="monster02"/></bean>
不管获取多少次都是通一个对象
动态实例工厂
<!-- 配置monster对象--><bean id="myInstanceFactory" class="com.factory.MyInstanceFactory"/><bean id="my_monster02" factory-bean="myInstanceFactory" factory-method="getMonster"><constructor-arg value="monster03"/></bean>
通过factory bean
package com.factory;import com.bean.Monster;
import org.springframework.beans.factory.FactoryBean;import java.util.HashMap;
import java.util.Map;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/11 12:55**/
public class MyFactoryBean implements FactoryBean<Monster> {private String key;//就是配置时,指定获取的对象private Map<String, Monster> monsterMap;{monsterMap = new HashMap<String, Monster>();monsterMap.put("monster05", new Monster(500,"反复","请求"));monsterMap.put("monster06", new Monster(600,"ff","qq"));}public void setKey(String key) {this.key = key;}public Monster getObject() throws Exception {return monsterMap.get(key);}public Class<?> getObjectType() {return Monster.class;}public boolean isSingleton() {return true;}
}
<!-- 配置factorybean--><bean id="my_monster05" class="com.factory.MyFactoryBean"><property name="key" value="monster05"/></bean>
bean的重用
<bean id="monster11" class="com.hspedu.spring.beans.Monster" parent="monster10"/>
或者
使用abstract = true 抽象用来继承
但是设置之后本类不能被实例化
bean创建顺序
默认是按照编写的顺序
当有依赖对象时,会先创建被依赖的对象
ioc是一个整体,先创建对象最后完成依赖关系 除depende之外
bean的单例和多实例
在spring容器中,默认按照单例创建的,即配置一个一个bean对象后,ioc容器只会创建一个bean实例
多例设置
添加标签
scope = "prototype"
设置多实例时,实例只会在执行getbean时创建
单例模式默认是lazy-init= false;不是懒加载,所以容器一加载对象就会创建
bean的生命周期
bean对象的创建是由jvm完成的
1,执行构造器
2,执行set方法
3,调用bean的初始化方法
4,使用bean
5,容器关闭时,调用销毁方法
bean的后置处理器
这个对象会在bean初始化方法调用之前和调用之后调用
功能可以操作整个bean文件的对象,就像一个切面
在Spring框架中,Bean后置处理器(Bean Post Processor)是一种特殊的组件,它允许你在Spring容器初始化任何bean之后,但在bean的属性被设置之后进行额外的处理。Bean后置处理器可以用来修改bean的定义、属性值,或者根据特定的逻辑决定是否要返回一个代理而不是原始bean实例。
关键特点:
1.实现接口:Bean后置处理器需要实现 org.springframework.beans.factory.config.BeanPostProcessor 接口。
两个主要方法:
postProcessBeforeInitialization(Object bean, String beanName):在bean的初始化方法(如@PostConstruct注解的方法或InitializingBean接口的afterPropertiesSet方法)之前调用。postProcessAfterInitialization(Object bean, String beanName):在bean的初始化方法之后调用。
3.作用时机:Bean后置处理器在Spring容器的bean生命周期的特定点被调用,允许开发者在bean完全初始化之前和之后执行自定义逻辑。
4.返回值:这两个方法都可以返回bean本身或一个代理对象。如果返回null,则Spring容器会忽略该bean,不再进行后续处理。
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// 在bean初始化之前可以进行的操作System.out.println("Before initialization: " + beanName);return bean; // 返回bean本身或修改后的bean}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {// 在bean初始化之后可以进行的操作System.out.println("After initialization: " + beanName);return bean; // 返回bean本身或修改后的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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 定义你的Bean后置处理器 --><bean id="myBeanPostProcessor" class="com.example.MyBeanPostProcessor"/><!-- 定义其他bean --><bean id="myService" class="com.example.MyService"><!-- bean的配置 --></bean><!-- 其他bean定义 --></bean
使用场景:
- 修改bean属性:在bean初始化前后修改其属性值。
- 条件性地返回代理:根据某些条件返回一个代理对象,例如用于AOP(面向切面编程)。
- 资源释放:在bean销毁之前执行清理工作。
- 日志记录:记录bean的创建和销毁过程。
通过过程文件给bean注入值
<!-- 配置monster--><context:property-placeholder location="classpath:my.properties"/><bean class="com.bean.Monster" id="monster10"><property name="monsterid" value="${monsterid}"/><property name="name" value="${name}"/><property name="skill" value="${skill}"/></bean>
创建一个配置文件
monsterid=1000
name=jack
skill=heal
自动装配bean
bytype------->通过类型
<!-- 配置orderdao orderservice--><bean class="com.dao.OederDao" id="oederDao"/>
<!-- autowire = "bytype" orderservice时通过类型的方式给对象属性自动完成赋值-->
<!-- 作用:在bean文件中寻找有没有orderdao类型的对象,有的化,就会自己完成装配-->
<!-- 注意bean文件中不能有两个orderfao的类型--><bean autowire="byType" class="com.service.OrderService" id="orderService"/>
<!-- 手动装配-->
<!-- <property name="oederDao" ref="oederDao"/>--><bean autowire="byType" class="com.web.OrderAction" id="orderAction"/>
创建三个文件为:dao,service,selvet
byname---->通过名字完成自动装配
按照setxxx方法set后边的名字
spring EL表达式
界定符#{}
基于注解配置bean
基本使用
在Java编程中,注解(Annotations)是一种用于为代码提供元数据的机制。注解不会直接影响代码的执行逻辑,但可以被编译器、其他工具或运行时环境读取,从而提供额外的信息或指导。注解在很多方面被广泛使用,包括但不限于依赖注入、事务管理、日志记录、安全性检查等。
注解的基本概念:
- 1.声明注解:使用
@interface关键字声明一个新的注解类型。
java
public @interface MyAnnotation {String value();
}
- 2.使用注解:在类、方法、变量等元素上使用注解。
java
@MyAnnotation(value = "Example")
public class ExampleClass {// ...
}
- 3.注解的保留策略:定义注解的生命周期,即它在源代码、字节码或运行时是否可用。通过
@Retention注解指定。
java
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {// ...
}
- 4.注解的适用目标:通过
@Target注解指定注解可以应用的目标类型。
java
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;@Target(ElementType.METHOD)
public @interface MyAnnotation {// ...
}
- 5.注解的继承:注解默认不会被子类继承,但可以通过
@Inherited注解指定注解可以被继承。
java
import java.lang.annotation.Inherited;@Inherited
public @interface MyAnnotation {// ...
}
常见的注解使用场景:
- 依赖注入:Spring框架使用
@Autowired、@Qualifier等注解来实现依赖注入。
java
@Autowired
private MyService myService;
- 事务管理:
@Transactional注解用于声明方法或类的事务边界。
java
@Transactional
public void performOperation() {// ...
}
- RESTful Web服务:JAX-RS和Spring MVC使用注解来定义资源和路由。
java
@RestController
@RequestMapping("/api")
public class MyController {@GetMapping("/hello")public String sayHello() {return "Hello, World!";}
}
- 单元测试:JUnit和TestNG使用注解来标记测试方法。
java
import org.junit.Test;
import static org.junit.Assert.*;public class ExampleTest {@Testpublic void testAddition() {assertEquals(2, 1 + 1);}
}
- 日志记录:使用如
@Log或@Log4j等注解来简化日志记录。
java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class MyClass {private static final Logger logger = LoggerFactory.getLogger(MyClass.class);public void myMethod() {logger.info("This is an info message");}
}
注解处理:
- 编译时处理:注解处理器可以在编译时生成额外的源代码、资源文件或进行其他操作。
- 运行时处理:在运行时,可以通过反射API读取注解信息并根据这些信息执行特定的逻辑。
基于注解的方式配置bean,主要是项目开发中的组件,比如Controller、Service、和Dao。
组件注解的形式有
- 1.
@Component表示当前注解标识的是一个组件。 - 2.
@Controller表示当前注解标识的是一个控制器,通常用于Servlet。 - 3.
@Service表示当前注解标识的是一个业务逻辑的类,通常用于service类。 - 4.
@Repository表示当前注解标识的是一个持久化层的类,通常用于Dao类。
spring框架的注解需要引入的jar包
1.spring-aop-5.3.8.jar
仿写注解
package com.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/12 13:29**/
//规定当前自定义的的注解可以作用于那些类型
@Target(ElementType.TYPE)
//指定注解的作用范围
@Retention(RetentionPolicy.RUNTIME)public @interface ComponentScan {//表示我们的注解可以传入一个value属性String value() default "";
}
package com.annotation;import org.springframework.stereotype.Component;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/12 16:01**/
@ComponentScan(value = "com.component")
public class SpringConfig {}
package com.annotation;import com.applicationcontext.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import javax.security.auth.login.Configuration;
import java.io.File;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/13 12:46**/
public class SpringApplicationContext {//这个类的作业是模仿容器//需要拿到class类型,因为要使用反射//定义一个属性private Class configClass;//因为在框架内,bean的信息要放入容器的hashMap//所以这一要创建一个mapprivate final ConcurrentHashMap<String, Object> ioc =new ConcurrentHashMap<>();//构造器,在初始化容器时,选用传入beans.xml文件//public SpringApplicationContext(Class configClass) {//配置类不止在构造器使用,跨方法使用//拿到配置类的class类型,就可以到的注解//就可以拿到注解的value里的路径信息this.configClass = configClass;
// System.out.println(this.configClass);//获取要扫描的包//1.先得到配置类的注解ComponentScan componentScan =(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);//通过comonentScan得到valueString path = componentScan.value();//获得value下的所有class的信息//1.得到类的加载器ClassLoader classLoader = ApplicationContext.class.getClassLoader();//2.通过加载器获得要扫描包的资源路径,class文件的信息不是在src而是在out//Java中解析路径识别不出. 所有要把查找出来com. 的路径转化为com/path = path.replace('.', '/');URL resource = classLoader.getResource(path);//3.将要加载的资料(.class)路径下的文件进行遍历//注意:如果要有子目录,需要进行递归File file = new File(resource.getFile());if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {System.out.println(f.getAbsolutePath());String fileAbsolutePath = f.getAbsolutePath();//过滤,只处理.class文件if (fileAbsolutePath.endsWith(".class")) {//获取全类名,反射对象,放入容器中//1.获取类名String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.lastIndexOf(".class"));//2.获取类的路径String classFullName = path.replace("/", ".") + "." + className;//判断文件是不是需要注入容器中try {//这时我们得到该类的class对象//forname 可以通过反射加载类对象//loadclass 也可以//但是forname会调用该类的静态方法,Class<?> aClass = classLoader.loadClass(classFullName);if(aClass.isAnnotationPresent(Component.class) || aClass.isAnnotationPresent(Controller.class)|| aClass.isAnnotationPresent(Service.class) ||aClass.isAnnotationPresent(Repository.class)) {//指定value 分配idif(aClass.isAnnotationPresent(Component.class)) {Component component= aClass.getDeclaredAnnotation(Component.class);String id = component.value();if (!"".endsWith(id)) {className = id;}}//这里为什么用forname 会调用静态方法,会把类的联系加载进去Class<?> aClazz = Class.forName(classFullName);Object instance = aClazz.newInstance();//类的小写首字母作为idioc.put(StringUtils.uncapitalize(className), instance);}} catch (Exception e) {e.printStackTrace();}}}}}//编写方法返回容器对象public Object getBean(String beanName) {return ioc.get(beanName);}
}
注解自动装配
1.基于注解配置bean,也可实现自动装配,使用的注解是:@AutoWired 或者 @Resource
@AutoWired 的规则说明:
-
在IOC容器中查找待装配的组件的类型,如果有唯一的bean匹配,则使用该bean装配。
-
如待装配的类型对应的bean在IOC容器中有多个,则使用待装配的属性名作为id值再进行查找,找到就装配,找不到就抛异常。
-
操作流程
<?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.xsd http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.component"/></beans>
在IOC容器中查找待装配的组件的类型,如果有唯一的bean匹配,则使用该bean装配。
@Controller
public class UserAction {@Autowiredprivate UserService userService;public void sayOk() {System.out.println("UserAction");userService.hi();}
}
@Service
public class UserService {public void hi(){System.out.println("hi");}
}
如待装配的类型对应的bean在IOC容器中有多个,则使用待装配的属性名作为id值再进行查找,找到就装配,找不到就抛异常。
<?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.xsd http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.component"/><bean class="com.component" id="userService200"/><bean class="com.component" id="userService300"/>
</beans>
3.@Resource 的规则说明:
- @Resource有两个属性是比较重要的,分别是name和type。Spring将@Resource注解的name属性解析为bean的名字,而type属性解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略;如果使用type属性,则使用byType的自动注入策略。
使用type时,对象必须是单例的]
- 如果@Resource没有name和type,则先使用byName注入策略,如果匹配不上,再使用byType策略,如果都不成功,就会报错。
建议,不管是@AutoWired 还是 @Resource,都应谨慎使用,确保理解其自动装配的规则和潜在的异常情况,以避免运行时错误。
@Controller
public class UserAction {@Resource(name = "userService200")private UserService userService;public void sayOk() {System.out.println("UserAction");System.out.println("useraction自动装配的" + userService);userService.hi();}
}
配置文件不变
泛型依赖注入
泛型:
- 1.普通成员可以使用泛型(属性、方法)
- 2.使用泛型的数组,不能初始化
- 3.静态方法中不能使用类的泛型
- 4.泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
- 5.如果在创建对象时,没有指定类型,默认为Object
aop
动态代理
动态代理是 Java 中一种非常强大的特性,它允许在运行时创建一个实现了某个接口的代理对象,这个代理对象可以作为目标对象的替代品。动态代理在很多场景下非常有用,比如:
- 1.日志记录:在方法调用前后添加日志记录。
- 2.事务管理:在方法调用前后管理数据库事务。
- 3.安全检查:在方法调用前进行权限验证。
- 4.缓存:缓存方法的返回结果,避免重复计算。
- 5.延迟加载:实现对象的延迟加载机制。
- 6.远程方法调用:在远程方法调用(RMI)中,动态代理可以用来封装远程调用的细节。
动态代理的实现
在 Java 中,动态代理主要通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来实现。
Proxy类用于生成动态代理实例。InvocationHandler接口定义了代理实例上的方法调用处理程序。
package com.aop;import java.lang.reflect.*;
import java.util.Arrays;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/14 15:22**/
public class ProxyProvider {//创建一个目标对象,这个对象要实现接口private SmartAnimal smart_traget;public ProxyProvider(SmartAnimal smart_traget) {this.smart_traget = smart_traget;}//创建一个代理对象,我们要用代理对象如执行目标对象的方法public SmartAnimal getProxy() {//1.类加载器ClassLoader classLoader = smart_traget.getClass().getClassLoader();//2.目标接口信息Class<?>[] interfaces = smart_traget.getClass().getInterfaces();//3.创建一个InvocationHandlerInvocationHandler invocationHandler = new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;try {//横切关注点System.out.println("方法执行前" + method.getName() + "参数" + Arrays.asList(args));//返回执行结果//使用反射方法调用方法result = method.invoke(smart_traget, args);//横切接入点System.out.println("方法执行后" + method.getName() + "结果" + result);} catch (Exception e) {e.printStackTrace();//如果执行方发时,出现异常,就会进入catchSystem.out.println("方法" + method.getName() + "执行异常 结果" + e.getClass().getName());} finally {//最中都会执行到这里//横切关注点,最终通知System.out.println("方法" + method.getName() + "名");}return result;}};//创建代理对象----动态代理类型SmartAnimal proxy = (SmartAnimal)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);return proxy;}}
面向切面编程(Aspect-Oriented Programming,AOP)是一种编程范式,旨在将横切关注点(cross-cutting concerns)从业务逻辑中分离出来,以提高模块化。横切关注点是指那些影响多个类的问题,比如日志记录、事务管理、安全性等。AOP 通过创建切面(aspects)来实现这一点,切面可以定义在何处以及如何将额外的行为插入到程序代码中。
AOP 的关键概念:
- 1.切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是切面的一个典型例子。
- 2.连接点(Join Point):在程序执行过程中某个特定的点,比如方法的调用或异常的抛出。在Spring AOP中,连接点总是方法的执行点。
- 3.通知(Advice):在切面的某个特定的连接点上执行的动作。不同类型的通知包括“前置通知”(在方法调用之前)、“后置通知”(在方法成功执行之后)、“异常通知”(在方法抛出异常后)、“最终通知”(无论方法执行成功还是失败都会执行)和“环绕通知”(围绕方法执行的通知)。
- 4.引入(Introduction):允许我们向现有的类添加新的方法或属性。
- 5.织入(Weaving):把切面应用到目标对象并创建新的代理对象的过程。织入可以在编译时、加载时或运行时完成。
AOP 在 Spring 中的实现:
Spring AOP 是 Spring 框架的一部分,它使用代理模式来实现 AOP。Spring AOP 只支持方法级别的连接点,这意味着它只能在方法执行时进行拦截。
- 使用 @Aspect 注解:在 Spring 中,你可以使用
@Aspect注解来定义一个切面类。 - 定义通知:使用
@Before、@After、@AfterReturning、@AfterThrowing和@Around等注解来定义不同类型的通知。 - 配置 AOP:在 Spring 配置中启用 AOP 支持,并指定哪些类或方法应用切面。
<context:component-scan base-package="com.asp"/>
<!-- 开启基于注解的AOP--><aop:aspectj-autoproxy/>
切入表达式:
通过表达式的方式定位一个或多个具体的连接点
execution[[权限修饰符] [返回类型] [简单类名/全类名] [方法名] [参数列表] ]
切入表达式的注意细节
1.切入表达式也可以指向类的方法,这时切入表达式会对该类/对象生效。
2.切入表达式也可以指向接口的方法,这时切入表达式会对实现了接口的类/对象生效。
3.切入表达式也可以对没有实现接口的类,进行切入.
动态代理机制1.jdk的proxy 面向接口 2.CGlib 是面向父类
两个动态代理的区别
- 1.JDK动态代理是面向接口的,只能增强实现类中接口中存在的方法。CGlib是面向父类的,可以增强父类的所有方法。
- 2.JDK得到的对象是JDK代理对象实例,而CGlib得到的对象是被代理对象的子类。
在类和切面类在一个包下可以简写类名
JoinPoint
public void beforeMethod(JoinPoint joinPoint){joinPoint.getSignature().getName(); // 获取目标方法名joinPoint.getSignature().getDeclaringType().getSimpleName(); // 获取目标方法所属类的简单类名joinPoint.getSignature().getDeclaringTypeName(); // 获取目标方法所属类的类名joinPoint.getSignature().getModifiers(); // 获取目标方法声明类型(public, private, protected)Object[] args = joinPoint.getArgs(); // 获取传入目标方法的参数,返回一个数组joinPoint.getTarget(); // 获取代理对象joinPoint.This(); // 获取代理对象自己
}
Aop切面优先级
@order(value=n) 来控制 n 值越小,优先级越高。
运行原理类似filter
基于xml配置Aop
bean.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:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 使用xml,完成aop编程--><bean class="com.aopxml.SmartAnimalAspect" id="animalAspect"/>
<!-- 配置一个smartdog--><bean class="com.aopxml.SmartDog" id="smartDog"/>
<!-- 配置切面类--><aop:config>
<!-- 这里指定切面对象-->
<!-- <aop:aspect ref="animalAspect" order="10"/>--><!--配置切入点--><aop:pointcut id="myPointCut" expression="execution(public float com.aopxml.SmartDog.getSum(float,float))"/>
<!-- 这里指定切面对象--><aop:aspect ref="animalAspect" order="10"><!--配置前置通知--><aop:before method="showBeginLog" pointcut-ref="myPointCut"/><!--返回通知--><aop:after-returning method="showSuccessEndLog" pointcut-ref="myPointCut" returning="res"/><!--异常通知--><aop:after-throwing method="showExceptionLog" pointcut-ref="myPointCut" throwing="throwable"/><!--最终通知--><aop:after method="showFinallyEndLog" pointcut-ref="myPointCut"/><!-- <aop:around method=""--></aop:aspect></aop:config>
</beans>
相关文章:
Spring01
spring框架 spring是轻量级的容器框架 spring framework 1、Spring核心学习内容 IOC、AOp, jdbcTemplate,声明式事务 2、IOC:控制反转,孚以管理部8号对象 3.AOP:切面编程4.JDBCTemplate:是spring提供一套访问数据库的技术,应用性强,相对好理解5.声明式…...
gogps 利用广播星历解算卫星位置matlab函数satellite_orbits详细注解版
主要注释了广播星历计算GPS BDS卫星位置的。 function [satp, satv] satellite_orbits(t, Eph, sat, sbas)% SYNTAX: % [satp, satv] satellite_orbits(t, Eph, sat, sbas); % % INPUT: % t clock-corrected GPS time % Eph ephemeris matrix % sat satellite…...
Oracle按照某一字段值排序并显示,相同的显示序号
Oracle按照某一字段值排序并显示,相同的显示序号 最近的工作遇到对于相同的字段,按照序号去显示值,并对相同的值进行排序 实验了半天,感觉满意的答案,分享给大家 第一种: ROW_NUMBER 语法: ROW_NUMBER() OVER (ORDER BY your_column) AS sequence_number 说明: 根据your_column…...
【Java基础】String详解
文章目录 String一、String 概述1、基本特性2、不可变性3、String、StringBuilder、StringBuffer 二、字符串 创建与内存分配1、字面量 / 双引号2、new关键字3、StringBuilder.toString()4、intern() 方法5、小结 三、字符串 拼接1、常量常量2、变量 拼接3、final变量 拼接4、拼…...
cmd命令
常用命令 查看电脑名称: hostname 查看网卡信息: ipconfig 快速打开网络设置界面: control.exe netconnections 或 rundll32.exe shell32.dll,Control_RunDLL ncpa.cpld 打开防火墙设置: wf.msc 指定网卡设置IP地址&#…...
《中文Python穿云箭量化平台二次开发技术11》股票基本信息获取分析及应用示例【前十大股东占股比例提取及分析】
《中文Python穿云箭量化平台二次开发技术11》股票基本信息获取分析及应用示例【前十大股东占股比例提取及分析】 《中文Python穿云箭量化平台》是纯Python开发的量化平台,因此其中很多Python模块,我们可以自己设计新的量化工具,例如自己新的行…...
OSINT技术情报精选·2024年9月第1周
OSINT技术情报精选2024年9月第1周 2024.8.15版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 1、中国信通院:《大模型落地路线图研究报告(2024年)》 近年来,大模型技术能力不断创出新高,产业应用持续走…...
51单片机应用开发---二进制、十六进制与单片机寄存器之间的关系(跑马灯、流水灯实例)
实现目标 1、掌握二进制与十六进制之间的转换 2、掌握单片机寄存器与二进制、十六进制之间的转换 3、掌握单片机驱动跑马灯、流水灯的原理 一、二进制与十六进制之间的转换 1、二进制 二进制(binary), 是在数学和数字电路中以2为基数的…...
信息安全工程师(6)网络信息安全现状与问题
一、网络信息安全现状 威胁日益多样化:网络攻击手段不断翻新,从传统的病毒、木马、蠕虫等恶意软件,到勒索软件、钓鱼攻击、DDoS攻击、供应链攻击等,威胁形式多种多样。这些攻击不仅针对个人用户,还广泛影响企业、政府等…...
亚数TrustAsia亮相第十四届智慧城市与智能经济博览会,入围“2024数据要素创新应用优秀成果”!
智博会 2024年9月6日至8日,由宁波市人民政府、浙江省经济和信息化厅、中国信息通信研究院、中国电子信息行业联合会、中国电信、中国移动、中国联通主办的2024世界数字经济大会暨第十四届智慧城市与智能经济博览会(以下简称“智博会”)在宁波…...
Linux基础开发环境(git的使用)
1.账号注册 git 只是一个工具,要想实现便捷的代码管理,就需要借助第三方平台进行操作,当然第三平台也是基于git 开发的 github 与 gitee 代码托管平台有很多,这里我们首选 Github ,理由很简单,全球开发者…...
VS Code终端命令执行后老是出现 __vsc_prompt_cmd_original: command not found
VS Code终端命令执行后老是出现 __vsc_prompt_cmd_original: command not found。 如下图(vscode终端中): 解决方案: 1、vim ~/.bashrc 2、在~/.bashrc里面加入命令:unset PROMPT_COMMAND 3、source ~/.bashrc...
春天(Spring Spring Boot)
基本概念 春天 Spring 是用于开发 Java 应用程序的开源框架,为解决企业应用开发的复杂性而创建。 Spring 的基本设计思想是利用 IOC(依赖注入)和 AOP (面向切面)解耦应用组件,降低应用程序各组件之间的耦…...
Oracle EBS AP预付款行分配行剩余预付金额数据修复
系统环境 RDBMS : 12.1.0.2.0 Oracle Applications : 12.2.6 问题情况 AP预付款已验证和自动审批但是未过账已经AP付款但是又撤消付款并且未过账问题症状 AP预付款暂挂: AP预付款行金额(等于发票金额)与分配行金额不相等: 取消AP预付款提示如下:...
【鸿蒙】HarmonyOS NEXT星河入门到实战7-ArkTS语法进阶
目录 1、Class类 1.1 Class类 实例属性 1.2 Class类 构造函数 1.3 Class类 定义方法 1.4 静态属性 和 静态方法 1.5 继承 extends 和 super 关键字 1.6 instanceof 检测是否实例 1.7.修饰符(readonly、private、protected 、public) 1.7.1 readonly 1.7.2 Private …...
Java设计模式—面向对象设计原则(六) ----->合成复用原则(CRP) (完整详解,附有代码+案例)
文章目录 3.6 合成复用原则(CRP)3.6.1 概述3.6.2 案列 3.6 合成复用原则(CRP) 合成复用原则(CRP):Composite Reuse Principle,CRP 又叫: 组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)…...
java坏境搭建
目录 安装 步骤1 步骤2 步骤3 步骤4 环境变量 1、在桌面“计算机”或“此电脑”图标上右键,选择“属性”,进入控制面板的计算机系统页面后,点击“高级系统设置”,不同操作系统可能界面不同: 2、点击“环境变量”…...
C#中判断socket是否已断开的方法
代码如下: Socket s new Socket(..); if (s.Poll(-1, SelectMode.SelectRead)) {int nRead s.Receive();if (nRead 0){//socket连接已断开} }参考:C#中判断socket是否已断开的方法...
Python编程 - 异常处理与文件读写
目录 前言 一、异常处理 (一)关键字 (二)捕获多个异常 (三)自定义异常 (四)抛出异常 (五)总结 二、文件读写 (一)打开文件 &…...
【C++】c++ 11
目录 前言 列表初始化 std::initializer_list 右值引用和移动拷贝 左值和右值 左值引用和右值引用的区别 万能引用(引用折叠) 完美转发 默认成员函数控制 列表初始化 在C98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
