Spring
Spring—春天 给软件行业带来了春天。
Spring理念----使现有技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PTjDQvkC-1625320588383)(C:\Users\ZYH.LAPTOP-32L7159T\AppData\Roaming\Typora\typora-user-images\image-20210701205324111.png)]
导入的包
优点:控制反转(IOC),面向切面编程(AOP),支持事务的处理,对框架整合的支持
- 总结一句话:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xoN41Ctp-1625320588387)(C:\Users\ZYH.LAPTOP-32L7159T\AppData\Roaming\Typora\typora-user-images\image-20210701205754505.png)]
Spring 7大模块
IOC理论推导
之前我们实现一个功能的大致步骤
1.UserDao接口
2.UserDaoImpl实现类
3.UserService业务接口
4.UserServiceImpl业务实现类
在我们之前的业务中,用户的需求会影响我们原来的代码,我们需要根据需求去修改源代码!
看spring-01-ioc1模块即可
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dJjWZbX4-1625320588389)(C:\Users\ZYH.LAPTOP-32L7159T\AppData\Roaming\Typora\typora-user-images\image-20210701215614555.png)]
首先在之前的方法中,我们修改对象时需要在UserServiceImpl.class中进行修改,使用
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
之后,我们修改输出,只需要在测试代码中进行修改,不需要改变源代码。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转是IoC容器,其实现方式是依赖注入(Dependency Injection ,DI)
第一个Spring程序
创建项目spring-study,删除src,创建模块spring-02-helloword。
导入依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.7</version>
</dependency>
</dependencies>
配置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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 使用Spring创建对象,在Spring中这些都称为Bean
bean=对象 new Hello()
类型 变量名 =new Hello();
id = 变量名
class = new 的对象
property 相当于给对象中的属性设置值
-->
<bean id="hello" class="com.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
</beans>
在pojo层创建Hello.class。
package com.pojo;
public class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Hello{" +
"name='" + name + '\'' +
'}';
}
public void show(){
System.out.println("Hello"+name);
}
}
注意:set方法一定要有,有set方法注入Spring。
在配置文件中注入
<bean id="hello" class="com.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
测试
public static void main(String[] args) {
//获取Spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//现在我们的对象都在Spring中管理了,我们要使用,直接取出即可
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
getBean( )中的参数是我们在配置文件中的id。
就这样我们的第一个Spring程序就完成了,控制反转就是我们不是主动创建对象,而是被动接收对象,主要是通过Spring的配置文件进行操作。所谓的IoC就是:对象由Spring来创建管理装配!!!!
IoC创建对象方式
1.使用无参构造创建对象,默认!
<bean id="hello" class="com.pojo.User">
<property name="name" value="zyh"/>
</bean>
2.使用有参构造创建对象
第一种下标赋值
<!-- 1. 下标赋值-->
<bean id="user" class="com.pojo.User">
<constructor-arg index="0" value="zyh"/>
</bean>
第二种方式(不推荐使用)
<!-- 2. 类型赋值-->
<bean id="user" class="com.pojo.User">
<constructor-arg type="java.lang.String" value="zyh"/>
</bean>
<!-- 不建议使用-->
当有参构造传参有2个String类型时
第三种方式(推荐使用)
<bean id="user" class="com.pojo.User"> <constructor-arg name="name" value="zyh"/> </bean>
Spring配置
别名alias(一般不需要,有name属性)
如果添加了别名,我们也可以使用别名来代替原名!!
<alias name="user" alias="userAlias"/>
Bean的配置
<!--
id:bean的唯一标识符,也就是相当于我们学的对象名
class:bean对象所对应的全限命名:包名+类型
name:也是别名而且name可以同时取多个别名,alias只能一对一,此外可以用,空格 ;进行分割
-->
<bean id="userT" class="com.pojo.UserT" name="user2,u2">
<property name="name" value="zyh"/>
</bean>
import
这个import,一般用于团队开发使用,他可以将多个配置文件,导入合并成为一个
假设现在项目中有多个人开发,每个人负责不同的类开发,不同的类需要注册不用的bean中,我们可以利用import将所有人的beans.xml合并为一个总的
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
我们在applicationContext.xml中加入上述import,在应用中,我们
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
即可。
依赖注入
构造器注入
已经说了可以看前面的笔记
constructor-arg、
<bean id="user" class="com.pojo.User"> <constructor-arg name="name" value="zyh"/></bean>
Set方式注入【重点】
依赖注入:Set注入
依赖:bean对象的创建依赖于容器
注入:bean对象中的所有属性,由容器来注入
【环境搭建】
1.复杂类型
2.真实测试对象
<bean id="address" class="com.pojo.Address"> <property name="address" value="whut"/> </bean> <bean id="student" class="com.pojo.Student"> <!--普通注入 value--> <property name="name" value="zyh"/> <!--bean注入ref--> <property name="address" ref="address"/> <!--数组注入--> <property name="books"> <array> <value>红楼梦</value> <value>西游记</value> <value>水浒传</value> <value>三国演义</value> </array> </property> <!--List注入--> <property name="hobbies"> <list> <value>吃饭</value> <value>睡觉</value> <value>打豆豆</value> </list> </property> <!--map注入--> <property name="card"> <map> <entry key="IDcard" value="123456"/> <entry key="yhcard" value="111111"/> </map> </property> <!--set注入--> <property name="games"> <set> <value>LOL</value> <value>COC</value> </set> </property> <!--null注入--> <property name="wife"> <null/> </property> <!--property注入--> <property name="info"> <props> <prop key="number">123456</prop> <prop key="sex">nan</prop> <prop key="age">22</prop> </props> </property> </bean>
拓展方式注入
p命名空间
p即property
步骤:
首先在applicationContext.xml中加入
xmlns:p="http://www.springframework.org/schema/p"
然后我们就可以使用bean注入
<bean id="user" class="com.pojo.User" p:name="zyh" p:age="22"/>
可以看到我们直接使用了p:name=“zyh” p:age=“22”
测试
@Test
public void userTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("userBeans.xml");
User user = context.getBean("user",User.class);
System.out.println(user);
}
c命名空间
c即constructor-arg,即要有有参构造函数
首先在applicationContext.xml中加入
xmlns:c="http://www.springframework.org/schema/c"
然后我们就可以使用bean注入
<bean id="user2" class="com.pojo.User" c:age="22" c:name="zyh"/>
测试
@Test
public void userTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("userBeans.xml");
User user = context.getBean("user2",User.class);
System.out.println(user);
}
注意p命名和c命名不能直接使用,需要导入命名空间
bean的作用域
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KzKbQHfD-1625320588395)(D:\study\java_studynotebook\image-20210702204019440.png)]
单例模式(默认)singleton:
<bean id="user2" class="com.pojo.User" c:age="22" c:name="zyh" scope="singleton"/>
原型模式:每次从容器中getBean()的对象不同 prototype
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
bean的自动装配
自动装配是Spring满足bean依赖的一种方式
Spring会在上下文中自动寻找,并自动给bean装配属性。
在Spring中有三种自动装配的方式:
1.在xml中显示的配置
2.在Java中显示配置
3.隐式的自动装配bean【重要!!!.】
测试
ByName自动装配
<!--
byname:会自动在容器的上下文中查找,有没有和自己对象set方法后面的值对应的beanid!
-->
<bean id="people" class="com.pojo.People" autowire="byName">
<property name="name" value="zyh"/>
</bean>
ByType自动装配
<!--
bytype:需要保证类型唯一,类型保持一致
-->
<bean id="people" class="com.pojo.People" autowire="byType">
<property name="name" value="zyh"/>
</bean>
bean自动装配时,我们只要保证我们的代码规范,用ByName自动装配就特别方便。
使用自动注解装配
jdk1.5支持的注解,Spring2.5就支持注解了!
需要在支持中加:
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
@Autowired
@Autowired
private Cat cat;
@Autowired
private Dog dog;
在属性上面加上@Autowired
那么我们在beans.xml中只需要
<bean id="cat" class="com.pojo.Cat"/>
<bean id="dog" class="com.pojo.Dog"/>
<bean id="people" class="com.pojo.People"/>
此外,这种方法后可以不用编写Set方法,前提是你这个自动装配的属性在IoC容器中存在且符合名字ByName!!!
科普:
@Nullable 字段标记了这个注解,说明这个字段可以为Null;
@Autowired(required = false)说明这个对象可以为null,用得很少,默认为true
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier(value=“xxx对象名”),指定一个唯一的bean对象
@Autowired是通过ByType自动装配的,如果该类型的对象不止一个,则使用@Qualifier(value=“xxx对象名”)。
使用注解开发
1.bean
//等价于 <bean id="user" class="com.pojo.User"/>
//@Component组件
@Component
public class User {
@Value("zyh")
//相当于<property name = "name" value = "zyh"/>
public String name;
public void setName(String name) {
this.name = name;
}
}
2.属性注入
@Value("zyh")
//相当于<property name = "name" value = "zyh"/>
public String name;
public void setName(String name) {
this.name = name;
}
也可以
public String name;
@Value("zyh")
public void setName(String name) {
this.name = name;
}
属性注入可以在参数值上设置,也可以在set方法上进行注入
3.衍生的注解
@Component有几个衍生的注解,我们在web开发中,会按照mvc三层构架分层
dao层-----【@Repository】
service层------【@Service】
controller层------【@Controller】
这四个注解功能一样,将某个类注册到Spring容器中,装配Bean。
4.自动装配注解
@Autowired
5.作用域
@Scope(“singleton”)
注解开发特别重要!!!使用注解之前一定要开启注解
6.使用java方式配置Spring
我们现在要完全不使用Spring的xml配置,全权交给java来做!
package com.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class User {
@Value("zyh")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
配置类
package com.config;
import com.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
@Bean
public User getUser(){
return new User();
}
}
这种纯java的配置方式,在Springboot种随处可见。
代理模式
为什么要学习代理模式?因为这就是SpringAOP的底层【SpringAOP和SpringMVC】
代理模式的分类:
静态代理
动态代理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yVYPReAe-1625320588398)(C:\Users\ZYH.LAPTOP-32L7159T\AppData\Roaming\Typora\typora-user-images\image-20210703104954521.png)]
代理模式类似于中介
静态代理
角色分析:
抽象角色:一般会使用接口或抽象类来解决
真实角色:被代理的角色
代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
客户:访问代理对象的人!
例如租房、: 房子是中介找到的,客户找不到房东,这个时候中介就是一个代理角色。
代理模式的好处:
可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
公共的业务交给代理角色,实现了业务的分工
公共业务发生拓展时,方便集中管理!
缺点:
一个真实角色就会产生一个代理角色,代码量翻倍,开发效率变低。
加深理解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iK0YOcn6-1625320588400)(C:\Users\ZYH.LAPTOP-32L7159T\AppData\Roaming\Typora\typora-user-images\image-20210703140216406.png)]
不改变原有的代码,加一个代理,一定程度上降低了代码的耦合!!!
动态代理
动态代理和静态代理角色一样。
动态代理的代理类是动态生成的,不是我们直接写好的
动态代理分为两大类:基于接口的动态代理;基于类的动态代理
基于接口—JDK动态代理
基于类—cglib
Java字节码实现----javasist
需要了解两个类:Proxy:代理,InvocationHandler:调用处理程序
可以封装成为工具类
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(rent, args);
return invoke;
}
测试
//真实角色
Host host = new Host();
//代理角色
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象!
pih.setRent(host);
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
AOP
AOP,意为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
提供声明式事务,允许用户自定义切面。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pYjrCarZ-1625320588401)(C:\Users\ZYH.LAPTOP-32L7159T\AppData\Roaming\Typora\typora-user-images\image-20210703153110978.png)]
方式一:使用Spring的API接口
<bean id="userService" class="com.service.UserServiceImpl"/>
<bean id="log" class="com.log.Log"/>
<bean id="afterlog" class="com.log.AfterLog"/>
<!--配置AOP-->
<aop:config>
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
</aop:config>
方式二:使用自定义类来实现AOP
<!--方式二:自定义类-->
<bean id="diy" class="com.diy.DiyPointCut"/>
<aop:config>
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
方式三:使用注解实现!
<!--方式三-->
<bean id="annotationPointCut" class="com.diy.AnnotationPointCut"/>
<!--开启注解-->
<aop:aspectj-autoproxy/>
@Before("execution(* com.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("方法执行前");
}
//在环绕增强中我们给定一个参数,代表我们要获取处理切入的点
@Around("execution(* com.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕增强");
joinPoint.getSignature();
Object proceed = joinPoint.proceed();
}