Java27:SPRING

一:SPRING介绍

1.spring的概念

广义的Spring:Spring技术栈(全家桶)

广义的Spring泛指以Spring Framework 为基础的Spring技术栈,Spring不在是一个单纯的应用框架,而是逐渐发展成为一个由不同子模块组成的成熟技术,包括:Spring Framework ,SpringMVC, SpringBoot,Spring Cloud,Spring Data,Spring Security等,其中Spring Framewokr

是其他子项目的基础。

狭义的Spring:Spring Framework(基础框架)

狭义的Spring特指 Spring Framework ,通常我们称它为Spring 框架。

Spring Framework 是一个开源的应用程序框架,由SpringSource公司开发,为了解决企业级开发中各种常见的问题而创建。它提供了很多功能,例如:依赖注入(Dependency Injection),面向切面编程(AOP),声明式事务管理(TX)等,其主要目标是使企业级应用程序的开发变得更加简单和快速,并且Spring框架被广泛用于Java企业开发领域。

2.功能模块

Core Container:核心容器,在Spring 环境下使用任何功能都必须基于IOC容器

AOP&Aspects: 面向切片编程

TX:声明式事务管理

Spring MVC  提供了面向Web应用程序的集成功能

3.Spring Framework 主要优势

1.丰富的生态系统:Spring生态系统非常丰富,支持许多模块和库,如Spring Boot ,Spring  Security,Spring Cloud 等等,可以帮助开发人员快速构建可靠性的企业级应用程序

2.模块化的设计:框架组件之间的松耦合和模块化设计使得 Spring Framework 具有良好的可用性,可扩展性和可维护性,开发人员可以轻松选择自己 需要的模块,根据自己的需求进行开发。

3.简化Java开发:Spring Framework简化了Java开发,提供了各种工具和API,可以降低开发复杂度和学习成本。同时Spring Framework 支持各种应用场景,包括Web应用程序,Restful API,消息传递,批处理等

4.不断创新和发展:Spring Framework成为了一个稳定,可靠,且创新的框架,为企业 级Java开发提供了一站式的解决方案。

二:Spring ioc容器

1.组件的概念

组件:可以复用的Java对象,组件一定是对象,对象不一定是组件

2.spring容器的功能

组件对象实例化

组件属性赋值

组件对象之间引用

组件对象存活周期管理

综上所述:Spring充当一个组件容器,创建,管理,存储组件,减少我们编码压力,让我们更加专业进行业务编写

3.ioc容器

Ioc(Inversion of control)控制反转

Ioc 主要针对对象的创建和调用控制而言的,也就是说,当应用程序需要使用一个对象时,不再是应用程序直接创建该对象,而是由Ioc容器创建和管理,即控制由应用程序转移到Ioc容器中,也就是“反转”了控制权,这种方式基本上是通过依赖查找的方式来实现的,即Ioc容器维护着构成应用程序的对象,并负责创建这些对象。

DI(Dependency Injection)依赖注入

DI 是指在组件之间传递依赖关系的过程中,将依赖关系在容器内部进行处理,这样就不必在应用程序代码中硬编码对象之间的依赖关系,实现了对象之间的解耦合。在Spring中,DI是通过XML配置文件或注解方式实现的。它提供了三种形式的依赖注入:构造函数注入,Setter方法注入和接口注入。

4.Ioc实践步骤

步骤1:配置元数据,旣是编写交给SpringIoc 容器管理组件的信息,配置方式有三种:xml方式,注解,配置类

步骤2: 实例化Ioc容器

步骤3:获取Bean(组件)

三.基于xml的ioc配置

1.组件类的xml配置

步骤1.创建父工程:ssm-ioc-part,创建子模块 ssm-ioc-xml01

步骤2:导入依赖(父工程)

<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.0.6</version>
</dependency>
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.10.0</version>
</dependency>i

步骤3:组件类ioc的xml配置

xml新建路径:resources-new-XML configuration File -Spring config(前提要导入spring依赖)

 1)基于无参构造函数:

组件类:

package com.cn.spring.xml.component;public class HappyComponent {public void work(){System.out.println("do work...");}
}

xml配置:

<!--1.使用无参构造函数实例化组件,如何进行ioc配置<bean  一个组件信息 - 一个组件对象id: 组件的唯一标识class:组件类的全限定符-->
<bean id="happyComponent" class="com.cn.spring.xml.component.HappyComponent"></bean>

 2)基于静态工厂类:

组件工厂类:

public class ClientService {private static ClientService clientService =new ClientService();private ClientService(){};public static ClientService createInstance(){return clientService;}
}

     xml配置:

<!-- 2.静态工厂如何进行ioc配置id: 组件的唯一标识class:工厂类的全限定符factory-method:静态工厂方法--><bean id="clientService" class="com.cn.spring.xml.component.ClientService" factory-method="createInstance"></bean>

   3)基于非静态工厂类:

组件类:

public class ClientServiceImpl {
}
package com.cn.spring.xml.component;public class DefaultServiceLocator {private static ClientServiceImpl clientServiceimpl = new ClientServiceImpl();public ClientServiceImpl createClientServiceImplInstance(){return clientServiceimpl;}
}

   xml配置:

