spring day1~day3笔记合辑
day1视频连接 day2视频连接 day3视频连接
day1代码连接 链接:https://pan.baidu.com/s/16Z8HjL0fmatZZw_-7Ps5Rg
提取码:ij2f
复制这段内容后打开百度网盘手机App,操作更方便哦
day2代码 day3代码连接
链接:https://pan.baidu.com/s/1frUC8Dumg2mnGv3HMgfAOg
提取码:cjbr
复制这段内容后打开百度网盘手机App,操作更方便哦
spring入门
spring历史
spring这个框架由rod,曾经2002-2004年,撰写了几本书,讨论J2EE设计与开发,以及讨论如何在不使用EJB的情况做java企业级开发。在书中它主要讨论这个IOC以及AOP概念,给出了概念的初步实现,以此为基础创建spring framework。
在社区快读的得到了反馈,我们除了J2EE的标准之外,还有额外的选择。尽量耦合降低。
传统的代码编写方式
UserService u = new UserServiceImpl();
UserServlet里面进行大量调用,其实当前我们的这个代码就已经和我们的UserServiceImpl有着密切的关系。
业务类或者接口散落在各个角落,替换代价很大。
IOC
控制翻转(Inversion of Control,IoC)也称依赖注入(Dependency Injection,DI),
是面向对象编程的一种理念,用来降低程序代码之间的耦合度。
依赖一般指通过局部变量、方法参数、返回值等建立的对于其他对象的调用关系。
控制反转:从 UserServiceImpl 转移到了 UserDaoFactory
Spring 提供完整的 IoC,让我们专注业务类和 DAO 类设计。
[外链图片转存失败(img-ZaaxHxYE-1565601618253)(assets/1564904982209.png)]
此时我们的开发人员要做的事情就是提供一些创建这些对象所必需的的一些信息(元数据)。只需要向容器申请要什么,不再负责创造什么。
[外链图片转存失败(img-trJDoK7J-1565601618254)(assets/1564905084531.png)]
IOC的实现
控制反转 又名DI(依赖注入)
1.创建对象
2.给对象的属性赋值
3.超级工厂 任何对象都可以存放
4.把创建对象的权力交给SPring管理 使用IOC 可以解决的程序耦合性高的问题
5.我们在需要的时候直接去取
6.将创建对象属性的方式进行了反转
从new() set() 反转为了从Spring IOC容器中getBean()
使用DI来完成IOC的理念,依赖注入,look up。
- setter注入
- 构造注入
spring入门体验
1 引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
2 定义pojo
package com.sz;
public class User {
private String name;
private int age;
public User() {
System.out.println("惟沉默是最高的轻蔑。");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
3 编写spring配置文件
<?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
豆豆,对象
依赖元数据,XML技术,反射,反射技术需要全限定类名,以及默认的无参构造器。
-->
<bean class="com.sz.User">
</bean>
</beans>
再使用springAPI完成初始化的工作
package com.sz;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringStyle {
@Test
public void m1(){
// 需要利用spring的API完成上下文的初始化,以及读取我们当前写的这个配置文件
// ApplicationContext 上下文接口
ApplicationContext ctx = new ClassPathXmlApplicationContext("app.xml");// 参数指定配置文件的名称;
}
}
4 官方参考网站: spring.io
具体官方指引可以参考:https://docs.spring.io/spring/docs/5.1.9.RELEASE/spring-framework-reference/core.html#spring-core
bean取名
- id 通过id可以给其命名,注意id,一定要保障唯一
- name 也可以取名,并且name可以写多个彼此可以使用分隔符
空格
,
;
- 还可以使用alias进行取别名(先有主名)
获取bean
上下文的接口里面定义了getBean方法。
1 通过指定名称获取,需要强转
2 通过Class类型获取,这个无名字也可以,但是要注意,如果不止一个就出问题了。
3 同时指定名字和类类型
属性注入
<bean class="com.sz.User" id="myUser" >
<property name="name" value="德华"/>
</bean>
这个里面的property是bean的一个子元素,用于给这个对象属性赋值,name是属性名称,value属性对应的值。它依赖于setXXx的方法。
Bean property 'name' is not writable or has an invalid setter method. Did you mean 'age'?
上面这个是没有setName的方法,所以无法完成属性的注入。
Caused by: java.lang.NoSuchMethodException: com.sz.User.<init>()
如果没有默认的无参构造器就会引发上述的问题。
值的类型问题
注入值
- 字面值可描述的
- 非字面值,必须使用引用的方式
引用的值必须使用ref的方式描述
<bean class="com.sz.Dog" id="dog">
<property name="poodle" value="泰迪"/>
</bean>
<bean class="com.sz.User" id="myUser" >
<property name="name" value="德华"/>
</bean>
<bean class="com.sz.User" id="yourUser" >
<property name="name" value="学友"/>
<property name="age" value="52"/>
<property name="dog" ref="dog"/>
</bean>
bean生命周期控制
- init
- destroy
分别用于控制bean初始化的工作,销毁工作
<bean class="com.sz.User" id="yourUser" init-method="cry" destroy-method="destroy" >
<property name="name" value="学友"/>
<property name="age" value="52"/>
<property name="dog" ref="dog"/>
</bean>
注意如果我们希望销毁方法也执行,我们必须手工关闭容器。
ApplicationContext ctx = new ClassPathXmlApplicationContext("app.xml");// 参数指定配置文件的名称;
User user = ctx.getBean("yourUser",User.class);
ClassPathXmlApplicationContext xctx = (ClassPathXmlApplicationContext) ctx;
xctx.close();
它不会再普通的业务bean里面使用,而是有特殊作用的bean,比如数据源,等其他资源类型组件。
注入方式
- setter注入
- 构造注入
构造注入
1 使用name + value形式注入
<bean class="com.sz.Car" id="car">
<constructor-arg name="brand" value="福特"/>
<constructor-arg name="price" value="23.33"/>
</bean>
2 使用type + value注入
<bean class="com.sz.Car" id="car">
<constructor-arg type="java.lang.String" value="穆罕默德"/>
<constructor-arg type="java.lang.Double" value="99.0"/>
</bean>
这种使用方式是存在着风险的,如果构造器多,此时它和构造器的位置都是由关系的。
使用下面的方式可以完美解决这个问题
<bean class="com.sz.Car" id="car">
<constructor-arg type="java.lang.String" index="0" value="牛顿"/>
<constructor-arg type="java.lang.Double" index="1" value="99.0"/>
</bean>
延迟初始化
- 懒加载技术,只有真正去向spring容器要这个实例的时候才会完成初始化的工作。
命名空间
提供了两种方式来简化编写。c空间,p空间针对构造注入的简写,以及属性注入的简写。
p命名空间
<bean class="com.sz.Car" p:brand="奔驰" p:price="99.3" id="car"/>
等价于
<bean class="com.sz.Car" id="car">
<property name="brand" value="奔驰"/>
<property name="price" value="99.2"/>
</bean>
c命名空间
<bean class="com.sz.Car" id="car" c:brand="宝马" c:speed="103.0"/>
bean scope控制
将bean设计的时候默认采用的方式是单例的。
整个上下文里面只存在着一份实例。
ApplicationContext ctx = new ClassPathXmlApplicationContext("app.xml");
Car car = ctx.getBean("car", Car.class);
Car car2 = ctx.getBean("car", Car.class);
System.out.println(car == car2);
上面结果为true
说明,spring确实将这个实例创建的 方式为单例
scope
缺省的值为singleton
。在非web环境下,我们使用两种方式
- singleton
- prototype
<bean class="com.sz.Car" id="car" c:brand="宝马" c:speed="103.0" scope="singleton"/>
<bean class="com.sz.Car" id="car" c:brand="宝马" c:speed="103.0" />
上述两种写法是一样的。
如果使用了prototype的话,那么结果就完全不一样
<bean class="com.sz.Car" id="car" c:brand="宝马" c:speed="103.0" scope="prototype"/>
每当你向容器申请要一个bean的时候,spring都会重新创建一个。
struts这个技术,都是设计为原型的。
复杂值注入问题
数组注入方式
错误示范
<bean class="com.sz.Person" id="person">
<property name="foods" value="辣条 豆腐 麻辣烫"/>
</bean>
正确的写法
<bean class="com.sz.Person" id="person">
<property name="foods">
<array>
<value>麻辣烫</value>
<value>臭豆腐</value>
<value>饺子</value>
</array>
</property>
</bean>
如果引用的是非字面值,一样可以通过bean元素完成注入
<bean class="com.sz.Person" id="person">
<property name="foods">
<array>
<value>麻辣烫</value>
<value>臭豆腐</value>
<value>饺子</value>
</array>
</property>
<property name="dogs">
<array>
<bean class="com.sz.Dog" p:brand="阿黄" p:age="2"/>
<bean class="com.sz.Dog" p:brand="阿红" p:age="3"/>
<bean class="com.sz.Dog" p:brand="阿紫" p:age="4"/>
</array>
</property>
</bean>
list注入
<bean class="com.sz.Person" id="person">
<property name="foods">
<array>
<value>麻辣烫</value>
<value>臭豆腐</value>
<value>饺子</value>
</array>
</property>
<property name="dogs">
<array>
<bean class="com.sz.Dog" p:brand="阿黄" p:age="2"/>
<bean class="com.sz.Dog" p:brand="阿红" p:age="3"/>
<bean class="com.sz.Dog" p:brand="阿紫" p:age="4"/>
</array>
</property>
<property name="luckyNums">
<list>
<value>1</value>
<value>3</value>
<value>7</value>
</list>
</property>
</bean>
set
<bean class="com.sz.Person" id="person">
<property name="foods">
<array>
<value>麻辣烫</value>
<value>臭豆腐</value>
<value>饺子</value>
</array>
</property>
<property name="dogs">
<array>
<bean class="com.sz.Dog" p:brand="阿黄" p:age="2"/>
<bean class="com.sz.Dog" p:brand="阿红" p:age="3"/>
<bean class="com.sz.Dog" p:brand="阿紫" p:age="4"/>
</array>
</property>
<property name="luckyNums">
<list>
<value>1</value>
<value>3</value>
<value>7</value>
</list>
</property>
<property name="cities">
<set>
<value>深圳</value>
<value>广州</value>
<value>上海</value>
</set>
</property>
</bean>
map
<bean class="com.sz.Person" id="person">
<property name="foods">
<array>
<value>麻辣烫</value>
<value>臭豆腐</value>
<value>饺子</value>
</array>
</property>
<property name="dogs">
<array>
<bean class="com.sz.Dog" p:brand="阿黄" p:age="2"/>
<bean class="com.sz.Dog" p:brand="阿红" p:age="3"/>
<bean class="com.sz.Dog" p:brand="阿紫" p:age="4"/>
</array>
</property>
<property name="luckyNums">
<list>
<value>1</value>
<value>3</value>
<value>7</value>
</list>
</property>
<property name="cities">
<set>
<value>深圳</value>
<value>广州</value>
<value>上海</value>
</set>
</property>
<property name="countries">
<map>
<entry key="CN" value="中国"/>
<entry key="USA" value="美国"/>
<entry key="JP" value="日本"/>
</map>
</property>
</bean>
properties
本质上也是一个map。
<bean class="com.sz.Person" id="person">
<property name="foods">
<array>
<value>麻辣烫</value>
<value>臭豆腐</value>
<value>饺子</value>
</array>
</property>
<property name="dogs">
<array>
<bean class="com.sz.Dog" p:brand="阿黄" p:age="2"/>
<bean class="com.sz.Dog" p:brand="阿红" p:age="3"/>
<bean class="com.sz.Dog" p:brand="阿紫" p:age="4"/>
</array>
</property>
<property name="luckyNums">
<list>
<value>1</value>
<value>3</value>
<value>7</value>
</list>
</property>
<property name="cities">
<set>
<value>深圳</value>
<value>广州</value>
<value>上海</value>
</set>
</property>
<property name="countries">
<map>
<entry key="CN" value="中国"/>
<entry key="USA" value="美国"/>
<entry key="JP" value="日本"/>
</map>
</property>
<property name="properties">
<props>
<prop key="username">root</prop>
<prop key="password">root</prop>
<prop key="driver">com.mysql.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring</prop>
</props>
</property>
</bean>
多配置文件的问题
1 可以在使用上下文对象的时候指定多个配置文件的名称即可
ApplicationContext ctx = new ClassPathXmlApplicationContext("app.xml","app-2.xml");
2 在XML里面引入其它配置文件
一般叫做applicationContext.xml
<import resource="classpath:app-2.xml"/>
注入空值
null
<bean class="com.sz.Dog" id="dog">
<property name="age" value="290"/>
<property name="brand">
<null/>
</property>
</bean>
- 明确表达,有这个东西,东西是null。
再次回顾IOC
instance ,实例,对象。
- metadata就是我们写的XML文件
- pojo 就是类似Car Person这样的数据
- 二者结合在一起就可以提供使用
源码
背面试题
基于注解开发方式
关于注解
- 如果你很熟悉,使用它更简洁
- 如果不熟悉,不要随意用
以XML为主,以annotation辅的方式进行。
我们开发用到的常用注解
- component
- service
- controller
- repository
- autowired
- resource
下面这四个注解
- component 通用组件
- service 业务组件
- controller 控制组件
- repository 数据访问组件
只有语义的区别,功能上来讲。
componentScan
<?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/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置扫描包-->
<context:component-scan base-package="com.sz.service">
</context:component-scan>
</beans>
basePackage能够扫描该包以及其子包。如果不在该包之下, 是无法成功的。
可以配置多个包名在里面
base-package="com.sz.service,com.sz.config"
com.sz.service这个包以及com.sz.config都能生效。
autowired
自动注入
如果有满足条件的多个bean,必须要指定一下。
@Autowired
@Qualifier("userServiceImpl")
private UserSerivce userSerivce;
使用上面的component组件的时候,spring默认取名为简写类名的首字母小写
package com.sz.service.impl;
import com.sz.model.User;
import com.sz.service.UserSerivce;
import org.springframework.stereotype.Service;
@Service("userServiceImpl2")
public class UserServiceImpl2 implements UserSerivce {
@Override
public void login(User user) {
System.out.println("登录222++" + user);
}
}
等同于下面的写法
package com.sz.service.impl;
import com.sz.model.User;
import com.sz.service.UserSerivce;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl2 implements UserSerivce {
@Override
public void login(User user) {
System.out.println("登录222++" + user);
}
}
不用注解等价于
<bean class="com.sz.controller.UserController" id="userController">
<property name="userSerivce" ref="userService2"/>
</bean>
<bean class="com.sz.service.impl.UserServiceImpl2" id="userService2"/>
spel
spring expression language
和EL基本上差不多
#{}
一般是去环境配置里面获取一些数据
案例
<property name="fullName" value="#{environment.getProperty('user.name')}"/>
可以取出系统配置,使用environment?这里为何能使用?
spring除了我们可以注册bean,它自身与预置好一些bean,而environment就是其中一个,所以我们这里就是相当于在使用一个bean对象,
对象偶遇方法getProperty 方法接收一个参数。记住这里的参数要使用单引号。
bean引用其它的数据
<bean class="com.sz.Student" id="student">
<property name="firstName" value="${firstName}"/>
<property name="lastName" value="${lastName}"/>
<!--
采用 ${} 用来引用外部值
#{} 用来计算
{{}}
-->
<property name="fullName" value="#{environment.getProperty('user.name')}"/>
</bean>
<bean class="com.sz.Student" id="student2">
<property name="firstName" value="#{student.firstName+'牛'}"></property>
<property name="lastName" value="#{student.lastName+'虎'}"></property>
<property name="fullName" value="#{student.fullName.toUpperCase()}"></property>
</bean>
计算
<property name="fullName" value="#{8 * 9 + 5 -18}"></property>
随机值使用
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>
日期案例
<property name="birth" value="#{T (java.time.LocalDate).now()}"></property>
AOP
面向切面编程,面向方面编程。
- annotation
- 注解
- 注释
- comment 计算机里面 注释
- comment annotation something
- annotation comment someting
rod 重点探讨。
OOP语言,解决了面向过程的痛苦。但是随着应用进一步扩大,发现了很多弊端,使用OOP无法解决这个问题。
- 日志
- 事务
- 安全控制
- 权限管理
如果使用OOP来解决,但是非常笨重。
如果使用OOP的方案,很容易让 日志功能遍布在系统的各个角落。
- 和我们的聚焦的业务关注点大量混淆
- 日志实现此时已经和所有的方法糅杂在了一起
- 如果我们日志方案要替换,牵一发动全身
以前编程是一种纵向思维,顺着你的业务流程自然的展开的。
90度转弯,横向思维。这些点连在一起是不是就是一个面。
这个面,我们就对它编程
面向面编程。
切面。
- 刀(java类 方法)
- 面
- 点
- 类里面方法
- 点
AOP概念
AOP这个技术主要使用代理技术完成。
代理技术:
-
jdk本身代理支持
-
CGLIB支持,字节码增强技术
-
源于1996年
-
Aspect 切面
-
Join point 连接点 (User login() logout()) 方法的执行
-
Advice 建议,通知(你要干嘛)
-
Pointcut 切入点 描述如何去匹配连接点( 切入表达式描述)
-
Weaving 织入 行为称之为织入
AOP实战
通知
- 前置
- 后置
- 返回值
- 返回异常
- 环绕(最强) 用的最少
表达式
execution(public * com.sz.service.UserService.show()
execution括号里面的内容
访问修饰符+返回值类型+包+类+方法名+参数+异常声明
execution(public void com.sz.service.UserService.show())
是public的并且方法返回值类型是void,在com.sz这个包下service包下的UserService的类里面的show的方法,并且是无参,而且没有异常的声明的方法
execution(public void com.sz.service.*.show())
是public的并且方法返回值类型是void,在com.sz这个包下service包下的任意的类里面的show的方法,并且是无参,而且没有异常的声明的方法
希望描述有参数的呢?
execution(public void com.sz.service.*.show(java.lang.String))
是public的并且方法返回值类型是void,在com.sz这个包下service包下的任意的类里面的show的方法,并且是一个参数类型是String的,而且没有异常的声明的方法
如果有多个参数,请使用逗号分隔。
我希望切com.sz.service下面的任意包下的任意的子包下面的任意类的任意方法都要可以切到。
execution(public * com.sz.service..*.*(..))
public的任意返回值的,com.sz.service包下以及所有子包的任意类的任意方法,切参数任意的方法
通知
如果我们的一开始使用的日志方案是log4j,log4j2 slf4j方案。
前置通知
…
后置通知
<!-- 切面具体配置-->
<aop:config>
<!-- 切面配置 用到一个具体的bean-->
<aop:aspect ref="logAdvice">
<!-- method 是用上面的logAdvice里面的哪个方法,
刀已经在手,切谁? 描述一下,pointcut描述如何切,这里需要一个表达式
execution 是execute 执行
-->
<aop:after method="log" pointcut="execution(public * com.sz.service..*.*(..))"></aop:after>
</aop:aspect>
</aop:config>
前置通知–>方法–>后置通知
返回值后通知
测试返回值类型为void的情况
看到你第一眼
我的理智
冲入脑海
直觉蔓延全身
前置通知–>方法–>后置通知–>返回后置通知
这边返回值不受影响,
只有有返回值的方法才有返回后的处理。这是错误的理解
异常之后通知
只有真的有异常才会去处理。如果没异常这段逻辑不执行。
这个通知一旦执行导致方法没有return掉,所以afterReturning就没有了。
环绕通知
around,有所有的生杀大权。
其它通知活得好好的,但是方法执行体不见了,去哪了。
package com.sz.aop;
import org.aspectj.lang.ProceedingJoinPoint;
/*
* 日志建议
*/
public class LogAdvice {
// 实际开发的时候,你要做的是,就是在这里添加你切面逻辑。
public void before(){
System.out.println("看到你第一眼");
}
public void after(){
System.out.println("直觉蔓延全身");
}
public void afterReturning(){
System.out.println("冲入脑海");
}
public void afterThrowing(){
System.out.println("这是我等的人");
}
// 注入一个参数, 处理连接点
public Object around(ProceedingJoinPoint pjp){
try {
System.out.println(pjp.getSignature().getName()+"================");
System.out.println(pjp.getSignature().getModifiers()+"================");
System.out.println(pjp.getSignature().getDeclaringTypeName()+"================");
Object result = pjp.proceed();
// 中间操作可以很多。
return result.toString()+1;
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
}
这个方法很强,但是用的很少。
来源:https://blog.csdn.net/weixin_44129498/article/details/99314801