GitHub | https://github.com/Web-Learn-GSF/Java_Learn_Examples |
---|---|
父工程 | Java_Framework_Spring |
Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器【框架】
中文文档:https://www.docs4dev.com/docs/zh/spring-framework/5.1.3.RELEASE/reference/core.html#beans
官方文档:https://spring.io/projects/spring-framework#learn
源码下载:https://repo.spring.io/ui/native/milestone/org/springframework/spring/
GitHub:https://github.com/spring-projects/spring-framework/releases
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>6.0.13version>
dependency>
导入该依赖,通过依赖传递性,会自动导入:spring-aop、spring-beans、spring-context、spring-core、spring-expression、spring-web
七大组成模块
模块 | 作用 |
---|---|
Spring Core | 核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开 |
Spring Context | Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能 |
Spring AOP | 通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中 |
Spring DAO | JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构 |
Spring ORM | Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构 |
Spring Web | Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作 |
Spring MVC | MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI |
Spring拓展:Spring Boot 和 Spring Cloud
Spring Boot | Spring Cloud |
---|---|
是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务 | 基于Spring Boot实现的 |
专注于快速、方便集成的单个微服务个体 | 关注全局的服务治理框架 |
使用了约束优于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置 | 很大的一部分是基于Spring Boot来实现,Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系 |
起承上启下的作用 | 如果要学习SpringCloud必须要学习SpringBoot |
三层结构:
底层源码,Dao:接口、实现
业务端,Service:业务接口、业务实现
客户端,Client:调用
源码实现
public interface UserDao {
public void getUser();
}
public class UserDaoImpl implements UserDao {
@Override
public void getUser() {
System.out.println("Dao获取用户数据");
}
}
public interface UserService {
public void getUser();
}
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
public class UserTest {
@Test
public void test(){
UserService service = new UserServiceImpl();
service.getUser();
}
}
若增加一个源码实现
public interface UserSql {
public void getUser();
}
public class UserSqlImpl implements UserSql {
@Override
public void getUser() {
System.out.println("Sql获取用户数据");
}
}
public class UserServiceImpl implements UserService {
// 原内容
private UserDao userDao = new UserDaoImpl();
// 修改后的内容
private UserDao userDao = new UserSqlImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
针对常规开发模式,每增加一个源码实现,都需要对服务端实现的代码进行更改,非常麻烦。代码耦合性太高
如何解决?利用Set函数
保持其他内容不变,修改服务端实现类的代码内容
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 利用set实现
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
客户端代码实现
@Test
public void test(){
UserServiceImpl service = new UserServiceImpl();
// Dao实现
service.setUserDao( new UserDaoMySqlImpl() );
service.getUser();
//Sql实现
service.setUserDao( new UserSqlImpl() );
service.getUser();
}
可以看到,针对新的源码实现,不再需要修改业务端逻辑,仅是在客户端,通过传入不同的业务端实现对象,即可完成更改
常规 | IOC原型 |
---|---|
对象创建主动权在程序 | 对象创建主动权在调用者 |
对象是写死的,更改实现对象,就需要更改业务端代码 | 对象是通过接口动态接收的,调用者给什么,就实现什么 |
IoC(Inversion of Control,控制反转) 是一种设计思想,DI(Dependency Injection,依赖注入) 是实现IoC的一种方法
没有IoC的程序中 , 我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,实现解耦。
源码准备
package com.learn.Hello;
public class HelloWorld {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("Hello,"+ name );
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hello" class="com.learn.Hello.HelloWorld">
<property name="name" value="Spring"/>
bean>
beans>
import com.learn.Hello.HelloWorld;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class HelloWorldTest {
@Test
public void test(){
//解析beans.xml文件, 生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean : 参数即为spring配置文件中bean的id .
HelloWorld hello = (HelloWorld) context.getBean("hello");
hello.show();
}
}
思考
Hello 对象是谁创建的? | 由Spring创建的 |
Hello 对象的属性是怎么设置的? | 由Spring容器设置的(根据set方法) |
IOC理解
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="MysqlImpl" class="com.learn.dao.UserMySqlImpl"/>
<bean id="DaoImpl" class="com.learn.dao.UserDaoImpl"/>
<bean id="ServiceImpl" class="com.learn.service.UserServiceImpl">
<property name="userDao" ref="OracleImpl"/>
bean>
beans>
// ===== 原内容 =====
@Test
public void test(){
UserServiceImpl service = new UserServiceImpl();
// Dao实现
service.setUserDao( new UserDaoMySqlImpl() );
service.getUser();
//Sql实现
service.setUserDao( new UserSqlImpl() );
service.getUser();
}
// ===== 修改后内容 =====
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl serviceImpl = (UserServiceImpl) context.getBean("ServiceImpl");
serviceImpl.getUser();
}
无参构造,属性的写入,本质是set方式注入
public class User {
private String name;
public User() {
System.out.println("user无参构造方法");
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+ name );
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.kuang.pojo.User">
<property name="name" value="kuangshen"/>
bean>
beans>
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//在执行getBean的时候, user已经创建好了, 通过无参构造
User user = (User) context.getBean("user");
//调用对象的方法 .
user.show();
}
带参构造,属性的写入,本质是构造器方式注入
public class UserT {
private String name;
public UserT(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+ name );
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userT" class="com.kuang.pojo.UserT">
<constructor-arg index="0" value="kuangshen2"/>
bean>
<bean id="userT" class="com.kuang.pojo.UserT">
<constructor-arg name="name" value="kuangshen2"/>
bean>
<bean id="userT" class="com.kuang.pojo.UserT">
<constructor-arg type="java.lang.String" value="kuangshen2"/>
bean>
beans>
@Test
public void testT(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 在配置文件加载的时候。其中管理的对象都已经初始化了!
UserT user = (UserT) context.getBean("userT");
user.show();
}
alias标签配置
<alias name="userT" alias="userNew"/>
bean标签中的name属性配置
<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
bean>
<import resource="{path}/beans.xml"/>
依赖注入(Dependency Injection,DI)
- 依赖 : 指Bean对象的创建依赖于容器.
- 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配.
注入方式 | 解释 |
---|---|
Set注入 | 针对无参构造的bean |
构造器注入 | 针对带参构造的bean |
p标签注入 | 本质还是set注入,需要有无参构造方法 |
c标签注入 | 本质是构造器注入,需要有带参构造方法 |
要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写
如果属性是boolean类型 , 没有set方法 , 是 is.【有疑问?IDEA 2023创建出来的boolean属性有set方法,但是没有get方法】
常见属性类型的set方式注入
package com.kuang.pojo;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class Student {
// 属性
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
// set方法
public void setName(String name) {
this.name = name;
}
public void setAddress(Address address) {
this.address = address;
}
public void setBooks(String[] books) {
this.books = books;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public void setGames(Set<String> games) {
this.games = games;
}
public void setWife(String wife) {
this.wife = wife;
}
public void setInfo(Properties info) {
this.info = info;
}
public void show(){
System.out.println("name="+ name
+ ",address="+ address.getAddress()
+ ",books="
);
for (String book:books){
System.out.print("<<"+book+">>\t");
}
System.out.println("\n爱好:"+hobbys);
System.out.println("card:"+card);
System.out.println("games:"+games);
System.out.println("wife:"+wife);
System.out.println("info:"+info);
}
}
常量注入
<bean id="student" class="com.kuang.pojo.Student">
<property name="name" value="小明"/>
bean>
bean注入
<bean id="addr" class="com.kuang.pojo.Address">
<property name="address" value="重庆"/>
bean>
<bean id="student" class="com.kuang.pojo.Student">
<property name="name" value="小明"/>
<property name="address" ref="addr"/>
bean>
<bean id="outer" class="...">
<property name="target">
<bean class="com.example.Person">
<property name="name" value="Fiona Apple"/>
<property name="age" value="25"/>
bean>
property>
bean>
数组注入
<bean id="student" class="com.kuang.pojo.Student">
<property name="name" value="小明"/>
<property name="address" ref="addr"/>
<property name="books">
<array>
<value>西游记value>
<value>红楼梦value>
<value>水浒传value>
array>
property>
bean>
List注入
<property name="hobbys">
<list>
<value>听歌value>
<value>看电影value>
<value>爬山value>
list>
property>
Map注入
<property name="card">
<map>
<entry key="中国邮政" value="456456456465456"/>
<entry key="建设" value="1456682255511"/>
map>
property>
集合set注入
<property name="games">
<set>
<value>LOLvalue>
<value>BOBvalue>
<value>COCvalue>
set>
property>
Null注入
<property name="wife"><null/>property>
Properties注入
<property name="info">
<props>
<prop key="学号">20190604prop>
<prop key="性别">男prop>
<prop key="姓名">小明prop>
props>
property>
<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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.kuang.pojo.User" p:name="狂神" p:age="18"/>
<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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.kuang.pojo.User" c:name="狂神" c:age="18"/>
在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。
简单地讲,bean就是由IoC容器初始化、装配及管理的对象。
类别 | 说明 |
---|---|
singleton(默认) | 在Spring IOC容器中仅存一个Bean实例,以单例方式存在 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XXXBean() |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
session | 同一个Http Session共享一个Bean,不同Session使用不同Bean,仅适用于WebApplicationContext环境 |
Singleton
<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">
Prototype
<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>
或者
<bean id="account" class="com.foo.DefaultAccount" singleton="false"/>
Request
Session
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
自动装配是Spring满足bean依赖的一种方式
不需要手动给与属性,Sping会在上下文中自动寻找,并自动给bean装配属性
public class Cat {
public void shout() {
System.out.println("miao~");
}
}
public class Dog {
public void shout() {
System.out.println("wang~");
}
}
package com.learn.Hello;
public class People {
private Cat cat;
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="cat" class="com.learn.Hello.Cat"/>
<bean id="dog" class="com.learn.Hello.Dog"/>
<bean id="People" class="com.learn.Hello.People">
<property name="name" value="张三"/>
<property name="dog" ref="dog"/>
<property name="cat" ref="cat"/>
bean>
beans>
会自动在上下文中查找id跟自己属性值一样的bean
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="cat" class="com.learn.Hello.Cat"/>
bean>
<bean id="dog" class="com.learn.Hello.Dog"/>
bean>
<bean id="People" class="com.learn.Hello.People" autowire="byName">
<property name="name" value="张三"/>
bean>
beans>
会根据属性的类型,自动去上下文中找对应属性的bean,这就要求属性全局唯一,不然idea会报错,不让用该种装配方法
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="cat" class="com.learn.Hello.Cat">
bean>
<bean id="dog" class="com.learn.Hello.Dog">
bean>
<bean id="People" class="com.learn.Hello.People" autowire="byType">
<property name="name" value="张三"/>
bean>
beans>
该方式可以不需要bean中有set方法
准备
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
beans>
测试
package com.learn.Hello;
import org.springframework.beans.factory.annotation.Autowired;
public class People {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
<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.learn.Hello.Cat"/>
<bean id="dog" class="com.learn.Hello.Dog"/>
<bean id="people" class="com.learn.Hello.People"/>
beans>
@Autowired参数
public class People {
@Autowired(required=false)
private Cat cat;
}
<bean id="dog1" class="com.kuang.pojo.Dog"/>
<bean id="dog2" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>
package com.learn.Hello;
import org.springframework.beans.factory.annotation.Autowired;
public class People {
@Autowired()
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
不是Spring框架的注解,而是Java自带的注解
@Resource如有参数指定name属性,先按该属性进行byName方式查找装配;其次再进行默认的byName方式进行装配
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/>
public class User {
//如果允许对象为null,设置required = false, 默认为true
@Resource(name = "cat2")
private Cat cat;
@Resource
private Dog dog;
private String str;
}
补充
jdk的版本影响注解@Resource的使用
解决方法1:使用jdk8开发
解决方法2:在maven中的pom.xml从新导入一个javax.annotation的jar包
<dependency>
<groupId>javax.annotationgroupId>
<artifactId>javax.annotation-apiartifactId>
<version>1.2version>
dependency>
注解方法 | 解释 |
---|---|
@Autowired | 默认按类型装配,默认情况下必须要求依赖对象必须存在 如果要允许null 值,可以设置它的required属性为false:@Autowired(required=false) |
@Qualifier | 按照name装配:配合@Autowired使用 当@Autowired无法按照类型装配的时候,配合@Qualifier,按照name装配 |
@Resource | 默认按照名称进行装配,名称可以通过name属性进行指定 如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找 如果注解写在setter方法上默认取属性名进行装配 当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配 |
从基于xml文件的bean标签实现依赖注入,到通过@Component注解形式实现注入
准备工作
xml文件中加入注解扫描包,通过指定注解扫描包,可以让注解的类完成注入
<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:component-scan base-package="com.kuang.pojo"/>
<context:annotation-config/>
beans>
AOP包的引入:
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>6.0.13version>
dependency>
指定包下编写类,并加入注解
@Component("user") // 相当于配置文件中
@Scope("prototype") // 相当于配置文件中
public class User {
@Value("张三") // 写在字段上:相当于配置文件中
public String name;
public int age;
@Value(18) // 写在set方法上:相当于配置文件中
public void setAge(int age) {
this.age = age;
}
}
测试
@Test
public void test(){
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("beans.xml");
User user = (User) applicationContext.getBean("user");
System.out.println(user.name);
}
@Component衍生注解
为了更好的进行分层,Spring可以使用其它三个注解,功能跟@Component都一样
注解 | 应用场景 |
---|---|
@Controller | 在Controller层进行注入时使用的注解 |
@Service | 在Service层进行注入时使用的注解 |
@Repository | 在Dao层进行注入时使用的注解 |
XML | 注解 |
---|---|
可以适用任何场景 ,结构清晰,维护方便 | 注解只能对特定的类生效,开发简单方便 |
JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能
从原先的xml文件配置依赖注入,到通过Java类配置依赖注入
实体类
@Component // 将这个类标注为Spring的一个组件,放到容器中!
public class Dog {
public String name = "dog";
}
Config包:起到beans.xml文件的作用
@Configuration //代表这是一个配置类
@ComponentScan("com.learn.Dog") // 代表要扫描Dog包下的类,带有注解的类将被放进Spring中管理
public class MyConfig {
@Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
public Dog getDog(){
return new Dog();
}
}
@ComponentScan 和 @Configuration 一般配合一起使用
如果没有@ComponentScan,会默认扫描@Configuration所注解的类所在的包
但为什么要配合使用?
如果类中用了@Controller,@Repository,@Service, @Component四大注解标识之一了,那么如果不加上@ComponentScan,Spring就不会自动扫描类上的四大注解中的任何一个,那么四大注解下的类就不会被Spring扫描到,更不会装入Spring容器中,因此配置的四大注解就失去了作用
测试
@Test
public void test2(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Dog dog = (Dog) applicationContext.getBean("getDog");
System.out.println(dog.name);
}
AOP的底层实现就是动态代理,学习AOP之前先了解动态代理
参考文章:https://blog.csdn.net/qq_45445505/article/details/134038354
先写下来,不求甚解了,边学边回顾吧
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
作用:提供声明式事务;允许用户自定义切面
相关名词:
名词 | 解释 |
---|---|
横切关注点 | 跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。 如日志 , 安全 , 缓存 , 事务等等 … |
切面(ASPECT) | 横切关注点 被模块化 的特殊对象。即,它是一个类 |
通知(Advice) | 切面必须要完成的工作。即,它是类中的一个方法 |
目标(Target) | 被通知对象 |
代理(Proxy) | 向目标对象应用通知之后创建的对象 |
切入点(PointCut) | 切面通知 执行的 “地点”的定义 |
连接点(JointPoint) | 与切入点匹配的执行点 |
Spring中支持的5种Advice类型
通知类型 | 连接点 | 实现接口 |
---|---|---|
前置通知 | 方法前 | org.springframework.aop.MethodBeforeAdvice |
后置通知 | 方法后 | org.springframework.aop.AfterReturningAdvice |
环绕通知 | 方法前后 | org.springframework.aop.MethodInterceptor |
异常抛出通知 | 方法抛出异常 | org.springframework.aop.ThrowsAdvice |
引介通知 | 类中增加新的方法属性 | org.springframework.aop.IntroductionInterceptor |
业务层
package GSF.Example.Service;
public interface UserService {
public void add();
public void delete();
public void update();
public void search();
}
package GSF.Example.Service;
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("执行User的add方法");
}
@Override
public void delete() {
System.out.println("执行User的delete方法");
}
@Override
public void update() {
System.out.println("执行User的update方法");
}
@Override
public void search() {
System.out.println("执行User的search方法");
}
}
测试
package GSF.Example.Test;
import GSF.Example.Service.UserService;
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");
UserService userService = context.getBean("userService", UserService.class);
userService.add();
// userService.delete();
// userService.update();
// userService.search();
}
}
项目名称:
- Java_Framework_Spring
- AOP_1_Xml_SpringAPI
前置通知示例
package GSF.Example.Log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class LogBeforeMethod implements MethodBeforeAdvice {
/***
*
* @param method 要执行的目标对象(UserServiceImpl)的方法
* @param args 被调用的方法的参数
* @param target 目标对象(UserServiceImpl)
* @throws Throwable
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("AOP前置通知:" + "在" + target.getClass().getName() + "的" + method.getName() + "方法调用前执行。目标对象-方法-参数,都可以获取到");
}
}
后置通知
package GSF.Example.Log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class LogAfterMethod implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("AOP后置通知:" + "在" + target.getClass().getName() + "的" + method.getName() + "方法调用后执行。目标对象-方法-参数,都可以获取到");
}
}
XML配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="GSF.Example.Service.UserServiceImpl" />
<bean id="logBefore" class="GSF.Example.Log.LogBeforeMethod" />
<bean id="logAfter" class="GSF.Example.Log.LogAfterMethod"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* GSF.Example.Service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="logBefore" pointcut-ref="pointcut" />
<aop:advisor advice-ref="logAfter" pointcut-ref="pointcut" />
aop:config>
beans>
项目名称:
- Java_Framework_Spring
- AOP_2_Xml_CustomClass
package GSF.Example.Log;
public class CustomLogClass {
public void before(){
System.out.println("---------基于XML自定义类方式实现,前置通知:方法执行前---------");
}
public void after(){
System.out.println("---------基于XML自定义类方式实现,后置通知:方法执行前---------");
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="GSF.Example.Service.UserServiceImpl" />
<bean id="customLogClass" class="GSF.Example.Log.CustomLogClass" />
<aop:config>
<aop:aspect ref="customLogClass">
<aop:pointcut id="pointcut" expression="execution(* GSF.Example.Service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut" />
aop:aspect>
aop:config>
beans>
项目名称:
- Java_Framework_Spring
- AOP_3_Xml_Annotation
package GSF.Example.Log;
import org.aspectj.lang.ProceedingJoinPoint;
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 AnnotationClass {
@Before("execution(* GSF.Example.Service.UserServiceImpl.*(..))")
public void before(){
System.out.println("---------基于注解方式实现,前置通知:方法执行前---------");
}
@After("execution(* GSF.Example.Service.UserServiceImpl.*(..))")
public void after(){
System.out.println("---------基于注解方式实现,后置通知:方法执行后---------");
}
@Around("execution(* GSF.Example.Service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable{
System.out.println("环绕通知:环绕前");
System.out.println(jp.getSignature());
// 执行目标方法
Object proceed = jp.proceed();
System.out.println(proceed);
System.out.println("环绕通知:环绕后");
}
}
XML配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="GSF.Example.Service.UserServiceImpl" />
<bean id="annotationPointcut" class="GSF.Example.Log.AnnotationClass" />
<aop:aspectj-autoproxy/>
beans>
特性 | 解释 |
---|---|
原子性(atomicity) | 事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用 |
一致性(consistency) | 一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中 |
隔离性(isolation) | 可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏 |
持久性(durability) | 事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中 |
Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。
Spring支持编程式事务管理和声明式的事务管理
项目名称:
- Java_Framework_Spring
- Transaction_Spring_Mybatis
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
无论使用Spring的哪种事务管理策略(编程式或者声明式)事务管理器都是必须的。
就是Spring的核心事务管理抽象,管理封装了一组独立于技术的方法
<bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="search*" propagation="REQUIRED"/>
<tx:method name="get" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
参数 | 含义 |
---|---|
propagation_requierd(默认) | 如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择 |
propagation_supports | 支持当前事务,如果没有当前事务,就以非事务方法执行 |
propagation_mandatory | 使用当前事务,如果没有当前事务,就抛出异常 |
propagation_required_new | 新建事务,如果当前存在事务,把当前事务挂起 |
propagation_not_supported | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
propagation_never | 以非事务方式执行操作,如果当前事务存在则抛出异常 |
propagation_nested | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作 |
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.kuang.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
aop:config>
<delete id="deleteUser" parameterType="int">
deletes from learn_mybatis.user where id=#{id};
delete>
package GSF.Example.Dao;
import GSF.Example.Pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
import java.util.Map;
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao{
@Override
public User getUserById(int id) {
User user = new User(id, "测试事务", "123235");
UserDao mapper = getSqlSession().getMapper(UserDao.class);
mapper.addUser(user);
mapper.deleteUser(id);
return mapper.getUserById(id);
}
@Override
public int addUser(User user) {
return getSqlSession().getMapper(UserDao.class).addUser(user);
}
@Override
public int deleteUser(int id) {
return getSqlSession().getMapper(UserDao.class).deleteUser(id);
}
}
package GSF.Example.Dao;
import GSF.Example.Pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserTest {
@Test
public void TestTransaction(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserDao userDaoImpl = (UserDao) context.getBean("userDaoImpl");
User userById = userDaoImpl.getUserById(28);
System.out.println(userById);
}
}