参考狂神说视频学习
1、Spring框架
Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益 -----(摘自百度百科)
- Spring:春天 —> 给软件行业带来了春天
- 2002,首次推出了Spring框架的雏形:interface 21框架
- Spring框架即以interface 21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日,发布1.0正式版
- Rod Johnson,Spring Framework创始人,著名作者。
- Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架。
- 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 Web MVC:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.13</version>
</dependency>
优点:
- Spring是一个开源的免费的框架(容器)
- Spring是一个轻量级的、非入侵式的框架
- 控制反转(IOC),面向切面编程(AOP)
- 支持事务的处理,对框架整合的支持
总结:Spring就是一个轻量级的控制反转(IOC)和 面向切面编程(AOP)的框架
组成:
- 核心容器
- 这是Spring框架最基础的部分,它提供了依赖注入(DependencyInjection)特征来实现容器对Bean的管理。这里最基本的概念是BeanFactory,它是任何Spring应用的核心。BeanFactory是工厂模式的一个实现,它使用IoC将应用配置和依赖说明从实际的应用代码中分离出来。
- 应用上下文(Context)模块
- 核心模块的BeanFactory使Spring成为一个容器,而上下文模块使它成为一个框架。这个模块扩展了BeanFactory的概念,增加了对国际化(I18N)消息、事件传播以及验证的支持。另外,这个模块提供了许多企业服务,例如电子邮件、JNDI访问、EJB集成、远程以及时序调度(scheduling)服务。也包括了对模版框架例如Velocity和FreeMarker集成的支持。
- Spring的AOP模块
- Spring在它的AOP模块中提供了对面向切面编程的丰富支持。这个模块是在Spring应用中实现切面编程的基础。为了确保Spring与其它AOP框架的互用性,Spring的AOP支持基于AOP联盟定义的API。AOP联盟是一个开源项目,它的目标是通过定义一组共同的接口和组件来促进AOP的使用以及不同的AOP实现之间的互用性。通过访问他们的站点,你可以找到关于AOP联盟的更多内容。
- Spring的AOP模块也将元数据编程引入了Spring。使用Spring的元数据支持,你可以为你的源代码增加注释,指示Spring在何处以及如何应用切面函数。
- JDBC抽象和DAO模块
- 使用JDBC经常导致大量的重复代码,取得连接、创建语句、处理结果集,然后关闭连接。Spring的JDBC和DAO模块抽取了这些重复代码,因此你可以保持你的数据库访问代码干净简洁,并且可以防止因关闭数据库资源失败而引起的问题。
- 这个模块还在几种数据库服务器给出的错误消息之上建立了一个有意义的异常层。使你不用再试图破译神秘的私有的SQL错误消息!另外,这个模块还使用了Spring的AOP模块为Spring应用中的对象提供了事务管理服务。
- 对象/关系映射集成模块
- 对那些更喜欢使用对象/关系映射工具而不是直接使用JDBC的人,Spring提供了ORM模块。Spring并不试图实现它自己的ORM解决方案,而是为几种流行的ORM框架提供了集成方案,包括Hibernate、JDO和iBATIS SQL映射。Spring的事务管理支持这些ORM框架中的每一个也包括JDBC。
- Spring的Web模块
- Web上下文模块建立于应用上下文模块之上,提供了一个适合于Web应用的上下文。另外,这个模块还提供了一些面向服务支持。例如:实现文件上传的multipart请求,它也提供了Spring和其它Web框架的集成,比如Struts、WebWork。
- Spring的MVC框架
- Spring为构建Web应用提供了一个功能全面的MVC框架。虽然Spring可以很容易地与其它MVC框架集成,例如Struts,但Spring的MVC框架使用IoC对控制逻辑和业务对象提供了完全的分离。
- 它也允许你声明性地将请求参数绑定到你的业务对象中,此外,Spring的MVC框架还可以利用Spring的任何其它服务,例如国际化信息与验证。Spring框架Web页面乱码问题
- 在做java Web项目时,乱码问题时常都会出现,解决方法也不尽相同,有简单也有复杂的;如果加入了Spring框架之后就不一样了,可以采用Spring框架自带的过滤器CharacterEncodingFilter,这样可以大大减轻了我们的工作量,即简单方便又容易理解
拓展:现代化的Java开发,说白就是基于Spring的开发
- SpringBoot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速的开发单个微服务
- 约定大于配置
- SpringCloud
- SpringCloud是基于SpringBoot实现的
现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring及SpringMVC,承上启下的作用
弊端:发展太久之后,违背了原来的理念。配置十分繁琐,人称 - 配置地狱
2、IOC理论推导
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从IoC容器中取出需要的对象
在我们平时的业务逻辑中,通常使用MVC三层架构的形式;用test下面的类充当controller层
UserServiceImpl
package com.blb.service;
import com.blb.dao.UserDao;
import com.blb.dao.UserDaoImpl;
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
public void getUser() {
userDao.getUser();
}
}
MyTest
import com.blb.service.UserServiceImpl;
public class MyTest {
public static void main(String[] args) {
// 用户实际调用的是业务层, dao层他们不需要接触
UserServiceImpl userServiceImpl = new UserServiceImpl();
userServiceImpl.getUser();
}
}
那么我们要使用Mysql,又要去service实现类里面修改对应的实现,假设我们的这种需求非常大,这种方式就根本不适用了,甚至反人类对吧,每次变动,都需要修改大量的代码。这种设计的耦合性太高了,牵一发而动全身
在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码!如果程序代码量十分大,修改一次的成本代价十分昂贵
所以利用set进行动态实现值的注入!,已经发生了革命性的变化
UserServiceImpl.java:
package com.blb.service;
import com.blb.dao.UserDao;
import com.blb.dao.UserDaoImpl;
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 利用set进行动态实现值的注入!
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void getUser() {
userDao.getUser();
}
}
MyTest.java
import com.blb.dao.UserDaoImpl;
import com.blb.dao.UserDaoMysqlImpl;
import com.blb.service.UserService;
import com.blb.service.UserServiceImpl;
public class MyTest {
public static void main(String[] args) {
// 用户实际调用的是业务层, dao层他们不需要接触
UserService userService = new UserServiceImpl();
// 接口不能使用实现类特有的方法, 需要强转
((UserServiceImpl) userService).setUserDao(new UserDaoImpl());
userService.getUser();
}
}
两次的结果并不相同,而仅仅是在一个类中改变了new的对象(这里不需要在UserServiceImpl类中修改代码去实现不同需求,而是在Service层外的Servlet或叫Control层里,通过外部页面传递进来的请求参数来确定实现new哪个UserDao实现类)
好处:
- 之前,程序是主动创建对象!控制权在程序猿手上
- 使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象
- 这种思想,从本质上解决了问题,我们程序猿不用再去管理对象的创建了
- 系统的耦合性大大降低,可以更加专注的在业务的实现上,这是IOC的原型
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)
3、Hello Spring
创建一个Hello.java
package com.blb.pojo;
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
然后在resources存放beans.xml,准备一个测试类(MyTest.java)
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">
<!-- 使用Spring来创建对象, 在Spring这些都称为Bean (id 随便写)
类型变量名 = new 类型();
Hello hello = new Hello();
bean = 对象 new Hello();
id = 变量名
class = new的对象
property 相当于给对象中属性设置一个值
-->
<bean id="hello" class="com.blb.pojo.Hello">
<property name="str" value="Spring" />
</bean>
</beans>
MyTest.java
import com.blb.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
// 获取Spring的上下文的对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 我们的对象现在都在Spring中的管理了, 我们要使用, 直接去里面取出来就可以了
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
Hello对象是谁创建的?
- Hello对象是由Spring创建的
Hello对象的属性是怎么设置的?
- Hello对象的属性是由Spring容器设置的
这个过程就叫控制反转:
- 控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的
- 反转:程序本身不创建对象,而变成被动的接收对象
- 依赖注入:就是利用set方法来进行注入的
IOC是一种编程思想,由主动的编程变成被动的接受,可以通过new ClassPathXmlApplicationContext
去浏览一下底层源码
到现在,我们彻底不用再程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IoC:对象由Spring来创建,管理,装配!
可以换一种思想来理解:
- 现在这套程序是:你告诉楼下餐厅,你要哪些菜,客人来的时候,餐厅把你需要的菜做好了送上来,就相当于你请人吃饭
- 原来那套程序是:你写好菜单买好菜,客人来了自己把菜炒好招待
- 此时的区别就是:如果我还需要做其他的菜,我不需要自己搞菜谱买材料再做好,而是告诉餐厅,我要什么菜,什么时候要,你做好送来
对于第一个程序而言,我们可以做一个改动:
beanx.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">
<bean id="mysqlImpl" class="com.blb.dao.UserDaoMysqlImpl" />
<bean id="UserServiceImpl" class="com.blb.service.UserServiceImpl">
<!--
ref: 引用Spring容器中创建好的对象
value: 具体的值, 基本数据类型
-->
<property name="userDao" ref="mysqlImpl" />
</bean>
</beans>
MyTest.java
import com.blb.dao.UserDaoMysqlImpl;
import com.blb.service.UserService;
import com.blb.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
// 用户实际调用的是业务层, dao层他们不需要接触
UserService userService = new UserServiceImpl();
// 接口不能使用实现类特有的方法, 需要强转
((UserServiceImpl) userService).setUserDao(new UserDaoMysqlImpl());
userService.getUser();
System.out.println("--------------------------");
// 获取ApplicationContext: 拿到Spring的容器
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 容器在手, 天下我有; 需要什么, 就直接get什么
UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("UserServiceImpl");
userService.getUser();
}
}
4、IOC创建方式
1、使用无参构造创建对象,默认!
User:(无参构造是默认的,除非设置有参构造)
package com.blb.pojo;
public class User {
private String name;
public User(){
System.out.println("User的无参构造");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.blb.pojo.User">
<property name="name" value="你好" />
</bean>
</beans>
MyTest.java
import com.blb.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
user.show();
}
}
2、假设我们要使用有参构造创建对象
MyTest.java
import com.blb.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
user.show();
}
}
- 第一种方式:下标赋值
<bean id="user" class="com.blb.pojo.User"> <!-- <constructor-arg value="你好"/>--> <!-- 第一种方式: 下标赋值 --> <constructor-arg index="0" value="你好" /> </bean>
- 第二种方式:通过类型创建,但不建议使用
<bean id="user" class="com.blb.pojo.User"> <constructor-arg type="java.lang.String" value="你好" /> </bean>
- 第三种方式:直接通过参数名
<bean id="user" class="com.blb.pojo.User"> <constructor-arg name="name" value="你好" /> </bean>
现在我再创建一个UserT类,里面也有私有属性和构造方法
package com.blb.pojo;
public class UserT {
private String name;
public UserT(){
System.out.println("UserT被创建了");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name=" + name);
}
}
然后在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">
<bean id="user" class="com.blb.pojo.User">
<constructor-arg name="name" value="你好" />
</bean>
<bean id="userT" class="com.blb.pojo.UserT">
</bean>
</beans>
MyTest.java
import com.blb.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
// Spring容器: 类似婚介网站
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user1 = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
user1.show();
System.out.println(user1 == user2);
}
}
从结果上看,我并没有初始化UserT,而结果却有UserT的无参构造方法的结果
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了
5、Spring配置说明
1、别名
beans.xml
<bean id="user" class="com.blb.pojo.User">
<constructor-arg name="name" value="你好" />
</bean>
<!-- 别名: 如果添加了别名, 我们也可以使用别名获取到这个对象 -->
<alias name="user" alias="userNew" />
MyTest.java
import com.blb.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
// Spring容器: 类似婚介网站
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user1 = (User) context.getBean("userNew");
user1.show();
}
}
2、Bean的配置
beanx.xml
<bean id="user" class="com.blb.pojo.User">
<constructor-arg name="name" value="你好" />
</bean>
<!--
id: bean的唯一标识符, 也就是相当于我们学的对象名
class: bean对象所对应的权限定名: 包名 + 类型
name: 也是别名, 而且name更高级(可以取多个别名), 多种符号分割名称都行
-->
<bean id="userT" class="com.blb.pojo.UserT" name="user2 u2, u3; u4">
<property name="name" value="你好" />
</bean>
MyTest.java
import com.blb.pojo.User;
import com.blb.pojo.UserT;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
// Spring容器: 类似婚介网站
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserT user1 = (UserT) context.getBean("u4");
user1.show();
}
}
3、import
这个import,一般用于团队开发使用,他可以将多个配置文件,导入合并为一个
假设,现在项目中有很多人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!
- 张三
- 李四
- 王五
- applicationContext.xml
<import resource="beans.xml" /> <import resource="beans2.xml" /> <import resource="beans3.xml" />
使用的使用,直接使用总的配置就好了
MyTest.java(虽然导入的是applicationContext.xml,但是在applicationContext.xml中import了其他xml文件)
import com.blb.pojo.User;
import com.blb.pojo.UserT;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
// Spring容器: 类似婚介网站
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserT user1 = (UserT) context.getBean("u4");
user1.show();
}
}
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">
<bean id="userT" class="com.blb.pojo.UserT" name="user2 u2, u3; u4">
<property name="name" value="你好" />
</bean>
</beans>
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">
<import resource="beans.xml" />
<import resource="beans2.xml" />
<import resource="beans3.xml" />
</beans>
6、依赖注入( DI注入 )
1、构造器注入
2、set注入
- 依赖注入:本质是set注入
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入
环境搭建:
-
复杂类型
public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
-
真实测试对象
public class Student { private String name; private Address address; private String[] books; private List<String> hobbies; 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> getHobbies() { return hobbies; } public void setHobbies(List<String> hobbies) { this.hobbies = hobbies; } 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; } }
-
beans.xml
<bean id="student" class="com.blb.pojo.Student"> <!-- 第一种: 普通值注入, value --> <property name="name" value="你好" /> </bean>
-
测试类
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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address" class="com.blb.pojo.Address" />
<bean id="student" class="com.blb.pojo.Student">
<!-- 第一种: 普通值注入, value -->
<property name="name" value="你好" />
<!-- 第二种: Bean注入, ref -->
<property name="address" ref="address" />
<!-- 数组注入 -->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>三国演义</value>
<value>水浒传</value>
</array>
</property>
<!-- List注入 -->
<property name="hobbies">
<list>
<value>听歌</value>
<value>敲代码</value>
<value>看电影</value>
</list>
</property>
<!-- Map注入 -->
<property name="card">
<map>
<entry key="身份证" value="123456" />
<entry key="银行卡" value="1234567890" />
</map>
</property>
<!-- Set注入 -->
<property name="games">
<set>
<value>LOL</value>
<value>DOTA</value>
</set>
</property>
<!-- null注入 -->
<property name="wife">
<null />
</property>
<!-- Properties注入 -->
<property name="info">
<props>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
MyTest.java
import com.blb.pojo.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
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.toString());
}
}
打印出的信息
Student{
name='你好', address=Address{address='null'},
books=[红楼梦, 西游记, 三国演义, 水浒传],
hobbies=[听歌, 敲代码, 看电影],
card={身份证=123456, 银行卡=1234567890},
games=[LOL, DOTA],
wife='null',
info={password=123456, username=root}
}
3、拓展注入
注意点:p命名和c命名空间不能直接使用,需要导入xml约束
<!-- p命名空间注入, 可以直接注入属性的值 -->
xmlns:p="http://www.springframework.org/schema/p"
<!-- c命名空间注入, 可以通过构造器注入, construct-args -->
xmlns:c="http://www.springframework.org/schema/c"
-
p命名空间注入
<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- p命名空间注入, 可以直接注入属性的值 --> <bean id="user" class="com.blb.pojo.User" p:name="你好" p:age="18" /> </beans>
User.java
package com.blb.pojo; public class User { private String name; private int age; public String getName() {return name;} public void setName(String name) {this.name = name;} public int getAge() {return age;} public void setAge(int age) {this.age = age;} @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
MyTest.java
import com.blb.pojo.Student; import com.blb.pojo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; 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.toString()); } @Test public void test2(){ ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml"); User user = context.getBean("user", User.class); System.out.println(user); } }
关于@Test注解,因为我们不需要测试一个类,就要在main方法里面进行改写代码,可以通过在@Test注解下的方法中进行测试,但需要在pom.xml中导入
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
-
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:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- c命名空间注入, 可以通过构造器注入, construct-args --> <bean id="user2" class="com.blb.pojo.User" c:name="世界" c:age="20" /> </beans>
User.java(有参构造)
public User(String name, int age) { this.name = name; this.age = age; }
MyTest.java
import com.blb.pojo.Student; import com.blb.pojo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; 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.toString()); } @Test public void test2(){ ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml"); User user = context.getBean("user2", User.class); System.out.println(user); } }
Bean的作用域
Scope | Description |
---|---|
singleton | (默认的)在每个Spring IoC容器中,一个bean定义对应只会有唯一的一个bean实例 |
prototype | 一个bean定义可以有多个bean实例 |
request | 一个bean定义对应于单个HTTP请求的生命周期。也就是说,每个HTTP请求都有一个bean实例,且该实例仅在这个HTTP请求的生命周期里有效。该作用域仅适用于WebApplicationContext环境 |
session | 一个bean定义对应于单个HTTP Session的生命周期,也就是说,每个HTTP Session都有一个bean实例,且该实例仅在这个HTTP Session的生命周期里有效。该作用域仅适用于WebApplicatonContext环境 |
application | 一个bean定义对应于单个ServletContext的生命周期。该作用域仅适用于WebApplicationContext环境 |
websocket | 一个bean定义对应于单个websocket的生命周期。该作用域仅适用于WebApplicationContext环境 |
The Singleton Scope
全局共享一个bean实例,下面实例中,我们看似创建两个bean,但实际上Spring当成一个
import com.blb.pojo.Student;
import com.blb.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
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.toString());
}
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user = context.getBean("user2", User.class);
User user2 = context.getBean("user2", User.class);
System.out.println(user == user2);
}
}
1、单例模式:Spring默认机制(单线程情况下使用)
<bean id="user2" class="com.blb.pojo.User" c:name="世界" c:age="20" scope="singleton" />
2、原型模式:每次从容器中get的时候,都会产生一个新对象(使用多线程可以选择使用,不过最好使用单例模式,因为是默认机制)
<bean id="user2" class="com.blb.pojo.User" c:name="世界" c:age="20" scope="prototype" />
3、其余的request、session、application、websocket,这些只能在web开发中使用到
7、Bean的自动装配
- 自动装配是Spring满足Bean依赖的一种方式
- Spring会在上下文中自动寻找,并自动给bean装配属性
在Spring中有三种自动装配的方式
- 1、在xml中显示的配置
- 2、在Java中显示配置
- 3、隐式的自动装配Bean(重点)
环境搭建:一个人有两个宠物
Dog.java
package com.blb.pojo;
public class Dog {
public void shot(){
System.out.println("汪汪汪");
}
}
cat.java
package com.blb.pojo;
public class Cat {
public void shot(){
System.out.println("喵喵喵");
}
}
person.java
package com.blb.pojo;
public class Person {
private Dog dog;
private Cat cat;
private String name;
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"dog=" + dog +
", cat=" + cat +
", name='" + name + '\'' +
'}';
}
}
MyTest.java
import com.blb.pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person person = context.getBean("person", Person.class);
person.getDog().shot();
person.getCat().shot();
}
}
在未使用自动装配前,对象一个一个引入
<?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.blb.pojo.Cat" />
<bean id="dog" class="com.blb.pojo.Dog" />
<bean id="person" class="com.blb.pojo.Person">
<property name="name" value="你好" />
<property name="dog" ref="dog" />
<property name="cat" ref="cat" />
</bean>
</beans>
在使用自动装配后
-
byName自动装配:需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性set方法的值一致
<bean id="cat" class="com.blb.pojo.Cat" /> <bean id="dog" class="com.blb.pojo.Dog" /> <bean id="person" class="com.blb.pojo.Person" autowire="byName"> <property name="name" value="你好" /> </bean>
-
byType自动装配:需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致
<bean class="com.blb.pojo.Cat" /> <bean class="com.blb.pojo.Dog" /> <bean id="person" class="com.blb.pojo.Person" autowire="byType"> <property name="name" value="你好" /> </bean>
使用注解实现自动装配
JDK1.5支持的注解,Spring2.5就支持注解
The introduciton 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
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.blb.pojo.Cat" />
<bean id="dog" class="com.blb.pojo.Dog" />
<bean id="person" class="com.blb.pojo.Person" />
</beans>
@Autowired
- 默认按照bean的类型进行装配
- @AutoWried按by type自动注入
- 直接在属性上使用即可,也可以在set方式上使用
- 使用Autowired我们可以不用编写Set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byName
person.java
package com.blb.pojo;
import org.springframework.beans.factory.annotation.Autowired;
public class Person {
@Autowired
private Dog dog;
@Autowired
private Cat cat;
private String name;
public Dog getDog() { return dog; }
public Cat getCat() { return cat; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@Override
public String toString() {
return "Person{" +
"dog=" + dog +
", cat=" + cat +
", name='" + name + '\'' +
'}';
}
}
科普:
-
@Nullable:字段标记了这个注解,说明这个字段可以为null
public @interface Autowired { boolean required() default true; // 默认为真 }
// 如果显示定义了Autowired的required属性为false, 说明这个对象可以为null, 否则为空 @Autowired(required = false) private Dog dog;
举例:
public Person(@Nullable String name) { this.name = name; }
-
@Qualifier:
- 如果有默认的与属性名称完全相同的bean id,也可以不用qualifier指定具体的
- 如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier(value = “XXX”)去配合@Autowired的使用,指定一个唯一的bean对象注入
@Autowired @Qualifier(value = "dog") private Dog dog; @Autowired @Qualifier(value = "cat222") private Cat cat;
-
@Resource注解:有两个重要属性,分别是name和type
- 既不指定name属性,也不指定type属性,则自动按byName方式进行查找。如果没有找到符合的bean,则回退为一个原始类型进行查找,如果找到就注入(此时name是变量名)
- 只是指定了@Resource注解的name,则按name后的名字去bean元素里查找有与之相等的name属性的bean
- 只指定@Resource注解的type属性,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
- 既指定了@Resource的name属性又指定了type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
@Resource(name = "dog222") private Dog dog;
<bean id="cat222" class="com.blb.pojo.Cat" /> <bean id="dog111" class="com.blb.pojo.Dog" /> <bean id="dog222" class="com.blb.pojo.Dog" /> <bean id="person" class="com.blb.pojo.Person" />
小结:@Resource和@Autowired的区别
- 都是用来自动装配的,都可以放在属性字段上
- @Autowired通过byType的方式实现,而且必须要求这个对象存在
- @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
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config />
</beans>
@Component:组件, 放在类上, 说明这个类被Spring管理了, 就是bean
-
User.java
package com.blb.dao; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component // 等价于 <bean id="user" class="com.blb.dao.User" /> // @Component 组件 public class User { @Value("你好") // 相当于 <property name="name" value="你好" /> public String name; @Value("你好") // 相当于 <property name="name" value="你好" /> // 也可以注入进来 public void setName(String name){ this.name = name; } }
-
applicationContext.xml
<!-- 指定要扫面的包, 这个包下的注解就会生效 --> <context:component-scan base-package="com.blb.dao" /> <context:annotation-config />
-
MyTest.java
import com.blb.dao.User; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); User user = context.getBean("user", User.class); System.out.println(user.name); } }
@Component:有几个衍生注解,我们在web开发中,会按照MVC三层架构分层
- dao【@Repository】
- service【@Service】
- controller【@Controller】
这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean
自动装配的注解:
- @Autowired:自动装配通过类型, 名字;如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value = “xxx”)
- @Nullable:字段标记了这个注解, 说明这个字段可以是null
- @Resource:自动装配通过名字, 类型
作用域:
@Scope("singleton") // @Scope("prototype")
public class User {
@Value("你好")
public String name;
}
小结:
- xml和注解
- xml更加万能,适用于任何场合,维护简单方便
- 注解 不是自己类使用不了,维护相当复杂
- xml与注解最佳实践:
- xml用来管理bean
- 注解只负责完成属性的注入
- 我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持
9、使用Java的方式配置Spring
我们现在要完全不使用Spring的xml配置了,全权交给Java来做
JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能
User.java(实体类)
package com.blb.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
// 这个注解的意思, 就是说明这个类被Spring接管了, 注册到了容器中
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 + '\'' +
'}';
}
}
MyConfig.java(配置文件)
package com.blb.config;
import com.blb.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
// 这个也会Spring容器托管, 注册到容器中, 因为他本来就是一个@Component
// @Configuration代表这是一个配置类, 就和我们之前看的beans.xml一样
@ComponentScan("com.blb.pojo")
@Import(MyConfig2.class)
public class MyConfig {
@Bean
// 注册一个bean, 就相当于我们之前写的一个bean标签
// 这个方法的名字, 就相当于bean标签中的id属性
// 这个方法的返回值, 就相当于bean标签中的class属性
public User getUser(){
return new User(); // 就是返回要注入到bean的对象
}
}
而仅仅是使用普通Java方法所创建的普通对象,这个对象不被Spring管理, 那么我们所定义所有的Bean的后置处理器都没有作用到它身上
不适用@Configuration依旧能够完成对象的实例化,简单的情况下可能没问题,但如果假如有一个class User,一个class Person的有参构造方法中需要传入一个User对象,并且在config类中创建对象使用的是Person的有参构造方法,这种依赖关系就得在创建时使用传入参数getUser()方法,这样的话,对于User来说,他被实例化了两次,单例被打破了,对于Person来说,他所依赖的User不是由Spring创建的
就是说,@Bean是相当于<bean>
标签创建的对象,而我们之前学的@Component是通过spring自动创建的这个被注解声明的对象;所以这里相当于有两个User对象被创建了,一个是bean标签创建的(@Bean),一个是通过扫描然后使用@Component,spring自动创建的User对象;所以这里去掉@Bean这些东西,然后开启扫描,之后在User头上用@Component即可达到spring自动创建User对象了
MyTest.java(测试类)
import com.blb.config.MyConfig;
import com.blb.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest {
public static void main(String[] args) {
// 如果完全使用了配置类方法去做, 我们就只能通过AnnotationConfig上下文来获取容器, 通过配置类的class对象加载
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = context.getBean("getUser", User.class);
System.out.println(user.getName());
}
}
这种纯Java的配置方式,在SpringBoot中随处可见
10、代理模式
代理模式的分类:
- 静态模式
- 动态代理
静态代理
- 角色分析
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色后,代理真实角色后,我们一般会做一些附属操作
- 客户:访问代理对象的人
代码步骤:
- 1、接口
// 租房 public interface Rent { public void rent(); }
- 2、真实角色
// 房东 public class Host implements Rent { public void rent() { System.out.println("房东要出租房子!!!"); } }
- 3、代理角色
public class Proxy implements Rent { private Host host; public Proxy(Host host) { this.host = host; } public Proxy() { } public void rent() { seeHouse(); host.rent(); contract(); fee(); } // 看房 public void seeHouse(){ System.out.println("中介带你看房"); } // 收中介费 public void fee(){ System.out.println("收中介费"); } // 合同 public void contract(){ System.out.println("签租赁合同"); } }
- 4、客户端访问代理角色
public class Client { public static void main(String[] args) { // 代理(中介帮房东租房子, 但是呢? 代理一般会有一些附属操作) Proxy proxy = new Proxy(new Host()); // 你不用面对房东, 直接找中介租房子即可 proxy.rent(); } }
代理模式的好处:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共也就交给代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
代理模式的缺点:
- 一个真实的角色就会产生一个代理角色,代码量会翻倍,开发效率会变低
加深理解:
- UserService.java
public interface UserService { public void add(); public void delete(); public void update(); public void select(); }
- UserServiceImpl.java
// 真实对象 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 select() { System.out.println("查找了一个用户"); } // 1、改动原有的业务代码, 在公司中是大忌 }
- UserServiceProxy.java
public class UserServiceProxy implements UserService { private UserServiceImpl userService; public void setUserService(UserServiceImpl userService) { this.userService = userService; } public void add() { log("add"); userService.add(); } public void delete() { log("delete"); userService.delete(); } public void update() { log("update"); userService.update(); } public void select() { log("select"); userService.select(); } // 日志方法 public void log(String msg){ System.out.println("使用了" + msg + "方法"); } }
- Client.java
public class Client { public static void main(String[] args) { UserServiceProxy userServiceProxy = new UserServiceProxy(); userServiceProxy.setUserService(new UserServiceImpl()); userServiceProxy.add(); } }
动态代理:
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口 — JDK 动态代理【我们在这里使用】
- 基于类:cglib
- Java字节码实现:Javassist
需要了解两个类:
- Proxy(代理)
- 提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类
- InvocationHandler(调用处理程序):
- 由代理实例的 调用处理程序实现的接口;
- 每个代理实例都有一个关联的调用处理程序
- 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法
ProxyInvocationHandler.java
// 等我们会用这个类, 自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
/*
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class<?>[] { Foo.class },
handler);
*/
// 被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
// 生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
}
// 处理代理实例, 并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
// 动态代理的本质, 就是使用反射机制实现
Object result = method.invoke(rent, args);
return result;
}
public void seeHouse(){
System.out.println("中介带去看房子");
}
}
Client.java
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就是动态生成的, 我们并没有写
proxy.rent();
}
}
接下来,我们可以通过改变私有属性来写一个万能模板(Object类型)
// 等我们会用这个类, 自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
// 生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
// 处理代理实例, 并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 动态代理的本质, 就是使用反射机制实现
Object result = method.invoke(target, args);
return result;
}
}
动态代理的好处:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共也就交给代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要实现了同一个接口即可
11、AOP
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
Aop在Spring中的作用:
- 提供声明式事务;允许用户自定义切面
- 横切关注点:跨域应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…
- 切面(ASPECT):横向关注点,被模块化的特殊对象。即它是一个类
- 通知(Advice):切面必须要完成的工作。即它是类中的一个方法
- 目标(Target):被通知的对象
- 代理(Proxy):向目标对象应用通知之后创建的对象
- 切入点(PointCut):切面通知执行的 “地点” 的定义
- 连接点(JointPoint):与切入点匹配的执行点
在SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice
通知类型 | 连接点 | |
---|---|---|
前置通知 | 方法前 | org.springframework.aop.MethodBeforeAdvice |
后置通知 | 方法后 | org.springframework.aop.AfterReturningAdvice |
环绕通知 | 方法前后 | org.aopalliance.intercept.MethodInterceptor |
异常抛出通知 | 方法抛出异常 | org.springframework.aop.ThrowsAdvice |
引介通知 | 类中增加新的方法属性 | org.springframework.aop.IntroductionInterceptor |
即Aop在不改变原有代码的情况下,去增加新的功能
方式一:使用Spring实现Aop:【重点】使用AOP植入,需要导入一个依赖包【主要是SpringAPI接口实现】
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
AfterLog.java
package com.blb.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
@Override
// returnValue: 返回值
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法, 返回结果为: "+returnValue);
}
}
log.java
package com.blb.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class log implements MethodBeforeAdvice {
@Override
// method: 要执行的目标对象的方法
// objects: 参数
// target: 目标对象
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
UserService.java
package com.blb.service;
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
UserServiceImpl.java
package com.blb.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("删除了一个用户");
}
}
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:context="http://www.springframework.org/schema/context" 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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config />
<!-- 注册bean -->
<bean id="userService" class="com.blb.service.UserServiceImpl" />
<bean id="log" class="com.blb.log.log" />
<bean id="afterLog" class="com.blb.log.AfterLog" />
<!-- 方式一: 使用原生Spring API接口 -->
<!-- 配置AOP: 需要导入AOP的约束 -->
<aop:config>
<!-- 切入点: expression表达式, execution(要执行的位置) *(修饰词) *(返回值) *(列名) *(方法名) *(参数) -->
<aop:pointcut id="pointcut" expression="execution(* com.blb.service.UserServiceImpl.*(..))" />
<!-- 执行环绕增加 -->
<aop:advisor advice-ref="log" pointcut-ref="pointcut" />
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut" />
</aop:config>
</beans>
MyTest.java
import com.blb.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 动态代理代理的是接口
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
方式二:自定义来实现AOP【主要是切面定义】
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:context="http://www.springframework.org/schema/context" 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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config />
<!-- 注册bean -->
<bean id="userService" class="com.blb.service.UserServiceImpl" />
<bean id="log" class="com.blb.log.log" />
<bean id="afterLog" class="com.blb.log.AfterLog" />
<!-- 方式二: 自定义类 -->
<bean id="diy" class="com.blb.diy.DiyPointCut" />
<aop:config>
<!-- 自定义切面: ref 要引用的类 -->
<aop:aspect ref="diy">
<!-- 切入点 -->
<aop:pointcut id="point" expression="execution(* com.blb.service.UserServiceImpl.*(..))"/>
<!-- 通知 -->
<aop:before method="before" pointcut-ref="point" />
<aop:after method="after" pointcut-ref="point" />
</aop:aspect>
</aop:config>
</beans>
DiyPointCut.java
package com.blb.diy;
public class DiyPointCut {
public void before(){
System.out.println("========方法执行前========");
}
public void after(){
System.out.println("========方法执行后========");
}
}
方式三:使用注解实现
AnnotationPointCut.java
package com.blb.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;
// 方式三: 使用注解的方式实现AOP
@Aspect // 标注这个类是一个切面
public class AnnotationPointCut {
@Before("execution(* com.blb.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("====方法执行前====");
}
@After("execution(* com.blb.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("====方法执行后====");
}
// 在环绕增强中, 我们可以给定一个参数, 代表我们要获取处理切入的点
@Around("execution(* com.blb.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp){
System.out.println("环绕前");
Signature signature = jp.getSignature(); // 获得签名
System.out.println("signature" + signature);
// 执行方法(过滤器)
try {
Object proceed = jp.proceed();
System.out.println("环绕后");
System.out.println(proceed);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
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:context="http://www.springframework.org/schema/context" 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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注册bean -->
<bean id="userService" class="com.blb.service.UserServiceImpl"
<!-- 方式三 -->
<bean id="annotationPointCut" class="com.blb.diy.AnnotationPointCut" />
<!-- 开启注解支持, JDK(默认 proxy-target-class="false") cglib -->
<aop:aspectj-autoproxy proxy-target-class="false" />
</beans>
12、整合Mybatis
步骤:
- 1、导入相关jar包
- junit
- mybatis
- mysql数据库
- spring相关的
- aop植入
- mybatis-spring【new】
- 2、编写配置文件
- 3、测试
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.14</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!-- Spring操作数据库的话, 还需要一个spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
回忆mybatis
- 1、编写实体类
- 2、编写核心配置文件
- 3、编写接口
- 4、编写Mapper.xml
- 5、测试
1、编写实体类
package com.blb.pojo;
import lombok.Data;
@Data
public class User {
private int id;
private String name;
private String pwd;
}
2、编写核心配置文件
<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- configuration核心配置文件 -->
<configuration>
<typeAliases>
<package name="com.blb.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="zya11230318"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.blb.mapper.UserMapper" />
</mappers>
</configuration>
3、编写接口
package com.blb.mapper;
import com.blb.pojo.User;
import java.util.List;
public interface UserMapper {
List<User> selectUser();
}
4、编写Mapper.xml
<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- configuration核心配置文件 -->
<mapper namespace="com.blb.mapper.UserMapper">
<select id="selectUser" resultType="com.blb.pojo.User">
select * from Mybatis.userone;
</select>
</mapper>
5、测试
import com.blb.mapper.UserMapper;
import com.blb.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyTest {
@Test
public void test() throws IOException {
String resources = "mybatis-config.xml";
InputStream in = Resources.getResourceAsStream(resources);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = sessionFactory.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.selectUser();
for (User user:userList) {
System.out.println(user);
}
}
}
整合Mybatis
-
1、编写数据源配置
-
2、sqlSessionFactory
-
3、sqlSessionTemplate
-
spring-dao.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"> <!-- DataSource: 使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid 我们这里使用Spring提供的JDBC: org.springframework.jdbc.datasource --> <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&characterEncoding=utf-8&serverTimezone=GMT%2B8"/> <property name="username" value="root"/> <property name="password" value="zya11230318"/> </bean> <!-- sqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="datasource" /> <!-- 绑定Mybatis的配置文件 --> <property name="configLocation" value="classpath:mybatis-config.xml" /> <property name="mapperLocations" value="classpath:com/blb/mapper/*.xml" /> </bean> <!-- SqlSessionTemplate: 就是我们使用的sqlSession --> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!-- 只能使用构造器注入sqlSessionFactory, 因为它没有set方法 --> <constructor-arg index="0" ref="sqlSessionFactory" /> </bean> </beans>
-
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"> <import resource="spring-dao.xml" /> <!-- --> <bean id="userMapper" class="com.blb.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSession" /> </bean> </beans>
-
-
4、需要给接口加实现类
- UserMapperImpl.java
package com.blb.mapper; import com.blb.pojo.User; import org.mybatis.spring.SqlSessionTemplate; import java.util.List; public class UserMapperImpl implements UserMapper { // 我们的所有操作, 都使用sqlSession来执行, 在原来, 现在都使用SqlSessionTemplate; private SqlSessionTemplate sqlSession; public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } @Override public List<User> selectUser() { return sqlSession.getMapper(UserMapper.class).selectUser(); } }
- UserMapperImpl.java
-
5、将自己写的实现类,注入到Spring中,使用即可
- MyTest.java
import com.blb.mapper.UserMapper; import com.blb.pojo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserMapper userMapper = context.getBean("userMapper", UserMapper.class); for (User user: userMapper.selectUser()) { System.out.println(user); } } }
- MyTest.java
SqlSessionDaoSupport【了解即可】:是一个抽象的支持类,用来为你提供SqlSession。调用getSqlSession()方法你会得到一个SqlSessionTemplate,之后可以用于执行SQL方法
UserMapperimpl2.java
package com.blb.mapper;
import com.blb.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
applicationContext.xml
<bean id="userMapper2" class="com.blb.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
MyTest.java
import com.blb.mapper.UserMapper;
import com.blb.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
for (User user: userMapper.selectUser()) {
System.out.println(user);
}
}
}
13、声明式事务
回顾事务:
- 把一组业务当成一个业务来做;要么都成功,要么都失败
- 事务在项目开发中,十分的重要,涉及到数据的一致性问题,不能马虎
- 确保完整性和一致性
事务ACID原则:
- 原子性:确保要么都成功,要么都失败
- 一致性:资源和状态都要保持一致的
- 隔离性:多个业务可能操作同一个资源,防止数据损坏
- 持久性:事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化的写到存储器(不一定是数据库)中
spring中的事务管理
- 声明式事务:AOP
- 编程式事务:需要在代码中,进行事务的管理
UserMapper.java
package com.blb.mapper;
import com.blb.pojo.User;
import java.util.List;
public interface UserMapper {
// 查询所有用户
List<User> selectUser();
// 添加一个用户
int addUser(User user);
// 删除一个用户
int deleteUser(int id);
}
UserMapper.xml
<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- configuration核心配置文件 -->
<mapper namespace="com.blb.mapper.UserMapper">
<select id="selectUser" resultType="com.blb.pojo.User">
select * from Mybatis.userone;
</select>
<insert id="addUser" parameterType="user">
insert into Mybatis.userone (id, name, pwd) values (#{id}, #{name}, #{pwd});
</insert>
<delete id="deleteUser" parameterType="int">
delete from Mybatis.userone where id=#{id};
</delete>
</mapper>
spring-dao.xml
<?xml version="1.0" encoding="UTF8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx" 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/tx
https://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- DataSource: 使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid
我们这里使用Spring提供的JDBC: org.springframework.jdbc.datasource
-->
<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&characterEncoding=utf-8&serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="zya11230318"/>
</bean>
<!-- sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource" />
<!-- 绑定Mybatis的配置文件 -->
<property name="configLocation" value="classpath:mybatis-config.xml" />
<property name="mapperLocations" value="classpath:com/blb/mapper/*.xml" />
</bean>
<!-- SqlSessionTemplate: 就是我们使用的sqlSession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 只能使用构造器注入sqlSessionFactory, 因为它没有set方法 -->
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
<!-- 配置声明式事务 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="datasource" />
<!-- <property name="dataSource" ref="datasource" />-->
</bean>
<!-- 结合AOP实现事务的植入 -->
<!-- 配置事务通知: -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 给哪些方法配置事务 -->
<!-- 配置事务的传播特性: new -->
<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="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!-- 配置事务切入 -->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.blb.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
</aop:config>
</beans>
UserMapperImpl.java
package com.blb.mapper;
import com.blb.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
User user = new User(5, "小王", "1233321");
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(user);
mapper.deleteUser(5);
return selectUser();
}
@Override
public int addUser(User user) {
return getSqlSession().getMapper(UserMapper.class).addUser(user);
}
@Override
public int deleteUser(int id) {
return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
}
}
MyTest.java
import com.blb.mapper.UserMapper;
import com.blb.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class MyTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
List<User> userList = userMapper.selectUser();
for (User user : userList) {
System.out.println(user);
}
}
}
思考:为什么要事务?
- 如果不配置事务,可能存在数据提交不一致的情况下
- 如果我们不在Spring中配置声明式事务,我们就需要在代码中手动配置事务
- 事务在项目的开发中十分重要,设计到数据的一致性和完整性问题,不容马虎