spring
Spring
简介
- Spring 是一个开源的 Java 框架,主要用于简化企业级应用开发。它提供了全面的基础设施支持,帮助开发者专注于业务逻辑,而不是底层技术细节。
- Spring 通过依赖注入、AOP 和模块化设计,简化了 Java 开发,提升了代码的可维护性和可扩展性,广泛应用于企业级开发。
优点
- 免费开源的轻量化框架容器(容器)
- 轻量性,非入侵性(导入不改变原本代码)
- 控制反转IOC,面向切面编程AOP
- 支持事务的处理,对框架整合的支持
导入相关依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.2.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.2.3</version>
</dependency>
Spring官方文档
Spring Framework Documentation
补充:DAO层和Service层
- DAO层:直接操作数据库,比如
saveUser
和findByUsername
。 - Service层:处理业务逻辑,比如检查用户名是否重复,然后调用DAO层保存数据。
在软件开发中,DAO层和Service层是常见的分层设计,各自承担不同的职责。
DAO层(数据访问层)
- 职责:负责与数据库直接交互,执行数据的增删改查操作。
- 功能:封装数据库操作,提供简单的接口供Service层调用。
- 例子:比如保存用户信息、查询订单数据等。
Service层(业务逻辑层)
- 职责:处理业务逻辑,调用DAO层获取或保存数据,并进行业务处理。
- 功能:实现核心业务规则,协调多个DAO操作,确保业务逻辑正确。
- 例子:比如用户注册时,先检查用户名是否已存在,再调用DAO层保存用户信息。
总结
- DAO层:专注数据库数据处理,sql
- Service层:专注于业务逻辑处理,判断是否应该执行处理,应该就调用dao的方法
- Service层一般会new DAO层的对象,调用时用DAO层对象的方法
这种分层设计让代码更清晰、易维护。
控制反转IOC
IOC 的全称是 Inversion of Control,中文翻译为控制反转。它是面向对象编程中的一种设计原则,也是 Spring 框架的核心思想之一。
控制反转(IOC)的核心思想
- 传统编程模式:
- 对象的创建和依赖关系的管理由程序代码直接控制。
- 例如:在代码中直接
new
一个对象,或者手动设置对象的依赖。
- 控制反转模式:
- 将对象的创建和依赖关系的管理交给外部容器(如 Spring 框架)来处理。
- 程序只需要定义好对象的依赖关系,容器负责创建对象并注入依赖。
-
控制反转
原来,程序主动创建对象,程序员手动修改代码,控制在程序员手
实现set后,改成接受用户不同传入,控制在用户手上,程序不再有主动性
public class UserServiceImpl implements UserService{
UserDao userDao;
@Override
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
package com.zhm.service;
import com.zhm.dao.JavaUserDaoImpl;
import com.zhm.dao.PyUserDaoImpl;
import com.zhm.dao.UserDaoImpl;
public class UserServiceTest {
public static void main(String[] args) {
// 用户调用service层就可以
UserService userService = new UserServiceImpl();
// 用户自己传入,()内自己选
userService.setUserDao(new UserDaoImpl());
// userService.setUserDao(new JavaUserDaoImpl());
userService.getUser();
}
}
测试第一个Spring
-
写需要的类
-
在resources内创建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="hello" class="com.zhm.pojo.Hello"> <!--底层本质就是set方法--> <property name="str" value="Spring容器创建了Hello对象"/> </bean> </beans>
-
调用ClassPathXmlApplicationContext 获得spring容器,getbean(id)获得对象
import com.zhm.pojo.Hello; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { // Spring创建对象,用户只用从spring容器中通过getBean调用需要的对象,用户不用new对象了 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Hello hello = (Hello) context.getBean("hello"); System.out.println(hello.getStr()); } }
- 用spring实现ioc案例
<?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="userDao" class="com.zhm.dao.UserDaoImpl"/>
<bean id="pyUserDao" class="com.zhm.dao.PyUserDaoImpl"/>
<bean id="javaUserDao" class="com.zhm.dao.JavaUserDaoImpl"/>
<bean id="userService" class="com.zhm.service.UserServiceImpl">
<property name="userDao" ref="javaUserDao"/>
</bean>
</beans>
package com.zhm.service;
import com.zhm.dao.JavaUserDaoImpl;
import com.zhm.dao.PyUserDaoImpl;
import com.zhm.dao.UserDaoImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserServiceTest {
public static void main(String[] args) {
// 用户调用service层就可以
UserService userService = new UserServiceImpl();
// 用户自己传入,()内自己选
userService.setUserDao(new UserDaoImpl());
// userService.setUserDao(new JavaUserDaoImpl());
userService.getUser();
// 第二种,用spring管理对象,只用改配置文件实现不同调用
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService1 = (UserService) context.getBean("userService");
userService1.getUser();
}
}
-
总结
对象由spring创建(构造器),对象的属性由spring设置(调用set),相对于在spring容器调用需要的对象
实现不同的操作,不需要改变程序,直接在beans.xml文件内修改就行了
ioc:对象的创建,装配,管理都由Spring执行
IOC创建对象
-
spring创对象默认调用无参,如果没有无参,就需要在beans.xml里配置有参
- 按下标
- 按类型
- 按kv(最常用)
<?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.zhm.pojo.User">--> <!-- <constructor-arg index="0" value="按下标执行有参"/>--> <!-- </bean>--> <!-- <bean id="user" class="com.zhm.pojo.User">--> <!-- <constructor-arg type="java.lang.String" value="按类型执行有参"/>--> <!-- </bean>--> <!--一般用这个--> <bean id="user" class="com.zhm.pojo.User"> <constructor-arg name="name" value="按kv键值执行有参"/> </bean> </beans>
-
spring容器类似婚介公司,对象在容器里都执行构造创建准备好了,需要哪个调用哪个
Spring配置
别名
<!--name是上面的id,alias是别名-->
<alias name="user" alias="user2"/>
bean配置
<!--id:唯一标识符,学过的对象名,调用时要用
class:bean对象对应的类位置,类限定名,包名 + 类型
name:别名,可以起多个,用逗号,空格等隔开,别名就是另一个名字-->
<bean id="userT" class="com.zhm.pojo.UserT" name="t1,t2,t3">
<constructor-arg name="name" value="按kv键值执行有参"/>
</bean>
import导入
-
用于团队开发,把不同的xml汇总到一个applicationContetx.xml里,之后只用一个spring配置文件就行
-
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <import resource="beans.xml"/> <import resource="beans2.xml"/> <import resource="beans3.xml"/> </beans>
依赖注入
- 注入:本质调用set方法,通过spring给属性赋值
- 依赖:通过Spring=依赖Spring
- 构造器注入(上面)
- set注入
set注入环境搭建
-
创建两个复杂属性的类,实现getset
package com.zhm.pojo; public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "Address{" + "address='" + address + '\'' + '}'; } }
package com.zhm.pojo; import java.util.*; public class Student { private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String,String> cards; private Set<String> games; private String wife; private Properties info; ... }
-
创建spring配置文件 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="student" class="com.zhm.pojo.Student"> <property name="name" value="我爱我家"/> <property name="address" ref="address"/> </bean> <bean id="address" class="com.zhm.pojo.Address"> <property name="address" value="和昌森林"/> </bean> </beans>
-
测试 CPX
import com.zhm.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.getName()); System.out.println(student.getAddress()); } }
实现
-
常用:普通注入value和bean注入ref
<?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.zhm.pojo.Address"> <property name="address" value="和昌森林"/> </bean> <bean id="student" class="com.zhm.pojo.Student"> <!--普通注入,value,常用--> <property name="name" value="我爱我家"/> <!--bean注入,ref,常用--> <property name="address" ref="address"/> <!--数组注入,array--> <property name="books"> <array> <value>西游记</value> <value>水浒传</value> <value>红楼梦</value> <value>三国演义</value> </array> </property> <!--list注入--> <property name="hobbys"> <list> <value>玩游戏</value> <value>打电脑</value> <value>用手机</value> </list> <!--map注入--> </property> <property name="cards"> <map> <entry key="身份证" value="420922200103163833"/> <entry key="学生卡" value="1905120428"/> </map> </property> <!--set注入--> <property name="games"> <set> <value>LOL</value> <value>MOBA</value> <value>PUBG</value> </set> </property> <!--null注入--> <property name="wife"> <null/> </property> <!--配置注入,props prop--> <property name="info"> <props> <prop key="driver">com.mysql.jdbc.Driver</prop> <prop key="url">localhost:3306</prop> <prop key="username">root</prop> <prop key="password">123456</prop> </props> </property> </bean> </beans>
测试结果
import com.zhm.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); /* Student{ name='我爱我家', address=Address{address='和昌森林'}, books=[西游记, 水浒传, 红楼梦, 三国演义], hobbys=[玩游戏, 打电脑, 用手机], cards={身份证=420922200103163833, 学生卡=1905120428}, games=[LOL, MOBA, PUBG], wife='null', info={password=123456, url=localhost:3306, driver=com.mysql.jdbc.Driver, username=root}} */ } }
拓展注入方式
-
p注入,对应set注入
-
c注入,对应构造器注入
-
导入依赖
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user" class="com.zhm.pojo.User" c:age="10" c:name="xaxxa" p:age="50" p:name="xamxa"/> </beans>
Bean作用域(scope)
-
singleton 单例模式,对象只有一个(默认就是,不用设置)
<bean id="user" class="com.zhm.pojo.User" scope="singleton "> <constructor-arg name="name" value="nkalxla"/> <constructor-arg name="age" value="110"/> </bean> user==user2 true
-
prototype 原型模式,每个对象都是新创建
<bean id="user" class="com.zhm.pojo.User" scope="prototype"> <constructor-arg name="name" value="nkalxla"/> <constructor-arg name="age" value="110"/> </bean> user==user2 false
自动装配autowire
-
环境配置
人养猫和狗,猫狗各有方法
-
装配方式
-
手动配置beans.xml文件,就是上面的依赖注入
-
通过手动配置java
-
自动装配beans.xml,加autowire
-
byName:会自动在容器上下文中查找,和自己对象set方法后的值对应的 bean id
id要唯一
-
byType:会自动在容器上下文中查找,和自己对象属性类型对应的 bean 类型
不能有多个同类型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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="cat" class="com.zhm.pojo.Cat"/> <bean id="dog" class="com.zhm.pojo.Dog"/> <!-- <bean id="dog2" class="com.zhm.pojo.Dog"/>--> <!--1.byName 要求set后的对象名和注册bean的id一样,且id要唯一 2.byType 要求set后的对象类型和支持的bean类型一样,不能有多个相同类型bean--> <bean id="person" class="com.zhm.pojo.Person" autowire="byName"> <property name="name" value="xiaoming"/> <!-- <property name="cat" ref="cat"/>--> <!-- <property name="dog" ref="dog"/>--> </bean> </beans>
-
注解实现自动装配
-
用@Autowired实现 @Qualifier进行调整
-
导入约束和支持,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" 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 id="cat2" class="com.zhm.pojo.Cat"/> <bean id="cat3" class="com.zhm.pojo.Cat"/> <bean id="dog2" class="com.zhm.pojo.Dog"/> <bean id="dog3" class="com.zhm.pojo.Dog"/> <bean id="person" class="com.zhm.pojo.Person"> <property name="name" value="xiaoming"/> </bean> </beans>
-
写注解
@Autowired执行过程:先根据 byname查找bean的id有没有符合,没有就bytype找bean的类型有没有符合,都没用就报错
或者
都没有可以用@Qualifier指定指向的id
public class Person { private String name; // 如果显式定义required = false 说明该对象可以null,否则不行 @Autowired(required = false) @Qualifier(value = "dog2") private Dog dog; @Autowired @Qualifier(value = "cat3") private Cat cat; }
-
补充,上面的约束相对于安全执行的规则,下面的支持代表可以使用这个配置
这是一个 Spring 框架的 XML 配置文件,用于定义和管理 Spring 容器中的 Bean 及其相关配置。它的结构和属性有以下意义:
1.
xmlns
(命名空间声明)
- 用于定义 XML 文档中使用的命名空间,避免标签名冲突。
- 例如:
xmlns="http://www.springframework.org/schema/beans"
:默认命名空间,表示没有前缀的标签(如<bean>
)属于 Spring Beans 的命名空间。xmlns:context="http://www.springframework.org/schema/context"
:定义context
前缀,用于 Spring 的上下文相关配置(如组件扫描)。xmlns:aop="http://www.springframework.org/schema/aop"
:定义aop
前缀,用于 Spring AOP(面向切面编程)的配置。
2.
xsi:schemaLocation
- 用于指定命名空间对应的 XML Schema 文件的位置,帮助验证 XML 文件的正确性。
- 例如:
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
。
3. 整体意义
- 这个配置文件告诉 Spring:
- 使用 Spring Beans 的核心功能(如定义
<bean>
)。- 使用 Spring 的上下文功能(如自动扫描组件
@Component
、@Service
等)。- 使用 Spring AOP 功能(如定义切面、增强逻辑等)。
- 通过这种方式,Spring 可以正确加载和解析配置文件,管理应用中的对象(Bean)及其依赖关系。
4. 简单比喻
把这个配置文件想象成一份“说明书”:
xmlns
是说明书的“目录”,告诉 Spring 有哪些功能模块(如 Beans、Context、AOP)。xsi:schemaLocation
是“页码”,告诉 Spring 每个模块的具体规则在哪里可以找到。- Spring 根据这份“说明书”正确地加载和管理应用程序中的各种组件。
5. 实际使用示例
在这个配置文件中,你可以:
定义 Bean:
<bean id="myService" class="com.example.MyService"/>
开启组件扫描:
<context:component-scan base-package="com.example"/>
配置 AOP:
<aop:config> <aop:aspect ref="myAspect"> <aop:pointcut id="myPointcut" expression="execution(* com.example.*.*(..))"/> <aop:before pointcut-ref="myPointcut" method="beforeAdvice"/> </aop:aspect> </aop:config>
总结
这个配置文件是 Spring 的核心配置文件之一,定义了 Spring 如何管理 Bean、扫描组件以及支持 AOP 等功能。通过命名空间和 Schema 文件,Spring 能够正确解析和执行这些配置。
注解开发
-
注册环境
pojo实体类层,dao数据库层,service逻辑处理层,controller用户交互层
Controller层、Service层和DAO层是软件开发中常见的三层架构,它们分工明确,各司其职,共同协作完成业务逻辑。它们的关系可以用一个简单的例子来说明:
1. Controller层(控制层)
- 作用:负责与用户直接交互,接收用户请求并返回响应。
- 类比:像餐厅的服务员,负责接收顾客的点单,并把做好的菜端给顾客。
- 职责:
- 接收用户输入(如HTTP请求)。
- 调用Service层处理业务逻辑。
- 将处理结果返回给用户(如JSON数据或页面)。
2. Service层(服务层)
- 作用:处理具体的业务逻辑,是核心业务操作的地方。
- 类比:像餐厅的厨师,负责根据顾客的点单做菜。
- 职责:
- 实现业务规则(如用户注册、订单计算等)。
- 调用DAO层获取或保存数据。
- 处理事务管理(如数据库事务)。
3. DAO层(数据访问层)
- 作用:负责与数据库直接交互,执行数据的增删改查操作。
- 类比:像餐厅的仓库管理员,负责从仓库中取食材或存放食材。
- 职责:
- 直接操作数据库(如查询用户信息、保存订单等)。
- 提供简单的数据访问接口,供Service层调用。
三者的关系
- Controller层调用Service层:Controller接收用户请求后,交给Service层处理业务逻辑。
- Service层调用DAO层:Service层处理业务时,如果需要操作数据库,就调用DAO层。
- DAO层操作数据库:DAO层负责与数据库交互,执行具体的SQL操作。
举个例子:用户注册功能
- Controller层:接收用户提交的注册表单(用户名、密码等)。
- Service层:检查用户名是否已存在,如果不存在则调用DAO层保存用户信息。
- DAO层:将用户信息插入数据库。
- Service层:返回注册成功或失败的结果。
- Controller层:将结果返回给用户(如跳转到成功页面或显示错误信息)。
总结
- Controller层:负责与用户交互。
- Service层:负责处理业务逻辑。
- DAO层:负责与数据库交互。
它们的关系是:Controller → Service → DAO,层层调用,职责分明,便于维护和扩展!
开启包扫描
扫描到该包内有组件,自动注册
<context:component-scan base-package="com.zhm"/>
<?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:component-scan base-package="com.zhm"/>
</beans>
bean
-
@Component 注解
注解注册bean,id就是class小写
等价
// 注解注册bean,id就是class小写 == <bean id="uesr" class="com.zhm.pojo.User"/>
@Component
@Scope("prototype|singleton")
public class User {
public String name;
public String getName() {
return name;
}
}
-
@Component衍生
DAO层 @Repository
Service层 @Service
Controller层 @Controller
都是注册bean,注解用法一样
依赖注入
-
@Value("")
-
注解注入属性,set方法上,value后面就算注入的值
等价
public class User {
public String name;
public String getName() {
return name;
}
// 注解注入属性,set方法上,value后面就算注入的值 == <property name="name" value="value注解"/>
@Value("value注解")
public void setName(String name) {
this.name = name;
}
}
自动装配
用@Autowired实现 @Qualifier进行调整
作用域
- 单列或者原型
@Scope("prototype|singleton")
总结
-
优点:方便
-
缺点:不能实现ref调用其他bean,维护困难
-
xml更加万能,复杂的还是要用xml
-
注意点:开启注解支持和约束条件
<context:component-scan base-package="com.zhm"/>
纯java配置Spring
-
@Configuration注解
把该config类对应成beans.xml
@ComponentScan("com.zhm.pojo") 扫包
package com.zhm.config; import com.zhm.pojo.User; import org.springframework.context.annotation.*; // 把这个类变成beans.xml,本质这个类也是被spring容器托管 @Configuration @ComponentScan("com.zhm.pojo") //@Import(ZConfig.class) public class ZConfig { @Bean // 注册bean // 方法名是id class是实际返回类型 return new User(); public User getUser(){ return new User(); } }
-
原注解一样使用
-
package com.zhm.pojo; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class User { private String name; public String getName() { return name; } @Value("axkax") public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } }
-
测试
修改处
ApplicationContext context = new AnnotationConfigApplicationContext(ZConfig.class);
import com.zhm.config.ZConfig; import com.zhm.pojo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class MyTest { @Test public void test1(){ // 修改处 ApplicationContext context = new AnnotationConfigApplicationContext(ZConfig.class); User user = context.getBean("getUser", User.class); System.out.println(user); } }
代理模式
- 23种设计模式之一
静态代理
-
案例:租房
-
角色分析:
- 抽象角色(租房方法,接口)
- 真实角色,被代理的角色(房东)
- 代理角色,代理真实角色(中介)
- 客户,访问代理对象(租房人)
-
两个类实现同一接口
-
步骤
- 接口
- 真实角色实现接口
- 代理角色实现接口,组合真实角色,在实现方法中调用真实角色的实现方法,自己补充需要方法
- 客户,面对中介就行
package com.zhm.demo01; // 抽象代理方法 public interface Rent { void rent(); }
package com.zhm.demo01; // 房东,被代理,更纯粹做事情 public class Master implements Rent{ @Override public void rent() { System.out.println("房东出租房"); } }
package com.zhm.demo01; // 中介,代理者,可以附加自己的操作 public class Proxy implements Rent{ private Master master; public Proxy() { } public Proxy(Master master) { this.master = master; } public Master getMaster() { return master; } public void setMaster(Master master) { this.master = master; } @Override public void rent() { tip(); master.rent(); getMoney(); } public void tip(){ System.out.println("签合同"); } public void getMoney(){ System.out.println("收房租"); } }
package com.zhm.demo01; // 用户,只用面对中介 public class Client { public static void main(String[] args) { Master master = new Master(); new Proxy(master).rent(); } }
-
好处
- 真实角色更纯粹,不用关注公共业务
- 公共业务交给代理模式,实现分工
- 公共业务拓展方便
-
坏处
一个真实角色就要有一个代理角色,代码量翻倍,开发效率低
动态代理
-
实现InvocationHandler接口 处理代理实例
调用Proxy类静态方法 通过代理获得代理类
只用传入实现代理类的接口就行
-
万能类,当成工具
package com.zhm.demo2; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyInvocationHandler implements InvocationHandler { private Object object; public void setObject(Object object) { this.object = object; } // 得到代理类 public Object getProxy(){ // 参数1类加载器 只有三种,随意Class对象.getClassLoader()获取就行 // 2 反射获取需要代理的接口 // 3 InvocationHandler处理代理实例,返回结果, // 这里这个类继承了InvocationHandler 所以可以用this return Proxy.newProxyInstance(this.getClass().getClassLoader(), object.getClass().getInterfaces(), this); } // 处理代理实例,返回结果 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { myFront(); Object o = method.invoke(object, args); myBehind(); return o; } public void myFront(){ // System.out.println("front"); } public void myBehind(){ // System.out.println("behind"); } }
-
上面的例子,不手动创建代理类,自动创建
package com.zhm.demo2; // 用户,只用面对中介 public class Client { public static void main(String[] args) { Master master = new Master(); ProxyInvocationHandler pih = new ProxyInvocationHandler(); pih.setObject(master); Rent proxy = (Rent) pih.getProxy(); proxy.rent(); } }
Spring实现AOP
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在将横切关注点(如日志、事务管理、安全等)与核心业务逻辑分离,以提升代码的模块化和可维护性。
- 核心概念:
- 切面(Aspect):封装横切关注点的模块,包含通知和切点。
- 通知(Advice):切面在特定时机执行的操作,如方法调用前后。
- 切点(Pointcut):定义通知应用的特定位置,通常通过表达式指定。
- 连接点(Join Point):程序执行中的特定点,如方法调用或异常抛出。
理解:程序执行就是一个个切面执行,代理就是在某个切入点加上需要的切面
使用原生Spring API,实现Spring API
-
导入依赖
<dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency> </dependencies>
-
service业务层 接口和对应实现
package com.zhm.service; public interface UserService { void insert(); void delete(); void update(); void query(); }
package com.zhm.service; public class UserServiceImpl implements UserService{ @Override public void insert() { System.out.println("插入一个用户"); } @Override public void delete() { System.out.println("删除一个用户"); } @Override public void update() { System.out.println("更新一个用户"); } @Override public void query() { System.out.println("查询一个用户"); } }
-
创建两个实现类,实现MethodBeforeAdvice,AfterReturningAdvice
写代理后实现类 的 方法前和方法后需要增加的业务
实现就行了,实现before就是业务前,After就是后
package com.zhm.log; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class BehindLog implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("我是后面:类"+target.getClass().getName()+"调用了方法"+method.getName()+"返回是"+returnValue); } }
package com.zhm.log; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class FrontLog implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("我是前面:类"+target.getClass().getName()+"调用了方法"+method.getName()); } }
-
配置beans.xml
加约束和支持
pointcut设置切面,就是作用域
advisor就是环绕
<?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 id="userService" class="com.zhm.service.UserServiceImpl"/> <bean id="behindLog" class="com.zhm.log.BehindLog"/> <bean id="frontLog" class="com.zhm.log.FrontLog"/> <aop:config> <!--设置切面点,execution(* com.zhm.service.UserServiceImpl.*(..))代表类下所有方法,*(..)方法名随意(参数任意)--> <aop:pointcut id="point" expression="execution(* com.zhm.service.UserServiceImpl.*(..))"/> <!--设置环绕--> <aop:advisor advice-ref="behindLog" pointcut-ref="point"/> <aop:advisor advice-ref="frontLog" pointcut-ref="point"/> </aop:config> </beans>
-
测试
import com.zhm.service.UserService; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test01(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 动态代理代理的是接口,注意点 UserService userService = (UserService) context.getBean("userService"); userService.insert(); } }
动态代理代理的是接口,注意点
自定义切面实现AOP
-
新建要加的业务类
package com.zhm.diycut; public class MyCut { public void beforePoint(){ System.out.println("方法执行前"); } public void afterPoint(){ System.out.println("方法执行后"); } }
-
注册业务类bean,配置beans.xml的aop配置,用业务类当切面
<aop:config> <!--将注册的class抽成切面--> <aop:aspect id="cut" ref="myCut"> <!--切入点--> <!--第一个*表示返回类型任意,第二个*表示该类下的所有方法--> <aop:pointcut id="point" expression="execution(* com.zhm.service.UserServiceImpl.*(..))"/> <!--通知,需要执行的方法--> <aop:before method="beforePoint" pointcut-ref="point"/> <aop:after method="afterPoint" pointcut-ref="point"/> </aop:aspect> </aop:config>
注解实现AOP
-
新建要加的业务类,注册bean,开启约束和注解使用
<?xml version="1.0" encoding="UTF-8"?> <!--约束--> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" 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 id="userService" class="com.zhm.service.UserServiceImpl"/> <bean id="behindLog" class="com.zhm.log.BehindLog"/> <bean id="frontLog" class="com.zhm.log.FrontLog"/> <bean id="myCut" class="com.zhm.diycut.MyCut"/> <bean id="annoCut" class="com.zhm.diycut.AnnoCut"/> <!--开启注解使用(先约束)--> <aop:aspectj-autoproxy/>
-
加注解,@aspect 标注类是切面,@Before@After后面写切入点
package com.zhm.diycut; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; // 标注类是切面 @Aspect public class AnnoCut { // 定义切入点(作用域)和切入位置 @Before("execution(* com.zhm.service.UserServiceImpl.*(..))") public void annoBefore(){ System.out.println("方法执行前业务"); } @After("execution(* com.zhm.service.*.*(..))") public void annoAfter(){ System.out.println("方法执行后业务"); } @Around("execution(* com.zhm.service.*.*(..))") public void annoAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("前业务"); // 执行业务 Object proceed = joinPoint.proceed(); System.out.println("后业务"); } }
Spring-Mybatis
Mybatis回顾
- 导入依赖,连接数据库
- mybatis-config.xml,mybatis配置文件
- MybatisUtils类,配置获取sqlsession的工具类,静态代码块和静态方法
- pojo User @lombok
- mapper UserMapper和对应UserMapper.xml配置实现类
- UserMapper.xml 命名空间,mapper实现方法,mybatis-config.xml注册UserMapper.xml
- 资源导出问题
- 测试
连接方法1
-
导入依赖
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.19</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.39</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.39</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.36</version> </dependency> </dependencies>
-
创建spring-dao.xml spring的bean配置,把原来mybatis-config的配置整合到这里
注意点:
一些类
源=DriverManagerDataSource
工厂=SqlSessionFactoryBean
sqlsession=SqlSessionTemplate
<?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"> <!--1 配置连接数据库的数据源--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean> <!--2 配置sqlSessionFactory,连上mybatis配置文件--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="mapper-config.xml"/> <property name="mapperLocations" value="classpath:com/zhm/mapper/*.xml"/> </bean> <!--3 配置SqlSessionTemplate(sqlSession的实现,等价),没有set方法和无参,用有参构造方法创建--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> <!--4 配置userMapperImpl,ref配置SqlSessionTemplate结合set,以后只用这个id userMapperImpl就可以了--> <bean id="userMapperImpl" class="com.zhm.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean> </beans>
-
创建UserMapperImpl实现类,配合SqlSessionTemplate(等价sqlsession)完成getMapper方法,注册bean
package com.zhm.mapper; import com.zhm.pojo.User; import org.mybatis.spring.SqlSessionTemplate; import java.util.List; public class UserMapperImpl implements UserMapper{ private SqlSessionTemplate sqlSession = null; public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } @Override public List<User> queryUser() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.queryUser(); } }
-
测试
@Test public void test01(){ ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml"); UserMapper userMapper = context.getBean("userMapperImpl", UserMapper.class); List<User> users = userMapper.queryUser(); for (User user : users) { System.out.println(user); } }
-
附加
可以把spring-dao.xml分成两个spring配置,一个只负责spring-mybatis配置,一个只负责注册需要的类bean
最后用import导入
<?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"> <import resource="spring-dao.xml"/> <!--专注注册bean,新的类--> <!--4 配置userMapperImpl,ref配置SqlSessionTemplate结合set,以后只用这个id userMapperImpl就可以了--> <bean id="userMapperImpl" class="com.zhm.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean> </beans>
<?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"> <!--专注数据库--> <!--1 配置连接数据库的数据源--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean> <!--2 配置sqlSessionFactory,连上mybatis配置文件--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="mapper-config.xml"/> <property name="mapperLocations" value="classpath:com/zhm/mapper/*.xml"/> </bean> <!--3 配置SqlSessionTemplate(sqlSession的实现,等价),没有set方法和无参,用有参构造方法创建--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> </beans>
连接方法2
-
修改方法1的第三步,继承SqlSessionDaoSupport,注册时属性传入sqlsessionfactory
package com.zhm.mapper; import com.zhm.pojo.User; import org.mybatis.spring.support.SqlSessionDaoSupport; import java.util.List; public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{ @Override public List<User> queryUser() { return getSqlSession().getMapper(UserMapper.class).queryUser(); } }
<bean id="userMapperImpl2" class="com.zhm.mapper.UserMapperImpl2"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean>
事务
ACID
一句话本质,要么都成功,要么都失败
- 原子性 要么都成功,要么都失败
- 一致性 前后结果要一致,取钱总和一样
- 隔离性 两个线程操作同一数据,保证数据一致
- 持久性 事务一旦提交就永久化,即便系统故障也不会丢失
Spring声明式事务
- 用切面织入实现
-
导入约束 tx和aop
-
配置事务管理器
-
结合aop实现事务的织入
-
spring实现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:context="http://www.springframework.org/schema/context" 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/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 http://www.springframework.org/schema/tx https://www.springframework.org/schema/aop/spring-tx.xsd"> <!--专注数据库--> <!--配置事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--结合aop实现事务的织入--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="insert" propagation="REQUIRED"/> <tx:method name="delete" propagation="REQUIRED"/> <tx:method name="update" propagation="REQUIRED"/> <tx:method name="query" read-only="true"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!--spring实现aop 动态代理--> <aop:config> <aop:pointcut id="txPoint" expression="execution(* com.zhm.mapper.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/> </aop:config> </beans>