<!-- 3 .非静态工厂如何进行ioc配置  -->
<!--  3.1配置 工厂类的组件信息-->
<bean id="defaultServiceLocator" class="com.cn.spring.xml.component.DefaultServiceLocator"></bean>
<!--  3.2通过指定非静态工厂对象和方法名来配置生成的ioc信息-->
<bean id="clientServiceImpl" factory-bean="defaultServiceLocator" factory-method="createClientServiceImplInstance"></bean>

2.组件类DI的xml配置

1)基于构造的函数的依赖注入:

组件类:

public class UserDao {
}
package com.cn.spring.xml.component;public class UserService {private UserDao userDao;private int age;private String name;public UserService(int age,String name,UserDao userDao){this.age=age;this.name=name;this.userDao=userDao;}
}

xml配置:

<!-- 触发构造函数进行注入-->
<bean id="userService" class="com.cn.spring.xml.component.UserService"><!-- 方案1:构造参数的顺序填写值,value直接赋值,ref 直接引用其他bean id--><constructor-arg value="18"></constructor-arg><constructor-arg value="张三"></constructor-arg><constructor-arg ref="userDao"></constructor-arg></bean>
<!-- -->
<bean id="userService1" class="com.cn.spring.xml.component.UserService"><!-- 方案2:构造参数的名称填写,不用考虑顺序 name=构造参数的名字 ,推荐使用--><constructor-arg name ="age" value="18"></constructor-arg><constructor-arg name="name" value="张三"></constructor-arg><constructor-arg name="userDao" ref="userDao"></constructor-arg></bean><bean id="userService2" class="com.cn.spring.xml.component.UserService"><!-- 方案3:构造参数的下角标指定填写,index=构造参数的下角标,从左到右,从0开始--><constructor-arg index="0" value="18"></constructor-arg><constructor-arg index="1" value="张三"></constructor-arg><constructor-arg index="2"  ref="userDao"></constructor-arg></bean>

2)基于setter方法的依赖注入

组件类:

public class MovieFinder {
}
package com.cn.spring.xml.component;public class SimpleMovieListener {private MovieFinder movieFinder;private String movieName;public void setMovieFinder(MovieFinder movieFinder) {this.movieFinder = movieFinder;}public void setMovieName(String movieName) {this.movieName = movieName;}
}

xml配置:

<!-- 触发setter方法进行注入-->
<bean id="movieFinder" class="com.cn.spring.xml.component.MovieFinder"></bean>
<bean id="simpleMovieListener" class="com.cn.spring.xml.component.SimpleMovieListener"><!--name:属性名 setter方法 去掉set和首字母小写的值!调用set方法的名setMovieFinder-> movieFindervalue/ref:直接二选一,value="直接属性值,ref="其他bean的id"--><property name="movieName" value="消失的她"></property><property name="movieFinder" ref="movieFinder"></property></bean>

3.Ioc容器实例化

创建容器,选择合适的容器实现类即可

接口:

BeanFactory

ApplicationContext

实现类:

可以直接通过构造函数实例化!

ClassPathXmlApplicationContext 读取类路径下的xml配置

FileSystemXmlApplicationContext 读取指定文件位置的xml配置方式

AnnotationConfigApplicationContext 读取配置类方式

WebApplicationContext  web项目专属的配置

public class SpringTest {
@Testpublic void test1(){//构造函数(String...配置文件)可以填写一个或多个ClassPathXmlApplicationContext  applicationContext =new ClassPathXmlApplicationContext("spring01.xml");System.out.println(applicationContext);
}
}

4.获取组件类对象

//创建Ioc容器对象ClassPathXmlApplicationContext applicationContext =new ClassPathXmlApplicationContext();applicationContext.setConfigLocation("spring01.xml");applicationContext.refresh();//  方式1:根据直接根据BeanId获取,返回值类型为Object 需强转,不推荐HappyComponent happyComponent = (HappyComponent) applicationContext.getBean("happyComponent");//方式2:根据直接根据BeanId获取,同时指定bean类型的classHappyComponent happyComponent1 = applicationContext.getBean("happyComponent", HappyComponent.class);//方式3:直接根据类型获取//TODO:根据bean类型获取,同一个类型,在ioc容器中只能有一个bean//TODO:如果ioc容器中存在多个同类型的bean,会出现NoUniqueBeanDefinitionException//TODO:ioc 容器配置的一定是实现类,但是可以根据接口类型获取值!getBean(类型)// HappyComponent happyComponent2 = applicationContext.getBean(HappyComponent.class);A happyComponent2 =  applicationContext.getBean(A.class);happyComponent2.work();}

5.组件类的周期对象

我们可以在组件类中定义方法,然后当Ioc容器实例化和销毁组件对象的时候进行调用

这两个方法我们称为周期方法,类似Servlet的init/destory方法,我们可以在周期

方法完成初始化和 释放资源等工作。

组件类的周期方法:

public class JavaBean {public void init(){//周期方法要求:方法名随意,但是要求方法必须是 public void 无形参列表System.out.println("init");}public  void end(){System.out.println("end");}
}

周期方法配置:

