优点
Spring是一个开源的免费的框架(容器) !
Spring是一个轻量级的,非入侵式的框架
控制反转(IOC), 面向切面编程(AOP)!
支持事务的处理,对框架整合的支持
总结:Spring就是一个轻量级的控制反转(IOC) 和 面向切面编程(AOP)的框架!
组成
拓展
在Spring的官网有这个介绍: 现代化的Java开发! 说白就是基于Spring的开发!
Spring Boot
一个快速开发的脚手架
基于Spring Boot可以快速的开发单个微服务
约定大于配置!
Spring Cloud
SpringCloud 是基于SpringBoot实现的
因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全
掌握Spring及SpringMVC! 承上启下的作用!
弊端:发展了太久之后,违背了原来的理念! 配置十分繁琐,人称: " 配置地狱! "
IOC理论推导
1. UserDao 接口
2. UserDaoImpl 实现类
3. UserService 业务接口
4. UserServiceImpl 业务实现类
在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改源代码! 如果程序代码量十分大,修改一次的成本代价十分昂贵!
我们使用一个Set接口实现 已经发生了革命性的变化!
之前,程序是主动创建对象! 控制权在程序员手上!
使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象!
这种思想,从本质上解决了问题,我们程序员不用再去管理对象的创建了。系统耦合性大大降低,可以更加专注的在业务的实现上! 这是IOC的原型!
IOC本质
控制反转IOC(Inversion of Control), 是一种设计思想, DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IOC的另一种说法,没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把
两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解) 并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection,DI)。
IOC创建对象的方式
1.使用无参构造创建对象,默认!
2.假设我们要使用有参构造创建对象。
①下标赋值
<!--第一种,下标赋值!--> <bean id="user" class="com.gl.pojo.User"> <constructor-arg index="0" value="小红"></constructor-arg> </bean>
②类型
<!--第二种方式:通过类型创建,不建议使用!--> <bean id="user" class="com.gl.pojo.User"> <constructor-arg type="java.lang.String" value="小黑"></constructor-arg> </bean>
③参数名
<!--第三种,直接通过参数名来设置--> <bean id="user" class="com.gl.pojo.User"> <constructor-arg name="name" value="小白"></constructor-arg> </bean>
总结: 在配置文件加载的时候,容器中管理的对象就已经初始化了!
Spring配置
别名
<!--别名,如果添加了别名,我们也可以使用别名获取到这个对象--> <alias name="user" alias="newUser"></alias>
Bean的配置
<!-- id: bean 的唯一标识符,也就是相当于我们学的对象名 class : bean对象所对应的全限定名: 包名+ 类名 name : 也是别名,而且name可以同时取多个别名 --> <bean id="userT" class="com.gl.pojo.UserT" name="user2 u2,u3;u4"> <property name="name" value="奥特曼"></property> </bean>
import
这个import,一般用于团队开发使用,他可以将多个配置文件,导入合并为一个
假设,现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!
张三
李四
王五
applicationContext.xml
<import resource="beans.xml"></import> <import resource="beans2.xml"></import> <import resource="beans3.xml"></import>
使用的时候,直接使用总的配置就可以了
依赖注入
set方式注入
依赖注入:Set注入!
依赖: bean对象的创建依赖于容器!
注入: bean对象中的所有属性,由容器来注入!
[环境搭建]
1.复杂类型
public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
2.真实测试对象
public class Student { private String name; private Address address; private String[] books; private List<String> hobby; private Map<String, String> card; private Set<String> games; private String wife; private Properties info;
3.beans.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="student" class="com.gl.pojo.Student"> <!--第一种,普通值注入,value--> <property name="name" value="小明"></property> </bean> </beans>
4.测试类
public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Student student = (Student)context.getBean("student"); System.out.println(student.getAddress()); } }
完善注入信息
<?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="address" class="com.gl.pojo.Address"> <property name="address" value="洛杉矶"></property> </bean> <bean id="student" class="com.gl.pojo.Student"> <!--第一种,普通值注入,value--> <property name="name" value="小明"></property> <!--第二种,Bean注入,ref--> <property name="address" ref="address"></property> <!--数组注入--> <property name="books"> <array> <value>七龙珠</value> <value>海贼王</value> <value>火影忍者</value> <value>名侦探柯南</value> </array> </property> <!--list--> <property name="hobby"> <list> <value>听歌</value> <value>看电影</value> <value>打篮球</value> </list> </property> <!--Map--> <property name="card"> <map> <entry key="身份证" value="12345678913246456"></entry> <entry key="银行卡" value="78946134674613461"></entry> </map> </property> <!--Set--> <property name="games"> <set> <value>游戏1</value> <value>游戏2</value> <value>游戏3</value> </set> </property> <!--null--> <property name="wife"> <null/> </property> <!--Properties--> <property name="info"> <props> <prop key="学号">20212112</prop> <prop key="性别">男</prop> <prop key="姓名">小吴</prop> </props> </property> </bean> </beans>
拓展方式注入
我们可以使用p空间命名和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:p="http://www.springframework.org/schema/p" 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"> <!--p命名空间注入,可以直接注入属性的值:property--> <bean id="user" class="com.gl.pojo.User" p:name="笑笑" p:age="18"> </bean> <!--c命名空间注入,通过构造器注入:constructor-arg--> <bean id="user2" class="com.gl.pojo.User" c:name="小卡" c:age="19"></bean> </beans>
测试:
@Test public void test2() { ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml"); User user = context.getBean("user2", User.class); System.out.println(user); }
注意点: p命名空间和c命名空间不能直接使用,需要导入xml约束!
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
bean的作用域
1.单例模式 (Spring默认机制)
<bean id="user2" class="com.gl.pojo.User" c:name="小卡" c:age="19" scope="singleton"></bean>
2.原型模式: 每次从容器get的时候,都会产生一个新对象!
<bean id="user2" class="com.gl.pojo.User" c:name="小卡" c:age="19" scope="prototype"></bean>
3.其余的request , session , application, 这些个只能在web开发中使用到!
Bean的自动装配
自动装配是Spring满足bean依赖一种方式!
Spring会在上下文中自动寻找,并自动给bean装配属性!
在Spring中有三种装配的方式
1.在XML中显示的配置
2.在java中显示配置
3.隐式的自动装配bean
测试
1.环境搭建
一个人有两个宠物!
ByName自动装配
<!-- byName: 会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid! --> <bean id="people" class="com.gl.pojo.People" autowire="byName"> <property name="name" value="小可爱"></property> </bean>
ByType自动装配
<bean class="com.gl.pojo.Cat"></bean> <bean class="com.gl.pojo.Dog"></bean> <!-- byName: 会自动在容器上下文中查找,和自己对象set方法后面的值对应的 beanid! byType: 会自动在容器上下文中查找,和自己对象属性类型相同的 beanid! --> <bean id="people" class="com.gl.pojo.People" autowire="byType"> <property name="name" value="小可爱"></property> </bean>
小结:
byName的时候,需要保证所有的bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致!
byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!
使用注解实现自动装配
jdk1.5支持的注解,spring2.5就支持注解了!
The introduction of annotation-based configuration raised the question of whether this approach is "better"than XML.
要使用注解须知:
1.导入约束: context约束
2.配置注解的支持: <context:annotation-config/>
<?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 http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> </beans>
@Autowired
直接在属性上使用即可! 也可以在set方法上使用!
使用Autowired我们可以不用编写Set方法了,前提是你这个自动装配的属性在IOC(Spring)
容器中存在,且符合名字byName!
拓展:
@Nullable 字段标记了这个注解,说明这个字段可以为null
public @interface Autowired { boolean required() default true; }
测试代码
public class People { //如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空 @Autowired(required = false) private Cat cat; @Autowired private Dog dog; private String name;
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解@Autowired完成的时候,我们可以使用@Qualifier(value = "xxx")去配置@Autowired的使用,指定一个唯一的
bean对象注入!
public class People { //如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空 @Autowired @Qualifier(value = "cataa") private Cat cat; @Autowired @Qualifier(value = "dogaa") private Dog dog; private String name;
@Resource注解
public class People { //如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空 @Resource(name="cat2") private Cat cat; @Resource private Dog dog; private String name;
小结:
@Resource和@Autowired的区别:
都是用来自动装配的,都可以放在属性字段上
@Autowired 通过byType的方式实现 , 而且必须要求这个对象存在!
@Resource默认通过byName的方式实现,如果找不到名字,则通过byType实现!
如果两个都找不到的情况下,就报错!
执行顺序不同: @Autowired 通过byType的方式实现。@Resource默认通过byName的方式实现
使用注解开发
在Spring4之后,要使用注解开发,必须要保证aop的包导入了
使用注解需要导入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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> </beans>
1.bean
//等价于 <bean id="user" class="com.gl.pojo.User"></bean> // @Component 组件 @Component public class User {
2.属性如何注入
@Component public class User { public String name; //相当于<property name="name" value="小可爱"></property> @Value("小可爱") public void setName(String name) { this.name = name; } }
3.衍生的注解
@Component 有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!
dao @Repository
service @Service
controller @Controller
这四个注解功能都是一样的,都是代表将这个类注册到Spring中,装配bean
4.自动装配
@Autowired: 自动装配通过类型。名字
如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value = "xxx")
@Nullable 字段标记上了这个注解,说明这个字段可以为null
@Resource : 自动装配通过名字。类型。
5.作用域
@Component @Scope("prototype") public class User { public String name; //相当于<property name="name" value="小可爱"></property> @Value("小可爱") public void setName(String name) { this.name = name; } }
6.总结:
xml 与 注解:
xml更加万能,适用于任何场合! 维护简单方便
注解 不是自己类使用不了, 维护相对复杂!
xml 与 注解最佳使用方式:
xml 用来配置bean;
注解只负责完成属性的注入
我们在使用的过程中,只需要注意一个问题: 必须让注解生效, 就需要开启注解的支持
<!--指定要扫描的包,这个包下的注解就会生效--> <context:component-scan base-package="com.gl"></context:component-scan> <context:annotation-config/>
使用Java的方式配置Spring
我们现在要完全不使用Spring的xml配置了,全权交给Java来做!
JavaConfig是Spring的一个子项目,在Spring 4之后,它成为了一个核心功能!
实体类
//这个注解的意思,表示这个类被Spring接管了,注册到了容器中 @Component public class User { private String name; public String getName() { return name; } @Value("aaa") //属性值 public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } }
配置文件
package com.gl.config; import com.gl.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration //这个注解代表这个类会被Spring容器托管,注册到容器中,因为这个注解里就是@Component //@Configuration 代表这是一个配置类,和beans.xml是一样的作用 @ComponentScan("com.gl.pojo") @Import(GlConfig2.class) public class GlConfig { //@Bean这个注解作用,相当于之前在beans.xml中写一个bean标签 //这个方法的名字,就相当于bean标签中的id属性 //这个方法的返回值,就相当于bean标签中的class属性 @Bean public User user(){ return new User(); //就是返回要注入到bean的对象! } }
测试类
public class MyTest { public static void main(String[] args) { //如果完全使用了配置类方式去做,我们就只能通过AnnotationConfig 上下文来获取容器,通过配置类的class对象加载! ApplicationContext context = new AnnotationConfigApplicationContext(GlConfig.class); User getUser = (User) context.getBean("user"); System.out.println(getUser.getName()); } }
这种纯Java的配置方式,在SpringBoot中随处可见!
AOP 代理模式
为什么要熟悉代理模式? 因为这就是SpringAOP的底层!
代理模式的分类:
静态代理
动态代理
静态代理
角色分析:
抽象角色 : 一般会使用接口或者抽象类来解决
真实角色 : 被代理的角色
代理角色 : 代理真实角色,代理真实角色后,我们一般会做一些附属操作
客户 : 访问代理对象的人!
代码步骤:
1.接口
//租房 public interface Rent { public void rent(); }
2.真实角色
//房东 public class Host implements Rent{ @Override public void rent() { System.out.println("房东要出租房子!"); } }
3.代理角色
public class Proxy implements Rent { private Host host; public Proxy() { } public Proxy(Host host) { this.host = host; } @Override public void rent() { seeHouse(); host.rent(); hetong(); fare(); } //看房 public void seeHouse() { System.out.println("中介带你看房"); } //签合同 public void hetong() { System.out.println("签租赁合同"); } //收中介费 public void fare() { System.out.println("收中介费"); } }
4.客户端访问代理角色
public class Client { public static void main(String[] args) { //房东要租房子 Host host = new Host(); //代理,中介帮房东租房子,但是代理一般都会有一些附属操作 Proxy proxy = new Proxy(host); //你不用面对房东,直接找中介租房即可! proxy.rent(); } }
代理模式的好处:
可以使真实角色的操作更加纯粹! 不用去关注一些公共的业务
公共交给代理角色! 实现了业务的分工!
公共业务发生扩展的时候,方便集中管理
缺点:
一个真实角色就会产生一个代理角色; 代码量会翻倍-开发效率会变低
AOP思路图
动态代理
动态代理和静态代理角色一样
动态代理的代理类是动态生成的,不是写好的
动态代理分为两大类 : 基于接口的动态代理,基于类的动态代理
基于接口----JDK动态代理
基于类 : cglib
java字节码实现 : javasist
需要了解两个类 : Proxy: 代理 ,InvocationHandler : 调用处理程序
动态代理的好处:
可以使真实角色的操作更加纯粹! 不用去关注一些公共的业务
公共交给代理角色! 实现了业务的分工!
公共业务发生扩展的时候,方便集中管理!
一个动态代理类代理的是一个接口,一般就是对应的一类业务
一个动态代理类可以代理多个类,只要是实现了同一个接口即可!
AOP
AOP概念:
AOP (Aspect Oriented Programming) 意为: 面向切面编程, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续, 是软件开发中的一个热点,
也是Spring框架中的一个重要内容, 是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP在Spring中的作用
提供声明式事务; 允许用户自定义切面
横切关注点: 跨越应用程序多个模块的方法或功能 。 即是, 与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等....
切面 (ASPECT) : 横切关注点 被模块化 的特殊对象。即,它是一个类。
通知 (Advice) : 切面必须要完成的工作。即, 它是类中的一个方法。
目标 (Target) : 被通知对象。
代理 (Proxy) : 向目标对象应用通知之后创建的对象。
切入点 (PointCut) : 切面通知 执行的"地点"的定义。
连接点 (JointPoint) : 与切入点匹配的执行点
SpringAOP中, 通过Advice定义横切逻辑, Spring中支持5种类型的Advice:
即AOP在不改变原有代码的情况下,去增加新的功能
使用Spring实现AOP
导入依赖
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency>
方式一 : 使用Spring的API 接口 (主要SpringAPI接口实现)
方式二 : 自定义来实现AOP (主要是切面定义)
方式三 :使用注解实现!
整合Mybatis
步骤:
1.导入相关jar包junit
mybatis
mysql数据库
spring相关的
aop织入
mybatis-spring
2.编写配置文件
3.测试
回忆mybatis
1.编写实体类
2.编写核心配置文件
3.编写接口
4.编写Mapper.xml
5.测试
Mybatis-spring
1.编写数据源配置
2.sqlSessionFactory
3.sqlSessionTemplate
4.需要给接口加实现类
5.将自己写的实现类,注入到Spring中
6.测试
声明式事务
回顾事务
把一组业务当成一个业务来做; 要么都成功,要么都失败!
事务在项目的开发中,十分的重要,涉及到数据的一致性问题,不能马虎!
确保完整性和一致性;
事务的ACID原则:原子性
一致性
隔离性 多个业务可能操作同一个资源,防止数据损坏
持久性 事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化的写到存储器中!
spring中的事务管理
声明式事务 : AOP
编程式事务 : 需要在代码中,进行事务的管理
思考:
为什么需要事务?如果不配置事务,可能存在数据提交不一致的情况
如果我们不在spring中去配置声明式事务,我们就需要在代码中手动配置事务!
事务在项目的开发中十分重要,设计到数据的一致性和完整性问题,不容马虎!