Spring5 学习笔记
1、Spring
1.1、简介
Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
-
目的:解决企业应用开发的复杂性
-
功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
-
范围:任何Java应用
Spring是一个轻量级 控制反转(IoC)和面向切面(AOP)的容器 框架。
- SSH:Struct2 + Spring + Hibernate
- SSM:SpringMVC + Spring + Mybatis
官网: https://spring.io/projects/spring-framework
官方下载地址: http://repo.spring.io/release/org/springframework/spring
GitHub地址:https://github.com/spring-projects/spring-framework
Maven 依赖
<!--spring-webmvc 依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.4</version>
</dependency>
<!-- spring-jdbc 依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.4</version>
</dependency>
1.2、优点
- Spring是开源免费的框架(容器)
- Spring是一个轻量级、非入侵式的框架
- 核心是 控制反转(IOC),面向切面编程(AOP)
- 支持事务处理,对框架的整合的支持!
总结:Spring就是一个轻量级的控制反转(IOC),面向切面编程(AOP)的框架!
1.3、组成
1.4、拓展
Spring的官网介绍:现代化Java开发
-
Spring Boot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速的开发单个微服务
- 约定大于配置
-
Spring Cloud
- SpringCloud 是基于SpringBoot实现的。
大多数公司都在使用Spring Boot进行快速开发
学习SpringBoot的前提,需要为完全掌握Spring及其SpringMVC
缺点:发展太久,配置太多,人称 “配置地狱”
2、IOC理论推导
1、以前的设计模式:
- 实体类接口 UserDao 接口
- 实体类接口实现类 UserDaoImpl 类
- 业务层实现接口 UserServive 接口
- 业务层实现类 UserServiceImpl 类
package com.wyx.Dao;
public interface UserDao {
void getUser();
}
package com.wyx.Dao;
public class UserDaoImpl implements UserDao{
public void getUser() {
System.out.println("默认用户获取数据!");
}
}
package com.wyx.service;
public interface UserService {
void getUser();
}
package com.wyx.service;
import com.wyx.Dao.UserDao;
import com.wyx.Dao.UserDaoImpl;
public class UserServiceImpl implements UserService{
private UserDao userDao = new UserDaoImpl();
public void getUser() {
userDao.getUser();
}
}
@Test
public void test(){
UserServiceImpl userService = new UserServiceImpl();
userService.getUser();
}
2、问题提出
-
当实体类接口的实现类增加时
-
用户想要修改调用新的实体类接口时,就不得不修改
UserServiceImpl
实现类的代码。package com.wyx.Dao; //新增的接口实现类 public class UserMysqlImpl implements UserDao{ public void getUser() { System.out.println("Mysql用户获取数据!"); } }
package com.wyx.service; import com.wyx.Dao.UserDao; import com.wyx.Dao.UserMysqlImpl; public class UserServiceImpl implements UserService{ //修改需要新实现的接口,如果接口过多,且用户需要过多,会大量增加程序员的负担 private UserDao userMysql = new UserMysqlImpl(); public void getUser() { userMysql.getUser(); } }
3、问题解决
-
在
UserServiceImpl
实现类中使用set
注入来解决,重复修改代码的方法 -
在
UserServiceImpl
业务层,需要什么接口的实现类,就可以调用什么接口的实现类package com.wyx.service; import com.wyx.Dao.UserDao; public class UserServiceImpl implements UserService{ //核心思想,将类设计成变量,利用 set 注入,就可以减少程序员的工作量这就是 IOC理论 控制反转 private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void getUser() { userDao.getUser(); } }
@Test public void test(){ UserServiceImpl userService = new UserServiceImpl(); userService.setUserDao(new UserSqlserverImpl()); userService.getUser(); }
-
之前,程序是主动创建对象,控制权在程序员手上
-
使用
set
注入后,程序不再具有主动性,而是变成了被动接受对象!
IOC
思想,从本质上解决了问题,程序员不用再去管理对象,系统耦合度大大降低。这就是 IOC
原型
4、IOC 本质
IOC
本质就是解耦
因为程序之间的耦合性很高,调用复杂,使用解耦能让程序更加易于理解
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
3、第一个Spring
1、Hello Spring
-
导入依赖
-
创建实体类
核心是用
set
注入,实体类必须要有set
方法 -
编写类的配置文件
-
测试
package com.wyx;
//实体类
public class Hello {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Hello{" +
"name='" + name + '\'' +
'}';
}
}
创建 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">
<!--使用Spring来创建对象,在Spring中这些都称为Bean
bean 相当于对象
类型 变量名 = new 类型();
Hello hello = new Hello();
id = 变量名
class = new的对象
property 相当于给对象中的属性设置一个值!
-->
<bean id="hello" class="com.wyx.Hello">
<property name="name" value="Spring"/>
</bean>
</beans>
编写测试类
@Test
public void test() {
//获取Spring的上下文对象!
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//我们的对象现在都在Spring中的管理了,我们需要使用,直接去里面取出来就可以!
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
2、对 IOC
推导的实体类修改
编写 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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--这里就是类的注册,
id 是变量名 , class 表示要 new 的类的全路径
property 表示类中的参数 后面接的 ref 或者 value 表示传入参数的值
ref:表示已经注册过的类 bean id
value:表示值,基本引用类型的值
-->
<bean id="userDaoImpl" class="com.wyx.Dao.UserDaoImpl">
</bean>
<bean id="userMysqlImpl" class="com.wyx.Dao.UserMysqlImpl">
</bean>
<!--
使用 property 一定要有 set 方法 不然必出错
这里我们以后如果需要改变实现类, 只需要将 ref 的值换成 自己需要的实现类即可
-->
<bean id="userServiceImpl" class="com.wyx.service.UserServiceImpl">
<property name="userDao" ref="userMysqlImpl"/>
</bean>
</beans>
编写测试类
@Test
public void test(){
// 获取操作类的 容器(框架)
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 获取要操作的类的 变量
UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("userServiceImpl");
// 执行方法
userServiceImpl.getUser();
}
4、IOC 创建对象的方式
1、Spring 无参构造
Spring
构建时,默认走无参构造.- 当无参构造被有参构造,消除时,我们需要用到下面的 3 种(有参构造方式)方式,进行配置 类的加载文件
beans.xml
2、Spring 有参构造
- 下标构造
<!--使用 下标方式注入参数
重 0 下标索引开始
-->
<bean id="user" class="com.wyx.pojo.User">
<constructor-arg index="0" value="王玉星"/>
</bean>
- 类型匹配注入 (不推荐,因为当有两个类型相同的属性时,会出错)
<!--使用类型注入,不推荐使用,类型相同时会出错-->
<bean id="user" class="com.wyx.pojo.User">
<constructor-arg type="java.lang.String" value="王玉星"/>
</bean>
- 属性名注入方式(推荐使用)
<!--属性名方式注入,推荐使用-->
<bean id="user" class="com.wyx.pojo.User">
<constructor-arg name="name" value="王玉星"/>
</bean>
5、Spring 配置
1、别名
<!--别名,如果添加了别名,我们可以使用别来获取对象 不常用,因为 bean的配置也可以取别名-->
<alias name="user" alias="user2"/>
2、Bean 配置
<!--
id:bean 的唯一标识,也就是我们利用 ApplicationContext 取的对象名,
class:bean 对象所对应的全限定类名:包名+类型
name:也就是别名,而且name 可以取多个,中间可以用以下使用的符号分割
-->
<bean id="user" class="com.wyx.pojo.User" name="user2 user3,user4;user5">
<constructor-arg name="name" value="王玉星"/>
</bean>
3、import
一般它用于团队开发,他可以将多个配置文件合并为一个 内容相同会自动优化
<!--
一般项目时的所有人写的beans.xml都需要导入一个名叫 applicationContext.xml 的配置文件,
import 就是将其他人写的配置文件导入进来
-->
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans1.xml"/>
6、依赖注入
1、构造器注入(上面已经写了)
2、Set注入【重点】
- 依赖注入:Set注入!
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入
【环境搭建】
-
复杂类型
package com.wyx.pojo; public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
package com.wyx.pojo; import java.util.*; public class Student { private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String,String> card; private Set<String> games; private String wife; private Properties info; public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public String[] getBooks() { return books; } public void setBooks(String[] books) { this.books = books; } public List<String> getHobbys() { return hobbys; } public void setHobbys(List<String> hobbys) { this.hobbys = hobbys; } public Map<String, String> getCard() { return card; } public void setCard(Map<String, String> card) { this.card = card; } public Set<String> getGames() { return games; } public void setGames(Set<String> games) { this.games = games; } public String getWife() { return wife; } public void setWife(String wife) { this.wife = wife; } public Properties getInfo() { return info; } public void setInfo(Properties info) { this.info = info; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", address=" + address + ", books=" + Arrays.toString(books) + ", hobbys=" + hobbys + ", card=" + card + ", games=" + games + ", wife='" + wife + '\'' + ", info=" + info + '}'; } }
-
applicationContext
编写<?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"> <bean id="student" class="com.wyx.pojo.Student"> <property name="name" value="王玉星"/> </bean> </beans>
-
真实测试对象
import com.wyx.pojo.Student; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest1{ public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = context.getBean("student",Student.class); System.out.println(student.getName()); } }
不同对象注入方式
<?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">
<bean id="address" class="com.wyx.pojo.Address">
<property name="address" value="贵州省贵阳市"/>
</bean>
<bean id="student" class="com.wyx.pojo.Student">
<!-- 普通值注入 value -->
<property name="name" value="王玉星"/>
<!-- bean注入 注入自定义类型 ref -->
<property name="address" ref="address"/>
<!-- 数组注入-->
<property name="books">
<array>
<value>语文书</value>
<value>数学书</value>
<value>英语书</value>
</array>
</property>
<!-- List注入-->
<property name="hobbys">
<list>
<value>踢足球</value>
<value>篮球</value>
<value>码农</value>
</list>
</property>
<!-- Map注入-->
<property name="card">
<map>
<entry key="身份证" value="520156464631"/>
<entry key="银行卡" value="5641313541311"/>
<entry key="学号" value="180524505"/>
</map>
</property>
<!-- Set注入-->
<property name="games">
<set>
<value>CF</value>
<value>LOL</value>
</set>
</property>
<!-- 空值注入-->
<property name="wife">
<null/>
</property>
<!-- Properties 注入-->
<property name="info">
<props>
<prop key="driver">54134631</prop>
<prop key="url">是否</prop>
<prop key="username">root</prop>
<prop key="password">970699</prop>
</props>
</property>
</bean>
</beans>
3、拓展方式
我们可以使用 p
命名空间和 c
命名空间进行注入
使用前需要先导入: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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- xmlns:p="http://www.springframework.org/schema/p"
在上面添加 这一行代码可以使用 p 命名空间 注入 如下
-->
<bean id="user1" class="com.wyx.pojo.User" p:name="王玉星" p:age="18" p:sex="男"/>
<!-- xmlns:c="http://www.springframework.org/schema/c"
使用 c 命名空间注入要求必须要有构造方法
在上面添加 这一行代码可以使用 c 命名空间 注入 如下
-->
<bean id="user2" class="com.wyx.pojo.User" c:name="小商贩" c:age="19" c:sex="男"/>
</beans>
4、bean的作用域
-
单例模式(Spring 默认机制)初始new 一次,后面都不new 快速 线程不安全
<bean id="user1" class="com.wyx.pojo.User" scope="singleton"/>
-
原型模式 : 每次从容器取的时候,都会产生一个新的对象,每次都 NEW 浪费资源,线程安全
<bean id="user1" class="com.wyx.pojo.User" scope="prototype"/>
-
其余的 request,application,websocket,session 这些只会在web开发中使用
7、Bean 的自动装配
- 自动装配是
Spring
满足Bean
依赖的一种方式! Spring
会在上下文中寻找,并给Bean
自动装配属性!
在 Spring
中的三种装配的方式
- 在
xml
中显示的装配 - 在
Java
中显示装配 - 隐式的自动装配
Bean
【重点】
1、测试
环境搭建:一个人两个宠物
package com.wyx.pojo;
public class Cat {
public void shout(){
System.out.println("miao!!");
}
}
package com.wyx.pojo;
public class Dog {
public void shout(){
System.out.println("wan!!");
}
}
package com.wyx.pojo;
public class People {
private Cat cat;
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
<?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">
<bean id="cat" class="com.wyx.pojo.Cat"/>
<bean id="dog" class="com.wyx.pojo.Dog"/>
<bean id="people" class="com.wyx.pojo.People">
<property name="name" value="小红安"/>
<property name="dog" value="dog"/>
<property name="cat" value="cat"/>
</bean>
</beans>
环境测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
People people = context.getBean("people", People.class);
people.getDog().shout();
people.getCat().shout();
}
2、ByName 自动装配
<?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">
<bean id="cat" class="com.wyx.pojo.Cat"/>
<bean id="dog" class="com.wyx.pojo.Dog"/>
<!--
autowire 自动装配,有以下可选值
1、byName:会自动在容器上下文中查找,和自己对象相同 set 后面的名字的小写,如果与id一样自动装配,不然报错
2、byType:会自动在容器上下文中查找,和自己对象属性相同的 id 但是必须保证全局 id 唯一
-->
<bean id="people" class="com.wyx.pojo.People" autowire="byName">
<property name="name" value="小红安"/>
</bean>
</beans>
3、ByType 自动装配
<?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">
<bean id="cat" class="com.wyx.pojo.Cat"/>
<bean id="dog" class="com.wyx.pojo.Dog"/>
<!--
autowire 自动装配,有以下可选值
1、byName:会自动在容器上下文中查找,和自己对象相同 set 后面的名字的小写,如果与id一样自动装配,保证全局 id 唯一,不然报错
2、byType:会自动在容器上下文中查找,和自己对象属性的类型一致
-->
<bean id="people" class="com.wyx.pojo.People" autowire="byType">
<property name="name" value="小红安"/>
</bean>
</beans>
总结:在代码标准的情况下,自动装配能节省许多代码量
4、使用注解实现自动装配
使用注解装配步骤:
-
导入注解配置约束
<!--使用注解实现自动装配,必须在上面属性中导入以下配置 在 <beans 这里加 > --> xmlns:context="http://www.springframework.org/schema/context" http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
-
配置注解支持
<context:annotation-config/>
-
@Autowired
直接在属性上面使用即可!也可以在
set
方法上使用使用
@Autowired
可以不用编写set
方法,前提是这个自动装配的属性在ICO (Spring)
容器中存在,并且符合ByName
自动装配标准
@NonNull //字段标记的属性可以为Null值
@Autowired(required = false) //注解这样定义,表示属性值可以为空,也不会报错,【不写会报错】
- 如果使用
@Autowired
自动装配的环境比较复杂,自动装配无法通过@Autowired
完成的时候,我们可以使用@Qualifier(value = "xxx")
来配合@Autowired
使用,指定唯一的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"
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/>
<!--
使用注解实现自动装配,必须在上面属性中导入以下配置
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
<context:annotation-config/>
导入后,直接在属性名上加 @Autowired 也可以在 set方法上面加(不推荐)
加上注解后,可以省略,set 方法的编写
-->
<bean id="cat" class="com.wyx.pojo.Cat"/>
<bean id="dog" class="com.wyx.pojo.Dog"/>
<bean id="people" class="com.wyx.pojo.People">
<property name="name" value="小红安"/>
</bean>
</beans>
package com.wyx.pojo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.lang.NonNull;
public class People {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
- @Resource 注解
public class People {
//java自带的注解,它先找id 找不到 再找属性,都找不到才会报错,也可以加参数来使用,效率没有上面的注解高,但是也能用
@Resource
private Cat cat;
@Resource(name = "dog2")
private Dog dog;
}
小结:
@Resource 和 @Autowired的区别
- 都是通过自动装配的,都可以放在属性字段上
@Autowired
默认通过ByType
的方式实现,如果类型多,则会走ByName
,并且必须要求这个对象存在【常用】@Resource
默认通过ByName
的方式实现,如果找不到名字,则通过ByType
实现,如果两个都找不到,则报错 【常用】
8、使用注解开发
在 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
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>
-
bean
//@Component 等价于 <bean id="user" class="com.wyx.pojo.User"/> @Component public class User { // @Value 等价于 <property name="name" value="王玉星"/> @Value("i但是公司") public String name; }
-
属性如何注入
//@Component 等价于 <bean id="user" class="com.wyx.pojo.User"/> @Component public class User { // @Value 等价于 <property name="name" value="王玉星"/> @Value("但是公司") public String name; }
-
衍生的注解
@Component
有几个衍生注解,在web
开发中,会按照mvc
三层架构来注解- dao 包 【@Repository】
- service 包 【@service】
- controller 包 【@controller】
这四个注解功能都是一样的,将某个类注册到
Spring
中,装配Bean
-
自动装配
//具体看上面已经解释过了 @Resource @Autowired @NonNull @Qualifier(value = "xxx")
-
作用域
@Scope("prototype") // 后面接作用域的值 singleton prototype 作用与类上
-
小结
xml 与 注解 区别:
- xml 更加万能,适合与任何场合!维护简单方便
- 注解 不是自己类使用不了,维护相对复杂!
xml 和 注解 的最佳使用
- xml 用来管理
bean
- 注解负责完成属性的注入
- 我们使用的过程中只需要一个问题,使用注解记得开启注解支持
@Bean 与 @Component 的区别
仔细观察,发现上面的两个注解都是,向IOC 容器注入一个类
但是一般他们的使用情况不太一样。一般@Confiuration配合@Bean使用,写在配置文件中,返回的是某一个对象的实例;而@Component是对应某一个类,配合@ComponentScan使用,然后让spring ioc容器实例化。而且使用@Confiugraion和@Bean, 会进行cglib增强,拿到的是一个代理对象;@Component拿到的是一个普通的java对象。
9、使用 Java 的方式来配置Spring
我们现在要完全不使用 Spring
的 xml
配置了,全权交给 Java
来做
Java Config
是 Spring
的一个子项目 ,在 Spring4
之后变成了核心工程
-
创建实体类
package com.wyx.pojo; import org.springframework.beans.factory.annotation.Value; public class User{ @Value("小明") private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } }
-
创建
Spring
配置类 (类似与appliCationContext.xml
)package com.wyx.config; import com.wyx.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; /*添加这个注解,类似 <beans> <bean id="userDao" class="com.wyx.pojo.User"/> </beans> @Configuration 声明是配置类 @ComponentScan("com.wyx.pojo") 扫描包 @Import("Config.class") 合并配置包 */ @Configuration @ComponentScan("com.wyx.pojo") public class Config { /* 方法上面需要加 @Bean 类似与配置文件的中的 <bean> 对应一个类 方法名对应配置文件中的 id return 的对象就是 class 里面的属性值注入 大多用注解注入,和前面一样 更多的类注解参考官网,现在很多公司还不流行用注解来开发 */ @Bean public User myService() { return new User(); } }
-
测试类
import com.wyx.config.Config; import com.wyx.pojo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Mytest { @Test public void test(){ // 用 AnnotationConfigApplicationContext 类来读取配置文件类,其他操作一样。 ApplicationContext context = new AnnotationConfigApplicationContext(Config.class); User myService = context.getBean("myService", User.class); System.out.println(myService); } }
10、代理模式
1、为什么需要代理模式
2、使用静态代理
俗话说,没有什么是加一次无法解决的,于是静态代理模式产生了
静态代码实现例子
package com.wyx.staticProxy;
public interface Rent {
//租房接口
public void rent();
}
package com.wyx.staticProxy;
public class Host implements Rent{
//房东类:做出租房子的工作
@Override
public void rent() {
System.out.println("房东要出租房子!!!!");
}
}
package com.wyx.staticProxy;
//静态代理实现类
public class Proxy implements Rent{
// 中介要出租房子,必须需要获得要代理的对象
private Host host;
//使用构造器来初始化需要代理的房东
public Proxy(Host host) {
this.host = host;
}
//重写代理方法即可,
@Override
public void rent() {
host.rent();
}
}
package com.wyx.staticProxy;
//测试类,租房的人调用,用中介调用租房的方法
public class Client {
public static void main(String[] args) {
// 获取房东,并将他给代理对象,代理对象复杂执行租房接口即可
Host host = new Host();
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
3、使用动态代理模式
动态代理模式实例
package com.wyx.dynamicProxy;
public interface Rent {
//租房接口
public void rent();
}
package com.wyx.dynamicProxy;
public class Host implements Rent {
//房东类:做出租房子的工作
@Override
public void rent() {
System.out.println("房东要出租房子!!!!");
}
}
package com.wyx.dynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//动态代理类
public class ProxyInvocationHandler implements InvocationHandler {
//要被代理的接口
private Rent rent;
//通过 set 注入要被代理的接口
public void setRent(Rent rent) {
this.rent = rent;
}
// 生成要被代理的类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(), this);
}
// 处理代理的实例,并放回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这里可以添加 需要添加的方法,比如日志输出
//方法之后实现
log(method.getName());
//动态代理的本质就是通过反射实现的
Object result = method.invoke(rent,args);
//方法之后实现
return result;
}
public void log(String msc){
System.out.println("调用了 "+msc+" 方法");
}
}
package com.wyx.dynamicProxy;
public class Client {
public static void main(String[] args) {
// 获取房东,并将他给代理对象,代理对象复杂执行租房接口即可
Host host = new Host();
// 代理角色:现在没有,需要通过反射来加载
ProxyInvocationHandler pih = new ProxyInvocationHandler();
// 通过调用程序处理角色来处理我们要调用的接口对象
pih.setRent(host);
//获取代理类
Rent proxy = (Rent) pih.getProxy();
//调用接口方法
proxy.rent();
}
}
动态代理模式模板
package com.wyx.dynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//动态代理类
public class ProxyInvocationHandler implements InvocationHandler {
//要被代理的接口(使用前需要修改理接口)
private Rent interface;
//通过 set 注入要被代理的接口(使用前也要修改 set 方法)
public void setRent(Rent rent) {
this.rent = rent;
}
//后面只需要修改变量名即可
// 生成要被代理的类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(), this);
}
// 处理代理的实例,并放回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//添加方法的地方
log(method.getName());
return method.invoke(rent,args);
}
public void log(String msc){
System.out.println("调用了 "+msc+" 方法");
}
}
总结:
- 动态代理和静态代理需要的角色都是一样的
- 动态代理的代理类是动态生成的,使用类的反射来实现
- 动态代理分为两大类,基于接口的动态代理,基于类的动态代理
- 基于接口 ---JDK 动态dail
- 基于类:cglib
- Java字节码实现:javassist
使用动态代理需要了解 Proxy 代理,InvocationHandler:调用处理程序
动态代理和静态代理的好处:
- 可以是真实角色操作更加简单,不需要考虑一些其他东西
- 公共的东西交给代理角色,实现业务风格
- 公共业务想要拓展时,方便集中管理!
- 动态代理的私有好处
- 一个动态代理类的是一个接口,一般就是对应的一类业务
- 一个动态代理可以代理多个类,只需要修改代理接口即可
11、AOP【重点】
1、什么是AOP
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
2、Aop在Spring中的作用
提供声明式事务;允许用户自定义切面
以下名词需要了解下:
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ....
- 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
- 目标(Target):被通知对象。
- 代理(Proxy):向目标对象应用通知之后创建的对象。
- 切入点(PointCut):切面通知 执行的 “地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 。
3、使用Spring实现AOP
方式一、使用Spring的API接口
【重点】使用AOP织入,需要导入一个依赖包!
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
package com.wyx.service;
//接口
public interface UserService {
void add();
void delete();
void update();
void select();
}
package com.wyx.service;
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加一个用户");
}
@Override
public void delete() {
System.out.println("删除一个用户");
}
@Override
public void update() {
System.out.println("更新一个用户");
}
@Override
public void select() {
System.out.println("查询一个用户");
}
}
package com.wyx.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
//执行之前需要执行上面类
public class BeforeLog implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
// method 反射中的方法,可以放回方法名
// args 参数名
// target 反射的类对象
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
package com.wyx.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] objects, Object target) throws Throwable {
// 参数和上面一样,多了一个返回值
// returnValue 放回的可能是对象,需要处理,或者强制转换
System.out.println("执行了"+method.getName()+"返回结果为"+returnValue);
}
}
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--要注册的类-->
<bean id="userService" class="com.wyx.service.UserServiceImpl"/>
<bean id="afterLog" class="com.wyx.log.AfterLog"/>
<bean id="beforelog" class="com.wyx.log.BeforeLog"/>
<!--
aop配置:
使用前,需要在头文件添加
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
对其中的参数解释:
pointcut 切入点,就是需要在那个类中添加切入的内容,
id:切入点名称
固定对象:* (写要切入的类的全类名).*(切入类中的哪一个方法,(..代表全部方法))
expression="execution(* com.wyx.service.UserServiceImpl.*(..))"
advisor 切入方式为环绕切入,
advice-ref="beforelog" 切入的类,切入点,前面定义切入点的名称
advice-ref="beforelog" pointcut-ref="point"
-->
<aop:config>
<aop:pointcut id="point" expression="execution(* com.wyx.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="beforelog" pointcut-ref="point"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="point"/>
</aop:config>
</beans>
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//注意动态代理,这里是代理接口,不能用实现类
UserService userService = (UserService) context.getBean("userService");
userService.add();
userService.delete();
userService.update();
userService.select();
}
方式二、自定义类来实现AOP
package com.wyx.diy;
public class Diy {
public void before(){
System.out.println("++++++++++执行之前+++++++++++");
}
public void after(){
System.out.println("++++++++++执行之后++++++++++");
}
}
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--要注册的类-->
<bean id="userService" class="com.wyx.service.UserServiceImpl"/>
<bean id="diy" class="com.wyx.diy.Diy"/>
<!--
aop配置:
使用前,需要在头文件添加
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
aop:aspect 自定义类(切面)时,使用他 ref="要切入的类bean中注册的 id "
<aop:pointcut id="point" expression="execution(* com.wyx.diy.Diy.*(..))"/> 切入点 id 和 execution 表达式,不会可以看 execution 表达式的用法
<aop:before method="before" pointcut-ref="point"/> 在类的什么地方切入
<aop:after method="after" pointcut-ref="point"/>
-->
<aop:config>
<aop:aspect ref="diy">
<aop:pointcut id="point" expression="execution(* com.wyx.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//注意动态代理,这里是代理接口,不能用实现类
UserService userService = (UserService) context.getBean("userService");
userService.add();
userService.delete();
userService.update();
userService.select();
}
方式三、注解实现AOP
//注解类的实现
package com.wyx.diy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
//标注这个类是一个切面
@Aspect
public class AnnotationPointCut {
// 切入点,后面接 execution 表达式
@Before("execution(* com.wyx.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("+++++方法执行前+++++");
}
@After("execution(* com.wyx.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("+++++方法执行后++++++");
}
//在环绕中增强,我们可以给定一个参数,代表我们要获取的处理切入掉
@Around("execution(* com.wyx.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint pj){
System.out.println("环绕前");
Signature signature = pj.getSignature();//获取方法签名
System.out.println(signature);
try {
//控制切点继续执行
Object proceed = pj.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕后");
}
}
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--要注册的类-->
<bean id="userService" class="com.wyx.service.UserServiceImpl"/>
<bean id="annotationPointCut" class="com.wyx.diy.AnnotationPointCut"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
</beans>
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//注意动态代理,这里是代理接口,不能用实现类
UserService userService = (UserService) context.getBean("userService");
userService.add();
userService.delete();
userService.update();
userService.select();
}
12、整合 Mybatis
1、导入jar包
-
junit
-
lombok
-
mybatis
-
mysql
-
spring
-
spring-jdbc
-
aop织入
-
mybatis-spring 【官网】http://mybatis.org/spring/zh/index.html
<!--junit 单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency> <!--lombok 依赖 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.18</version> </dependency> <!--mysql 数据库连接--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.23</version> </dependency> <!--mybatis 依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <!--spring-webmvc 依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.12.RELEASE</version> </dependency> <!--spring-jdbc 依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.12.RELEASE</version> </dependency> <!--(aop织入包)aspectjweaver 依赖--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> <!--mybatis-spring 依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency>
2、编写配置文件
配置 applicationContsxt.xml
文件
将 mybatis-config.xml
的数据源替换到 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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
使用Spring的数据源替换Mybatis的数据源 c3p0 dbcp druid
这里使用 Spring 提供的 JDBC 驱动
-->
<!--1、配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&charterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="970699"/>
</bean>
<!--2、将配置的数据源,用来注册 sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--将上面配置的数据源插入,到 sqlSessionFactory-->
<property name="dataSource" ref="dataSource" />
<!--将 mybatis-config.xml 的配置整合给 Spring -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--将每个接口设置的 Mapper.xml文件注册到 Spring
这样 Mybatis-config.xml 就负责,取别名,和配置 Mybatis的设置了
其实所有的方法都可以注册过来,但是放在在那边能让文件更好的分工明确
-->
<property name="mapperLocations" value="classpath:com/wyx/Mapper/*.xml"/>
</bean>
<!--3、Spring中已经又做好了的 sqlSession,他放在 org.mybatis.spring.SqlSessionTemplate下的 sqlSessionFactory
我们只需要将他注入,Bean就可以了注入后将名字改为 sqlSession 在任何地方都i可以调用,不在需要托管
(这里只能使用构造器注入因为没有 set 方法,可以使用 index=0,也可以使用name注入 name="sqlSessionFactory")
-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
步骤分析:
- 使用
DriverManagerDataSource
配置数据源,注入bean
中 - 使用
SqlSessionFactoryBean
整合数据源,生成sqlSessionFactory
注入bean
中 - 使用
SqlSessionTemplate
将第二步生成的sqlSessionFactory
整合成sqlSession
- 自此
sqlSession
配置完成,
第二种方式,比较简单,不做讲解
13、声明式事务
- 事务类似与
转账
,要么都成功,要么都失败 - 事务在项目开发中,十分重要,设计数据的一致性问题
- 确保完整性和一致性
ACID 原则:
- 原子性
- 一致性
- 隔离性
- 多个业务操作同一资源,防止数据损坏
- 持久性
- 事务一旦提交,结果就不会改变
1、Spring中的事务管理
- 声明式事务:AOP
- 编程式事务:需要在代码中,进行事务的管理
使用事务applicationContext.xml中添加的东西 :
基本是固定写法.
<!--配置声明式事务
固定的,不需要改变
-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource"/>
</bean>
<!--结合AOP实现事务的织入
使用前添加以下的参数
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
<tx:method name="*" propagation="REQUIRED"/>
name 后面接的参数代表走什么方法,要过事务管理器,propagation一般都设置 REQUIRED 还有其他参数,具体自己查
-->
<!--配置事务的类-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="query" read-only="true"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置事务的切入 切入走,事务管理器-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.wyx.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
propagation
表示使用哪种事务的实现方式,一共有七种,但基本使用默认的,可以不写.
实现类:
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
public List<User> selectUser() {
User user = new User(5, "tanshishi", "5465465441");
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(user);
mapper.deleteUser(6);
return mapper.selectUser();
}
public void addUser(User user) {
getSqlSession().getMapper(UserMapper.class).addUser(user);
}
public int deleteUser(int id) {
return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
}
}
测试时增和删要么都成功要么都失败
思考:
为什么需要事务?
- 如果不配置事务,可能存在数据提交不一致的情况下;
- 如果我们不在Spring中去配置声明式事务,我们就需要在代码中手动配置事务!
- 事务在项目的开发中十分重要,设计到数据的一致性和完整性问题,不容马虎!
完结散花!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
完结散花!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
完结散花!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!