   <!-- init-method="初始化方法名"destroy-method=“销毁方法名”--><bean id="javaBean" class="com.cn.spring.xml.component.JavaBean" init-method="init" destroy-method="end"></bean>
</beans>

测试初始方法和销毁方法调用:

@Test
public void test3(){
//实例化调用init方法
ClassPathXmlApplicationContext applicationContext =new ClassPathXmlApplicationContext("spring02.xml");
//调用销毁方法
applicationContext.close();
}

6.综合项目:使用三层架构实现查询 数据库student表的全部信息 

步骤1.数据库准备:

create table students(
    id int PRIMARY KEY ,
    name varchar(50) not null ,
    gender varchar(10) not null,
    age int,
    class varchar(50)
)

insert into students (id,name,gender,age,class)values
(1,'张三','男',20,'高中一班'),
(2,'李四','男',19,'高中一班'),
(3,'王五','女',18,'高中一班'),
(4,'赵六','女',20,'高中一班'),
(5,'刘七','男',19,'高中一班'),
(6,'陈八','男',20,'高中一班'),
(7,'杨九','男',20,'高中一班'),
(8,'吴十','男',20,'高中一班'),
(9,'陈十一','男',20,'高中一班');

步骤2:创建模块并导入jar包

<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.0.6</version></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.10.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.25</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.8</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>6.0.6</version></dependency></dependencies>

步骤3:java 三层模型实现类

pojo类:(setter,getter ,toString 省略)

package com.cn.review.pojo;public class Student {private int id;private String name;private int age;private String gender;private String classes;    

dao层:

public interface StudentDao {public List<Student> qurryall();
}
public class StduentDaoImpl implements StudentDao{private JdbcTemplate jdbcTemplete;public void setJdbcTemplete(JdbcTemplate jdbcTemplete) {this.jdbcTemplete = jdbcTemplete;}@Overridepublic List<Student> qurryall() {String sql="select id,name,age,gender,class as classes from students";List<Student> studentlist = jdbcTemplete.query(sql, new BeanPropertyRowMapper<>(Student.class));return studentlist;}
}

service 层:

public interface StudentService {public List<Student> findall();
}
public class StudentServiceIml implements StudentService{private StudentDao studentDao;public void setStudentDao(StudentDao studentDao) {this.studentDao = studentDao;}@Overridepublic List<Student> findall() {return studentDao.qurryall();}
}

controller层:

public class StudentContoller {private StudentService studentService;public void setStudentService(StudentService studentService) {this.studentService = studentService;}public void findall(){List<Student> findall = studentService.findall();System.out.println(findall);}
}
步骤4:xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--引入外部文件-->
<context:property-placeholder location="classpath:jdbc.properties"/><!--druid 配置--><bean id ="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="url" value="${jdbc.url}"></property><property name="driverClassName" value="${jdbc.driver}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property>
</bean><!--jdbcTemplate 配置--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean><!--StudentDao 配置--><bean id="stduentDao"  class="com.cn.review.dao.StduentDaoImpl"><property name="jdbcTemplete" ref="jdbcTemplate"></property></bean><!--StudentService 配置--><bean id="StudentService" class="com.cn.review.service.StudentServiceIml"><property name="studentDao" ref="stduentDao"></property></bean><!--StudentController配置--><bean id="studentContoller" class="com.cn.review.controller.StudentContoller"><property name="studentService" ref="StudentService"></property></bean>
</beans>

步骤5:测试:

@Test
public void test1(){//创建ioc容器ClassPathXmlApplicationContext applicationContext =new ClassPathXmlApplicationContext("spring02.xml");//获取组件对象StudentContoller contoller = applicationContext.getBean(StudentContoller.class);//调用方法contoller.findall();//关闭容器applicationContext.close();}

四.基于注解的ioc配置

1.组件添加注解

注解的理解:

和xml配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能更

是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作

本质上:所有的一切操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行。

组件添加注解:

Spring 提供了以下多个注解,这些注解可以直接标注在java类上,将它们定义称Spring Bean

  对于Spring 使用ioc容器管理这些组件来说没有区别,也就是语法层面没有区别,

所以@Controller,@Service,@Repostitory 这三个注解只是给开发人员看的,以便于分辨组件的作用。

import org.springframework.stereotype.Component;@Component //bean id="commonComponent" class="CommonComponent"
public class CommonComponent {
}

2.扫描理解

Spring 为了知道程序员在哪些地方标记了什么注解,就需要通过扫描的方式,来进行检测。然后根据注解进行后续操作。

<!--普通包扫描
base-package 指定ioc容器去哪些包下查找注解类-> ioc容器一个包或者多个包 com.cn.review,com.cn.learn  包,包指定包,相当于指定了子包内的所有类-->
<context:component-scan base-package="com.cn.review"></context:component-scan><!-- 指定包,但是排除注解-->
<context:component-scan base-package="com.cn.review"><context:exclude-filter type="annotation" expression="com.cn.review.controller"/>
</context:component-scan>

3.DI注解

@Autowired注解

在成员变量上直接标记@Autowired注解即可,不需要提供setxXX()方法。

Service代码:

@Service
public interface UserService {public void show();
}
@Service
public class UserServiceImpl implements UserService{@Overridepublic void show() {System.out.println("useserivce..show");}
}
@Service
public class NewUserServiceImpl implements UserService{@Overridepublic void show() {System.out.println("NewUserServiceImpl..show");}
}

controller代码:

@Controller
public class UseController {@Autowired
//@Qualifier("newUserServiceImpl")
private UserService userService;public void  show(){userService.show();}
}

测试:

@Test
public void test2(){//创建ioc容器ClassPathXmlApplicationContext applicationContext =new ClassPathXmlApplicationContext("spring02.xml");//获取组件对象UseController useController = applicationContext.getBean(UseController.class);//场景1:ioc容器中只有一个UserService接口对应的实现类//场景2:同一个类型有个对应的组件类@Autowired报错,无法选择//解决1:成员属性名指定@Autowired 多个组件的时候,默认根据成员属性名去查找//解决2:@Qualifier("newUserServiceImpl"),使用该注解指定获取beanid,不能单独使用,必须配合Autowired使用useController.show();
}

4.属性注解

基本数据类型属性赋值可以直接赋值,使用@value注解赋值,用于读取外部配置文件

import org.springframework.stereotype.Component;@Component
public class JavaBean {/** <bean id class*    <property name="name" value="张三"** */private String name="张三";private int age=18;@Value("${jdbc.username:admin}")private String username;@Value("${jdbc.password}")private String password;@Overridepublic String toString() {return "JavaBean{" +"name='" + name + '\'' +", age=" + age +", username='" + username + '\'' +", password='" + password + '\'' +'}';}
}

5.综合项目:使用三层架构实现查询 数据库student表的全部信息(注解方式)

步骤1:数据库准备同xml方式

步骤2:jar包准备同xml方式

步骤3:java 三层模型实现类

controller层:

@Controller
public class StudentController {@Autowiredprivate StudentService studentService;public void findAll(){List<Student> all = studentService.findAll();for(Student stu:all){System.out.println(stu);}}

service层:

@Service
public interface StudentService {public List<Student> findAll();
}
@Service
public class StudentServiceImpl implements  StudentService{
@Autowiredprivate StudentDao studentDao;@Overridepublic List<Student> findAll() {return studentDao.querryAll();}
}

dao层:

@Repository
public interface StudentDao {public List<Student> querryAll();
}
@Repository
public class StudentDaoImpl implements StudentDao{@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic List<Student> querryAll() {String sql ="select id,name,age,gender,class as classes from students";List<Student> studentList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Student.class));return studentList;}
}

步骤4:xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--  引入外部配置文件--><context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder><!--配置扫描包--><context:component-scan base-package="com.cn.review"></context:component-scan><!--配置druid连接池--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="url" value="${jdbc.url}"></property><property name="driverClassName" value="${jdbc.driver}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean><!--配置jdbcTemplate--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property>
</bean>
</beans>

步骤5:测试

@Test
public void test1(){//创建ioc容器ClassPathXmlApplicationContext applicationContext =new ClassPathXmlApplicationContext("spring02.xml");//获取组件对象StudentContoller contoller = applicationContext.getBean(StudentContoller.class);//调用方法contoller.findall();//关闭容器applicationContext.close();}

五.基于配置类的ioc配置

1.配置类和完全注解及Bean注解

1. 包扫描注解配置@ComponentScan("com.cn.review")

2.引用外部的配置文件:@PropertySource("classpath:jdbc.properties")

3.声明第三方依赖的bean组件

步骤1. 添加@Configuration代表我们是配置类

步骤2:实现上面的三个功能注解

<bean- > 一个方法

方法的返回值类型== bean组件的类型或者他的接口和父类

方法的名字=bean id

方法体可以自定义实现过程即可!

最重要的一步:@bean 会真正让配置类的方法创建的主键存储到ico容器

@PropertySource("classpath:jdbc.properties")
@ComponentScan("com.cn.review")
@Configuration
public class StudentConfig {@Value("${jdbc.url}")private String ulr;@Value("${jdbc.driver}")private String driver;@Value("${jdbc.username}")private String username;@Value("${jdbc.password}")private String password;@Beanpublic DruidDataSource dataSource(){DruidDataSource dataSource =new DruidDataSource();dataSource.setUrl(ulr);dataSource.setDriverClassName(driver);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}@Beanpublic JdbcTemplate  jdbcTemplate(DataSource dataSource){JdbcTemplate jdbcTemplate= new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);return  jdbcTemplate;}
}

测试代码:

    @Testpublic void test2(){ApplicationContext applicationContext =new AnnotationConfigApplicationContext(StudentConfig.class);StudentController controller = applicationContext.getBean(StudentController.class);controller.findAll();}
}

2.综合项目:使用三层架构实现查询 数据库student表的全部信息(配置类+完全注解方式)

步骤1:数据库准备同xml方式

步骤2:jar包准备同xml方式

步骤3:java 三层模型实现类

controller层:

@Controller
public class StudentController {@Autowiredprivate StudentService studentService;public void findAll(){List<Student> all = studentService.findAll();for(Student stu:all){System.out.println(stu);}}

service层:

@Service
public interface StudentService {public List<Student> findAll();
}
@Service
public class StudentServiceImpl implements  StudentService{
@Autowiredprivate StudentDao studentDao;@Overridepublic List<Student> findAll() {return studentDao.querryAll();}
}

dao层:

@Repository
public interface StudentDao {public List<Student> querryAll();
}
@Repository
public class StudentDaoImpl implements StudentDao{@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic List<Student> querryAll() {String sql ="select id,name,age,gender,class as classes from students";List<Student> studentList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Student.class));return studentList;}
}

