Spring
一、spring介绍
1、spring简介
- Spring:春天------->给软件行业带来了春天
- 2002年,首次推出了Spring框架的雏形:interface21框架!
- Spring框架即以interface21框架作为基础,经过重新设计,并不断丰富其内涵于2004年3月24日,发布了1.0正式版
- [Rod Johnson](https://baike.baidu.com/item/Rod Johnson) ,SpringFramework创始人,著名作者。很难想象 [Rod Johnson](https://baike.baidu.com/item/Rod Johnson) 的学历,他是悉尼大学音乐学博士
- 目的: 解决企业应用开发的复杂性,他本身就是一个大杂烩,整合了现有的技术框架
2、框架
- SSH : Struct2 + Spring + Hibernate(全自动数据库框架)
- SSM : SpringMVC + Spring(容器) + Mybatis(半自动数据库框架)
3、依赖
1、spring依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
2、jdbc依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.5</version>
</dependency>
4、Spring优点
1、Spring是一个免费的开源的框架(容器)
2、Spring是一个轻量级的,非入侵式的框架
3、控制反转(IOC),面向切面编程(AOP)
4、支持事务的处理,对框架整合的支持
总的说就是一句话:Spring就是一个轻量级的控制反转(IOC),和面向切面编程(AOP)的框架
弊端:配置的东西太多,人称“配置地狱”
5、Spring7大模块
1、核心容器(Spring Code)
1、概念
- 是Spring的核心容器,提供了Spring框架的基本功能;
- 此模块包含的BeanFactory类是Spring的核心类,负责产生和管理Bean,是工程模式的实现;
- 采用Factory(工厂模式)实现了IOC(控制反转)将应用的配置和依赖性规范与实际的应用程序代 码分开;
- Spring以bean的方式组织和管理Java应用中发各个组件及其关系。
2、应用上下文(Spring Context)
1、概念
- 是一个配置文件,向Spring框架提供上下文信息;
- SpringContext模块继承BeanFactory类,添加了事件处理、国际化、资源装载、透明装载、以及数据校验等功能;
- 还提供了框架式的Bean的访问方式和企业级的功能,如JNDI访问,支持EJB、远程调用、继承模板框架、Email和定时任务调度等;
3、面向切面编程(Spring AOP)
1、概念
-
Spring AOP直接将面向方面的编程功能集成到了Spring框架中,所以很容易的使Spring框架管理的任何对象支持AOP(Spring集成了所有AOP功能。通过事务管理可以使任意Spring管理的对象AOP化);
-
Spring AOP为基于Spring的应用程序中的对象提供了事务管理服务;
-
通过使用Spring AOP,不用依赖EJB组件,就可以将声明性事务管集成到应用程序中。
4、JDBC和DAO模块(Spring DAO)
1、概念
- DAO(DataAccessObject)模式思想是将业务逻辑代码与数据库交互代码分离,降低两者耦合;
- 通过DAO模式可以使结构变得更为清晰,代码更为简;
- DAO模块中的JDBC的抽象层,提供了有意义的异常层次结构,用该结构来管理异常处理,和不同数据库供应商所抛出的错误信息;
- 异常层次结构简化了数据库厂商的异常错误(不再从SQLException继承大批代码),极大的降低了需要编写的代码数量,并且提供了对声明式事务和编程式事务的支持;
5、对象实体映射(Spring ORM)
1、概念
- SpringORM模块提供了对现有ORM框架的支持;
- 提供了ORM对象的关系工具,其中包括了Hibernate、JDO和 IBatis SQL Map等,所有的这些都遵从Spring的通用事务和DAO异常层次结构;
- 注意这里Spring是提供各类的接口(support),目前比较流行的下层数据库封闭映射框架,如ibatis,Hibernate等;
6、Web模块(Spring Web)
1、概念
- 此模块建立在SpringContext基础之上,提供了Servlet监听器的Context和Web应用的上下文;
- 对现有的Web框架,如JSF、Tapestry、Structs等提供了集成;
- SpringWeb模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
7、MVC模块(Spring Web MVC)
1、概念
- SpringWebMVC模块建立在Spring核心功能之上,拥有Spring框架的所有特性,能够适应多种多视图、模板技术、国际化和验证服务,实现控制逻辑和业务逻辑的清晰分离;
- 通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI;
2、MVC模型:
- Servlet控制器为应用程序提供了一个进行前-后端处理的中枢。一方面为输入数据的验证、身份认证、日志及实现国际化编程提供了一个合适的切入点;另一方面也提供了将业务逻辑从JSP文件剥离的可能;
- 业务逻辑从JSP页面分离后,JSP文件蜕变成一个单纯完成显示任务的东西,这就是常说的View;
- 而独立出来的事务逻辑变成人们常说的Model,再加上控制器Control本身,就构成了MVC模式
8、Spring扩展
在Spring官网有这句话,现代化的java开发,就是基于Spring的开发
1、Spring Boot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速的开发单个的微服务
- 约定大于配置
2、Spring Cloud
- Spring cloud是基于SpringBoot实现的
二、Ioc理论推导
1、原来的简单模式
1、UserDao接口
public interface UserDao {
void getUser();
}
2、UserDaoImpl实现类(根据用户的业务添加实现类)
public class UserDaoImpl implements UserDao{
public void getUser() {
System.out.println("我是一个简单的User");
}
}
public class UserDaoMysqlImpl implements UserDao{
public void getUser() {
System.out.println("我是mysql用户");
}
}
public class UserDaoOracleImpl implements UserDao {
public void getUser() {
System.out.println("我是oracle用户");
}
}
3、UserServiceDao业务接口
public interface UserServiceDao {
void getUser();
}
4、UserSeviceDaoImpl业务实现类(每次都要修改接口的实现类对象,麻烦)
public class UserServiceImpl implements UserServiceDao {
//引入UserDaoImpl()对象
// private UserDao userDao = new UserDaoImpl();
//引入UserDaoMysqlImpl()对象
//private UserDao userDao = new UserDaoMysqlImpl();
//引入UserDaoOracleImpl()对象
//private UserDao userDao = new UserDaoOracleImpl();
public void getUser() {
userDao.getUser();
}
}
5、测试
public class MyTest {
//用户实际调用的是业务曾,Dao层他们不接触
@Test
public void getUser(){
UserServiceImpl userService = new UserServiceImpl();
userService.getUser();
}
}
缺点:
- 每次业务的改变,都要在业务的实现类中,进行手动的添加每个实现类的对象,这样如果是有好多业务,每次都要改变实现类的对象,就显得非常的麻烦,效率低
- 耦合度高
- 控制权在程序员手中
2、使用set方法的注入
1、UserDao接口(不变)
2、UserDaoImpl实现类(不变)
3、UserServiceDao业务接口(不变)
4、UserSeviceDaoImpl业务实现类(使用Set方法的动态注入,程序员就不用每次修改接口类的对象)
public class UserServiceImpl implements UserServiceDao {
//使用Set方法的注入,程序员就不用每次进行,修改接口类的对象
//利用set进行动态,实现值的注入
private UserDao userDao;
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
public void getUser() {
userDao.getUser();
}
}
5、测试(只需要,根据不同用户的需求,使用set方法注入不同的实现类,即可)
public class MyTest {
//用户实际调用的是业务曾,Dao层他们不接触
@Test
public void getUser(){
UserServiceImpl userService = new UserServiceImpl();
//只需要,根据不同用户的需求,使用set方法注入不同的实现类,即可
userService.setUserDao(new UserDaoImpl());
userService.getUser();
}
}
优点(IOC原型):
-
程序员不用再去管理对象的创建了
-
系统的耦合性大大降低
-
可以使程序员更加专注的实现业务
-
主动权在用户手中
3、使用xml注入
(每次只改变ref中的id的值,即可实现不同的用户需求)
<!--
使用Spring来创建对象,在spring中这些都称为bean
类型 变量名 = new 类型();
UserDaoImpl userdao = new UserDaoImpl();
id = 变量名
class = new 的对象
property 相当于给属性设置的一个值
ref:引用Springr容器中创建好的对象
value:具体的值,基本数据类型
-->
<!--创建UserDaoImpl对象-->
<bean id="UserImpl" class="com.sh.dao.UserDaoImpl"/>
<!--创建UserDaoMysqlImpl对象-->
<bean id="MysqlImpl" class="com.sh.dao.UserDaoMysqlImpl"/>
<!--创建UserDaoOracleImpl对象-->
<bean id="OracleImpl" class="com.sh.dao.UserDaoOracleImpl"/>
<!--创建UserServiceImpl对象-->
<bean id="UserServiceImpl" class="com.sh.service.UserServiceImpl">
<!--在UserServiceImpl类中声明一个UserImpl对象-->
<!--ref:中的值,就是上面创建好对象中bean标签中的id的值-->
<property name="userDao" ref="UserImpl"/>
</bean>
</beans>
测试
@Test
public void getUser(){
//解析bean.xml,生成管理相应的bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//getBean:参数为Spring配置文件中的bean标签的id
//得到UserServiceImpl对象
UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("UserServiceImpl");
//调用UserServiceImpl类中的方法
userServiceImpl.getUser();
}
3、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)。
三、HelloSpring
1、导入jar包
注 : spring 需要导入commons-logging进行日志记录 . 我们利用maven , 他会自动下载对应的依赖项 .
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
2、编写代码
1、编写一个Hello实体类
public class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("Hello,"+ name );
}
}
2、编写bean.xml
<!--
使用Spring来创建对象,在spring中这些都称为bean
类型 变量名 = new 类型();
UserDaoImpl userdao = new UserDaoImpl();
id = 变量名
class = new 的对象
property 相当于给属性设置的一个值
ref:引用Springr容器中创建好的对象
ref中的值,就是创建好对象中bean标签中的id的值,这样就可以在一个对象中引用另一个对象了
value:具体的值,基本数据类型
value中的值就是,传入set方法中的值
-->
<!--创建一个Hello对象-->
<bean id="hello" class="com.sh.pojo.Hello">
<!--这里的name就是Hello类中的 private String name;-->
<property name="name" value="航航爱吃糖"/>
</bean>
3、测试
@Test
public void test(){
//解析bean.xml,获得context上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//调用getBean(),得到Hello对象
Hello hello = (Hello) context.getBean("hello");
hello.show();
}
这个过程就叫控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身创建的,使用Spring后,对象是由Spring来创建的
反转:程序本身不创建对象,而变成被动的接受对象
依赖注入:就是利用set方法进行注入的
IOC是一种编程思想,由主动的编程编程被动的接收
最后,我们彻底不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IoC,一句话搞定:对象由Spring来创建,管理,装配
四、Ioc创建对象的方式
1、无参构造(默认方式)
【方式一==方式二】
方式一:采用无参构造
@Test
public void testNoConstructor(){
User user = new User();
}
方式二:
<!--1、默认采用无参构造方法,创建对象-->
<bean id="user" class="com.sh.dao.User"/>
@Test
public void testNoConstructor(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean("user",User.class);
}
2、有参构造(4种)
【方式1==方式二】
方式一:
@Test
public void test(){
User user2 = new User("腾讯");
}
方式二:4种赋值方式
<!--参数名赋值-->
<bean id="user2" class="com.sh.dao.User">
<constructor-arg name="name" value="阿里"/>
</bean>
<!--2、下标赋值-->
<bean id="user2" class="com.sh.dao.User">
<constructor-arg index="0" value="字节跳动"/>
</bean>
<!--3、类型赋值
不建议使用,如果多个参数相同的话,就不太清楚那个参数是那个的
-->
<bean id="user2" class="com.sh.dao.User">
<constructor-arg type="java.lang.String" value="网易"/>
</bean>
<!--4、ref,引用对象作为有参构造的参数-->
<bean id="user2" class="com.sh.dao.User">
<!--引用另外一个class对象-->
<constructor-arg name="student" ref="student" />
</bean>
<!--再次采用,普通赋值->
<bean id="student" class="com.sh.dao.Student">
<constructor-arg name="address" value="西安"/>
</bean>
@Test
public void testNoConstructor(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); User user2 = context.getBean("user2",User.class);
User user3 = context.getBean("user2",User.class);
System.out.println(user2==user3);//true
}
3、知识点:
- 只要在bean.xml中进行了bean注册,就是调用了无参构造,不关我们在测试类中是否调用
- 每个bean对象,在内存中只有一个实例,所以不管在测视类种调用几次,那都是一个对象
五、Spring配置
1、别名:
在测试类种,既可以使用user2,也可以使用user2222
<bean id="user2" class="com.sh.dao.User">
<constructor-arg name="name" value="阿里"/>
</bean>
<!--别名-->
<alias name="user2" alias="user2222"/>
2、bean配置
<!--bean配置
id:bean对象的唯一标识符, 在测试类中getBean方法中的参数,也是new对象的变量名
class:需要实例化的class对象, 包名+类名
name:别名,还可以同时起多个别名,有多种分割别名的符号
-->
<bean id="student1" class="com.sh.dao.Student" name="student2,student3 student4;student5">
<constructor-arg name="address" value="北京"/>
</bean>
3、import
-
import,用于团队协作,可以将多个bean.xm导入一个总的applicationContext.xml中 ,
-
在使用的时候,就调用总的applicationContext.xml就可以了
-
xml中的id可以重复,idea会自动去里面找
<!--导入bean.xml,bean2.xml,bean3.xml到applicationContext.xml-->
<import resource="bean.xml"/>
<import resource="bean2.xml"/>
<import resource="bean3.xml"/>
<!--也可以使用,这种通配符导入多个xml-->
<import resource="*.xml"/>
六、依赖注入( Dependency Injection )
1、构造器注入
前面有讲到
2、Set注入【重点】
依赖注入:Set注入
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入
搭建环境
1、pojo的两个实体类
package com.sh.pojo;
//地址类
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String show(){
return this.address;
}
}
package com.sh.pojo;
import java.util.*;
public class Person {
private String name;//基本类型
private Address address;//引用类型
private String[] books;//数组
private List<String> hobbys;//list
private Map<String,String> card;//map
private Set<String> games;//set
private String dog;//null注入
private Properties info;//Properties
private String cat;//空值注入
public String getCat() {
return cat;
}
public void setCat(String cat) {
this.cat = cat;
}
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 getDog() {
return dog;
}
public void setDog(String dog) {
this.dog = dog;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", address=" + address.show() +
", books=" + Arrays.toString(books) +
", hobbys=" + hobbys +
", card=" + card +
", games=" + games +
", dog='" + dog + '\'' +
", info=" + info +
", cat='" + cat + '\'' +
'}';
}
}
2、applicationContext.xml
<!--各种类型的依赖注入-->
<bean id="person" class="com.sh.pojo.Person">
<!--1、基本类型-->
<property name="name" value="航航"/>
<!--2、引用类型-->
<property name="address" ref="address"/>
<!--3、数组-->
<property name="books">
<array>
<value>Harry Potter</value>
<value>西游戏</value>
<value>三国演义</value>
<value>战争与和平</value>
</array>
</property>
<!--4、List集合-->
<property name="hobbys">
<list>
<value>看电影</value>
<value>听歌曲</value>
<value>敲代码</value>
</list>
</property>
<!--5、map-->
<property name="card">
<map>
<entry key="小明" value="123456789"/>
<entry key="小虎虎" value="0987654321"/>
<entry key="小阳" value="888888888"/>
</map>
</property>
<!--6、set-->
<property name="games">
<set>
<value>LOL</value>
<value>COC</value>
<value>BOB</value>
</set>
</property>
<!--7、properties-->
<property name="info">
<props>
<prop key="学号">2018020137</prop>
<prop key="姓名">航航</prop>
<prop key="身高">180</prop>
<prop key="喜欢">Java</prop>
</props>
</property>
<!--8、null值注入-->
<property name="dog">
<null></null>
</property>
<!--9、空值注入-->
<property name="cat" value=""/>
<!--2的引用-->
</bean>
<bean id="address" class="com.sh.pojo.Address">
<property name="address" value="石家庄"/>
</bean>
</beans>
3、测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = context.getBean("person", Person.class);
System.out.println(person.toString());
}
// Person{
// name='航航',
// address=石家庄,
// books=[Harry Potter, 西游戏, 三国演义, 战争与和平],
// hobbys=[看电影, 听歌曲, 敲代码],
// card={小明=123456789, 小虎虎=0987654321, 小阳=888888888},
// games=[LOL, COC, BOB],
// dog='null',
// info={学号=2018020137, 喜欢=Java, 姓名=航航, 身高=180},
// cat=''
// }
3、扩展方式注入
p:命名空间注入(依赖于无参构造)
xmlns:p="http://www.springframework.org/schema/p"
c:命名空间注入(通过有参构造器注入)
xmlns:c="http://www.springframework.org/schema/c"
UserBean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
<!--引入p-->
xmlns:p="http://www.springframework.org/schema/p"
<!--引入c-->
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">
<!--p命名空间注入,可以直接注入属性的值,但是依赖于无参构造 property-->
<bean id="user1" class="com.sh.pojo.User" p:id="10"/>
<!--c命名空间注入,通过有参构造器注入:constructs-->
<bean id="user2" class="com.sh.pojo.User" c:id="6"/>
</beans>
知识点:
- 实体类切勿忘掉写set方法
- c:命名空间,需要将所有的有参构造方法中的参数,都在xml中注入
七、bean的作用域
Scope | Description |
---|---|
范围 | 描述 |
singleton | (默认)为每个Spring IoC容器将单个bean定义的作用域限定为单个对象实例。 |
原型 prototype | 将单个bean定义的作用域限定为任意数量的对象实例。 |
request | 将单个bean定义的范围限定为单个HTTP请求的生命周期。 也就是说,每个HTTP请求都有一个在单个bean定义的后面创建的bean实例。 仅在基于Web的Spring上下文中有效 ApplicationContext . |
session | 将单个bean定义范围限定为HTTP的生命周期 Session 。 仅在基于Web的Spring上下文中有效 ApplicationContext . |
application | 将单个bean定义范围限定为一个bean的生命周期 ServletContext 。 仅在基于Web的Spring上下文中有效 ApplicationContext . |
socket | 将单个bean定义范围限定为一个bean的生命周期 WebSocket 。 仅在基于Web的Spring上下文中有效 ApplicationContext . |
1、singleton(单例模式)默认机制
UserBean.xml
<!--设置单例模式-->
<bean id="user" class="com.sh.pojo.User" scope="singleton">
<property name="id" value="9"/>
</bean>
测试:他们三个是一个对象的实例
@Test
public void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("UserBean.xml");
User user1 = context.getBean("user", User.class);
User user2 = context.getBean("user", User.class);
User user3 = context.getBean("user", User.class);
System.out.println(user1 == user2&&user2==user3);//true
}
2、prototype(原型模式)
UserBean.xml
<!--原型模式-->
<bean id="user" class="com.sh.pojo.User" scope="prototype">
<property name="id" value="10"/>
</bean>
测试:他们三个是不同的三个对象的实例
@Test
public void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("UserBean.xml");
User user1 = context.getBean("user", User.class);
User user2 = context.getBean("user", User.class);
User user3 = context.getBean("user", User.class);
System.out.println(user1 == user2&&user2==user3);//false
}
3、其余的session,request,application,都必须再web项目中使用
八、Spring的自动装配
- 自动装配是Spring满足bean依赖的一种方式
- Spring会在上下文中自动寻找,并自动给bean装配属性
1、在xml中的显示配置
2、在java中显示的配置
3、隐式的自动装配
搭建环境
一个人有两个宠物
实体类
package com.sh.pojo;
public class Person {
private int id;
private Cat cat;
private Dog dog;
public Person() {
}
public Person(int id, Cat cat, Dog dog) {
this.id = id;
this.cat = cat;
this.dog = dog;
}
public void setId(int id) {
this.id = id;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public void shout(){
cat.shout();
dog.shout();
}
}
package com.sh.pojo;
public class Cat {
public void shout(){
System.out.println("miao~");
}
}
public class Dog {
public void shout(){
System.out.println("wang~");
}
}
application.xml
普通的显示xml装配
<!--普通的显示装配-->
<bean id="cat" class="com.sh.pojo.Cat"/>
<bean id="dog" class="com.sh.pojo.Dog"/>
<bean id="person" class="com.sh.pojo.Person">
<property name="id" value="1"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
自动装配【byName】
需要保证被调用的bean的id唯一,并且这个bean的id需要和调用者class里面的set方法后面的名字一样
<!--byName:会在容器上下文中查找,和自己对象的set方法后面的的值对应的beanid-->
<bean id="cat" class="com.sh.pojo.Cat"/>
<bean id="dog" class="com.sh.pojo.Dog"/>
<bean id="person" class="com.sh.pojo.Person" autowire="byName"/>
自动装配【byType】
需要保证被调用的bean的唯一,并且这个bean的class需要和调用者里面的对象的class类型一致
<!--byType:会在容器中查找,和自己对象的属性类型相同bean,只看class,所以不关是否有id-->
<bean class="com.sh.pojo.Cat"/>
<bean id="dog" class="com.sh.pojo.Dog"/>
<bean id="person" class="com.sh.pojo.Person" autowire="byType"/>
测试
@Test
public void testAutoWire(){
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Person person = context.getBean("person", Person.class);
person.shout();
}
使用注解实现自动装配
使用注解的条件
1、导入约束: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"
<!--context约束-->
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
<!---contentext-->
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
</beans>
2、配置注解: 在application.xml的beans中添加
<context:annotation-config/>
@Autowire
直接在属性上面使用即可,也可以在set方法上面使用
1、属性上面使用【不能给基本类型注解】
注意:可以不用set方法,前提是你的自动装配的属性在IoC(bean)中定义,且符合名字byName
Person.java
public class Person {
//如果显示的定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空
@Autowired(required = false)
private int id;
@Autowired
private Cat cat;
@Autowired
private Dog dog;
public Person() {
}
public Person(int id, Cat cat, Dog dog) {
this.id = id;
this.cat = cat;
this.dog = dog;
}
public void setId(int id) {
this.id = id;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public void shout(){
cat.shout();
dog.shout();
}
}
application.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: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/>
<bean id="cat" class="com.sh.pojo.Cat"/>
<bean id="dog" class="com.sh.pojo.Dog"/>
<bean id="person" class="com.sh.pojo.Person">
<property name="id" value="1"/>
</bean>
</beans>
2、在set方法上面使用
public class Person {
@Autowired
public void setId(int id) {
this.id = id;
}
@Autowired
public void setCat(Cat cat) {
this.cat = cat;
}
}
@Nullable
属性标记了这个注解,说明这个注解可以为null
@Qualifier(value = “bean的id”)
如果使用@Autowire的环境比较复杂,自动装配无法通过一个注解【@Autowire】完成的时候,我们可以使用Qualifier(value = “bean的id”)去配合@Autowire注解的使用,指定一个唯一的bean对象的注入
例如:这里的application.xml中这里的id的前缀和class都不唯一,这时候的@Autowire注解就比较不太好用,所以加上 @Qualifier(value = “cat1”),就会在application.xml中找到对应的bean对象实现注解的自动装配
public class Person {
@Autowired
@Qualifier(value = "cat1")
private Cat cat;
@Autowired
@Qualifier(value = "dog1")
private Dog dog;
}
application.xml
<!--这里的id的前缀和class都不唯一-->
<bean id="cat1" class="com.sh.pojo.Cat"/>
<bean id="dog1" class="com.sh.pojo.Dog"/>
<bean id="cat2" class="com.sh.pojo.Cat"/>
<bean id="dog2" class="com.sh.pojo.Dog"/>
<bean id="person" class="com.sh.pojo.Person">
@Resource
只要保证在application.xml文件中id,或者class只要有一个唯一,就可以
public class Person {
@Resource
private Cat cat;
@Resource
private Dog dog;
}
application.xml
<!--1和2只能用一个-->
<!--1、id唯一,class唯一-->
<bean id="cat1" class="com.sh.pojo.Cat"/>
<bean id="dog1" class="com.sh.pojo.Dog"/>
<!--2、class唯一-->
<bean id="11" class="com.sh.pojo.Cat"/>
<bean id="22" class="com.sh.pojo.Dog"/>
<bean id="person" class="com.sh.pojo.Person"/>
@Resource(name = “bean的id”)
指定对应的bean的id进行注入
这里就不管application.xml中那些id,calss的是否复杂,唯一了
public class Person {
@Resource(name = "11")
private Cat cat;
@Resource(name = "22")
private Dog dog;
}
<!--这里的id和class没有限制,主要根据person.java中的name进行匹配-->
<bean id="cat1" class="com.sh.pojo.Cat"/>
<bean id="dog1" class="com.sh.pojo.Dog"/>
<bean id="cat2" class="com.sh.pojo.Cat"/>
<bean id="dog2" class="com.sh.pojo.Dog"/>
<bean id="11" class="com.sh.pojo.Cat"/>
<bean id="22" class="com.sh.pojo.Dog"/>
<bean id="person" class="com.sh.pojo.Person"/>
小结:
@Autowire和@Resource的区别
- @Autowire默认是通过byType实现的,然后是byName
- @Resource默认是通过byName实现的,如果找不到名字,则通过byType实现
九、使用注解开发
注意:
1、在spring4之后,要使用注解开发,必须要导入aop包
2、导入context约束和增加注解的支持
3、指定要扫描的包,这个包下的注解就会生效
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<!--context约束-->
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
<!---contentext约束-->
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--增加注解的支持-->
<context:annotation-config/>
<!--3、扫描包-->
<context:component-scan base-package="com.sh.loyal.pojo"/>
</beans>
1、bean的注解
User.java
package com.sh.loyal.pojo;
import org.springframework.stereotype.Component;
//@Component组件相当于<bean id="user" class="com.sh.loyal.pojo.User"/>
@Component
public class User {
public String name=" be loyal yourself";
}
applicationConrext.xml
<!--三步走实现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"
<!--1、导入context约束-->
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">
<!--2、增加注解的支持-->
<context:annotation-config/>
<!--3、扫描包-->
<context:component-scan base-package="com.sh.loyal.pojo"/>
</beans>
测试:getBean(“user”, User.class)中的参数user,默认是pojo的class的首字母小写类名
@Test
public void test01() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = context.getBean("user", User.class);
System.out.println(user.name);
}
2、属性的注入
User.java
@Value用在属性上
//@Component相当于<bean id="user" class="com.sh.loyal.pojo.User"/>
@Component
public class User {
//@Value("Be loyal yourself")==<property name="name" value="Be loyal yourself"/>
@Value("Be loyal yourself")
public String name;
}
@Value用在set方法上
//@Component相当于<bean id="user" class="com.sh.loyal.pojo.User"/>
@Component
public class User {
public String name;
//@Value("Be loyal yourself")==<property name="name" value="Be loyal yourself"/>
@Value("Be loyal yourself")
public void setName(String name) {
this.name = name;
}
}
applicationConrext.xml【不变】
测试【不变】
3、衍生的注解
@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!
- dao--------【@Repository】
@Repository public class UserDao {}
- service----------【@Service】
@Service public class UserService {}
- contronller----------【@Contronller】
@Controllerpublic class Contronller {}
这四个注解的功能都是一样的,都是代表将某个类注册到Spring容器(applicationContext.xml)中,装配Bean
4、自动装配注解
(详解:见第八章)
@Autowire:自动装配通过类型,名字
- 如果Autowired不能唯一装配属性,则需要通过@Qualfier(value=“xxx”)
@Nullable:字段标记了这个注解,说明这个字段可以为null
@Resource:自动装配通过名字
5、作用域
singleton(单体)
@Scope("singleton")Component==<bean id="user" class="com.sh.loyal.pojo.User" scope="singleton"/>
public class User {}
prototype(原型)
@Scope("prototype")Component==<bean id="user" class="com.sh.loyal.pojo.User" scope="prototype"/>
public class User {}
6、小结
xml与注解
- xml:更加万能,适用于任何场合,维护方便
- 注解:不是自己的类使用不了,维护复杂
xml与注解的最佳实践
- xml用来管理bean
- 注解只负责完成属性的注入
十、使用Java的方式配置Spring
我们现在完全不使用Spring的xml配置了,全权交给Java来做
JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能
搭建环境
User.java
package com.sh.loyal.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//@Component:说明这个类被Spring容器接管了,注册到了容器中
@Component
public class User {
@Value("hanghang")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
配置类
相当于applicationContext.xml
/*
说白了,这就是一个resources文件下的applicationContext.xml
*/
/*
@Configuration:相当于applicationContext.xml,它被Spring,说明他也是一个@Component
*/
@Configuration
//原来的扫描包都要在applicationContext.xml文件中,现在注解也可以扫描
@ComponentScan("com.sh.loyal.pojo")
//我们这里也可以像applicationContext.xml一样引入多个.xml的文件
@Import(ShConfig2.class)
public class ShConfig {
/*
@Bean:就相当于applicationContext.xml文件中的bean标签
getUser:相当于bean标签中的id
User:相当于bean标签中的class
*/
@Bean
User getUser(){
return new User();
}
}
测试
@Test
public void test(){
/*如果完全使用了JavaConfig的配置方式去做,我们只能通过AnnotationConfigApplicationContext,上下文来获取容器,通过配置类的class对象加载
*/
ApplicationContext context = new AnnotationConfigApplicationContext(ShConfig.class);
/*
context.getBean("getUser", User.class);
getUser:相当于bean标签中的id
User.class:就相当于bean标签中的class,在这里代表返回值
*/
User user = context.getBean("getUser", User.class);
System.out.println(user.toString());
}
十一、代理模式
为什么要学习代理模式?
因为这就是SpringAop的底层【SpringAop和SpringMvc】
代理模式的分类
-
静态代理
-
动态代理
1、静态代理
角色分析
- 抽象角色:一般会使用接口或,抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户:访问代理对象的人
案例一:租房子
接口:抽象角色
//抽象角色:就是真实角色需要做的事情
public interface Rent {
public void rent();
}
真实角色
//真实角色:要出租房子的人
public class Host implements Rent{
public void rent() {
System.out.println("房东要出租房子了");
}
}
代理角色
//代理角色:给真实做所有的事,
public class Proxy implements Rent {
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
public void rent() {
host.rent();
}
public void seeHome(){
System.out.println("中介看房子");
}
public void hetong(){
System.out.println("中介签合同");
}
public void fire(){
System.out.println("中介收取中介费");
}
}
客户端访问代理角色
//客户:要租房子的人
public class Client {
public static void main(String[] args) {
//房东要出租房子
Host host = new Host();
//代理帮助房东出租房子,但是有下面的一些附属操作
Proxy proxy = new Proxy(host);
proxy.rent();
//看房子,签合同,收中介费
proxy.seeHome();
proxy.hetong();
proxy.fire();
}
}
案例二:加深理解代理模式
接口:抽象角色
//抽象角色,真实角色需要做的一些事
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
真实角色:只实现增删改查的方法【纯粹】
//真实角色
public class UserServiseImpl implements UserService{
public void add() {
System.out.println("增加用户");
}
public void delete() {
System.out.println("删除用户");
}
public void update() {
System.out.println("更新用户");
}
public void query() {
System.out.println("查询用户");
}
}
代理角色:帮助真实角色,做所有的事
//代理角色:帮助真实角色,做所有的事
//这里是为每个方法增添了一个方法说明
public class UserServiceImplProxy implements UserService{
private UserServiseImpl userServise;
public UserServiceImplProxy() {
}
public UserServiceImplProxy(UserServiseImpl userServise) {
this.userServise = userServise;
}
public void add() {
lomgl("add");
userServise.add();
}
public void delete() {
lomgl("delete");
userServise.delete();
}
public void update() {
lomgl("update");
userServise.update();
}
public void query() {
lomgl("query");
userServise.query();
}
public void lomgl(String msg){
System.out.println("调用了"+msg+"方法");
}
}
客户端:访问代理角色
public class Client {
public static void main(String[] args) {
UserServiseImpl userService = new UserServiseImpl();
UserServiceImplProxy proxy = new UserServiceImplProxy(userService);
proxy.add();
}
}
静态代理的好处
- 可以使真实角色的操作更加的纯粹,不用关注一些公共的业务
- 公共的事情,就交给了代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
缺点:一个真实角色,就会产生一个代理角色;代码量会翻倍-开发效率降低
2、动态代理【Aop底层】
1、动态代理的角色和静态代理的角色一样,都有四个角色
2、动态代理的代理类是自动生成的,不是我们直接写好的
3、动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口-------JDK动态代理【我们在这里使用】
- 基于类:cglib
- java字节码实现:javasist
我们需要了解两个类:这两个类就是动态代理的核心
1、proxy:代理
2、InvocationHandler【接口】:调用处理程序
1.InvocationHandler
- InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;
- 在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。
代码:
Rent.java【抽象角色】
//抽象角色:就是真实角色需要做的事情
public interface Rent {
public void rent();
}
Rent.java【真实角色】
//真实角色:要出租房子的人
public class Host implements Rent{
public void rent() {
System.out.println("房东要出租房子了");
}
}
动态代理类ProxyInvocationHandler.java
//等会我们会用这个类,自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成得到代理类
/**
* 通过Proxy类的newProxyInstance方法创建代理对象,我们来看下方法中的参数
* 第一个参数:this.getClass().getClassLoader(),使用handler对象的classloader对象来加载我们的代理对象
* 第二个参数:this.getClass().getInterfaces(),这里为代理类提供的接口是真实对象实现的接口,这样代理对象就能像真实对象一样调用接口中的所有方法
* 第三个参数:this,我们将代理对象关联到上面的InvocationHandler对象上
*/
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
/**
* proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
* method:我们所要调用某个对象真实的方法的Method对象
* args:指代代理对象方法传递的参数
*/
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在真实的对象执行之前我们可以添加自己的操作
seeHome();
//动态代理的本质,就是使用反射机制实现
Object result = method.invoke(rent, args);
//在真实的对象执行之后我们可以添加自己的操作
fare();
return result;
}
public void seeHome(){
System.out.println("看房");
}
public void fare(){
System.out.println("收中介费");
}
}
客户端角色
public class Client {
@Test
public void test(){
//要代理的真实对象
Host host = new Host();
//代理对象的调用处理程序
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//设置需要代理的对象
//我们将要代理的真实对象传入set方法中,最终代理对象的调用处理程序会调用真实对象的方法
pih.setRent(host);
//生成动态代理类
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
动态代理的好处
- 可以使真实角色的操作更加的纯粹,不用关注一些公共的业务
- 公共的事情,就交给了代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理可以代理多个类,只要是实现了同一个接口即可
十二、AOP
什么是AOP
- AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译的方式和运动期动态代理实现程序功能的统一维护的一种技术。
- AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型。
- 利用AOP可以对业务逻辑的各个部分进行隔离,从而使业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的的效率。
AOP在Spring中的作用
提供声明式业务,允许一哦农户自定义切面
以下名词需要了解
- 横切关注点:跨越应用程序的多个模块的方法或功能。即是,语文i们业务扩及无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等。
- 切面(ASPECT):横切关注点被模块化的特殊对象。即,它是一个类。
- 通知(Advice):切面必须要完成的工作。即,它是一个类中的方法。
- 目标(Target):被通知的对象。
- 代理(Proxy):向目标应用统治之后创建的对象。
- 切入点(PointCut):切面通知执行的“地点”的定义。
- 连接点(JoinPoint):与切入点匹配的执行点。
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .
使用Spring实现AOP
使用AOP切入,需要导入一个依赖包【重点】
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<!--AOP依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
第一种方式【通过Spring API实现】
1、编写业务接口和实现类
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("更新了一个用户");
}
public void query() {
System.out.println("查询了一个用户");
}
}
2、编写增强类 , 我们编写两个 , 一个前置增强 一个后置增强
public class Log implements MethodBeforeAdvice {
/**
*
* method:要执行的目标对象的方法
* args:参数
* target:目标对象
*/
public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
public class AfterLog implements AfterReturningAdvice {
//returnValue:返回值
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
}
}
3、注册bean,到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"
<!--导入aop约束-->
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
<!--导入aop约束-->
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="userServiceImpl" class="com.sh.service.UserServiceImpl"/>
<bean id="log" class="com.sh.log.Log"/>
<bean id="afterlog" class="com.sh.log.AfterLog"/>
<!--方式一:使用原生Spring API接口-->
<!--配置AOP,需要导入AOP的约束-->
<aop:config>
<!--切入点:experssion:表达式,execution(从这个方法中切入)-->
<aop:pointcut id="pointcut" expression="execution(* com.sh.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加,将注册的bean映射到切入点中-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
4、测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口,不是实现类,所以使用UserService作为返回值类型
UserService userService = context.getBean("userServiceImpl", UserService.class);
userService.add();
}
}
知识点:
- Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业务加进来 .
- 实现公共业务的重复利用 . 领域业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理 .
第二种方式【自定义类实现】
1、写入一个切入类
/*
自定义切入类
*/
public class DiyPointCut {
public void before(){
System.out.println("========方法执行前========");
}
public void afeter(){
System.out.println("=======方法执行后=========");
}
}
2、在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"
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-->
<bean id="userServiceImpl" class="com.sh.service.UserServiceImpl"/>
<!--方式二自定义切面【重点】-->
<!--注册bean-->
<bean id="diy" class="com.sh.diy.DiyPointCut"/>
<!--配置切面-->
<aop:config>
<!--自定义切面,ref:将注册的bean对象,引入切面-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.sh.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="afeter" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>
execution表达式的使用
eg:execution(* com.sample.service.impl…*.*(…)) 解释如下:
符号 | 含义 |
---|---|
**execution() ** | **表达式的主体; ** |
**第一个”*“符号 ** | 表示返回值的类型任意; |
com.sample.service.impl | AOP所切的服务的包名,即,我们的业务部分 |
包名后面的”…“ | 表示当前包及子包 |
第二个”*“ | 表示类名,*即所有类。此处可以自定义,下文有举例 |
.*(…) | 表示任何方法名,括号表示参数,两个点表示任何参数类型 |
第三种方式【使用注解实现】
1、 编写一个注解实现的增强类
/**
* 方式三:使用注解
* @Aspect标注这个类是一个切面
*/
@Aspect
public class AnnotationPointCut {
@Before("execution(* com.sh.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("========方法执行前========");
}
@After("execution(* com.sh.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("=========方法执行后========");
}
//在环绕增强中,我们可以给定一个参数,代表我们要获取处理的切入的点jp
@Around("execution(* com.sh.service.UserServiceImpl.*(..))")
public void round(ProceedingJoinPoint jp) throws Throwable {
System.out.println("=========环绕前=========");
Object proceed = jp.proceed();//执行业务逻辑方法
System.out.println("========环绕后==========");
}
}
2、 在Spring配置文件中,注册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: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-->
<bean id="userServiceImpl" class="com.sh.service.UserServiceImpl"/>
<bean id="log" class="com.sh.log.Log"/>
<bean id="afterlog" class="com.sh.log.AfterLog"/>
<!--方式三:使用注解-->
<!--导入bean-->
<bean id="annotation" class="com.sh.diy.AnnotationPointCut"/>
<!--开启注解支持:有两种,见下文
JDK(默认:proxy-target-class="false"):表示使用jdk动态代理织入增强
cglib(proxy-target-class="true"):表示使用CGLib动态代理技术织入增强
-->
<aop:aspectj-autoproxy/>
</beans>
aop:aspectj-autoproxy:说明
-
通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspect切面的bean创建代理,织入切面。
-
当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被<aop:aspectj-autoproxy />隐藏起来了
-
<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,
-
<aop:aspectj-autoproxy poxy-target-class=“true”/>时,表示使用CGLib动态代理技术织入增强。
-
不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。
十三、整合Spring-mybatis
什么是 MyBatis-Spring?
- MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。
- 它将允许 MyBatis 参与到 Spring 的事务管理之中,将映射器 mapper 和
SqlSession
并注入到 bean中,以及将 Mybatis 的异常转换为 Spring 的DataAccessException
。 - 最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。
Mybatis-Spring的使用
方式一:SqlSessionFactoryBean【重点】
SqlSessionFactoryBean创建sqlSessionFactory
-
在基础的 MyBatis 用法中,是通过
SqlSessionFactoryBuilder
来创建SqlSessionFactory
的。 -
而在 MyBatis-Spring 中,则使用
SqlSessionFactoryBean
来创建。
1、导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>Spring-study</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Spring-10-mybatis</artifactId>
<!--导入依赖-->
<dependencies>
<!--mybatis所需的jar包-->
<!--导入数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!--导入mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--导入junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--导入log4j日志-->
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--注解实体类-->
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
</dependency>
<!--spring所需的jar包-->
<!--webmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
<!--spring要操作数据库,需要导入spring-jdbc包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.5</version>
</dependency>
<!--AOP依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<!--spring-mybatis整合的jar包-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
</dependencies>
<!--每个项目都必须导-->
<!--Maven项src/main/java目录下配置文件无法被导出或者生效的问题和处理方案-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
2、User.java(实体类)
@Data
public class User {
private int id;
private String name;
private String psw;
}
3、接口
public interface UserMapper {
List<User> getUser();
}
4、核心配置文件
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--mybatis核心配置文件-->
<configuration>
<!--以后这里面只写,别名的设置,和一些settings,其他的一些配置都在spring-dao.xml中配置-->
<typeAliases>
<typeAlias type="com.sh.pojo.User" alias="user"/>
</typeAliases>
</configuration>
5、UserMapper.xml
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.sh.mapper.UserMapper" >
<!--select查询语句-->
<select id="getUser" resultType="user">
select * from mybatis.user
</select>
</mapper>
6、spring-dao.xml(代码==图)
核心步骤
- 编写数据源配置
- 注册sqlSessionFactory
- 注册sqlSessionTemplate
- 需要给接口加实现类
- 将自己写的实现类,注入到Spring的bean中
- 测试
1、2、3就是一个公式,以后直接在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"
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">
<!--1、2、3就是一个公式,以后直接在applicationContext.xml中导入接口的实现类即可-->
<!--1、配置数据源DataSource:使用Spring的数据源替换Myabtis的配置 c3p0 dbcp druid
我们这里使用Spring提供的JDBC:org.springframework.jdbc.datasource
-->
<bean id="dataSource1" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--2、配置sqlSessionFactory-->
<bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--映射到数据源-->
<property name="dataSource" ref="dataSource1"/>
<!--绑定mybatis-config.xml的配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--绑定UserMapper.xml-->
<property name="mapperLocations" value="classpath:com/sh/mapper/*.xml"/>
</bean>
<!--
第一种方式方式用第三个bean,
第二种方式不用,因为它的实现类,继承了SqlSessionDaoSupport,这个实现类中有getSqlSession()方法,
通过这个方式,可以直接返回sqlSession对象
-->
<!--3、SqlSessionTemplate:就是我们使用的sqlSession对象-->
<bean id="sqlSession1" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能使用构造器注入sqlSessionFactory,因为这里的源码没有set方法-->
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory1"/>
</bean>
</beans>
applicationContext.xml
<!--导入spring-dao.xml-->
<import resource="spring-dao.xml"/>
<!--将自己写的实现类,注入到Spring的bean中-->
<bean id="userMapperImpl" class="com.sh.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession1"/>
</bean>
7、UserMapperImpl.java(实现UserMapper接口)
public class UserMapperImpl implements UserMapper {
//在原来,我们所有的操作,都使用sqlSession来执行
//现在,都是用SqlSessionTemplate;
private SqlSessionTemplate sqlSession;
//使用set方法注入sqlSession
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> getUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> user = mapper.getUser();
return user;
}
}
8、测试
@Test
public void getAllUser(){
//加载spring-bean.xml,获得ApplicationContext上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper userMapper = context.getBean("userMapperImpl", UserMapper.class);
List<User> user = userMapper.getUser();
for (User user1 : user) {
System.out.println(user1);
}
}
方式二:SqlSessionDaoSupport
SqlSessionDaoSupport
是一个抽象的支持类,用来为你提供 SqlSession
。调用 getSqlSession()
方法你会得到一个 SqlSessionTemplate
,之后可以用于执行 SQL 方法,就像下面这样:
1-5不变
6、编写UserMapperImpl2.java继承SqlSessionDaoSupport
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
public List<User> getUser() {
SqlSession sqlSession = getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> user = mapper.getUser();
return user;
}
}
7、spring-dao.xml
<!--1、2、3就是一个公式,以后直接在applicationContext.xml中导入接口的实现类即可-->
<!--1、配置数据源DataSource:使用Spring的数据源替换Myabtis的配置 c3p0 dbcp druid
我们这里使用Spring提供的JDBC:org.springframework.jdbc.datasource
-->
<bean id="dataSource1" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--2、配置sqlSessionFactory-->
<bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--映射到数据源-->
<property name="dataSource" ref="dataSource1"/>
<!--绑定mybatis-config.xml的配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--绑定UserMapper.xml-->
<property name="mapperLocations" value="classpath:com/sh/mapper/*.xml"/>
</bean>
<!--
第一种方式方式用第三个bean,
第二种方式不用,因为它的实现类,继承了SqlSessionDaoSupport,这个实现类中有getSqlSession()方法,通过这个方法,可以直接返回sqlSession对象
-->
<!--3、SqlSessionTemplate:就是我们使用的sqlSession对象-->
<bean id="sqlSession1" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能使用构造器注入sqlSessionFactory,因为这里的源码没有set方法-->
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory1"/>
</bean>
8、applicationContext.xml
<!--导入spring-dao.xml-->
<import resource="spring-dao.xml"/>
<!--第二种方式,得到sqlSession对象-->
<bean id="userMapperImpl2" class="com.sh.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory1"/>
</bean>
9、测试也不变
十四、声明式事务
什么是事务
事务是指是程序中一系列严密的逻辑操作,而且所有操作必须全部成功完成,否则在每个操作中所作的所有更改都会被撤消。
可以通俗理解为:就是把多件事情当做一件事情来处理,好比大家同在一条船上,要活一起活,要完一起完
事物的四个特性(ACID)
- 原子性(Atomicity)**:**操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。
- 一致性(Consistency)**:**事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。
- 隔离性(Isolation)**:**隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
- 持久性(Durability)**:**当事务正确完成后,它对于数据的改变是永久性的
Spring中的事务管理
1、声明式事务:AOP实现
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--1、配置数据源DataSource:使用Spring的数据源替换Myabtis的配置 c3p0 dbcp druid
我们这里使用Spring提供的JDBC:org.springframework.jdbc.datasource
-->
<bean id="dataSource1" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--这样就可以实现,事务的管理,要么所有的方法都成功,要么所有的方法都失败-->
<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource1"/>
</bean>
<!--结合AOP实现事务的织入-->
<!--配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给那些增删改配置事务-->
<!--配置事务的传播特性:propagation:不关增氧都要新建事务,一般都用这个-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="select" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--配置事务的切入-->
<aop:config >
<!--切入点为dao包下的所有类,所有方法-->
<aop:pointcut id="txPointCut" expression="execution(* com.sh.loyal.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
</beans>
2、编程式事务:需要在代码中,手动进行事务管理
为什么需要事务?
- 如果不配置事务,可能存在数据提交不一致的情况
Isolation)**:**隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
- 持久性(Durability):当事务正确完成后,它对于数据的改变是永久性的
Spring中的事务管理
1、声明式事务:AOP实现
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--1、配置数据源DataSource:使用Spring的数据源替换Myabtis的配置 c3p0 dbcp druid
我们这里使用Spring提供的JDBC:org.springframework.jdbc.datasource
-->
<bean id="dataSource1" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--这样就可以实现,事务的管理,要么所有的方法都成功,要么所有的方法都失败-->
<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource1"/>
</bean>
<!--结合AOP实现事务的织入-->
<!--配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给那些增删改配置事务-->
<!--配置事务的传播特性:propagation:不关增氧都要新建事务,一般都用这个-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="select" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--配置事务的切入-->
<aop:config >
<!--切入点为dao包下的所有类,所有方法-->
<aop:pointcut id="txPointCut" expression="execution(* com.sh.loyal.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
</beans>
2、编程式事务:需要在代码中,手动进行事务管理
为什么需要事务?
- 如果不配置事务,可能存在数据提交不一致的情况