1.Spring
1.1 简介
Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架,它是为了解决企业应用开发的复杂性而创建的。Spring 的核心是控制反转(IoC)和面向切面编程(AOP)。 Spring 的主要作用就是为代码“解耦”,降低代码间的耦合度。
1.2 IOC
控制反转(IoC,Inversion of Control)是一个概念,指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,依赖的管理。
依赖:classA 类中含有 classB 的实例,在 classA 中调用 classB 的方法完 成功能,即 classA 对 classB 有依赖
Spring 容器负责创建、管理所有的 Java 对象,这些 Java 对象被称为 Bean。Spring 容器管理着容器中 Bean 之间的依赖关系, Spring 使用“依赖注入”的方式来管理 Bean 之间的依赖关系。
1.2.1 基本配置
maven基本依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
创建 Spring 配置文件
在 src/main/resources/目录现创建一个 xml 文件,文件名可以随意,但 Spring 建议的名称为 applicationContext.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">
<import resource="appli.xml"/>
<bean id = "some1" class="com.impl.SomeImpl"/>
<bean id = "some2" class="com.impl.SomeImpl">
</bean>
</beans>
:用于定义一个实例对象。一个实例对应一个 bean 元素。
id:该属性是 Bean 实例的唯一标识,程序通过 id 属性访问 Bean,Bean与 Bean 间的依赖关系也是通过 id 属性关联的。
class:指定该 Bean 所属的类,注意这里只能是类,不能是接口。
定义测试类
public class Test {
public static void main(String[] args) throws Exception {
//配置文件的位置和名称
String be = "beans.xml";
//创建容器对象
ApplicationContext atx = new ClassPathXmlApplicationContext(be);
//通过ID获取对象
SomeImpl ww = (SomeImpl) atx.getBean("some1");
//执行对象的方法
ww.doSome();
U1 uu = (U1) atx.getBean("U11");
uu.doe();
}
}
ApplicationContext 容器,会在容器对象初始化时,将其中的所有对象一次性全部装配好。以后代码中若要使用到这些对象,只需从内存中直接获取即可。执行效率较高。但占用内存。
1.2.2 依赖注入
bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初
始化。初始化是由容器自动完成的,称为注入。
根据注入方式的不同,常用的有两类:set 注入、构造注入。
set 注入(掌握)
set 注入也叫设值注入是指,通过 setter 方法传入被调用者的实例。这种注入方式简单、直观,因而在 Spring 的依赖注入中大量使用。
//lombook生成set方法
@Data
public class SomeImpl implements Some {
String name;
int a = 0;
SomeImpl(){
System.out.println("我是SomeImpl的构造方法");
}
public void doSome() {
System.out.println(name+"的a为"+a);
}
}
通过标签给属性赋值即可
<bean id = "some1" class="com.impl.SomeImpl">
<property name="name" value="som1"/>
<property name="a" value="1"/>
</bean>
<bean id = "some2" class="com.impl.SomeImpl">
<property name="name" value="som2"/>
<property name="a" value="2"/>
</bean>
引用类型
当指定 bean 的某属性值为另一 bean 的实例时,通过 ref 指定它们间的引用关系。ref 的值必须为某 bean 的 id 值。
构造注入(理解)
构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。即使用构造器设置依赖关系。
标签中用于指定参数的属性有:
name:指定参数名称。
index:指明该参数对应着构造器的第几个参数,从 0 开始。不过,该属性不要也行,但要注意,若参数类型相同,或之间有包含关系,则需要保证赋值顺序要与构造器中的参数顺序一致。
为应用指定多个 Spring 配置文件
<import resource="appli.xml"/>
1.2.3基于注解的 DI
对于 DI 使用注解,将不再需要在 Spring 配置文件中声明 bean 实例。需要在 Spring 配置文件中配置组件扫描器,用于在指定的基本包中扫描注解。
<context:component-scan base-package="com"/>
base-package 的值表是基本包,容器启动会扫描包及其子包中的注解,也会扫描到子包下级的子包。
定义 Bean 的注解@Component
//相当于以前的bean标签,U11为id
@Component("U11")
public class U1 {
public void doe(){
System.out.println("U1");
}
}
不指定 value 属性,bean 的 id 是类名的首字母小写。
简单类型属性注入@Value
byType 自动注入@Autowired(掌握)
需要在引用属性上使用注解@Autowired,该注解默认使用按类型自动装配 Bean 的方式。
byName 自动注入@Autowired 与@Qualifier
需要在引用属性上联合使用注解@Autowired 与@Qualifier。@Qualifier的 value 属性用于指定要匹配的 Bean 的 id 值。类中无需 set 方法,也可加到set 方法上。
@Autowired 还有一个属性 required,默认值为 true,表示当匹配失败后,会终止程序运行。若将其值设置为 false,则匹配失败,将被忽略,未匹配的属性值为 null。
1.3 AOP 面向切面编程
AOP 为 Aspect Oriented Programming 面向切面编程,可通过运行期动态代理实现程序功能的统一维护的一种技术。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
1.3.1 静态代理与动态代理
静态代理:由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
动态代理:动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。
1.3.2不使用 AOP 的开发方式
先定义好接口与一个实现类,该实现类中除了要实现接口中的方法外,还要再写两个非业务方法。非业务方法也称为交叉业务逻辑:
doTransaction():用于事务处理
doLog():用于日志处理
然后,再使接口方法调用它们。接口方法也称为主业务逻辑。
接口:
可以有另一种解决方案:将这些交叉业务逻辑代码放到专门的工具类或处理类中,主业务逻辑调用。
1.3.3 AOP开发
面向切面编程,将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到主业务逻辑中。
交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、事务、日志、缓存等。
若不使用 AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样,会使主业务逻辑变的混杂不清。
1.减少重复;
2.专注业务;
注意:面向切面编程只是面向对象编程的一种补充。
AOP 编程术语
(1) 切面(Aspect)
切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面是通知(Advice)。实际就是对主业务逻辑的一种增强。
(2) 连接点(JoinPoint)
连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
(3) 切入点(Pointcut)
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
(4) 目标对象(Target)
目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。
(5) 通知(Advice)
通知表示切面的执行时间,Advice 也叫增强。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。切入点定义切入的位置,通知定义切入的时间。
1.3.4 AspectJ 对 AOP 的实现
对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,而且还支持注解式开发。所以,Spring 又将AspectJ 的对于 AOP 的实现也引入到了自己的框架中。
AspectJ 中常用的通知有五种类型:
(1)前置通知
(2)后置通知
(3)环绕通知
(4)异常通知
(5)最终通知
AspectJ 的切入点表达式
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-
pattern(param-pattern)
throws-pattern?)
modifiers-pattern] 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
throws-pattern 抛出异常类型
?表示可选的部分
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
execution(public * *(..))
指定切入点为:任意公共方法。
execution(* set*(..))
指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.xyz.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现
在类名中时,后面必须跟“*”,表示包、子包下的所有类。
execution(* *..service.*.*(..))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(* *.service.*.*(..))
指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(* *.ISomeService.*(..))
指定只有一级包下的 ISomeSerivce 接口中所有方法为切入点
execution(* *..ISomeService.*(..))
指定所有包下的 ISomeSerivce 接口中所有方法为切入点
execution(* com.xyz.service.IAccountService.*(..))
指定切入点为:IAccountService 接口中的任意方法。
execution(* com.xyz.service.IAccountService+.*(..))
指定切入点为:IAccountService 若为接口,则为接口中的任意方法及其所有
实现类中的任意方法;若为类,则为该类及其子类中的任意方法。
execution(* joke(String,int)))
指定切入点为:所有的 joke(String,int)方法,且 joke()方法的第一个参数是
String,第二个参数是 int。如果方法中的参数类型是 java.lang 包下的类,可
以直接使用类名,否则必须使用全限定类名,如 joke( java.util.List, int)。
execution(* joke(String,*)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,第二个参数
可以是任意类型,如 joke(String s1,String s2)和 joke(String s1,double d2)
都是,但 joke(String s1,double d2,String s3)不是。
execution(* joke(String,..)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,后面可以有
任意个参数且参数类型不限,如 joke(String s1)、joke(String s1,String s2)和
joke(String s1,double d2,String s3)都是。
execution(* joke(Object))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类
型。joke(Object ob)是,但,joke(String s)与 joke(User u)均不是。
execution(* joke(Object+)))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型
或该类的子类。不仅 joke(Object ob)是,joke(String s)和 joke(User u)也是。
前置通知(1) 实现步骤
Step2:定义切面类
类中定义了若干普通方法,将作为不同的通知方法,用来增强功能。
Step3:声明目标对象切面类对象
Step4:注册 AspectJ 的自动代理
Step5:测试类中使用目标对象的 id
JoinPoint 参数
在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个JoinPoint 类型参数。通过该参数,可获取切入点表达式、方法签名、目标对象等。不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该参数(要是第一个参数)。
后置通知
//returning 要和方法的参数同名,即为该方法的返回值
@AfterReturning(value = "execution(public String com.iml.ServiceImpl.doOther(String))",
returning = "obj")
public void myAfter(Object obj){
System.out.println("切面的功能在目标功能之后执行,返回的对象为" + obj);
}
String be = "app.xml";
ApplicationContext atx = new ClassPathXmlApplicationContext(be);
Service mySer = (Service) atx.getBean("ser1");
mySer.doSome("d");
mySer.doOther("doO");
环绕通知@Around
ProceedingJoinPoint 参数
在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,Object 类型。并且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个 proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。
@Override
public String doFirst(String str) {
System.out.println("执行doFirst方法");
return str;
}
@Around("execution(public String com.iml.ServiceImpl.doFirst(String))")
public Object myAround(ProceedingJoinPoint pdj) throws Throwable {
Object obj;
System.out.println("方法之前执行");
obj = pdj.proceed();
obj = obj + "被环绕通知处理了";
System.out.println("方法之后执行");
return obj;
}
String str = mySer.doFirst("first");
System.out.println(str);
@Pointcut 定义切入点
当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。
其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的value 属性值均可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用@Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。
2.Spring Boot
Spring Boot基本上是Spring框架的扩展,它消除了设置Spring应用程序所需的XML配置,为更快,更高效的开发生态系统铺平了道路。
与Spring相比,思想上基本没有区别,类似于全部使用注解注入。
2.1 基本配置
maven依赖
spring依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.6.7</version>
</dependency>
aop依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.1</version>
</dependency>
SpringBoot项目都有一个固定的类作为入口
//这个注解是固定的
@SpringBootApplication
public class FoolRpcClientFactory {
public static void main(String... args){
//这里这个run和spring获得的容器类似,直接getBean获取实例
ConfigurableApplicationContext run = SpringApplication.run(FoolRpcClientFactory.class,args);
Service ser = run.getBean(Service.class);
ser.doSome("ww");
}
}
2.1.1注入
springBoot采用和spring相似的注解注入,不要再配置xml文件
配置类注入:
@Configuration()//表示这是一个配置类。会把里面@Bean的方法放到容器里
public class ConfigClass {
@Bean
@Scope("prototype")//加这个表示每次容器获取都是新实例,否则为同一个实例
public Do myDo(){
return new Do();
}
}
注解直接放到容器:
@Component//将这个类放到容器
public class ServiceImpl implements Service {
@Autowired//按类型自动从容器里找相应实例赋值
public Do d1;
@Autowired
public Do d2;
@Override
public void doSome(String str) {
System.out.println("doSome方法");
d1.dow();
System.out.println("d1.hashcode:"+ d1.hashCode() +
" d2.hashcode" + d2.hashCode());
}
}
2.1.2aop
aop区别不大,不需要在xml里注册声明,直接注解即可
@Component
@Aspect
public class MyAspect {
@Before(value = "execution(public void com.lll.iml.ServiceImpl.doSome(String))")
public void myBefore(){
System.out.println("前置");
}
}
2.2.yml
YAML 是 “YAML Ain’t Markup Language”(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)。
非常适合用来做以数据为中心的配置文件。
基本语法
- key: value;kv之间有空格
- 大小写敏感
- 使用缩进表示层级关系
- 缩进不允许使用tab,只允许空格
- 缩进的空格数不重要,只要相同层级的元素左对齐即可
- '#'表示注释
- 字符串无需加引号,如果要加,单引号’’、双引号""表示字符串内容会被 转义、不转义
'\n'字符输出,“\n”换行输出
数据类型
- 字面量:单个的、不可再分的值。date、boolean、string、number、null
k: v
对象:键值对的集合。map、hash、set、object
#行内写法:
k: {k1:v1,k2:v2,k3:v3}
#或
k:
k1: v1
k2: v2
k3: v3
数组:一组按次序排列的值。array、list、queue
#行内写法:
k: [v1,v2,v3]
#或者
k:
- v1
- v2
- v3
实例
@Data
public class Person {
private String userName;
private Boolean boss;
private Date birth;
private Integer age;
private Pet pet;
private String[] interests;
private List<String> animal;
private Map<String, Object> score;
private Set<Double> salarys;
private Map<String, List<Pet>> allPets;
}
@Data
public class Pet {
private String name;
private Double weight;
}
person:
userName: zhangsan
boss: false
birth: 2019/12/12 20:12:33
age: 18
pet:
name: tomcat
weight: 23.4
interests: [篮球,游泳]
animal:
- jerry
- mario
score:
english:
first: 30
second: 40
third: 50
math: [131,140,148]
chinese: {first: 128,second: 136}
salarys: [3999,4999.98,5999.99]
allPets:
sick:
- {name: tom}
- {name: jerry,weight: 47}
health: [{name: mario,weight: 47}]