步骤4:配置类:

@PropertySource("classpath:jdbc.properties")
@ComponentScan(basePackages = "review.*")
@Configuration
public class StudentConfig {@Beanpublic DruidDataSource dataSource(@Value("${jdbc.url}") String url, @Value("${jdbc.driver}") String driver, @Value("${jdbc.username}") String username, @Value("${jdbc.password}") String password){DruidDataSource dataSource = new DruidDataSource();dataSource.setUrl(url);dataSource.setDriverClassName(driver);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}@Beanpublic JdbcTemplate jdbcTemplate(DataSource dataSource){JdbcTemplate jdbcTemplate =new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);return jdbcTemplate;}
}

5.测试:

@Testpublic void test1(){//创建ioc容器AnnotationConfigApplicationContext  applicationContext =new AnnotationConfigApplicationContext(StudentConfig.class);// 获取组件StudentController studentController = applicationContext.getBean(StudentController.class);//调用方法studentController.findAll();}

六.AOP编程 

1.面向切面编程思维(Aop)

AOP:Aspect Oriented Programming 面向切面编程

AOP 可以说是面向对象编程的补充和完善,OOP引入封装,继承,多态等概念来建立

一种对象层次结构,用于模拟公告行为的一个集合。不过OOP允许开发者定义纵向的关系,但并适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他的代码,如安全性,异常处理和透明的持续性也都是如此,这种散步在各处的无关的代码称为横切,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP技术恰恰相反,它利用一种称为横切的技术,剖解开封装的对象内部,并将那些影响了各个

类的公共行为封装到一个可重用模块,并将其命名为:Aspect,即切面。所谓 切面 ,简单的说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,

降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

使用AOP ,可以在不修改原来代码的基础上添加新功能。

2.AOP思想 主要的应用场景

a 日志记录 :在系统中记录日志是非常重要的,可以使用AOP来实现日志记录的功能,可以在

方法执行前,执行后或异常抛出时记录日志

b 事务处理:在数据库操作中使用事务可以保证数据的一致性,可以使用AOP来实现事务处理的偶功能,可以在方法前开启事务,在方法执行完毕后提交或回滚事务

c:安全控制:在系统中包含某些需要安全控制的操作,如登录,修改密码,授权等,可以使用AOP 来实现安全控制的功能。可以在方法执行前进行权限判断,如果用户没有权限,则抛出

异常或转向到错误页面,以防止未经授权的访问

d:性能监控:在系统运行过程中,有时需要对某些方法的性能进行监控,以找到系统的瓶颈

并进行优化。可以使用AOP来实现性能监控的功能,可以在方法执行前记录时间戳,在方法执行完毕后计算方法执行时间并输出到日志中。

e:异常处理:系统中可能出现各种异常情况,如空指针异常,数据库连接异常等,可以使用AOP

来实现异常处理的功能,在方法执行过程中,如果出现异常,则进行异常处理(记录日志,发生邮件)

f:缓存控制:在系统中有些数据可以缓存起来以提高访问速度,可以使用AOP来实现缓存控制的功能,可以在方法执行前查询缓存中是否有数据,如果有则返回,否则执行方法并将方法返回值存如缓存中

g;动态代理:AOP的实现方式之一是通过动态代理,可以代理某个类的所有方法,用于实现各种功能。

3.AOP核心名词:

1-横切关注点:从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对 相关方法进行多个不同方面的增强。

AOP 把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证,日志,事务,异常等

AOP的作用在于分离系统中各种关注点,将核心关注点和横切关注点分离开来。

2通知(增强)

每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法叫做通知方法。

前置通知:在被代理的目标方法前执行

返回通知:在被代理的目标方法成功结束后执行

异常通知:在被代理的目标方法异常结束后执行

后置通知:在被代理的目标方法最终结束后执行

环绕通知:使用try catch finally 结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置

3:连接点 joinpoint

    这也是一个纯逻辑概念,不是语法定义的。

指那些被拦截到的点。在Spring中,可以被动态代理拦截目标类的方法。

4.切入点poincut

定位连接点的方式,或者可以理解成被选中的连接点

是一个表达式,比如(execution(*com.spring.service.impl..(..)))符合条件的每个方法都是一个具体的连接点。

5.切面 aspect

 切入点和通知的结合。是一个类。

6.目标 target

 被代理的目标对象

7.代理 proxy

向目标对象应用通知之后创建的代理对象

8 织入 weave

 指把通知应用到目标上,生成代理对象的过程。可以在编译期织入

也可以在运行期织入,Spring采用后者。

3.获取切点信息

定义增强方法,获取目标方法

1.定义方法:增强代码

2:使用注解指定对应的位置

3.配置切点表达式选中方法

4.切面和ioc配置

5.开启aspectj注解的支持

增强方法中获取目标方法信息

1.全部增强方法中,获取目标方法的 信息(方法名,参数,访问修饰符,所属类的信息)

(JoinPoint joinPoint)joinPoint包含目标方法的信息!

2.返回的结果-@AfterReturning

(Object result)result 接收返回结果

@AfterReturning(value = "execution(* com.cn.review.*.*(..))",returning = "形参名")

3.异常信息-@AfterThrowing

(Throwable t) t接收异常信息

@AfterThrowing(value = "execution(* com.cn.review.*.*(..))",throwing = "形参名")
@Component
@Aspect
public class MyAdvice {@Before("execution(* com.cn.review.*.*(..))")public void before(JoinPoint joinPoint){//获取所属方法的类名String simpleName = joinPoint.getTarget().getClass().getSimpleName();//获取方法名String name = joinPoint.getSignature().getName();//获取形参列表Object[] args = joinPoint.getArgs();//获取权限类型int modifiers = joinPoint.getSignature().getModifiers();}@AfterReturning(value = "execution(* com.cn.review.*.*(..))",returning = "result")public void afterReturning(JoinPoint joinPoint,Object result){}@After(value = "execution(* com.cn.review.*.*(..))")public void after(){}@AfterThrowing(value = "execution(* com.cn.review.*.*(..))",throwing = "throwable")public void afterthrowing(JoinPoint joinPoint,Throwable throwable){}
}

4.切点表达式

固定语法 execution(1 2 3.4.5(6))

1.访问修饰符

public /private

2.方法的返回参数类型

String  int void

如果 不考虑访问修饰符和返回值,这两位整合成一起写*

如果要是不考虑,必须两个都不考虑! 不能出现 * String

3.包的位置

具体包:

com.cn.review.service

单层模糊:com.cn.review.*  *单层模糊

多层模糊:com..review   ..任意层的模糊

细节:..不能开头

找所有impl包:com..impl 不能写..impl   写成 *..impl

4. 类的名称

具体:MyCalulator

模糊 *

部分模糊 *Impl

5.方法名 语法和类名一致

6 形参列表

   没有参数()

 有具体参数 (String) (int ) (String int)

模糊参数:(..)有没有参数都可以,有多个也可以

部分模糊 (String ..) String 后面有没有无所谓

(..int )最后一个参数是int

(String..int)

练习:

1.查询某包某类下,访问修饰符是公有,返回值是int的全部方法

execution(public int  xx.xx.jj.*(..))

2.查询某包下类的第一个参数是String的方法

execution(* xx.xx.*.*(Stirng..))

3.查询全部包下,无参数的方法

execution(* *..*.*())

4.查询com包下,以int参数结尾的方法

execution(* com..*.*(.. int))

5 查询指定包下,Servcie开头类的私有返回Service值int的无参数方法

execution(private int  xx.xx.Service*.*())

5.统一切点管理

统一创建一个切点类,单独维护切点表达式,用注解@Pointcut

其他类的切点方法 类的全限定符.方法名()

@Component
public class MyPointCut {@Pointcut("execution(* com.cn..*.*(..))")public void pc(){}}
@Component
@Aspect
public class MyAdvice {//引用切点表达式@Before(value = "com.cn.pointcut.MyPointCut.pc()")public void before(JoinPoint joinPoint){}@AfterReturning(value = "com.cn.pointcut.MyPointCut.pc()",returning = "result")public void afterReturning(JoinPoint joinPoint,Object result){}@After(value = "com.cn.pointcut.MyPointCut.pc()")public void after(){}@AfterThrowing(value = "com.cn.pointcut.MyPointCut.pc()",throwing = "throwable")public void afterthrowing(JoinPoint joinPoint,Throwable throwable){}
}

6.环绕通知及优先级

1.环绕通知需要在通知中,定义目标方法的执行

2.@Order(10):指定一个优先级值,值越小,优先级越高,越高的前置先执行,后置后执行

普通事务增强:

public class TxAdvice {@Before("com.cn.MyPointcut.pc()")public void begin(){System.out.println("开启事务");}@After("com.cn.MyPointcut.pc()")public void commit(){System.out.println("事务提交");}@AfterThrowing("com.cn.MyPointcut.pc()")public void rollback(){System.out.println("事务回滚");}
}

环绕事务增强:

@Component
public class MyAroundAdvice {@Around("com.cn.MyPointcut.pc()")public Object tranaction(ProceedingJoinPoint point) {//保证目标方法执行Object[] args = point.getArgs();Object proceed=null;try {System.out.println("开启事务");args = point.getArgs();proceed = point.proceed(args);System.out.println("事务提交");} catch (Throwable e) {System.out.println("事务回滚");throw new RuntimeException(e);}return proceed;}
}

七.声明式事务

1.声明式事务介绍

声明式事务是指使用注解或者XML配置的方式来控制的事务的提交和回滚。

开发者只需要添加配置即可,具体事务的实现由第三方框架实现,避免我们直接进行事务操作

使用声明式事务可以将事务的控制和业务逻辑分离开来,提高代码的可读性和可维护性

区别:

编程式事务需要手动编写代码来管理事务

而声明式事务可以通过配置文件或注解控制事务

2.Spring 事务管理器

Spring 声明式事务对应依赖

Spring-tx:包含声明式事务实现的基本规范(事务管理器规范接口和事务增强)

Spring-jdbc:包含DataSource 方式事务管理器实现类 DataSourceTransactionManager

 Spring-orm:包含其他持久层框架的事务管理器实现类例如:Hibernate/Jpa等

Spring 声明式事务对应的接口和实现类

 

 3.声明式事务实现项目

步骤1:创建工程并配置依赖:

<dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>6.0.6</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>6.0.6</version>
</dependency>

步骤2: 创建配置类 :

@Configuration
@ComponentScan("com.cn")
@PropertySource("classpath:jdbc.properties")
@EnableTransactionManagement //开启事务注解的支持
public class TxConfig {@Beanpublic DruidDataSource dataSource(@Value("${jdbc.url}") String url, @Value("${jdbc.driver}") String driver, @Value("${jdbc.username}") String username, @Value("${jdbc.password}") String password){DruidDataSource dataSource = new DruidDataSource();dataSource.setUrl(url);dataSource.setDriverClassName(driver);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}@Beanpublic JdbcTemplate jdbcTemplate(DataSource dataSource){JdbcTemplate jdbcTemplate= new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);return  jdbcTemplate;}@Bean //获取事务实现类public TransactionManager  transactionManager(DataSource dataSource){DataSourceTransactionManager dataSourceTransactionManager = new        DataSourceTransactionManager();dataSourceTransactionManager.setDataSource(dataSource);return  dataSourceTransactionManager;}
}

步骤三:service及dao实现:

@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;@Transactional// 添加事务public void changeInfo(){studentDao.updateAgeById(20,1);int i= 1/0;//抛出异常,测试事务studentDao.updateNameById("jerry",1);}}
@Repository
public class StudentDao {@Autowiredprivate JdbcTemplate jdbcTemplate;public void updateNameById(String name,int id){String sql= "update students set name=? where id=?";jdbcTemplate.update(sql,name,id);}public void updateAgeById(int age,int id){String sql= "update students set age=? where id=?";jdbcTemplate.update(sql,age,id);}}

步骤四:测试

public class SpringTxTest {@Testpublic void test() {//创建ioc容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TxConfig.class);//获取组件StudentService studentService = applicationContext.getBean(StudentService.class);//调用方法studentService.changeInfo();}

4.事务的其他属性

@Transactional(readOnly = true,timeout = 3,rollbackFor = Exception.class,propagation = Propagation.REQUIRED)

只读模式:

只读模式可以提升查询事务效率,推荐事务中只有查询代码,使用只读模式

默认: boolean readOnly() default false;

 解释: 一般情况下,都是通过类添加注解,添加事务!

类下所有的方法都有事务!

查询方法可以通过再次添加注解,设置为只读模式,提高效率!

 超时时间:

默认: 永不超时 -1

设置 timeout= 时间   秒数  ,超过时间,就会回滚事务和释放异常

如果类上设置事务属性,方法上也设置了事务属性,方法会覆盖类上的属性

指定异常回滚:

默认情况下,指定发生运行时异常事务才会回滚

我们可以指定Exception 异常 来控制所有异常都回滚

rollbackFor = Exception.class

隔离级别设置:

推荐设置第二个隔离级别:

isolation=Isolation.READ_COMMITED

事务的传播行为:

Propagation propagation() default Propagation.REQUIRED;

REQUIRED(默认值):如果父方法由事务,就加入,如果没有就新建自己独立

REQUIRES_NEW :        不管父方法有没有事务,都新建事务,都是独立的

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/68081.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据标注开源框架 Label Studio

数据标注开源框架 Label Studio Label Studio 是一个开源的、灵活的数据标注平台&#xff0c;旨在帮助开发者和数据科学家轻松创建高质量的训练数据集。它支持多种类型的数据&#xff08;如文本、图像、音频、视频等&#xff09;以及复杂的标注任务&#xff08;如分类、命名实体…

k8s简介,k8s环境搭建

目录 K8s简介环境搭建和准备工作修改主机名&#xff08;所有节点&#xff09;配置静态IP&#xff08;所有节点&#xff09;关闭防火墙和seLinux&#xff0c;清除iptables规则&#xff08;所有节点&#xff09;关闭交换分区&#xff08;所有节点&#xff09;修改/etc/hosts文件&…

单片机内存管理剖析

一、概述 在单片机系统中&#xff0c;内存资源通常是有限的&#xff0c;因此高效的内存管理至关重要。合理地分配和使用内存可以提高系统的性能和稳定性&#xff0c;避免内存泄漏和碎片化问题。单片机的内存主要包括程序存储器&#xff08;如 Flash&#xff09;和数据存储器&a…

1. 握手问题python解法——2024年省赛蓝桥杯真题

原题传送门&#xff1a;1.握手问题 - 蓝桥云课 问题描述 小蓝组织了一场算法交流会议&#xff0c;总共有 50人参加了本次会议。在会议上&#xff0c;大家进行了握手交流。按照惯例他们每个人都要与除自己以外的其他所有人进行一次握手 (且仅有一次)。但有 7 个人&#xff0c;…

15.7k!DISM++一款快捷的系统优化工具

软件介绍 链接 软件介绍 dism是一款由初雨团队开发的win系统优化工具&#xff0c;可当作是微软系统命令行工具dism的GUI版本。可用作系统垃圾清除、启动项管理、程序卸载、驱动管理、系统优化等 该工具个人感觉最重要的就是系统优化选项&#xff0c;它将一些实用、无用或者没…

idea修改模块名导致程序编译出错

本文简单描述分别用Idea菜单、pom.xml文件管理项目模块module 踩过的坑&#xff1a; 通过idea菜单创建模块&#xff0c;并用idea菜单修改模块名&#xff0c;结构程序编译报错&#xff0c;出错的代码莫名奇妙。双击maven弹窗clean时&#xff0c;还是报错。因为模块是新建的&am…

微服务学习-Nacos 注册中心实战

1. 注册中心的设计思路 1.1. 微服务为什么会用到注册中心&#xff1f; 服务与服务之间调用需要有服务发现功能&#xff1b;例如订单服务调用库存服务&#xff0c;库存服务如果有多个&#xff0c;订单服务到底调用那个库存服务呢&#xff08;负载均衡器&#xff09;&#xff0…

vim如何设置自动缩进

:set autoindent 设置自动缩进 :set noautoindent 取消自动缩进 &#xff08;vim如何使设置自动缩进永久生效&#xff1a;vim如何使相关设置永久生效-CSDN博客&#xff09;

景联文科技加入AIIA联盟数据标注分委会

2025年1月16日&#xff0c;中国人工智能产业发展联盟&#xff08;简称AIIA&#xff09;数据委员会数据标注分委会&#xff08;以下简称“分委会”&#xff09;正式成立。景联文科技成为第一批AIIA联盟数据标注分委会委员单位。 数据标注分委会的成立旨在搭建数据标注领域产学研…

ELK环境搭建

文章目录 1.ElasticSearch安装1.安装的版本选择1.SpringBoot版本&#xff1a;2.4.2 找到依赖的spring-data-elasticsearch的版本2.spring-data-elasticsearch版本&#xff1a;4.1.3 找到依赖的elasticsearch版本3.elasticsearch版本&#xff1a;7.9.3 2.安装1.官方文档2.下载压…

Ubuntu20.04 运行 PL-VIO

文章目录 运行后不知为何没有线特征 运行后不知为何没有线特征

C#与AI的共同发展

C#与人工智能(AI)的共同发展反映了编程语言随着技术进步而演变&#xff0c;以适应新的挑战和需要。自2000年微软推出C#以来&#xff0c;这门语言经历了多次迭代&#xff0c;不仅成为了.NET平台的主要编程语言之一&#xff0c;还逐渐成为构建各种类型应用程序的强大工具。随着时…

什么情况该换手机?先看后买不踩坑

现在的智能手机发展的非常快&#xff0c;很多刚出来的1000多元的手机性能已经可以流畅玩游戏、刷视频了&#xff0c;而且基本上也能使用3-5年的时。如果真要把手机用到实在不能用了&#xff0c;可能真的会影响生活体验&#xff0c;还有可能因为电池鼓包等问题发生危险&#xff…

centos操作系统上以service形式运行blackbox_exporter监控网页端口

文章目录 前言一、blackbox_exporter是什么二、使用步骤1.获取二进制文件2.准备部署脚本3.执行命令&#xff0c;进行部署4.prometheus中增加需要监控页面的job信息 三、查看部署结果四、配置到grafana中总结 前言 记录一下centos操作系统上以简单的service形式运行blackbox_ex…

使用github提交Pull Request的完整流程

文章目录 1.Fork仓库2. git clone 仓库在本地3.对项目进行修改开发4.上传项目到远程仓库操作补充1. git add .2. git commit -m "提交信息"3. git pull4. git push总结完整工作流程示例 5.将更新的项目pull Request给原来的仓库主人 当多人进行项目的开发的时候&…

python编写Socket程序

文章目录 编写非阻塞的TCP连接程序编写UDP的socket程序创建连接发送数据 多线程管理udp 编写非阻塞的TCP连接程序 下面代码使用了select模块来管理多个 socket 连接&#xff0c;server_socket.setblocking(0)将服务器 socket 设置为非阻塞模式 &#xff0c;在接收数据时&#…

PHP礼品兑换系统小程序

&#x1f381; 礼品兑换系统&#xff1a;革新企业礼品管理&#xff0c;专属神器来袭&#xff01; &#x1f4bb; 一款专为追求高效与个性化的现代企业量身打造的礼品兑换系统&#xff0c;它基于强大的ThinkPHP框架与前沿的Uniapp技术栈深度融合&#xff0c;不仅完美适配礼品卡…

mapbox加载geojson,鼠标移入改变颜色,设置样式以及vue中的使用

全国地图json数据下载地址 目录 html加载全部代码 方式一&#xff1a;使用html方式加载geojson 1. 初始化地图 2. 加载geojson数据 设置geojson图层样式&#xff0c;设置type加载数据类型 设置线条 鼠标移入改变颜色&#xff0c;设置图层属性&#xff0c;此处是fill-extru…

Langchain+讯飞星火大模型Spark Max调用

1、安装langchain #安装langchain环境 pip install langchain0.3.3 openai -i https://mirrors.aliyun.com/pypi/simple #灵积模型服务 pip install dashscope -i https://mirrors.aliyun.com/pypi/simple #安装第三方集成,就是各种大语言模型 pip install langchain-comm…

【kong gateway】5分钟快速上手kong gateway

kong gateway的请求响应示意图 安装 下载对应的docker 镜像 可以直接使用docker pull命令拉取&#xff0c;也可以从以下地址下载&#xff1a;kong gateway 3.9.0.0 docker 镜像 https://download.csdn.net/download/zhangshenglu1/90307400&#xff0c; postgres-13.tar http…